@@ -27,7 +27,7 @@ public class ErrorArgs : EventArgs
2727 public int Threshold { get ; set ; }
2828 public bool Enable { get ; set ; }
2929
30- private ConcurrentQueue < IPyDisposable > _objQueue = new ConcurrentQueue < IPyDisposable > ( ) ;
30+ private ConcurrentQueue < IntPtr > _objQueue = new ConcurrentQueue < IntPtr > ( ) ;
3131 private int _throttled ;
3232
3333 #region FINALIZER_CHECK
@@ -42,7 +42,7 @@ public class ErrorArgs : EventArgs
4242 public class IncorrectFinalizeArgs : EventArgs
4343 {
4444 public IntPtr Handle { get ; internal set ; }
45- public ICollection < IPyDisposable > ImpactedObjects { get ; internal set ; }
45+ public ICollection < IntPtr > ImpactedObjects { get ; internal set ; }
4646 }
4747
4848 public class IncorrectRefCountException : Exception
@@ -73,8 +73,6 @@ private Finalizer()
7373 Threshold = 200 ;
7474 }
7575
76- [ Obsolete ( "forceDispose parameter is unused. All objects are disposed regardless." ) ]
77- public void Collect ( bool forceDispose ) => this . DisposeAll ( ) ;
7876 public void Collect ( ) => this . DisposeAll ( ) ;
7977
8078 internal void ThrottledCollect ( )
@@ -85,14 +83,14 @@ internal void ThrottledCollect()
8583 this . Collect ( ) ;
8684 }
8785
88- public List < WeakReference > GetCollectedObjects ( )
86+ internal List < IntPtr > GetCollectedObjects ( )
8987 {
90- return _objQueue . Select ( T => new WeakReference ( T ) ) . ToList ( ) ;
88+ return _objQueue . ToList ( ) ;
9189 }
9290
93- internal void AddFinalizedObject ( IPyDisposable obj )
91+ internal void AddFinalizedObject ( ref IntPtr obj )
9492 {
95- if ( ! Enable )
93+ if ( ! Enable || obj == IntPtr . Zero )
9694 {
9795 return ;
9896 }
@@ -103,6 +101,7 @@ internal void AddFinalizedObject(IPyDisposable obj)
103101 {
104102 this . _objQueue . Enqueue ( obj ) ;
105103 }
104+ obj = IntPtr . Zero ;
106105 }
107106
108107 internal static void Shutdown ( )
@@ -123,29 +122,44 @@ private void DisposeAll()
123122#if FINALIZER_CHECK
124123 ValidateRefCount ( ) ;
125124#endif
126- IPyDisposable obj ;
127- while ( _objQueue . TryDequeue ( out obj ) )
125+ IntPtr obj ;
126+ Runtime . PyErr_Fetch ( out var errType , out var errVal , out var traceback ) ;
127+
128+ try
128129 {
129- try
130- {
131- obj . Dispose ( ) ;
132- }
133- catch ( Exception e )
130+ while ( ! _objQueue . IsEmpty )
134131 {
135- var handler = ErrorHandler ;
136- if ( handler is null )
132+ if ( ! _objQueue . TryDequeue ( out obj ) )
133+ continue ;
134+
135+ Runtime . XDecref ( obj ) ;
136+ try
137137 {
138- throw new FinalizationException (
139- "Python object finalization failed" ,
140- disposable : obj , innerException : e ) ;
138+ Runtime . CheckExceptionOccurred ( ) ;
141139 }
142-
143- handler . Invoke ( this , new ErrorArgs ( )
140+ catch ( Exception e )
144141 {
145- Error = e
146- } ) ;
142+ var handler = ErrorHandler ;
143+ if ( handler is null )
144+ {
145+ throw new FinalizationException (
146+ "Python object finalization failed" ,
147+ disposable : obj , innerException : e ) ;
148+ }
149+
150+ handler . Invoke ( this , new ErrorArgs ( )
151+ {
152+ Error = e
153+ } ) ;
154+ }
147155 }
148156 }
157+ finally
158+ {
159+ // Python requires finalizers to preserve exception:
160+ // https://docs.python.org/3/extending/newtypes.html#finalization-and-de-allocation
161+ Runtime . PyErr_Restore ( errType , errVal , traceback ) ;
162+ }
149163 }
150164 }
151165
@@ -158,33 +172,26 @@ private void ValidateRefCount()
158172 }
159173 var counter = new Dictionary < IntPtr , long > ( ) ;
160174 var holdRefs = new Dictionary < IntPtr , long > ( ) ;
161- var indexer = new Dictionary < IntPtr , List < IPyDisposable > > ( ) ;
175+ var indexer = new Dictionary < IntPtr , List < IntPtr > > ( ) ;
162176 foreach ( var obj in _objQueue )
163177 {
164- IntPtr [ ] handles = obj . GetTrackedHandles ( ) ;
165- foreach ( var handle in handles )
178+ var handle = obj ;
179+ if ( ! counter . ContainsKey ( handle ) )
166180 {
167- if ( handle == IntPtr . Zero )
168- {
169- continue ;
170- }
171- if ( ! counter . ContainsKey ( handle ) )
172- {
173- counter [ handle ] = 0 ;
174- }
175- counter [ handle ] ++ ;
176- if ( ! holdRefs . ContainsKey ( handle ) )
177- {
178- holdRefs [ handle ] = Runtime . Refcount ( handle ) ;
179- }
180- List < IPyDisposable > objs ;
181- if ( ! indexer . TryGetValue ( handle , out objs ) )
182- {
183- objs = new List < IPyDisposable > ( ) ;
184- indexer . Add ( handle , objs ) ;
185- }
186- objs . Add ( obj ) ;
181+ counter [ handle ] = 0 ;
182+ }
183+ counter [ handle ] ++ ;
184+ if ( ! holdRefs . ContainsKey ( handle ) )
185+ {
186+ holdRefs [ handle ] = Runtime . Refcount ( handle ) ;
187+ }
188+ List < IntPtr > objs ;
189+ if ( ! indexer . TryGetValue ( handle , out objs ) )
190+ {
191+ objs = new List < IntPtr > ( ) ;
192+ indexer . Add ( handle , objs ) ;
187193 }
194+ objs . Add ( obj ) ;
188195 }
189196 foreach ( var pair in counter )
190197 {
@@ -227,12 +234,13 @@ private void ValidateRefCount()
227234
228235 public class FinalizationException : Exception
229236 {
230- public IPyDisposable Disposable { get ; }
237+ public IntPtr PythonObject { get ; }
231238
232- public FinalizationException ( string message , IPyDisposable disposable , Exception innerException )
239+ public FinalizationException ( string message , IntPtr disposable , Exception innerException )
233240 : base ( message , innerException )
234241 {
235- this . Disposable = disposable ?? throw new ArgumentNullException ( nameof ( disposable ) ) ;
242+ if ( disposable == IntPtr . Zero ) throw new ArgumentNullException ( nameof ( disposable ) ) ;
243+ this . PythonObject = disposable ;
236244 }
237245 }
238246}
0 commit comments