Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Fixed CollectBasicObject test#1313

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
filmor merged 3 commits intopythonnet:masterfromlosttech:bugs/1309
Dec 12, 2020
Merged
Show file tree
Hide file tree
Changes fromall 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
6 changes: 3 additions & 3 deletionssrc/embed_tests/TestDomainReload.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -191,7 +191,7 @@ from Python.EmbeddingTest.Domain import MyClass
def test_obj_call():
obj = MyClass()
obj.Method()
obj.StaticMethod()
MyClass.StaticMethod()
obj.Property = 1
obj.Field = 10
Expand DownExpand Up@@ -288,7 +288,7 @@ void ExecTest()

GC.Collect();
GC.WaitForPendingFinalizers();// <- this will put former `num` into Finalizer queue
Finalizer.Instance.Collect(forceDispose:true);
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);
Expand DownExpand Up@@ -333,7 +333,7 @@ void ExecTest()
PythonEngine.Initialize();// <- "run" 2 starts
GC.Collect();
GC.WaitForPendingFinalizers();
Finalizer.Instance.Collect(forceDispose:true);
Finalizer.Instance.Collect();
Assert.False(objRef.IsAlive);
}
finally
Expand Down
149 changes: 33 additions & 116 deletionssrc/embed_tests/TestFinalizer.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,9 @@
using Python.Runtime;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;

namespace Python.EmbeddingTest
Expand All@@ -28,26 +28,14 @@ public void TearDown()
PythonEngine.Shutdown();
}

private staticbool FullGCCollect()
private staticvoid FullGCCollect()
{
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
try
{
return GC.WaitForFullGCComplete() == GCNotificationStatus.Succeeded;
}
catch (NotImplementedException)
{
// Some clr runtime didn't implement GC.WaitForFullGCComplete yet.
return false;
}
finally
{
GC.WaitForPendingFinalizers();
}
GC.Collect();
GC.WaitForPendingFinalizers();
}

[Test]
[Ignore("Ignore temporarily")]
[Obsolete("GC tests are not guaranteed")]
public void CollectBasicObject()
{
Assert.IsTrue(Finalizer.Instance.Enable);
Expand All@@ -64,11 +52,7 @@ public void CollectBasicObject()
Assert.IsFalse(called, "The event handler was called before it was installed");
Finalizer.Instance.CollectOnce += handler;

WeakReference shortWeak;
WeakReference longWeak;
{
MakeAGarbage(out shortWeak, out longWeak);
}
IntPtr pyObj = MakeAGarbage(out var shortWeak, out var longWeak);
FullGCCollect();
// The object has been resurrected
Warn.If(
Expand All@@ -86,7 +70,7 @@ public void CollectBasicObject()
var garbage = Finalizer.Instance.GetCollectedObjects();
Assert.NotZero(garbage.Count, "There should still be garbage around");
Warn.Unless(
garbage.Any(T => ReferenceEquals(T.Target, longWeak.Target)),
garbage.Contains(pyObj),
$"The {nameof(longWeak)} reference doesn't show up in the garbage list",
garbage
);
Expand All@@ -104,33 +88,45 @@ public void CollectBasicObject()
}

[Test]
[Ignore("Ignore temporarily")]
[Obsolete("GC tests are not guaranteed")]
Copy link
Member

Choose a reason for hiding this comment

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

Is NUnit aware of this attribute or does this just generate yet another compile-time warning? Does it help to retry the test usinghttps://docs.nunit.org/articles/nunit/writing-tests/attributes/retry.html?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

NUnit is not aware. This actually removes warning from the usage of other[Obsolete] code.

public void CollectOnShutdown()
{
IntPtr op = MakeAGarbage(out var shortWeak, out var longWeak);
int hash = shortWeak.Target.GetHashCode();
List<WeakReference> garbage;
if (!FullGCCollect())
{
Assert.IsTrue(WaitForCollected(op, hash, 10000));
}
FullGCCollect();
Assert.IsFalse(shortWeak.IsAlive);
garbage = Finalizer.Instance.GetCollectedObjects();
List<IntPtr>garbage = Finalizer.Instance.GetCollectedObjects();
Assert.IsNotEmpty(garbage, "The garbage object should be collected");
Assert.IsTrue(garbage.Any(r => ReferenceEquals(r.Target, longWeak.Target)),
Assert.IsTrue(garbage.Contains(op),
"Garbage should contains the collected object");

PythonEngine.Shutdown();
garbage = Finalizer.Instance.GetCollectedObjects();
Assert.IsEmpty(garbage);
}

[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] // ensure lack of references to obj
[Obsolete("GC tests are not guaranteed")]
private static IntPtr MakeAGarbage(out WeakReference shortWeak, out WeakReference longWeak)
{
PyLong obj = new PyLong(1024);
shortWeak = new WeakReference(obj);
longWeak = new WeakReference(obj, true);
return obj.Handle;
IntPtr handle = IntPtr.Zero;
WeakReference @short = null, @long = null;
// must create Python object in the thread where we have GIL
IntPtr val = PyLong.FromLong(1024);
// must create temp object in a different thread to ensure it is not present
// when conservatively scanning stack for GC roots.
// see https://xamarin.github.io/bugzilla-archives/17/17593/bug.html
var garbageGen = new Thread(() =>
{
var obj = new PyObject(val, skipCollect: true);
@short = new WeakReference(obj);
@long = new WeakReference(obj, true);
handle = obj.Handle;
});
garbageGen.Start();
Assert.IsTrue(garbageGen.Join(TimeSpan.FromSeconds(5)), "Garbage creation timed out");
shortWeak = @short;
longWeak = @long;
return handle;
}

private static long CompareWithFinalizerOn(PyObject pyCollect, bool enbale)
Expand DownExpand Up@@ -191,62 +187,6 @@ public void SimpleTestMemory()
}
}

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;
var errorMessage = "";
EventHandler<Finalizer.ErrorArgs> handleFunc = (sender, args) =>
{
called = true;
errorMessage = args.Error.Message;
};
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;
}
Assert.AreEqual(errorMessage, "MyPyObject");
}

[Test]
public void ValidateRefCount()
{
Expand DownExpand Up@@ -279,36 +219,13 @@ public void ValidateRefCount()
}
}

[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] // ensure lack of references to s1 and s2
private static IntPtr CreateStringGarbage()
{
PyString s1 = new PyString("test_string");
// s2 steal a reference from s1
PyString s2 = new PyString(s1.Handle);
return s1.Handle;
}

private static bool WaitForCollected(IntPtr op, int hash, int milliseconds)
{
var stopwatch = Stopwatch.StartNew();
do
{
var garbage = Finalizer.Instance.GetCollectedObjects();
foreach (var item in garbage)
{
// The validation is not 100% precise,
// but it's rare that two conditions satisfied but they're still not the same object.
if (item.Target.GetHashCode() != hash)
{
continue;
}
var obj = (IPyDisposable)item.Target;
if (obj.GetTrackedHandles().Contains(op))
{
return true;
}
}
} while (stopwatch.ElapsedMilliseconds < milliseconds);
return false;
}
}
}
7 changes: 7 additions & 0 deletionssrc/runtime/debughelper.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -137,5 +137,12 @@ public static void PrintHexBytes(byte[] bytes)
Console.WriteLine();
}
}

[Conditional("DEBUG")]
public static void AssertHasReferences(IntPtr obj)
{
long refcount = Runtime.Refcount(obj);
Debug.Assert(refcount > 0, "Object refcount is 0 or less");
}
}
}
9 changes: 2 additions & 7 deletionssrc/runtime/delegatemanager.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -181,7 +181,7 @@ A possible alternate strategy would be to create custom subclasses
too "special" for this to work. It would be more work, so for now
the 80/20 rule applies :) */

public class Dispatcher : IPyDisposable
public class Dispatcher
{
public IntPtr target;
public Type dtype;
Expand All@@ -202,7 +202,7 @@ public Dispatcher(IntPtr target, Type dtype)
return;
}
_finalized = true;
Finalizer.Instance.AddFinalizedObject(this);
Finalizer.Instance.AddFinalizedObject(ref target);
}

public void Dispose()
Expand DownExpand Up@@ -276,11 +276,6 @@ public object TrueDispatch(ArrayList args)
Runtime.XDecref(op);
return result;
}

public IntPtr[] GetTrackedHandles()
{
return new IntPtr[] { target };
}
}


Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp