11using NUnit . Framework ;
22using Python . Runtime ;
33using System ;
4+ using System . Collections . Generic ;
5+ using System . ComponentModel ;
6+ using System . Diagnostics ;
47using System . Linq ;
58using System . Threading ;
69
@@ -25,10 +28,22 @@ public void TearDown()
2528 PythonEngine . Shutdown ( ) ;
2629 }
2730
28- private static void FullGCCollect ( )
31+ private static bool FullGCCollect ( )
2932 {
3033 GC . Collect ( GC . MaxGeneration , GCCollectionMode . Forced ) ;
31- GC . WaitForPendingFinalizers ( ) ;
34+ try
35+ {
36+ return GC . WaitForFullGCComplete ( ) == GCNotificationStatus . Succeeded ;
37+ }
38+ catch ( NotImplementedException )
39+ {
40+ // Some clr runtime didn't implement GC.WaitForFullGCComplete yet.
41+ return false ;
42+ }
43+ finally
44+ {
45+ GC . WaitForPendingFinalizers ( ) ;
46+ }
3247 }
3348
3449 [ Test ]
@@ -87,12 +102,34 @@ public void CollectBasicObject()
87102 Assert . GreaterOrEqual ( objectCount , 1 ) ;
88103 }
89104
90- private static void MakeAGarbage ( out WeakReference shortWeak , out WeakReference longWeak )
105+ [ Test ]
106+ [ Ignore ( "Ignore temporarily" ) ]
107+ public void CollectOnShutdown ( )
108+ {
109+ IntPtr op = MakeAGarbage ( out var shortWeak , out var longWeak ) ;
110+ int hash = shortWeak . Target . GetHashCode ( ) ;
111+ List < WeakReference > garbage ;
112+ if ( ! FullGCCollect ( ) )
113+ {
114+ Assert . IsTrue ( WaitForCollected ( op , hash , 10000 ) ) ;
115+ }
116+ Assert . IsFalse ( shortWeak . IsAlive ) ;
117+ garbage = Finalizer . Instance . GetCollectedObjects ( ) ;
118+ Assert . IsNotEmpty ( garbage , "The garbage object should be collected" ) ;
119+ Assert . IsTrue ( garbage . Any ( r => ReferenceEquals ( r . Target , longWeak . Target ) ) ,
120+ "Garbage should contains the collected object" ) ;
121+
122+ PythonEngine . Shutdown ( ) ;
123+ garbage = Finalizer . Instance . GetCollectedObjects ( ) ;
124+ Assert . IsEmpty ( garbage ) ;
125+ }
126+
127+ private static IntPtr MakeAGarbage ( out WeakReference shortWeak , out WeakReference longWeak )
91128 {
92129 PyLong obj = new PyLong ( 1024 ) ;
93130 shortWeak = new WeakReference ( obj ) ;
94131 longWeak = new WeakReference ( obj , true ) ;
95- obj = null ;
132+ return obj . Handle ;
96133 }
97134
98135 private static long CompareWithFinalizerOn ( PyObject pyCollect , bool enbale )
@@ -249,5 +286,28 @@ private static IntPtr CreateStringGarbage()
249286 return s1 . Handle ;
250287 }
251288
289+ private static bool WaitForCollected ( IntPtr op , int hash , int milliseconds )
290+ {
291+ var stopwatch = Stopwatch . StartNew ( ) ;
292+ do
293+ {
294+ var garbage = Finalizer . Instance . GetCollectedObjects ( ) ;
295+ foreach ( var item in garbage )
296+ {
297+ // The validation is not 100% precise,
298+ // but it's rare that two conditions satisfied but they're still not the same object.
299+ if ( item . Target . GetHashCode ( ) != hash )
300+ {
301+ continue ;
302+ }
303+ var obj = ( IPyDisposable ) item . Target ;
304+ if ( obj . GetTrackedHandles ( ) . Contains ( op ) )
305+ {
306+ return true ;
307+ }
308+ }
309+ } while ( stopwatch . ElapsedMilliseconds < milliseconds ) ;
310+ return false ;
311+ }
252312 }
253313}
0 commit comments