2
2
using System . Collections . Concurrent ;
3
3
using System . Collections . Generic ;
4
4
using System . ComponentModel ;
5
+ using System . Diagnostics ;
5
6
using System . Linq ;
6
7
using System . Threading ;
7
8
using System . Threading . Tasks ;
@@ -30,10 +31,12 @@ public class ErrorArgs : EventArgs
30
31
[ DefaultValue ( DefaultThreshold ) ]
31
32
public int Threshold { get ; set ; } = DefaultThreshold ;
32
33
34
+ volatile bool started ;
35
+
33
36
[ DefaultValue ( true ) ]
34
37
public bool Enable { get ; set ; } = true ;
35
38
36
- private ConcurrentQueue < IntPtr > _objQueue = new ConcurrentQueue < IntPtr > ( ) ;
39
+ private ConcurrentQueue < PendingFinalization > _objQueue = new ( ) ;
37
40
private int _throttled ;
38
41
39
42
#region FINALIZER_CHECK
@@ -79,6 +82,8 @@ internal IncorrectRefCountException(IntPtr ptr)
79
82
80
83
internal void ThrottledCollect ( )
81
84
{
85
+ if ( ! started ) throw new InvalidOperationException ( $ "{ nameof ( PythonEngine ) } is not initialized") ;
86
+
82
87
_throttled = unchecked ( this . _throttled + 1 ) ;
83
88
if ( ! Enable || _throttled < Threshold ) return ;
84
89
_throttled = 0 ;
@@ -87,12 +92,13 @@ internal void ThrottledCollect()
87
92
88
93
internal List < IntPtr > GetCollectedObjects ( )
89
94
{
90
- return _objQueue . ToList ( ) ;
95
+ return _objQueue . Select ( o => o . PyObj ) . ToList ( ) ;
91
96
}
92
97
93
- internal void AddFinalizedObject ( ref IntPtr obj )
98
+ internal void AddFinalizedObject ( ref IntPtr obj , int run )
94
99
{
95
- if ( ! Enable || obj == IntPtr . Zero )
100
+ Debug . Assert ( obj != IntPtr . Zero ) ;
101
+ if ( ! Enable )
96
102
{
97
103
return ;
98
104
}
@@ -101,14 +107,20 @@ internal void AddFinalizedObject(ref IntPtr obj)
101
107
lock ( _queueLock )
102
108
#endif
103
109
{
104
- this . _objQueue . Enqueue ( obj ) ;
110
+ this . _objQueue . Enqueue ( new PendingFinalization { PyObj = obj , RuntimeRun = run } ) ;
105
111
}
106
112
obj = IntPtr . Zero ;
107
113
}
108
114
115
+ internal static void Initialize ( )
116
+ {
117
+ Instance . started = true ;
118
+ }
119
+
109
120
internal static void Shutdown ( )
110
121
{
111
122
Instance . DisposeAll ( ) ;
123
+ Instance . started = false ;
112
124
}
113
125
114
126
private void DisposeAll ( )
@@ -124,36 +136,31 @@ private void DisposeAll()
124
136
#if FINALIZER_CHECK
125
137
ValidateRefCount ( ) ;
126
138
#endif
127
- IntPtr obj ;
128
139
Runtime . PyErr_Fetch ( out var errType , out var errVal , out var traceback ) ;
129
140
141
+ int run = Runtime . GetRun ( ) ;
142
+
130
143
try
131
144
{
132
145
while ( ! _objQueue . IsEmpty )
133
146
{
134
- if ( ! _objQueue . TryDequeue ( out obj ) )
147
+ if ( ! _objQueue . TryDequeue ( out var obj ) )
148
+ continue ;
149
+
150
+ if ( obj . RuntimeRun != run )
151
+ {
152
+ HandleFinalizationException ( obj . PyObj , new RuntimeShutdownException ( obj . PyObj ) ) ;
135
153
continue ;
154
+ }
136
155
137
- Runtime . XDecref ( obj ) ;
156
+ Runtime . XDecref ( obj . PyObj ) ;
138
157
try
139
158
{
140
159
Runtime . CheckExceptionOccurred ( ) ;
141
160
}
142
161
catch ( Exception e )
143
162
{
144
- var errorArgs = new ErrorArgs
145
- {
146
- Error = e ,
147
- } ;
148
-
149
- ErrorHandler ? . Invoke ( this , errorArgs ) ;
150
-
151
- if ( ! errorArgs . Handled )
152
- {
153
- throw new FinalizationException (
154
- "Python object finalization failed" ,
155
- disposable : obj , innerException : e ) ;
156
- }
163
+ HandleFinalizationException ( obj . PyObj , e ) ;
157
164
}
158
165
}
159
166
}
@@ -166,6 +173,23 @@ private void DisposeAll()
166
173
}
167
174
}
168
175
176
+ void HandleFinalizationException ( IntPtr obj , Exception cause )
177
+ {
178
+ var errorArgs = new ErrorArgs
179
+ {
180
+ Error = cause ,
181
+ } ;
182
+
183
+ ErrorHandler ? . Invoke ( this , errorArgs ) ;
184
+
185
+ if ( ! errorArgs . Handled )
186
+ {
187
+ throw new FinalizationException (
188
+ "Python object finalization failed" ,
189
+ disposable : obj , innerException : cause ) ;
190
+ }
191
+ }
192
+
169
193
#if FINALIZER_CHECK
170
194
private void ValidateRefCount ( )
171
195
{
@@ -235,6 +259,12 @@ private void ValidateRefCount()
235
259
#endif
236
260
}
237
261
262
+ struct PendingFinalization
263
+ {
264
+ public IntPtr PyObj ;
265
+ public int RuntimeRun ;
266
+ }
267
+
238
268
public class FinalizationException : Exception
239
269
{
240
270
public IntPtr Handle { get ; }
@@ -259,5 +289,21 @@ public FinalizationException(string message, IntPtr disposable, Exception innerE
259
289
if ( disposable == IntPtr . Zero ) throw new ArgumentNullException ( nameof ( disposable ) ) ;
260
290
this . Handle = disposable ;
261
291
}
292
+
293
+ protected FinalizationException ( string message , IntPtr disposable )
294
+ : base ( message )
295
+ {
296
+ if ( disposable == IntPtr . Zero ) throw new ArgumentNullException ( nameof ( disposable ) ) ;
297
+ this . Handle = disposable ;
298
+ }
299
+ }
300
+
301
+ public class RuntimeShutdownException : FinalizationException
302
+ {
303
+ public RuntimeShutdownException ( IntPtr disposable )
304
+ : base ( "Python runtime was shut down after this object was created." +
305
+ " It is an error to attempt to dispose or to continue using it even after restarting the runtime." , disposable )
306
+ {
307
+ }
262
308
}
263
309
}
0 commit comments