Skip to content

Commit 885cd3e

Browse files
Fix memory leak in finalizer
- `finalizer` was leaking `_pendingArgs` (global memory) when the call to `Py_AddPendingCall` was unsuccessful. - Changing `lock` for `Monitor.TryEnter` at `AddPendingCollect()` so threads don't block each other.
1 parent f83c884 commit 885cd3e

File tree

1 file changed

+30
-20
lines changed

1 file changed

+30
-20
lines changed

src/runtime/finalizer.cs

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ struct PendingArgs
4141
private ConcurrentQueue<IPyDisposable> _objQueue = new ConcurrentQueue<IPyDisposable>();
4242
private bool _pending = false;
4343
private readonly object _collectingLock = new object();
44-
private IntPtr _pendingArgs;
44+
private IntPtr _pendingArgs = IntPtr.Zero;
4545

4646
#region FINALIZER_CHECK
4747

@@ -128,7 +128,7 @@ internal void AddFinalizedObject(IPyDisposable obj)
128128
_objQueue.Enqueue(obj);
129129
}
130130
GC.ReRegisterForFinalize(obj);
131-
if (_objQueue.Count >= Threshold)
131+
if (!_pending && _objQueue.Count >= Threshold)
132132
{
133133
AddPendingCollect();
134134
}
@@ -164,26 +164,28 @@ internal static void Shutdown()
164164

165165
private void AddPendingCollect()
166166
{
167-
if (_pending)
167+
if(Monitor.TryEnter(_collectingLock))
168168
{
169-
return;
170-
}
171-
lock (_collectingLock)
172-
{
173-
if (_pending)
169+
try
174170
{
175-
return;
171+
if (!_pending)
172+
{
173+
_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)
179+
{
180+
// Full queue, append next time
181+
FreePendingArgs();
182+
_pending = false;
183+
}
184+
}
176185
}
177-
_pending = true;
178-
var args = new PendingArgs() { cancelled = false };
179-
IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PendingArgs)));
180-
Marshal.StructureToPtr(args, p, false);
181-
_pendingArgs = p;
182-
IntPtr func = Marshal.GetFunctionPointerForDelegate(_collectAction);
183-
if (Runtime.Py_AddPendingCall(func, p) != 0)
186+
finally
184187
{
185-
// Full queue, append next time
186-
_pending = false;
188+
Monitor.Exit(_collectingLock);
187189
}
188190
}
189191
}
@@ -205,8 +207,8 @@ private static int OnPendingCollect(IntPtr arg)
205207
}
206208
finally
207209
{
210+
Instance.FreePendingArgs();
208211
Instance.ResetPending();
209-
Marshal.FreeHGlobal(arg);
210212
}
211213
return 0;
212214
}
@@ -244,12 +246,20 @@ private void DisposeAll()
244246
}
245247
}
246248

249+
private void FreePendingArgs()
250+
{
251+
if (_pendingArgs != IntPtr.Zero)
252+
{
253+
Marshal.FreeHGlobal(_pendingArgs);
254+
_pendingArgs = IntPtr.Zero;
255+
}
256+
}
257+
247258
private void ResetPending()
248259
{
249260
lock (_collectingLock)
250261
{
251262
_pending = false;
252-
_pendingArgs = IntPtr.Zero;
253263
}
254264
}
255265

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy