Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8083f3b
Finalizer for PyObject
amos402 Jun 23, 2018
af33e74
Avoid test interdependency
amos402 Jun 24, 2018
6d9f897
Add source to .csproj
amos402 Jun 24, 2018
7140fd0
Make sure recover the environment
amos402 Jun 24, 2018
f66697d
Add StackTrace of C# exception
amos402 Jun 24, 2018
799d37e
Clean up the test and interface
amos402 Jun 24, 2018
cb55163
Update CHANGELOG.md
amos402 Jun 24, 2018
bfc0392
Mono doesn't have GC.WaitForFullGCComplete
amos402 Jun 24, 2018
59b614d
Fixed PythonException leak
amos402 Jun 25, 2018
569cd94
Merge branch 'master' into pyobject-finalizer
den-run-ai Jul 5, 2018
f4f5032
Fixed nPython.exe crash on Shutdown
amos402 Jul 10, 2018
0967a12
Merge branch 'master' into pyobject-finalizer
filmor Jul 17, 2018
f071c55
Add error handler
amos402 Aug 4, 2018
cfda491
Merge branch 'master' into pyobject-finalizer
amos402 Aug 4, 2018
f6c6e42
Merge branch 'master' into pyobject-finalizer
filmor Aug 30, 2018
0d96641
Make collect callback without JIT
amos402 Sep 6, 2018
34713f7
Merge branch 'master' into pyobject-finalizer
filmor Oct 16, 2018
b4e30ac
Merge branch 'master' into pyobject-finalizer
filmor Oct 17, 2018
f836ffa
Merge remote-tracking branch 'remotes/upstream/master' into pyobject-…
amos402 Oct 20, 2018
a4bb82d
Add pending marker
amos402 Oct 23, 2018
21da86d
Merge branch 'master' into pyobject-finalizer
amos402 Oct 24, 2018
5254c65
Remove PYTHONMALLOC setting
amos402 Oct 25, 2018
916e85e
Merge remote-tracking branch 'remotes/upstream/master' into pyobject-…
amos402 Nov 22, 2018
eee3683
Fix ref count error
amos402 Nov 22, 2018
cee8e17
Add ref count check for helping discover the bugs of decref too much
amos402 Nov 22, 2018
247e2d9
Fix ref count error
amos402 Nov 25, 2018
90c67ca
typo error
amos402 Nov 26, 2018
6d68d70
Merge branch 'master' into pyobject-finalizer
filmor Mar 6, 2019
4eff81e
Merge branch 'master' into pyobject-finalizer
filmor Mar 29, 2019
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 committed Aug 4, 2018
commit f071c557d52c5c92dd5fddec96bc327d0a8ee1f1
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>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have a few places that fail in presence of multiple appdomains, this would too, I guess. Can you add a comment on this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What failures will have occurred, can you give me some examples?


[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;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this optimisation really needed? If not, I'd rather like the code to be obviously safe.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a simple double-check, make it check without lock at first.

lock (_collectingLock)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lock should probably encompass the whole function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why it needs to be lock whole function, the flag already set.
I can't image any situations that Py_AddPendingCall will be called in multiple threads on same time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't follow, either you expect multiple concurrent executions, then the lock needs to surround the whole function, or you don't, then you don't need any locking.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yes, thank you for you reminder, I neglect that there is another store at line 114.

{
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 @@ -354,10 +354,10 @@ internal static void Initialize()

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

Expand Down