Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add error handler
Synchronized fixed
  • Loading branch information
amos402 authored and Martin-Molinero committed Mar 27, 2019
commit 3a9be30e31f1e343a1e6283fb85fd55a06a2d679
54 changes: 54 additions & 0 deletions src/embed_tests/TestFinalizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,5 +151,59 @@ public void SimpleTestMemory()
Finalizer.Instance.Enable = oldState;
}
}

class MyPyObject : PyObject
{
public MyPyObject(IntPtr op) : base(op)
{
}

protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
GC.SuppressFinalize(this);
throw new Exception("MyPyObject");
}
internal static void CreateMyPyObject(IntPtr op)
{
Runtime.Runtime.XIncref(op);
new MyPyObject(op);
}
}

[Test]
public void ErrorHandling()
{
bool called = false;
EventHandler<Finalizer.ErrorArgs> handleFunc = (sender, args) =>
{
called = true;
Assert.AreEqual(args.Error.Message, "MyPyObject");
};
Finalizer.Instance.Threshold = 1;
Finalizer.Instance.ErrorHandler += handleFunc;
try
{
WeakReference shortWeak;
WeakReference longWeak;
{
MakeAGarbage(out shortWeak, out longWeak);
var obj = (PyLong)longWeak.Target;
IntPtr handle = obj.Handle;
shortWeak = null;
longWeak = null;
MyPyObject.CreateMyPyObject(handle);
obj.Dispose();
obj = null;
}
FullGCCollect();
Finalizer.Instance.Collect();
Assert.IsTrue(called);
}
finally
{
Finalizer.Instance.ErrorHandler -= handleFunc;
}
}
}
}
41 changes: 31 additions & 10 deletions src/runtime/finalizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,21 @@ public class CollectArgs : EventArgs
public int ObjectCount { get; set; }
}

public class ErrorArgs : EventArgs
{
public Exception Error { get; set; }
}

public static readonly Finalizer Instance = new Finalizer();

public event EventHandler<CollectArgs> CollectOnce;
public event EventHandler<ErrorArgs> ErrorHandler;

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

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int PedingCall(IntPtr arg);
private readonly PedingCall _collectAction;
private delegate int PendingCall(IntPtr arg);
private readonly PendingCall _collectAction;

private bool _pending = false;
private readonly object _collectingLock = new object();
Expand Down Expand Up @@ -87,24 +93,27 @@ internal static void Shutdown()
}
Instance.DisposeAll();
Instance.CallPendingFinalizers();
Runtime.PyErr_Clear();
}

private void AddPendingCollect()
{
if (_pending)
{
return;
}
lock (_collectingLock)
{
if (_pending)
{
return;
}
_pending = true;
}
IntPtr func = Marshal.GetFunctionPointerForDelegate(_collectAction);
if (Runtime.Py_AddPendingCall(func, IntPtr.Zero) != 0)
{
// Full queue, append next time
_pending = false;
IntPtr func = Marshal.GetFunctionPointerForDelegate(_collectAction);
if (Runtime.Py_AddPendingCall(func, IntPtr.Zero) != 0)
{
// Full queue, append next time
_pending = false;
}
}
}

Expand All @@ -124,7 +133,19 @@ private void DisposeAll()
IDisposable obj;
while (_objQueue.TryDequeue(out obj))
{
obj.Dispose();
try
{
obj.Dispose();
Runtime.CheckExceptionOccurred();
}
catch (Exception e)
{
// We should not bother the main thread
ErrorHandler?.Invoke(this, new ErrorArgs()
{
Error = e
});
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ public class Runtime
internal static bool IsPython2 = pyversionnumber < 30;
internal static bool IsPython3 = pyversionnumber >= 30;

public static int MainManagedThreadId { get; internal set; }
public static int MainManagedThreadId { get; private set; }

/// <summary>
/// Encoding to use to convert Unicode to/from Managed to Native
Expand Down Expand Up @@ -362,10 +362,10 @@ internal static void Initialize()

internal static void Shutdown()
{
Finalizer.Shutdown();
AssemblyManager.Shutdown();
Exceptions.Shutdown();
ImportHook.Shutdown();
Finalizer.Shutdown();
Py_Finalize();
}

Expand Down