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,20 @@ 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 void Collect ( bool forceDispose = true )
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 ) ;
95
86
}
96
- Runtime . Py_MakePendingCalls ( ) ;
97
- }
98
-
99
- public void Collect ( )
100
- {
101
- using ( var gilState = new Py . GILState ( ) )
87
+ else if ( forceDispose )
102
88
{
103
- DisposeAll ( ) ;
89
+ Instance . DisposeAll ( ) ;
104
90
}
105
91
}
106
92
@@ -141,25 +127,7 @@ internal static void Shutdown()
141
127
Instance . _objQueue = new ConcurrentQueue < IPyDisposable > ( ) ;
142
128
return ;
143
129
}
144
- Instance . DisposeAll ( ) ;
145
- if ( Thread . CurrentThread . ManagedThreadId != Runtime . MainManagedThreadId )
146
- {
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 ;
161
- }
162
- Instance . CallPendingFinalizers ( ) ;
130
+ Instance . Collect ( forceDispose : true ) ;
163
131
}
164
132
165
133
private void AddPendingCollect ( )
@@ -171,16 +139,17 @@ private void AddPendingCollect()
171
139
if ( ! _pending )
172
140
{
173
141
_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 )
142
+ // should already be complete but just in case
143
+ _finalizerTask ? . Wait ( ) ;
144
+
145
+ _finalizerTask = Task . Factory . StartNew ( ( ) =>
179
146
{
180
- // Full queue, append next time
181
- FreePendingArgs ( ) ;
182
- _pending = false ;
183
- }
147
+ using ( Py . GIL ( ) )
148
+ {
149
+ Instance . DisposeAll ( ) ;
150
+ _pending = false ;
151
+ }
152
+ } ) ;
184
153
}
185
154
}
186
155
finally
@@ -190,29 +159,6 @@ private void AddPendingCollect()
190
159
}
191
160
}
192
161
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
162
private void DisposeAll ( )
217
163
{
218
164
CollectOnce ? . Invoke ( this , new CollectArgs ( )
@@ -246,23 +192,6 @@ private void DisposeAll()
246
192
}
247
193
}
248
194
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
195
#if FINALIZER_CHECK
267
196
private void ValidateRefCount ( )
268
197
{
0 commit comments