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
Next Next commit
Track Runtime run number. Assert, that PyObjects are only disposed in…
… the same run they were created in.
  • Loading branch information
lostmsu committed Nov 12, 2021
commit 4f657d46b34f14cd4a5ff43087ceedde54618663
8 changes: 4 additions & 4 deletions src/runtime/pybuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,10 @@ private void Dispose(bool disposing)

~PyBuffer()
{
if (disposedValue)
{
return;
}
Debug.Assert(!disposedValue);

_exporter.CheckRun();

Finalizer.Instance.AddFinalizedObject(ref _view.obj);
}

Expand Down
44 changes: 28 additions & 16 deletions src/runtime/pyobject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public partial class PyObject : DynamicObject, IDisposable
#endif

protected internal IntPtr obj = IntPtr.Zero;
readonly int run = Runtime.GetRun();

public static PyObject None => new PyObject(new BorrowedReference(Runtime.PyNone));
internal BorrowedReference Reference => new BorrowedReference(this.obj);
Expand Down Expand Up @@ -95,11 +96,15 @@ internal PyObject(in StolenReference reference)
// when the managed wrapper is garbage-collected.
~PyObject()
{
if (obj == IntPtr.Zero)
{
return;
}
Debug.Assert(obj != IntPtr.Zero);

#if TRACE_ALLOC
CheckRun();
#endif

Finalizer.Instance.AddFinalizedObject(ref obj);

Dispose(false);
}


Expand Down Expand Up @@ -167,17 +172,6 @@ public object AsManagedObject(Type t)

internal bool IsDisposed => obj == IntPtr.Zero;

/// <summary>
/// Dispose Method
/// </summary>
/// <remarks>
/// The Dispose method provides a way to explicitly release the
/// Python object represented by a PyObject instance. It is a good
/// idea to call Dispose on PyObjects that wrap resources that are
/// limited or need strict lifetime control. Otherwise, references
/// to Python objects will not be released until a managed garbage
/// collection occurs.
/// </remarks>
protected virtual void Dispose(bool disposing)
{
if (this.obj == IntPtr.Zero)
Expand All @@ -188,6 +182,8 @@ protected virtual void Dispose(bool disposing)
if (Runtime.Py_IsInitialized() == 0)
throw new InvalidOperationException("Python runtime must be initialized");

CheckRun();

if (!Runtime.IsFinalizing)
{
long refcount = Runtime.Refcount(this.obj);
Expand Down Expand Up @@ -221,10 +217,26 @@ protected virtual void Dispose(bool disposing)
this.obj = IntPtr.Zero;
}

/// <summary>
/// The Dispose method provides a way to explicitly release the
/// Python object represented by a PyObject instance. It is a good
/// idea to call Dispose on PyObjects that wrap resources that are
/// limited or need strict lifetime control. Otherwise, references
/// to Python objects will not be released until a managed garbage
/// collection occurs.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
Dispose(true);
}

internal void CheckRun()
{
if (run != Runtime.GetRun())
throw new InvalidOperationException(
"PythonEngine was shut down after this object was created." +
" It is an error to attempt to dispose or to continue using it.");
}

internal BorrowedReference GetPythonTypeReference()
Expand Down
30 changes: 29 additions & 1 deletion src/runtime/runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,15 @@ internal static Version PyVersion
}
}

/// <summary>
static int run = 0;

internal static int GetRun()
{
int runNumber = run;
Debug.Assert(runNumber > 0, "This must only be called after Runtime is initialized at least once");
return runNumber;
}

/// Initialize the runtime...
/// </summary>
/// <remarks>Always call this method from the Main thread. After the
Expand All @@ -110,6 +118,9 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
if (!interpreterAlreadyInitialized)
{
Py_InitializeEx(initSigs ? 1 : 0);

NewRun();

if (PyEval_ThreadsInitialized() == 0)
{
PyEval_InitThreads();
Expand All @@ -130,6 +141,16 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
{
PyGILState_Ensure();
}

BorrowedReference pyRun = PySys_GetObject("__pynet_run__");
Comment thread
filmor marked this conversation as resolved.
Outdated
if (pyRun != null)
{
run = checked((int)PyLong_AsSignedSize_t(pyRun));
}
else
{
NewRun();
}
}
MainManagedThreadId = Thread.CurrentThread.ManagedThreadId;

Expand Down Expand Up @@ -175,6 +196,13 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
inspect = GetModuleLazy("inspect");
}

static void NewRun()
{
run++;
using var pyRun = PyLong_FromLongLong(run);
PySys_SetObject("__pynet_run__", pyRun);
}

private static void InitPyMembers()
{
IntPtr op;
Expand Down