1
1
using System ;
2
2
using System . Collections . Concurrent ;
3
3
using System . Collections . Generic ;
4
- using System . Diagnostics ;
5
4
using System . Linq ;
6
- using System . Runtime . InteropServices ;
7
5
using System . Threading ;
6
+ using System . Threading . Tasks ;
8
7
9
8
namespace Python . Runtime
10
9
{
@@ -28,20 +27,10 @@ public class ErrorArgs : EventArgs
28
27
public int Threshold { get ; set ; }
29
28
public bool Enable { get ; set ; }
30
29
31
- [ StructLayout ( LayoutKind . Sequential , CharSet = CharSet . Ansi ) ]
32
- struct PendingArgs
33
- {
34
- public bool cancelled ;
35
- }
36
-
37
- [ UnmanagedFunctionPointer ( CallingConvention . Cdecl ) ]
38
- private delegate int PendingCall ( IntPtr arg ) ;
39
- private readonly PendingCall _collectAction ;
40
-
41
30
private ConcurrentQueue < IPyDisposable > _objQueue = new ConcurrentQueue < IPyDisposable > ( ) ;
42
31
private bool _pending = false ;
43
32
private readonly object _collectingLock = new object ( ) ;
44
- private IntPtr _pendingArgs = IntPtr . Zero ;
33
+ private Task _finalizerTask ;
45
34
46
35
#region FINALIZER_CHECK
47
36
@@ -84,23 +73,26 @@ private Finalizer()
84
73
{
85
74
Enable = true ;
86
75
Threshold = 200 ;
87
- _collectAction = OnPendingCollect ;
88
76
}
89
77
90
- public void CallPendingFinalizers ( )
78
+ public bool CallPendingFinalizers ( )
91
79
{
92
- if ( Thread . CurrentThread . ManagedThreadId != Runtime . MainManagedThreadId )
80
+ if ( Instance . _finalizerTask != null
81
+ && ! Instance . _finalizerTask . IsCompleted )
93
82
{
94
- throw new Exception ( "PendingCall should execute in main Python thread" ) ;
83
+ var ts = PythonEngine . BeginAllowThreads ( ) ;
84
+ Instance . _finalizerTask . Wait ( ) ;
85
+ PythonEngine . EndAllowThreads ( ts ) ;
86
+ return true ;
95
87
}
96
- Runtime . Py_MakePendingCalls ( ) ;
88
+ return false ;
97
89
}
98
90
99
91
public void Collect ( )
100
92
{
101
- using ( var gilState = new Py . GILState ( ) )
93
+ if ( ! Instance . CallPendingFinalizers ( ) )
102
94
{
103
- DisposeAll ( ) ;
95
+ Instance . DisposeAll ( ) ;
104
96
}
105
97
}
106
98
@@ -141,25 +133,10 @@ internal static void Shutdown()
141
133
Instance . _objQueue = new ConcurrentQueue < IPyDisposable > ( ) ;
142
134
return ;
143
135
}
144
- Instance . DisposeAll ( ) ;
145
- if ( Thread . CurrentThread . ManagedThreadId != Runtime . MainManagedThreadId )
136
+ if ( ! Instance . CallPendingFinalizers ( ) )
146
137
{
147
- if ( Instance . _pendingArgs == IntPtr . Zero )
148
- {
149
- Instance . ResetPending ( ) ;
150
- return ;
151
- }
152
- // Not in main thread just cancel the pending operation to avoid error in different domain
153
- // It will make a memory leak
154
- unsafe
155
- {
156
- PendingArgs * args = ( PendingArgs * ) Instance . _pendingArgs ;
157
- args ->cancelled = true ;
158
- }
159
- Instance . ResetPending ( ) ;
160
- return ;
138
+ Instance . DisposeAll ( ) ;
161
139
}
162
- Instance . CallPendingFinalizers ( ) ;
163
140
}
164
141
165
142
private void AddPendingCollect ( )
@@ -171,16 +148,14 @@ private void AddPendingCollect()
171
148
if ( ! _pending )
172
149
{
173
150
_pending = true ;
174
- var args = new PendingArgs { cancelled = false } ;
175
- _pendingArgs = Marshal . AllocHGlobal ( Marshal . SizeOf ( typeof ( PendingArgs ) ) ) ;
176
- Marshal . StructureToPtr ( args , _pendingArgs , false ) ;
177
- IntPtr func = Marshal . GetFunctionPointerForDelegate ( _collectAction ) ;
178
- if ( Runtime . Py_AddPendingCall ( func , _pendingArgs ) != 0 )
151
+ // should already be complete but just in case
152
+ _finalizerTask ? . Wait ( ) ;
153
+
154
+ _finalizerTask = Task . Factory . StartNew ( ( ) =>
179
155
{
180
- // Full queue, append next time
181
- FreePendingArgs ( ) ;
156
+ Instance . DisposeAll ( ) ;
182
157
_pending = false ;
183
- }
158
+ } ) ;
184
159
}
185
160
}
186
161
finally
@@ -190,29 +165,6 @@ private void AddPendingCollect()
190
165
}
191
166
}
192
167
193
- private static int OnPendingCollect ( IntPtr arg )
194
- {
195
- Debug . Assert ( arg == Instance . _pendingArgs ) ;
196
- try
197
- {
198
- unsafe
199
- {
200
- PendingArgs * pendingArgs = ( PendingArgs * ) arg ;
201
- if ( pendingArgs ->cancelled )
202
- {
203
- return 0 ;
204
- }
205
- }
206
- Instance . DisposeAll ( ) ;
207
- }
208
- finally
209
- {
210
- Instance . FreePendingArgs ( ) ;
211
- Instance . ResetPending ( ) ;
212
- }
213
- return 0 ;
214
- }
215
-
216
168
private void DisposeAll ( )
217
169
{
218
170
CollectOnce ? . Invoke ( this , new CollectArgs ( )
@@ -223,46 +175,32 @@ private void DisposeAll()
223
175
lock ( _queueLock )
224
176
#endif
225
177
{
178
+ using ( Py . GIL ( ) )
179
+ {
226
180
#if FINALIZER_CHECK
227
- ValidateRefCount ( ) ;
181
+ ValidateRefCount ( ) ;
228
182
#endif
229
- IPyDisposable obj ;
230
- while ( _objQueue . TryDequeue ( out obj ) )
231
- {
232
- try
183
+ IPyDisposable obj ;
184
+ while ( _objQueue . TryDequeue ( out obj ) )
233
185
{
234
- obj . Dispose ( ) ;
235
- Runtime . CheckExceptionOccurred ( ) ;
236
- }
237
- catch ( Exception e )
238
- {
239
- // We should not bother the main thread
240
- ErrorHandler ? . Invoke ( this , new ErrorArgs ( )
186
+ try
241
187
{
242
- Error = e
243
- } ) ;
188
+ obj . Dispose ( ) ;
189
+ Runtime . CheckExceptionOccurred ( ) ;
190
+ }
191
+ catch ( Exception e )
192
+ {
193
+ // We should not bother the main thread
194
+ ErrorHandler ? . Invoke ( this , new ErrorArgs ( )
195
+ {
196
+ Error = e
197
+ } ) ;
198
+ }
244
199
}
245
200
}
246
201
}
247
202
}
248
203
249
- private void FreePendingArgs ( )
250
- {
251
- if ( _pendingArgs != IntPtr . Zero )
252
- {
253
- Marshal . FreeHGlobal ( _pendingArgs ) ;
254
- _pendingArgs = IntPtr . Zero ;
255
- }
256
- }
257
-
258
- private void ResetPending ( )
259
- {
260
- lock ( _collectingLock )
261
- {
262
- _pending = false ;
263
- }
264
- }
265
-
266
204
#if FINALIZER_CHECK
267
205
private void ValidateRefCount ( )
268
206
{
0 commit comments