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

Commitff86507

Browse files
committed
reworked Finalizer to only store raw python object handles instead of PyObject instances
1 parentd888244 commitff86507

9 files changed

+88
-158
lines changed

‎src/embed_tests/TestDomainReload.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ void ExecTest()
288288

289289
GC.Collect();
290290
GC.WaitForPendingFinalizers();// <- this will put former `num` into Finalizer queue
291-
Finalizer.Instance.Collect(forceDispose:true);
291+
Finalizer.Instance.Collect();
292292
// ^- this will call PyObject.Dispose, which will call XDecref on `num.Handle`,
293293
// but Python interpreter from "run" 1 is long gone, so it will corrupt memory instead.
294294
Assert.False(numRef.IsAlive);
@@ -333,7 +333,7 @@ void ExecTest()
333333
PythonEngine.Initialize();// <- "run" 2 starts
334334
GC.Collect();
335335
GC.WaitForPendingFinalizers();
336-
Finalizer.Instance.Collect(forceDispose:true);
336+
Finalizer.Instance.Collect();
337337
Assert.False(objRef.IsAlive);
338338
}
339339
finally

‎src/embed_tests/TestFinalizer.cs

Lines changed: 4 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,7 @@ public void CollectBasicObject()
5252
Assert.IsFalse(called,"The event handler was called before it was installed");
5353
Finalizer.Instance.CollectOnce+=handler;
5454

55-
WeakReferenceshortWeak;
56-
WeakReferencelongWeak;
57-
{
58-
MakeAGarbage(outshortWeak,outlongWeak);
59-
}
55+
IntPtrpyObj=MakeAGarbage(outvarshortWeak,outvarlongWeak);
6056
FullGCCollect();
6157
// The object has been resurrected
6258
Warn.If(
@@ -74,7 +70,7 @@ public void CollectBasicObject()
7470
vargarbage=Finalizer.Instance.GetCollectedObjects();
7571
Assert.NotZero(garbage.Count,"There should still be garbage around");
7672
Warn.Unless(
77-
garbage.Any(T=>ReferenceEquals(T.Target,longWeak.Target)),
73+
garbage.Contains(pyObj),
7874
$"The{nameof(longWeak)} reference doesn't show up in the garbage list",
7975
garbage
8076
);
@@ -98,9 +94,9 @@ public void CollectOnShutdown()
9894
IntPtrop=MakeAGarbage(outvarshortWeak,outvarlongWeak);
9995
FullGCCollect();
10096
Assert.IsFalse(shortWeak.IsAlive);
101-
List<WeakReference>garbage=Finalizer.Instance.GetCollectedObjects();
97+
List<IntPtr>garbage=Finalizer.Instance.GetCollectedObjects();
10298
Assert.IsNotEmpty(garbage,"The garbage object should be collected");
103-
Assert.IsTrue(garbage.Any(r=>ReferenceEquals(r.Target,longWeak.Target)),
99+
Assert.IsTrue(garbage.Contains(op),
104100
"Garbage should contains the collected object");
105101

106102
PythonEngine.Shutdown();
@@ -191,63 +187,6 @@ public void SimpleTestMemory()
191187
}
192188
}
193189

194-
classMyPyObject:PyObject
195-
{
196-
publicMyPyObject(IntPtrop):base(op)
197-
{
198-
}
199-
200-
protectedoverridevoidDispose(booldisposing)
201-
{
202-
base.Dispose(disposing);
203-
GC.SuppressFinalize(this);
204-
thrownewException("MyPyObject");
205-
}
206-
internalstaticvoidCreateMyPyObject(IntPtrop)
207-
{
208-
Runtime.Runtime.XIncref(op);
209-
newMyPyObject(op);
210-
}
211-
}
212-
213-
[Test]
214-
[Obsolete("GC tests are not guaranteed")]
215-
publicvoidErrorHandling()
216-
{
217-
boolcalled=false;
218-
varerrorMessage="";
219-
EventHandler<Finalizer.ErrorArgs>handleFunc=(sender,args)=>
220-
{
221-
called=true;
222-
errorMessage=args.Error.Message;
223-
};
224-
Finalizer.Instance.Threshold=1;
225-
Finalizer.Instance.ErrorHandler+=handleFunc;
226-
try
227-
{
228-
WeakReferenceshortWeak;
229-
WeakReferencelongWeak;
230-
{
231-
MakeAGarbage(outshortWeak,outlongWeak);
232-
varobj=(PyObject)longWeak.Target;
233-
IntPtrhandle=obj.Handle;
234-
shortWeak=null;
235-
longWeak=null;
236-
MyPyObject.CreateMyPyObject(handle);
237-
obj.Dispose();
238-
obj=null;
239-
}
240-
FullGCCollect();
241-
Finalizer.Instance.Collect();
242-
Assert.IsTrue(called);
243-
}
244-
finally
245-
{
246-
Finalizer.Instance.ErrorHandler-=handleFunc;
247-
}
248-
Assert.AreEqual(errorMessage,"MyPyObject");
249-
}
250-
251190
[Test]
252191
publicvoidValidateRefCount()
253192
{

‎src/runtime/debughelper.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,5 +137,12 @@ public static void PrintHexBytes(byte[] bytes)
137137
Console.WriteLine();
138138
}
139139
}
140+
141+
[Conditional("DEBUG")]
142+
publicstaticvoidAssertHasReferences(IntPtrobj)
143+
{
144+
longrefcount=Runtime.Refcount(obj);
145+
Debug.Assert(refcount>0,"Object refcount is 0 or less");
146+
}
140147
}
141148
}

‎src/runtime/delegatemanager.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ A possible alternate strategy would be to create custom subclasses
181181
too "special" for this to work. It would be more work, so for now
182182
the 80/20 rule applies :) */
183183

184-
publicclassDispatcher:IPyDisposable
184+
publicclassDispatcher
185185
{
186186
publicIntPtrtarget;
187187
publicTypedtype;
@@ -202,7 +202,7 @@ public Dispatcher(IntPtr target, Type dtype)
202202
return;
203203
}
204204
_finalized=true;
205-
Finalizer.Instance.AddFinalizedObject(this);
205+
Finalizer.Instance.AddFinalizedObject(reftarget);
206206
}
207207

208208
publicvoidDispose()
@@ -276,11 +276,6 @@ public object TrueDispatch(ArrayList args)
276276
Runtime.XDecref(op);
277277
returnresult;
278278
}
279-
280-
publicIntPtr[]GetTrackedHandles()
281-
{
282-
returnnewIntPtr[]{target};
283-
}
284279
}
285280

286281

‎src/runtime/finalizer.cs

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class ErrorArgs : EventArgs
2727
publicintThreshold{get;set;}
2828
publicboolEnable{get;set;}
2929

30-
privateConcurrentQueue<IPyDisposable>_objQueue=newConcurrentQueue<IPyDisposable>();
30+
privateConcurrentQueue<IntPtr>_objQueue=newConcurrentQueue<IntPtr>();
3131
privateint_throttled;
3232

3333
#region FINALIZER_CHECK
@@ -42,7 +42,7 @@ public class ErrorArgs : EventArgs
4242
publicclassIncorrectFinalizeArgs:EventArgs
4343
{
4444
publicIntPtrHandle{get;internalset;}
45-
publicICollection<IPyDisposable>ImpactedObjects{get;internalset;}
45+
publicICollection<IntPtr>ImpactedObjects{get;internalset;}
4646
}
4747

4848
publicclassIncorrectRefCountException:Exception
@@ -73,8 +73,6 @@ private Finalizer()
7373
Threshold=200;
7474
}
7575

76-
[Obsolete("forceDispose parameter is unused. All objects are disposed regardless.")]
77-
publicvoidCollect(boolforceDispose)=>this.DisposeAll();
7876
publicvoidCollect()=>this.DisposeAll();
7977

8078
internalvoidThrottledCollect()
@@ -85,14 +83,14 @@ internal void ThrottledCollect()
8583
this.Collect();
8684
}
8785

88-
publicList<WeakReference>GetCollectedObjects()
86+
internalList<IntPtr>GetCollectedObjects()
8987
{
90-
return_objQueue.Select(T=>newWeakReference(T)).ToList();
88+
return_objQueue.ToList();
9189
}
9290

93-
internalvoidAddFinalizedObject(IPyDisposableobj)
91+
internalvoidAddFinalizedObject(refIntPtrobj)
9492
{
95-
if(!Enable)
93+
if(!Enable||obj==IntPtr.Zero)
9694
{
9795
return;
9896
}
@@ -103,6 +101,7 @@ internal void AddFinalizedObject(IPyDisposable obj)
103101
{
104102
this._objQueue.Enqueue(obj);
105103
}
104+
obj=IntPtr.Zero;
106105
}
107106

108107
internalstaticvoidShutdown()
@@ -123,29 +122,44 @@ private void DisposeAll()
123122
#ifFINALIZER_CHECK
124123
ValidateRefCount();
125124
#endif
126-
IPyDisposableobj;
127-
while(_objQueue.TryDequeue(outobj))
125+
IntPtrobj;
126+
Runtime.PyErr_Fetch(outvarerrType,outvarerrVal,outvartraceback);
127+
128+
try
128129
{
129-
try
130-
{
131-
obj.Dispose();
132-
}
133-
catch(Exceptione)
130+
while(!_objQueue.IsEmpty)
134131
{
135-
varhandler=ErrorHandler;
136-
if(handlerisnull)
132+
if(!_objQueue.TryDequeue(outobj))
133+
continue;
134+
135+
Runtime.XDecref(obj);
136+
try
137137
{
138-
thrownewFinalizationException(
139-
"Python object finalization failed",
140-
disposable:obj,innerException:e);
138+
Runtime.CheckExceptionOccurred();
141139
}
142-
143-
handler.Invoke(this,newErrorArgs()
140+
catch(Exceptione)
144141
{
145-
Error=e
146-
});
142+
varhandler=ErrorHandler;
143+
if(handlerisnull)
144+
{
145+
thrownewFinalizationException(
146+
"Python object finalization failed",
147+
disposable:obj,innerException:e);
148+
}
149+
150+
handler.Invoke(this,newErrorArgs()
151+
{
152+
Error=e
153+
});
154+
}
147155
}
148156
}
157+
finally
158+
{
159+
// Python requires finalizers to preserve exception:
160+
// https://docs.python.org/3/extending/newtypes.html#finalization-and-de-allocation
161+
Runtime.PyErr_Restore(errType,errVal,traceback);
162+
}
149163
}
150164
}
151165

@@ -158,33 +172,26 @@ private void ValidateRefCount()
158172
}
159173
varcounter=newDictionary<IntPtr,long>();
160174
varholdRefs=newDictionary<IntPtr,long>();
161-
varindexer=newDictionary<IntPtr,List<IPyDisposable>>();
175+
varindexer=newDictionary<IntPtr,List<IntPtr>>();
162176
foreach(varobjin_objQueue)
163177
{
164-
IntPtr[]handles=obj.GetTrackedHandles();
165-
foreach(varhandleinhandles)
178+
varhandle=obj;
179+
if(!counter.ContainsKey(handle))
166180
{
167-
if(handle==IntPtr.Zero)
168-
{
169-
continue;
170-
}
171-
if(!counter.ContainsKey(handle))
172-
{
173-
counter[handle]=0;
174-
}
175-
counter[handle]++;
176-
if(!holdRefs.ContainsKey(handle))
177-
{
178-
holdRefs[handle]=Runtime.Refcount(handle);
179-
}
180-
List<IPyDisposable>objs;
181-
if(!indexer.TryGetValue(handle,outobjs))
182-
{
183-
objs=newList<IPyDisposable>();
184-
indexer.Add(handle,objs);
185-
}
186-
objs.Add(obj);
181+
counter[handle]=0;
182+
}
183+
counter[handle]++;
184+
if(!holdRefs.ContainsKey(handle))
185+
{
186+
holdRefs[handle]=Runtime.Refcount(handle);
187+
}
188+
List<IntPtr>objs;
189+
if(!indexer.TryGetValue(handle,outobjs))
190+
{
191+
objs=newList<IntPtr>();
192+
indexer.Add(handle,objs);
187193
}
194+
objs.Add(obj);
188195
}
189196
foreach(varpairincounter)
190197
{
@@ -227,12 +234,13 @@ private void ValidateRefCount()
227234

228235
publicclassFinalizationException:Exception
229236
{
230-
publicIPyDisposableDisposable{get;}
237+
publicIntPtrPythonObject{get;}
231238

232-
publicFinalizationException(stringmessage,IPyDisposabledisposable,ExceptioninnerException)
239+
publicFinalizationException(stringmessage,IntPtrdisposable,ExceptioninnerException)
233240
:base(message,innerException)
234241
{
235-
this.Disposable=disposable??thrownewArgumentNullException(nameof(disposable));
242+
if(disposable==IntPtr.Zero)thrownewArgumentNullException(nameof(disposable));
243+
this.PythonObject=disposable;
236244
}
237245
}
238246
}

‎src/runtime/pybuffer.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespacePython.Runtime
99
{
10-
publicsealedclassPyBuffer:IPyDisposable
10+
publicsealedclassPyBuffer:IDisposable
1111
{
1212
privatePyObject_exporter;
1313
privatePy_buffer_view;
@@ -236,7 +236,7 @@ private void Dispose(bool disposing)
236236
{
237237
return;
238238
}
239-
Finalizer.Instance.AddFinalizedObject(this);
239+
Finalizer.Instance.AddFinalizedObject(ref_view.obj);
240240
}
241241

242242
/// <summary>
@@ -248,10 +248,5 @@ public void Dispose()
248248
Dispose(true);
249249
GC.SuppressFinalize(this);
250250
}
251-
252-
publicIntPtr[]GetTrackedHandles()
253-
{
254-
returnnewIntPtr[]{_view.obj};
255-
}
256251
}
257252
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp