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

Commitf83c884

Browse files
authored
Merge pull requestpythonnet#692 from amos402/pyobject-finalizer
PyObject finalizer
2 parents5ee234a +4eff81e commitf83c884

17 files changed

+903
-246
lines changed

‎CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
2121
- Catches exceptions thrown in C# iterators (yield returns) and rethrows them in python ([#475][i475])([#693][p693])
2222
- Implemented GetDynamicMemberNames() for PyObject to allow dynamic object members to be visible in the debugger ([#443][i443])([#690][p690])
2323
- Incorporated reference-style links to issues and pull requests in the CHANGELOG ([#608][i608])
24+
- Added PyObject finalizer support, Python objects referred by C# can be auto collect now ([#692][p692]).
2425
- Added detailed comments about aproaches and dangers to handle multi-app-domains ([#625][p625])
2526
- Python 3.7 support, builds and testing added. Defaults changed from Python 3.6 to 3.7 ([#698][p698])
2627

2728
###Changed
29+
- PythonException included C# call stack
2830

2931
- Reattach python exception traceback information (#545)
3032
- PythonEngine.Intialize will now call`Py_InitializeEx` with a default value of 0, so signals will not be configured by default on embedding. This is different from the previous behaviour, where`Py_Initialize` was called instead, which sets initSigs to 1. ([#449][i449])

‎src/embed_tests/Python.EmbeddingTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
<CompileInclude="TestCustomMarshal.cs" />
8989
<CompileInclude="TestDomainReload.cs" />
9090
<CompileInclude="TestExample.cs" />
91+
<CompileInclude="TestFinalizer.cs" />
9192
<CompileInclude="TestPyAnsiString.cs" />
9293
<CompileInclude="TestPyFloat.cs" />
9394
<CompileInclude="TestPyInt.cs" />

‎src/embed_tests/TestFinalizer.cs

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
usingNUnit.Framework;
2+
usingPython.Runtime;
3+
usingSystem;
4+
usingSystem.Linq;
5+
usingSystem.Threading;
6+
7+
namespacePython.EmbeddingTest
8+
{
9+
publicclassTestFinalizer
10+
{
11+
privateint_oldThreshold;
12+
13+
[SetUp]
14+
publicvoidSetUp()
15+
{
16+
_oldThreshold=Finalizer.Instance.Threshold;
17+
PythonEngine.Initialize();
18+
Exceptions.Clear();
19+
}
20+
21+
[TearDown]
22+
publicvoidTearDown()
23+
{
24+
Finalizer.Instance.Threshold=_oldThreshold;
25+
PythonEngine.Shutdown();
26+
}
27+
28+
privatestaticvoidFullGCCollect()
29+
{
30+
GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced);
31+
GC.WaitForPendingFinalizers();
32+
}
33+
34+
[Test]
35+
publicvoidCollectBasicObject()
36+
{
37+
Assert.IsTrue(Finalizer.Instance.Enable);
38+
39+
intthId=Thread.CurrentThread.ManagedThreadId;
40+
Finalizer.Instance.Threshold=1;
41+
boolcalled=false;
42+
EventHandler<Finalizer.CollectArgs>handler=(s,e)=>
43+
{
44+
Assert.AreEqual(thId,Thread.CurrentThread.ManagedThreadId);
45+
Assert.GreaterOrEqual(e.ObjectCount,1);
46+
called=true;
47+
};
48+
49+
WeakReferenceshortWeak;
50+
WeakReferencelongWeak;
51+
{
52+
MakeAGarbage(outshortWeak,outlongWeak);
53+
}
54+
FullGCCollect();
55+
// The object has been resurrected
56+
Assert.IsFalse(shortWeak.IsAlive);
57+
Assert.IsTrue(longWeak.IsAlive);
58+
59+
{
60+
vargarbage=Finalizer.Instance.GetCollectedObjects();
61+
Assert.NotZero(garbage.Count);
62+
Assert.IsTrue(garbage.Any(T=>ReferenceEquals(T.Target,longWeak.Target)));
63+
}
64+
65+
Assert.IsFalse(called);
66+
Finalizer.Instance.CollectOnce+=handler;
67+
try
68+
{
69+
Finalizer.Instance.CallPendingFinalizers();
70+
}
71+
finally
72+
{
73+
Finalizer.Instance.CollectOnce-=handler;
74+
}
75+
Assert.IsTrue(called);
76+
}
77+
78+
privatestaticvoidMakeAGarbage(outWeakReferenceshortWeak,outWeakReferencelongWeak)
79+
{
80+
PyLongobj=newPyLong(1024);
81+
shortWeak=newWeakReference(obj);
82+
longWeak=newWeakReference(obj,true);
83+
obj=null;
84+
}
85+
86+
privatestaticlongCompareWithFinalizerOn(PyObjectpyCollect,boolenbale)
87+
{
88+
// Must larger than 512 bytes make sure Python use
89+
stringstr=newstring('1',1024);
90+
Finalizer.Instance.Enable=true;
91+
FullGCCollect();
92+
FullGCCollect();
93+
pyCollect.Invoke();
94+
Finalizer.Instance.Collect();
95+
Finalizer.Instance.Enable=enbale;
96+
97+
// Estimate unmanaged memory size
98+
longbefore=Environment.WorkingSet-GC.GetTotalMemory(true);
99+
for(inti=0;i<10000;i++)
100+
{
101+
// Memory will leak when disable Finalizer
102+
newPyString(str);
103+
}
104+
FullGCCollect();
105+
FullGCCollect();
106+
pyCollect.Invoke();
107+
if(enbale)
108+
{
109+
Finalizer.Instance.Collect();
110+
}
111+
112+
FullGCCollect();
113+
FullGCCollect();
114+
longafter=Environment.WorkingSet-GC.GetTotalMemory(true);
115+
returnafter-before;
116+
117+
}
118+
119+
/// <summary>
120+
/// Because of two vms both have their memory manager,
121+
/// this test only prove the finalizer has take effect.
122+
/// </summary>
123+
[Test]
124+
[Ignore("Too many uncertainties, only manual on when debugging")]
125+
publicvoidSimpleTestMemory()
126+
{
127+
boololdState=Finalizer.Instance.Enable;
128+
try
129+
{
130+
using(PyObjectgcModule=PythonEngine.ImportModule("gc"))
131+
using(PyObjectpyCollect=gcModule.GetAttr("collect"))
132+
{
133+
longspan1=CompareWithFinalizerOn(pyCollect,false);
134+
longspan2=CompareWithFinalizerOn(pyCollect,true);
135+
Assert.Less(span2,span1);
136+
}
137+
}
138+
finally
139+
{
140+
Finalizer.Instance.Enable=oldState;
141+
}
142+
}
143+
144+
classMyPyObject:PyObject
145+
{
146+
publicMyPyObject(IntPtrop):base(op)
147+
{
148+
}
149+
150+
protectedoverridevoidDispose(booldisposing)
151+
{
152+
base.Dispose(disposing);
153+
GC.SuppressFinalize(this);
154+
thrownewException("MyPyObject");
155+
}
156+
internalstaticvoidCreateMyPyObject(IntPtrop)
157+
{
158+
Runtime.Runtime.XIncref(op);
159+
newMyPyObject(op);
160+
}
161+
}
162+
163+
[Test]
164+
publicvoidErrorHandling()
165+
{
166+
boolcalled=false;
167+
EventHandler<Finalizer.ErrorArgs>handleFunc=(sender,args)=>
168+
{
169+
called=true;
170+
Assert.AreEqual(args.Error.Message,"MyPyObject");
171+
};
172+
Finalizer.Instance.Threshold=1;
173+
Finalizer.Instance.ErrorHandler+=handleFunc;
174+
try
175+
{
176+
WeakReferenceshortWeak;
177+
WeakReferencelongWeak;
178+
{
179+
MakeAGarbage(outshortWeak,outlongWeak);
180+
varobj=(PyLong)longWeak.Target;
181+
IntPtrhandle=obj.Handle;
182+
shortWeak=null;
183+
longWeak=null;
184+
MyPyObject.CreateMyPyObject(handle);
185+
obj.Dispose();
186+
obj=null;
187+
}
188+
FullGCCollect();
189+
Finalizer.Instance.Collect();
190+
Assert.IsTrue(called);
191+
}
192+
finally
193+
{
194+
Finalizer.Instance.ErrorHandler-=handleFunc;
195+
}
196+
}
197+
198+
[Test]
199+
publicvoidValidateRefCount()
200+
{
201+
if(!Finalizer.Instance.RefCountValidationEnabled)
202+
{
203+
Assert.Pass("Only run with FINALIZER_CHECK");
204+
}
205+
IntPtrptr=IntPtr.Zero;
206+
boolcalled=false;
207+
Finalizer.IncorrectRefCntHandlerhandler=(s,e)=>
208+
{
209+
called=true;
210+
Assert.AreEqual(ptr,e.Handle);
211+
Assert.AreEqual(2,e.ImpactedObjects.Count);
212+
// Fix for this test, don't do this on general environment
213+
Runtime.Runtime.XIncref(e.Handle);
214+
returnfalse;
215+
};
216+
Finalizer.Instance.IncorrectRefCntResolver+=handler;
217+
try
218+
{
219+
ptr=CreateStringGarbage();
220+
FullGCCollect();
221+
Assert.Throws<Finalizer.IncorrectRefCountException>(()=>Finalizer.Instance.Collect());
222+
Assert.IsTrue(called);
223+
}
224+
finally
225+
{
226+
Finalizer.Instance.IncorrectRefCntResolver-=handler;
227+
}
228+
}
229+
230+
privatestaticIntPtrCreateStringGarbage()
231+
{
232+
PyStrings1=newPyString("test_string");
233+
// s2 steal a reference from s1
234+
PyStrings2=newPyString(s1.Handle);
235+
returns1.Handle;
236+
}
237+
238+
}
239+
}

‎src/embed_tests/TestPyAnsiString.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public void TestCtorPtr()
6363
conststringexpected="foo";
6464

6565
vart=newPyAnsiString(expected);
66+
Runtime.Runtime.XIncref(t.Handle);
6667
varactual=newPyAnsiString(t.Handle);
6768

6869
Assert.AreEqual(expected,actual.ToString());

‎src/embed_tests/TestPyFloat.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public void Dispose()
2525
publicvoidIntPtrCtor()
2626
{
2727
vari=newPyFloat(1);
28+
Runtime.Runtime.XIncref(i.Handle);
2829
varii=newPyFloat(i.Handle);
2930
Assert.AreEqual(i.Handle,ii.Handle);
3031
}

‎src/embed_tests/TestPyInt.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public void TestCtorSByte()
8686
publicvoidTestCtorPtr()
8787
{
8888
vari=newPyInt(5);
89+
Runtime.Runtime.XIncref(i.Handle);
8990
vara=newPyInt(i.Handle);
9091
Assert.AreEqual(5,a.ToInt32());
9192
}
@@ -94,6 +95,7 @@ public void TestCtorPtr()
9495
publicvoidTestCtorPyObject()
9596
{
9697
vari=newPyInt(5);
98+
Runtime.Runtime.XIncref(i.Handle);
9799
vara=newPyInt(i);
98100
Assert.AreEqual(5,a.ToInt32());
99101
}

‎src/embed_tests/TestPyLong.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public void TestCtorDouble()
102102
publicvoidTestCtorPtr()
103103
{
104104
vari=newPyLong(5);
105+
Runtime.Runtime.XIncref(i.Handle);
105106
vara=newPyLong(i.Handle);
106107
Assert.AreEqual(5,a.ToInt32());
107108
}
@@ -110,6 +111,7 @@ public void TestCtorPtr()
110111
publicvoidTestCtorPyObject()
111112
{
112113
vari=newPyLong(5);
114+
Runtime.Runtime.XIncref(i.Handle);
113115
vara=newPyLong(i);
114116
Assert.AreEqual(5,a.ToInt32());
115117
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp