Skip to content

Commit f071c55

Browse files
committed
Add error handler
Synchronized fixed
1 parent 0967a12 commit f071c55

File tree

3 files changed

+87
-12
lines changed

3 files changed

+87
-12
lines changed

src/embed_tests/TestFinalizer.cs

+54
Original file line numberDiff line numberDiff line change
@@ -151,5 +151,59 @@ public void SimpleTestMemory()
151151
Finalizer.Instance.Enable = oldState;
152152
}
153153
}
154+
155+
class MyPyObject : PyObject
156+
{
157+
public MyPyObject(IntPtr op) : base(op)
158+
{
159+
}
160+
161+
protected override void Dispose(bool disposing)
162+
{
163+
base.Dispose(disposing);
164+
GC.SuppressFinalize(this);
165+
throw new Exception("MyPyObject");
166+
}
167+
internal static void CreateMyPyObject(IntPtr op)
168+
{
169+
Runtime.Runtime.XIncref(op);
170+
new MyPyObject(op);
171+
}
172+
}
173+
174+
[Test]
175+
public void ErrorHandling()
176+
{
177+
bool called = false;
178+
EventHandler<Finalizer.ErrorArgs> handleFunc = (sender, args) =>
179+
{
180+
called = true;
181+
Assert.AreEqual(args.Error.Message, "MyPyObject");
182+
};
183+
Finalizer.Instance.Threshold = 1;
184+
Finalizer.Instance.ErrorHandler += handleFunc;
185+
try
186+
{
187+
WeakReference shortWeak;
188+
WeakReference longWeak;
189+
{
190+
MakeAGarbage(out shortWeak, out longWeak);
191+
var obj = (PyLong)longWeak.Target;
192+
IntPtr handle = obj.Handle;
193+
shortWeak = null;
194+
longWeak = null;
195+
MyPyObject.CreateMyPyObject(handle);
196+
obj.Dispose();
197+
obj = null;
198+
}
199+
FullGCCollect();
200+
Finalizer.Instance.Collect();
201+
Assert.IsTrue(called);
202+
}
203+
finally
204+
{
205+
Finalizer.Instance.ErrorHandler -= handleFunc;
206+
}
207+
}
154208
}
155209
}

src/runtime/finalizer.cs

+31-10
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,21 @@ public class CollectArgs : EventArgs
1414
public int ObjectCount { get; set; }
1515
}
1616

17+
public class ErrorArgs : EventArgs
18+
{
19+
public Exception Error { get; set; }
20+
}
21+
1722
public static readonly Finalizer Instance = new Finalizer();
1823

1924
public event EventHandler<CollectArgs> CollectOnce;
25+
public event EventHandler<ErrorArgs> ErrorHandler;
2026

2127
private ConcurrentQueue<IDisposable> _objQueue = new ConcurrentQueue<IDisposable>();
2228

2329
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
24-
private delegate int PedingCall(IntPtr arg);
25-
private readonly PedingCall _collectAction;
30+
private delegate int PendingCall(IntPtr arg);
31+
private readonly PendingCall _collectAction;
2632

2733
private bool _pending = false;
2834
private readonly object _collectingLock = new object();
@@ -87,24 +93,27 @@ internal static void Shutdown()
8793
}
8894
Instance.DisposeAll();
8995
Instance.CallPendingFinalizers();
90-
Runtime.PyErr_Clear();
9196
}
9297

9398
private void AddPendingCollect()
9499
{
100+
if (_pending)
101+
{
102+
return;
103+
}
95104
lock (_collectingLock)
96105
{
97106
if (_pending)
98107
{
99108
return;
100109
}
101110
_pending = true;
102-
}
103-
IntPtr func = Marshal.GetFunctionPointerForDelegate(_collectAction);
104-
if (Runtime.Py_AddPendingCall(func, IntPtr.Zero) != 0)
105-
{
106-
// Full queue, append next time
107-
_pending = false;
111+
IntPtr func = Marshal.GetFunctionPointerForDelegate(_collectAction);
112+
if (Runtime.Py_AddPendingCall(func, IntPtr.Zero) != 0)
113+
{
114+
// Full queue, append next time
115+
_pending = false;
116+
}
108117
}
109118
}
110119

@@ -124,7 +133,19 @@ private void DisposeAll()
124133
IDisposable obj;
125134
while (_objQueue.TryDequeue(out obj))
126135
{
127-
obj.Dispose();
136+
try
137+
{
138+
obj.Dispose();
139+
Runtime.CheckExceptionOccurred();
140+
}
141+
catch (Exception e)
142+
{
143+
// We should not bother the main thread
144+
ErrorHandler?.Invoke(this, new ErrorArgs()
145+
{
146+
Error = e
147+
});
148+
}
128149
}
129150
}
130151
}

src/runtime/runtime.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ public class Runtime
199199
internal static bool IsPython2 = pyversionnumber < 30;
200200
internal static bool IsPython3 = pyversionnumber >= 30;
201201

202-
public static int MainManagedThreadId { get; internal set; }
202+
public static int MainManagedThreadId { get; private set; }
203203

204204
/// <summary>
205205
/// Encoding to use to convert Unicode to/from Managed to Native
@@ -354,10 +354,10 @@ internal static void Initialize()
354354

355355
internal static void Shutdown()
356356
{
357-
Finalizer.Shutdown();
358357
AssemblyManager.Shutdown();
359358
Exceptions.Shutdown();
360359
ImportHook.Shutdown();
360+
Finalizer.Shutdown();
361361
Py_Finalize();
362362
}
363363

0 commit comments

Comments
 (0)