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

Commit7fd6fc4

Browse files
author
dse
committed
Finalizer implementation improvement. PythonException memory leak fix. Review fixes.
1 parent37c2449 commit7fd6fc4

File tree

9 files changed

+165
-72
lines changed

9 files changed

+165
-72
lines changed

‎src/embed_tests/TestFinalizer.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,67 @@ public void TestClrObjectFullRelease()
6464

6565
Assert.IsFalse(weakRef.IsAlive,"Clr object should be collected.");
6666
}
67+
68+
[Test]
69+
[Ignore("For debug only")]
70+
publicvoidTestExceptionMemoryLeak()
71+
{
72+
dynamicpymodule;
73+
PyObjectgc;
74+
dynamictestmethod;
75+
76+
varts=PythonEngine.BeginAllowThreads();
77+
IntPtrpylock=PythonEngine.AcquireLock();
78+
79+
#ifNETCOREAPP
80+
conststrings="../../fixtures";
81+
#else
82+
conststrings="../fixtures";
83+
#endif
84+
stringtestPath=Path.Combine(TestContext.CurrentContext.TestDirectory,s);
85+
86+
IntPtrstr=Runtime.Runtime.PyString_FromString(testPath);
87+
IntPtrpath=Runtime.Runtime.PySys_GetObject("path");
88+
Runtime.Runtime.PyList_Append(path,str);
89+
90+
{
91+
PyObjectsys=PythonEngine.ImportModule("sys");
92+
gc=PythonEngine.ImportModule("gc");
93+
94+
pymodule=PythonEngine.ImportModule("MemoryLeakTest.pyraise");
95+
testmethod=pymodule.test_raise_exception;
96+
}
97+
98+
PythonEngine.ReleaseLock(pylock);
99+
100+
doublefloatarg1=5.1f;
101+
dynamicres=null;
102+
{
103+
for(inti=1;i<=10000000;i++)
104+
{
105+
using(Py.GIL())
106+
{
107+
try
108+
{
109+
res=testmethod(Py.kw("number",floatarg1),Py.kw("astring","bbc"));
110+
}
111+
catch(Exceptione)
112+
{
113+
if(i%10000==0)
114+
{
115+
TestContext.Progress.WriteLine(e.Message);
116+
}
117+
}
118+
}
119+
120+
if(i%10000==0)
121+
{
122+
GC.Collect();
123+
}
124+
}
125+
}
126+
127+
PythonEngine.EndAllowThreads(ts);
128+
}
67129
}
68130
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
deftest_raise_exception(number=3,astring='abc'):
2+
raiseValueError("testing for memory leak")
3+
returnastring*int(number)
4+
5+
deftest_raise_exception2(number,astring):
6+
#raise ValueError("testing for memory leak")
7+
#astring * int(number)
8+
return"test"

‎src/runtime/debughelper.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,10 @@ internal static void debug(string msg)
116116
Console.WriteLine(" {0}",msg);
117117
}
118118

119-
/// <summary>
119+
/// <summary>
120120
/// Helper function to inspect/compare managed to native conversions.
121-
/// Especially useful when debugging CustomMarshaler.
122-
/// </summary>
121+
/// Especially useful when debugging CustomMarshaler.
122+
/// </summary>
123123
/// <param name="bytes"></param>
124124
[Conditional("DEBUG")]
125125
publicstaticvoidPrintHexBytes(byte[]bytes)

‎src/runtime/pyiter.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,20 @@ public PyIter(PyObject iterable)
4040
}
4141
}
4242

43-
protectedoverridevoidDispose(booldisposing)
43+
protectedinternaloverridevoidDispose(booldisposing)
4444
{
45-
if(null!=_current)
45+
try
4646
{
47-
_current.Dispose();
48-
_current=null;
47+
if(null!=_current)
48+
{
49+
_current.Dispose(disposing);
50+
_current=null;
51+
}
52+
}
53+
finally
54+
{
55+
base.Dispose(disposing);
4956
}
50-
base.Dispose(disposing);
5157
}
5258

5359
publicboolMoveNext()

‎src/runtime/pyobject.cs

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -132,39 +132,32 @@ public T As<T>()
132132
/// to Python objects will not be released until a managed garbage
133133
/// collection occurs.
134134
/// </remarks>
135-
protectedvirtualvoidDispose(booldisposing)
135+
protectedinternalvirtualvoidDispose(booldisposing)
136136
{
137137
if(!disposed)
138138
{
139139
disposed=true;
140+
IntPtrobjToDispose=obj;
141+
obj=IntPtr.Zero;
140142

141143
if(disposing)
142144
{
143-
try
145+
if(Runtime.Py_IsInitialized()>0&&!Runtime.IsFinalizing)
144146
{
145-
if(Runtime.Py_IsInitialized()>0&&!Runtime.IsFinalizing)
147+
IntPtrgs=PythonEngine.AcquireLock();
148+
try
146149
{
147-
IntPtrgs=PythonEngine.AcquireLock();
148-
try
149-
{
150-
Runtime.XDecref(obj);
151-
obj=IntPtr.Zero;
152-
}
153-
finally
154-
{
155-
PythonEngine.ReleaseLock(gs);
156-
}
150+
Runtime.XDecref(objToDispose);
151+
}
152+
finally
153+
{
154+
PythonEngine.ReleaseLock(gs);
157155
}
158-
}
159-
catch
160-
{
161-
// Do nothing.
162156
}
163157
}
164158
else
165159
{
166-
_referenceDecrementer?.ScheduleDecRef(obj);
167-
obj=IntPtr.Zero;
160+
_referenceDecrementer?.ScheduleDecRef(objToDispose);
168161
}
169162
}
170163
}

‎src/runtime/pyreferencedecrementer.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@
77

88
namespacePython.Runtime
99
{
10+
/// <summary>
11+
/// Background python objects reference decrementer.
12+
/// !!! By some unknown reasons python references decrements have been performed in the GC thread - affects GC. !!!
13+
/// This component contains background thread with the queue of IntPtr python object references.
14+
/// Finalizers should schedule reference decrement operation in this component to avoid problem with GC.
15+
/// Each PythonEngine Init/Shutdown have it's own PyReferenceDecrementer, so PyObject should also have a field with the
16+
/// reference to the correct PyReferenceDecrementer.
17+
/// </summary>
1018
internalclassPyReferenceDecrementer:IDisposable
1119
{
1220
privatestaticreadonlyDedicatedThreadTaskSchedulerDedicatedThreadTaskScheduler=newDedicatedThreadTaskScheduler();

‎src/runtime/pyscope.cs

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class PyScope : DynamicObject, IDisposable
3131
/// <summary>
3232
/// the python Module object the scope associated with.
3333
/// </summary>
34-
internalreadonlyIntPtrobj;
34+
internalIntPtrobj;
3535

3636
/// <summary>
3737
/// the variable dict of the scope.
@@ -522,34 +522,37 @@ protected virtual void Dispose(bool disposing)
522522
{
523523
if(!_isDisposed)
524524
{
525-
_isDisposed=true;
526-
525+
IntPtrobjToDispose=obj;
527526
if(disposing)
528527
{
529528
try
530529
{
531-
if(Runtime.Py_IsInitialized()>0&&!Runtime.IsFinalizing)
532-
{
533-
IntPtrgs=PythonEngine.AcquireLock();
534-
try
535-
{
536-
Runtime.XDecref(obj);
537-
}
538-
finally
539-
{
540-
PythonEngine.ReleaseLock(gs);
541-
}
542-
}
530+
OnDispose?.Invoke(this);
531+
}
532+
finally
533+
{
534+
_isDisposed=true;
535+
obj=IntPtr.Zero;
543536
}
544-
catch
537+
538+
if(Runtime.Py_IsInitialized()>0&&!Runtime.IsFinalizing)
545539
{
546-
// Do nothing.
540+
IntPtrgs=PythonEngine.AcquireLock();
541+
try
542+
{
543+
Runtime.XDecref(objToDispose);
544+
}
545+
finally
546+
{
547+
PythonEngine.ReleaseLock(gs);
548+
}
547549
}
548-
OnDispose?.Invoke(this);
549550
}
550551
else
551552
{
552-
_referenceDecrementer?.ScheduleDecRef(obj);
553+
_isDisposed=true;
554+
obj=IntPtr.Zero;
555+
_referenceDecrementer?.ScheduleDecRef(objToDispose);
553556
}
554557
}
555558
}

‎src/runtime/pythonengine.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ internal GILState()
604604

605605
protectedvirtualvoidDispose(booldisposing)
606606
{
607-
//ReleeaseLock is thread bound and if it's called in finalizer thread it can wrongly release lock.
607+
//ReleaseLock is thread bound and if it's called in finalizer thread it can wrongly release lock.
608608
if(disposing)
609609
{
610610
PythonEngine.ReleaseLock(state);

‎src/runtime/pythonexception.cs

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ public PythonException()
2121
_referenceDecrementer=PythonEngine.CurrentRefDecrementer;
2222
IntPtrgs=PythonEngine.AcquireLock();
2323
Runtime.PyErr_Fetch(ref_pyType,ref_pyValue,ref_pyTB);
24-
Runtime.XIncref(_pyType);
25-
Runtime.XIncref(_pyValue);
26-
Runtime.XIncref(_pyTB);
24+
25+
// Those references already owned by the caller
26+
////Runtime.XIncref(_pyType);
27+
////Runtime.XIncref(_pyValue);
28+
////Runtime.XIncref(_pyTB);
2729
if(_pyType!=IntPtr.Zero&&_pyValue!=IntPtr.Zero)
2830
{
2931
stringtype;
@@ -44,7 +46,13 @@ public PythonException()
4446
}
4547
if(_pyTB!=IntPtr.Zero)
4648
{
47-
PyObjecttb_module=PythonEngine.ImportModule("traceback");
49+
50+
if(_tbModule==null)
51+
{
52+
_tbModule=PythonEngine.ImportModule("traceback");
53+
}
54+
PyObjecttb_module=_tbModule;
55+
4856
Runtime.XIncref(_pyTB);
4957
using(varpyTB=newPyObject(_pyTB))
5058
{
@@ -54,11 +62,14 @@ public PythonException()
5462
PythonEngine.ReleaseLock(gs);
5563
}
5664

65+
privatestaticPyObject_tbModule;
66+
5767
// Ensure that encapsulated Python objects are decref'ed appropriately
5868
// when the managed exception wrapper is garbage-collected.
5969

6070
~PythonException()
6171
{
72+
6273
Dispose(false);
6374
}
6475

@@ -152,49 +163,51 @@ protected void Dispose(bool disposing)
152163
{
153164
disposed=true;
154165

166+
IntPtrpyTypeToDispose=_pyType;
167+
_pyType=IntPtr.Zero;
168+
169+
IntPtrpyValueToDispose=_pyValue;
170+
_pyValue=IntPtr.Zero;
171+
172+
IntPtrpyTBToDispose=_pyTB;
173+
_pyTB=IntPtr.Zero;
174+
155175
if(disposing)
156176
{
157177
if(Runtime.Py_IsInitialized()>0&&!Runtime.IsFinalizing)
158178
{
179+
IntPtrgs=PythonEngine.AcquireLock();
159180
try
160181
{
161-
IntPtrgs=PythonEngine.AcquireLock();
162-
try
163-
{
164-
Runtime.XDecref(_pyType);
165-
Runtime.XDecref(_pyValue);
166-
// XXX Do we ever get TraceBack? //
167-
if(_pyTB!=IntPtr.Zero)
168-
{
169-
Runtime.XDecref(_pyTB);
170-
}
171-
}
172-
finally
182+
Runtime.XDecref(pyTypeToDispose);
183+
Runtime.XDecref(pyValueToDispose);
184+
// XXX Do we ever get TraceBack? //
185+
if(pyTBToDispose!=IntPtr.Zero)
173186
{
174-
PythonEngine.ReleaseLock(gs);
187+
Runtime.XDecref(pyTBToDispose);
175188
}
176189
}
177-
catch
190+
finally
178191
{
179-
// Do nothing.
192+
PythonEngine.ReleaseLock(gs);
180193
}
181194
}
182195
}
183196
else
184197
{
185-
if(_pyType!=IntPtr.Zero)
198+
if(pyTypeToDispose!=IntPtr.Zero)
186199
{
187-
_referenceDecrementer?.ScheduleDecRef(_pyType);
200+
_referenceDecrementer?.ScheduleDecRef(pyTypeToDispose);
188201
}
189202

190-
if(_pyValue!=IntPtr.Zero)
203+
if(pyValueToDispose!=IntPtr.Zero)
191204
{
192-
_referenceDecrementer?.ScheduleDecRef(_pyValue);
205+
_referenceDecrementer?.ScheduleDecRef(pyValueToDispose);
193206
}
194207

195-
if(_pyTB!=IntPtr.Zero)
208+
if(pyTBToDispose!=IntPtr.Zero)
196209
{
197-
_referenceDecrementer?.ScheduleDecRef(_pyTB);
210+
_referenceDecrementer?.ScheduleDecRef(pyTBToDispose);
198211
}
199212
}
200213
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp