8000 Add pending marker · losttech/pythonnet@994449e · GitHub
[go: up one dir, main page]

Skip to content

Commit 994449e

Browse files
amos402Martin-Molinero
authored andcommitted
Add pending marker
1 parent eb569f2 commit 994449e

File tree

1 file changed

+60
-6
lines changed

1 file changed

+60
-6
lines changed

src/runtime/finalizer.cs

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Concurrent;
33
using System.Collections.Generic;
4+
using System.Diagnostics;
45
using System.Linq;
56
using System.Runtime.InteropServices;
67
using System.Threading;
@@ -24,16 +25,23 @@ public class ErrorArgs : EventArgs
2425
public event EventHandler<CollectArgs> CollectOnce;
2526
public event EventHandler<ErrorArgs> ErrorHandler;
2627

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+
}
2836

2937
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
3038
private delegate int PendingCall(IntPtr arg);
3139
private readonly PendingCall _collectAction;
3240

41+
private ConcurrentQueue<IDisposable> _objQueue = new ConcurrentQueue<IDisposable>();
3342
private bool _pending = false;
3443
private readonly object _collectingLock = new object();
35-
public int Threshold { get; set; }
36-
public bool Enable { get; set; }
44+
private IntPtr _pendingArgs;
3745

3846
private Finalizer()
3947
{
@@ -92,6 +100,23 @@ internal static void Shutdown()
92100
return;
93101
}
94102
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+
}
95120
Instance.CallPendingFinalizers();
96121
}
97122

@@ -108,8 +133,12 @@ private void AddPendingCollect()
108133
return;
109134
}
110135
_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;
111140
IntPtr func = Marshal.GetFunctionPointerForDelegate(_collectAction);
112-
if (Runtime.Py_AddPendingCall(func, IntPtr.Zero) != 0)
141+
if (Runtime.Py_AddPendingCall(func, p) != 0)
113142
{
114143
// Full queue, append next time
115144
_pending = false;
@@ -119,8 +148,24 @@ private void AddPendingCollect()
119148

120149
private static int OnPendingCollect(IntPtr arg)
121150
{
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+
}
124169
return 0;
125170
}
126171

@@ -148,5 +193,14 @@ private void DisposeAll()
148193
}
149194
}
150195
}
196+
197+
private void ResetPending()
198+
{
199+
lock (_collectingLock)
200+
{
201+
_pending = false;
202+
_pendingArgs = IntPtr.Zero;
203+
}
204+
}
151205
}
152206
}

0 commit comments

Comments
 (0)
0