@@ -27,7 +27,7 @@ public class ErrorArgs : EventArgs
27
27
public int Threshold { get ; set ; }
28
28
public bool Enable { get ; set ; }
29
29
30
- private ConcurrentQueue < IPyDisposable > _objQueue = new ConcurrentQueue < IPyDisposable > ( ) ;
30
+ private ConcurrentQueue < IntPtr > _objQueue = new ConcurrentQueue < IntPtr > ( ) ;
31
31
private int _throttled ;
32
32
33
33
#region FINALIZER_CHECK
@@ -42,7 +42,7 @@ public class ErrorArgs : EventArgs
42
42
public class IncorrectFinalizeArgs : EventArgs
43
43
{
44
44
public IntPtr Handle { get ; internal set ; }
45
- public ICollection < IPyDisposable > ImpactedObjects { get ; internal set ; }
45
+ public ICollection < IntPtr > ImpactedObjects { get ; internal set ; }
46
46
}
47
47
48
48
public class IncorrectRefCountException : Exception
@@ -73,8 +73,6 @@ private Finalizer()
73
73
Threshold = 200 ;
74
74
}
75
75
76
- [ Obsolete ( "forceDispose parameter is unused. All objects are disposed regardless." ) ]
77
- public void Collect ( bool forceDispose ) => this . DisposeAll ( ) ;
78
76
public void Collect ( ) => this . DisposeAll ( ) ;
79
77
80
78
internal void ThrottledCollect ( )
@@ -85,14 +83,14 @@ internal void ThrottledCollect()
85
83
this . Collect ( ) ;
86
84
}
87
85
88
- public List < WeakReference > GetCollectedObjects ( )
86
+ internal List < IntPtr > GetCollectedObjects ( )
89
87
{
90
- return _objQueue . Select ( T => new WeakReference ( T ) ) . ToList ( ) ;
88
+ return _objQueue . ToList ( ) ;
91
89
}
92
90
93
- internal void AddFinalizedObject ( IPyDisposable obj )
91
+ internal void AddFinalizedObject ( ref IntPtr obj )
94
92
{
95
- if ( ! Enable )
93
+ if ( ! Enable || obj == IntPtr . Zero )
96
94
{
97
95
return ;
98
96
}
@@ -103,6 +101,7 @@ internal void AddFinalizedObject(IPyDisposable obj)
103
101
{
104
102
this . _objQueue . Enqueue ( obj ) ;
105
103
}
104
+ obj = IntPtr . Zero ;
106
105
}
107
106
108
107
internal static void Shutdown ( )
@@ -123,29 +122,44 @@ private void DisposeAll()
123
122
#if FINALIZER_CHECK
124
123
ValidateRefCount ( ) ;
125
124
#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
128
129
{
129
- try
130
- {
131
- obj . Dispose ( ) ;
132
- }
133
- catch ( Exception e )
130
+ while ( ! _objQueue . IsEmpty )
134
131
{
135
- var handler = ErrorHandler ;
136
- if ( handler is null )
132
+ if ( ! _objQueue . TryDequeue ( out obj ) )
133
+ continue ;
134
+
135
+ Runtime . XDecref ( obj ) ;
136
+ try
137
137
{
138
- throw new FinalizationException (
139
- "Python object finalization failed" ,
140
- disposable : obj , innerException : e ) ;
138
+ Runtime . CheckExceptionOccurred ( ) ;
141
139
}
142
-
143
- handler . Invoke ( this , new ErrorArgs ( )
140
+ catch ( Exception e )
144
141
{
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
+ }
147
155
}
148
156
}
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
+ }
149
163
}
150
164
}
151
165
@@ -158,33 +172,26 @@ private void ValidateRefCount()
158
172
}
159
173
var counter = new Dictionary < IntPtr , long > ( ) ;
160
174
var holdRefs = new Dictionary < IntPtr , long > ( ) ;
161
- var indexer = new Dictionary < IntPtr , List < IPyDisposable > > ( ) ;
175
+ var indexer = new Dictionary < IntPtr , List < IntPtr > > ( ) ;
162
176
foreach ( var obj in _objQueue )
163
177
{
164
- IntPtr [ ] handles = obj . GetTrackedHandles ( ) ;
165
- foreach ( var handle in handles )
178
+ var handle = obj ;
179
+ if ( ! counter . ContainsKey ( handle ) )
166
180
{
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 ) ;
187
193
}
194
+ objs . Add ( obj ) ;
188
195
}
189
196
foreach ( var pair in counter )
190
197
{
@@ -227,12 +234,13 @@ private void ValidateRefCount()
227
234
228
235
public class FinalizationException : Exception
229
236
{
230
- public IPyDisposable Disposable { get ; }
237
+ public IntPtr PythonObject { get ; }
231
238
232
- public FinalizationException ( string message , IPyDisposable disposable , Exception innerException )
239
+ public FinalizationException ( string message , IntPtr disposable , Exception innerException )
233
240
: base ( message , innerException )
234
241
{
235
- this . Disposable = disposable ?? throw new ArgumentNullException ( nameof ( disposable ) ) ;
242
+ if ( disposable == IntPtr . Zero ) throw new ArgumentNullException ( nameof ( disposable ) ) ;
243
+ this . PythonObject = disposable ;
236
244
}
237
245
}
238
246
}
0 commit comments