1
1
using System ;
2
2
using System . Collections . Concurrent ;
3
3
using System . Collections . Generic ;
4
+ using System . Diagnostics ;
4
5
using System . Linq ;
5
6
using System . Runtime . InteropServices ;
6
7
using System . Threading ;
@@ -24,16 +25,23 @@ public class ErrorArgs : EventArgs
24
25
public event EventHandler < CollectArgs > CollectOnce ;
25
26
public event EventHandler < ErrorArgs > ErrorHandler ;
26
27
27
- private ConcurrentQueue < IDisposable > _objQueue = new ConcurrentQueue < IDisposable > ( ) ;
28
+ public int Threshold { get ; set ; }
29
+ public bool Enable { get ; set ; }
30
+
31
+ [ StructLayout ( LayoutKind . Sequential , CharSet = CharSet . Ansi ) ]
32
+ struct PendingArgs
33
+ {
34
+ public bool cancelled ;
35
+ }
28
36
29
37
[ UnmanagedFunctionPointer ( CallingConvention . Cdecl ) ]
30
38
private delegate int PendingCall ( IntPtr arg ) ;
31
39
private readonly PendingCall _collectAction ;
32
40
41
+ private ConcurrentQueue < IDisposable > _objQueue = new ConcurrentQueue < IDisposable > ( ) ;
33
42
private bool _pending = false ;
34
43
private readonly object _collectingLock = new object ( ) ;
35
- public int Threshold { get ; set ; }
36
- public bool Enable { get ; set ; }
44
+ private IntPtr _pendingArgs ;
37
45
38
46
private Finalizer ( )
39
47
{
@@ -92,6 +100,23 @@ internal static void Shutdown()
92
100
return ;
93
101
}
94
102
Instance . DisposeAll ( ) ;
103
+ if ( Thread . CurrentThread . ManagedThreadId != Runtime . MainManagedThreadId )
104
+ {
105
+ if ( Instance . _pendingArgs == IntPtr . Zero )
106
+ {
107
+ Instance . ResetPending ( ) ;
108
+ return ;
109
+ }
110
+ // Not in main thread just cancel the pending operation to avoid error in different domain
111
+ // It will make a memory leak
112
+ unsafe
113
+ {
114
+ PendingArgs * args = ( PendingArgs * ) Instance . _pendingArgs ;
115
+ args ->cancelled = true ;
116
+ }
117
+ Instance . ResetPending ( ) ;
118
+ return ;
119
+ }
95
120
Instance . CallPendingFinalizers ( ) ;
96
121
}
97
122
@@ -108,8 +133,12 @@ private void AddPendingCollect()
108
133
return ;
109
134
}
110
135
_pending = true ;
136
+ var args = new PendingArgs ( ) { cancelled = false } ;
137
+ IntPtr p = Marshal . AllocHGlobal ( Marshal . SizeOf ( typeof ( PendingArgs ) ) ) ;
138
+ Marshal . StructureToPtr ( args , p , false ) ;
139
+ _pendingArgs = p ;
111
140
IntPtr func = Marshal . GetFunctionPointerForDelegate ( _collectAction ) ;
112
- if ( Runtime . Py_AddPendingCall ( func , IntPtr . Zero ) != 0 )
141
+ if ( Runtime . Py_AddPendingCall ( func , p ) != 0 )
113
142
{
114
143
// Full queue, append next time
115
144
_pending = false ;
@@ -119,8 +148,24 @@ private void AddPendingCollect()
119
148
120
149
private static int OnPendingCollect ( IntPtr arg )
121
150
{
122
- Instance . DisposeAll ( ) ;
123
- Instance . _pending = false ;
151
+ Debug . Assert ( arg == Instance . _pendingArgs ) ;
152
+ try
153
+ {
154
+ unsafe
155
+ {
156
+ PendingArgs * pendingArgs = ( PendingArgs * ) arg ;
157
+ if ( pendingArgs ->cancelled )
158
+ {
159
+ return 0 ;
160
+ }
161
+ }
162
+ Instance . DisposeAll ( ) ;
163
+ }
164
+ finally
165
+ {
166
+ Instance . ResetPending ( ) ;
167
+ Marshal . FreeHGlobal ( arg ) ;
168
+ }
124
169
return 0 ;
125
170
}
126
171
@@ -148,5 +193,14 @@ private void DisposeAll()
148
193
}
149
194
}
150
195
}
196
+
197
+ private void ResetPending ( )
198
+ {
199
+ lock ( _collectingLock )
200
+ {
201
+ _pending = false ;
202
+ _pendingArgs = IntPtr . Zero ;
203
+ }
204
+ }
151
205
}
152
206
}
0 commit comments