Skip to content

Track Runtime run number #1074

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Nov 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 6 additions & 1 deletion src/embed_tests/Codecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -421,13 +421,18 @@ public PyObject TryEncode(object value)
}
}

class InstancelessExceptionDecoder : IPyObjectDecoder
class InstancelessExceptionDecoder : IPyObjectDecoder, IDisposable
{
readonly PyObject PyErr = Py.Import("clr.interop").GetAttr("PyErr");

public bool CanDecode(PyType objectType, Type targetType)
=> PythonReferenceComparer.Instance.Equals(PyErr, objectType);

public void Dispose()
{
PyErr.Dispose();
}

public bool TryDecode<T>(PyObject pyObj, out T value)
{
if (pyObj.HasAttr("value"))
Expand Down
16 changes: 16 additions & 0 deletions src/embed_tests/GlobalTestsSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ namespace Python.EmbeddingTest
[SetUpFixture]
public partial class GlobalTestsSetup
{
[OneTimeSetUp]
public void GlobalSetup()
{
Finalizer.Instance.ErrorHandler += FinalizerErrorHandler;
}

private void FinalizerErrorHandler(object sender, Finalizer.ErrorArgs e)
{
if (e.Error is RuntimeShutdownException)
{
// allow objects to leak after the python runtime run
// they were created in is gone
e.Handled = true;
}
}

[OneTimeTearDown]
public void FinalCleanup()
{
Expand Down
1 change: 1 addition & 0 deletions src/embed_tests/Inheritance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public void SetUp()
[OneTimeTearDown]
public void Dispose()
{
ExtraBaseTypeProvider.ExtraBase.Dispose();
PythonEngine.Shutdown();
}

Expand Down
110 changes: 0 additions & 110 deletions src/embed_tests/TestDomainReload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,116 +179,6 @@ public static void CrossDomainObject()

#endregion

#region Tempary tests

// https://github.com/pythonnet/pythonnet/pull/1074#issuecomment-596139665
[Test]
public void CrossReleaseBuiltinType()
{
void ExecTest()
{
try
{
PythonEngine.Initialize();
var numRef = CreateNumReference();
Assert.True(numRef.IsAlive);
PythonEngine.Shutdown(); // <- "run" 1 ends
PythonEngine.Initialize(); // <- "run" 2 starts

GC.Collect();
GC.WaitForPendingFinalizers(); // <- this will put former `num` into Finalizer queue
Finalizer.Instance.Collect();
// ^- this will call PyObject.Dispose, which will call XDecref on `num.Handle`,
// but Python interpreter from "run" 1 is long gone, so it will corrupt memory instead.
Assert.False(numRef.IsAlive);
}
finally
{
PythonEngine.Shutdown();
}
}

var errorArgs = new List<Finalizer.ErrorArgs>();
void ErrorHandler(object sender, Finalizer.ErrorArgs e)
{
errorArgs.Add(e);
}
Finalizer.Instance.ErrorHandler += ErrorHandler;
try
{
for (int i = 0; i < 10; i++)
{
ExecTest();
}
}
finally
{
Finalizer.Instance.ErrorHandler -= ErrorHandler;
}
Assert.AreEqual(errorArgs.Count, 0);
}

[Test]
public void CrossReleaseCustomType()
{
void ExecTest()
{
try
{
PythonEngine.Initialize();
var objRef = CreateConcreateObject();
Assert.True(objRef.IsAlive);
PythonEngine.Shutdown(); // <- "run" 1 ends
PythonEngine.Initialize(); // <- "run" 2 starts
GC.Collect();
GC.WaitForPendingFinalizers();
Finalizer.Instance.Collect();
Assert.False(objRef.IsAlive);
}
finally
{
PythonEngine.Shutdown();
}
}

var errorArgs = new List<Finalizer.ErrorArgs>();
void ErrorHandler(object sender, Finalizer.ErrorArgs e)
{
errorArgs.Add(e);
}
Finalizer.Instance.ErrorHandler += ErrorHandler;
try
{
for (int i = 0; i < 10; i++)
{
ExecTest();
}
}
finally
{
Finalizer.Instance.ErrorHandler -= ErrorHandler;
}
Assert.AreEqual(errorArgs.Count, 0);
}

private static WeakReference CreateNumReference()
{
var num = 3216757418.ToPython();
Assert.AreEqual(num.Refcount, 1);
WeakReference numRef = new WeakReference(num, false);
return numRef;
}

private static WeakReference CreateConcreateObject()
{
var obj = new Domain.MyClass().ToPython();
Assert.AreEqual(obj.Refcount, 1);
WeakReference numRef = new WeakReference(obj, false);
return numRef;
}

#endregion Tempary tests

/// <summary>
/// This is a magic incantation required to run code in an application
/// domain other than the current one.
Expand Down
16 changes: 12 additions & 4 deletions src/embed_tests/pyinitialize.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ public static void LoadSpecificArgs()
{
using (var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv")))
{
Assert.AreEqual(args[0], argv[0].ToString());
Assert.AreEqual(args[1], argv[1].ToString());
using var v0 = argv[0];
using var v1 = argv[1];
Assert.AreEqual(args[0], v0.ToString());
Assert.AreEqual(args[1], v1.ToString());
}
}
}
Expand All @@ -54,12 +56,16 @@ public void ImportClassShutdownRefcount()

PyObject ns = Py.Import(typeof(ImportClassShutdownRefcountClass).Namespace);
PyObject cls = ns.GetAttr(nameof(ImportClassShutdownRefcountClass));
BorrowedReference clsRef = cls.Reference;
#pragma warning disable CS0618 // Type or member is obsolete
cls.Leak();
#pragma warning restore CS0618 // Type or member is obsolete
ns.Dispose();

Assert.Less(cls.Refcount, 256);
Assert.Less(Runtime.Runtime.Refcount(clsRef), 256);

PythonEngine.Shutdown();
Assert.Greater(cls.Refcount, 0);
Assert.Greater(Runtime.Runtime.Refcount(clsRef), 0);
}

/// <summary>
Expand Down Expand Up @@ -176,6 +182,7 @@ public static void TestRunExitFuncs()
{
Assert.Fail(msg);
}
PythonEngine.InteropConfiguration = InteropConfiguration.MakeDefault();
return;
}
bool called = false;
Expand All @@ -187,6 +194,7 @@ public static void TestRunExitFuncs()
atexit.Dispose();
Runtime.Runtime.Shutdown();
Assert.True(called);
PythonEngine.InteropConfiguration = InteropConfiguration.MakeDefault();
}
}

Expand Down
11 changes: 10 additions & 1 deletion src/runtime/Codecs/DecoderGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Python.Runtime.Codecs
/// <summary>
/// Represents a group of <see cref="IPyObjectDecoder"/>s. Useful to group them by priority.
/// </summary>
public sealed class DecoderGroup: IPyObjectDecoder, IEnumerable<IPyObjectDecoder>
public sealed class DecoderGroup: IPyObjectDecoder, IEnumerable<IPyObjectDecoder>, IDisposable
{
readonly List<IPyObjectDecoder> decoders = new List<IPyObjectDecoder>();

Expand Down Expand Up @@ -46,6 +46,15 @@ public bool TryDecode<T>(PyObject pyObj, out T value)
/// <inheritdoc />
public IEnumerator<IPyObjectDecoder> GetEnumerator() => this.decoders.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this.decoders.GetEnumerator();

public void Dispose()
{
foreach (var decoder in this.decoders.OfType<IDisposable>())
{
decoder.Dispose();
}
this.decoders.Clear();
}
}

public static class DecoderGroupExtensions
Expand Down
11 changes: 10 additions & 1 deletion src/runtime/Codecs/EncoderGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Python.Runtime.Codecs
/// <summary>
/// Represents a group of <see cref="IPyObjectDecoder"/>s. Useful to group them by priority.
/// </summary>
public sealed class EncoderGroup: IPyObjectEncoder, IEnumerable<IPyObjectEncoder>
public sealed class EncoderGroup: IPyObjectEncoder, IEnumerable<IPyObjectEncoder>, IDisposable
{
readonly List<IPyObjectEncoder> encoders = new List<IPyObjectEncoder>();

Expand Down Expand Up @@ -47,6 +47,15 @@ public PyObject TryEncode(object value)
/// <inheritdoc />
public IEnumerator<IPyObjectEncoder> GetEnumerator() => this.encoders.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this.encoders.GetEnumerator();

public void Dispose()
{
foreach (var encoder in this.encoders.OfType<IDisposable>())
{
encoder.Dispose();
}
this.encoders.Clear();
}
}

public static class EncoderGroupExtensions
Expand Down
12 changes: 11 additions & 1 deletion src/runtime/InteropConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ namespace Python.Runtime
{
using System;
using System.Collections.Generic;
using System.Linq;

using Python.Runtime.Mixins;

public sealed class InteropConfiguration
public sealed class InteropConfiguration: IDisposable
{
internal readonly PythonBaseTypeProviderGroup pythonBaseTypeProviders
= new PythonBaseTypeProviderGroup();
Expand All @@ -24,5 +25,14 @@ public static InteropConfiguration MakeDefault()
},
};
}

public void Dispose()
{
foreach (var provider in PythonBaseTypeProviders.OfType<IDisposable>())
{
provider.Dispose();
}
PythonBaseTypeProviders.Clear();
}
}
}
10 changes: 9 additions & 1 deletion src/runtime/Mixins/CollectionMixinsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Python.Runtime.Mixins
{
class CollectionMixinsProvider : IPythonBaseTypeProvider
class CollectionMixinsProvider : IPythonBaseTypeProvider, IDisposable
{
readonly Lazy<PyObject> mixinsModule;
public CollectionMixinsProvider(Lazy<PyObject> mixinsModule)
Expand Down Expand Up @@ -86,5 +86,13 @@ static Type[] NewInterfaces(Type type)

static Type GetDefinition(Type type)
=> type.IsGenericType ? type.GetGenericTypeDefinition() : type;

public void Dispose()
{
if (this.mixinsModule.IsValueCreated)
{
this.mixinsModule.Value.Dispose();
}
}
}
}
4 changes: 2 additions & 2 deletions src/runtime/converterextensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ internal static void Reset()
{
clrToPython.Clear();
pythonToClr.Clear();
encoders.Clear();
decoders.Clear();
encoders.Dispose();
decoders.Dispose();
}
}

Expand Down
Loading