Skip to content

Commit 40cecb3

Browse files
committed
Stash exception state when calling destructors
1 parent 59a107c commit 40cecb3

File tree

2 files changed

+94
-55
lines changed

2 files changed

+94
-55
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PThreadState.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory;
5050
import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
5151
import com.oracle.graal.python.builtins.objects.dict.PDict;
52+
import com.oracle.graal.python.nodes.object.GetClassNode;
5253
import com.oracle.graal.python.runtime.PythonContext;
5354
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
5455
import com.oracle.truffle.api.CompilerDirectives;
@@ -113,6 +114,11 @@ private static Object allocateCLayout(PythonThreadState threadState) {
113114
writePtrNode.write(ptr, CFields.PyThreadState__dict, toNative.execute(threadStateDict));
114115
CApiContext cApiContext = PythonContext.get(null).getCApiContext();
115116
writePtrNode.write(ptr, CFields.PyThreadState__small_ints, cApiContext.getOrCreateSmallInts());
117+
if (threadState.getCurrentException() != null) {
118+
// See TransformExceptionToNativeNode
119+
Object exceptionType = GetClassNode.executeUncached(threadState.getCurrentException().getUnreifiedException());
120+
CStructAccess.WritePointerNode.getUncached().write(ptr, CFields.PyThreadState__curexc_type, PythonToNativeNode.getUncached().execute(exceptionType));
121+
}
116122
return ptr;
117123
}
118124
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/CApiTransitions.java

Lines changed: 88 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromCharPointerNode;
6464
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
6565
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
66+
import com.oracle.graal.python.builtins.objects.cext.capi.PThreadState;
6667
import com.oracle.graal.python.builtins.objects.cext.capi.PrimitiveNativeWrapper;
6768
import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper;
6869
import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper;
@@ -85,13 +86,15 @@
8586
import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
8687
import com.oracle.graal.python.builtins.objects.floats.PFloat;
8788
import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorDeleteMarker;
89+
import com.oracle.graal.python.builtins.objects.traceback.LazyTraceback;
8890
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
8991
import com.oracle.graal.python.nodes.PGuards;
9092
import com.oracle.graal.python.nodes.PNodeWithContext;
9193
import com.oracle.graal.python.nodes.PRaiseNode;
9294
import com.oracle.graal.python.nodes.object.GetClassNode;
9395
import com.oracle.graal.python.runtime.GilNode;
9496
import com.oracle.graal.python.runtime.PythonContext;
97+
import com.oracle.graal.python.runtime.exception.PException;
9598
import com.oracle.graal.python.runtime.sequence.storage.NativeSequenceStorage;
9699
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
97100
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage.StorageType;
@@ -355,69 +358,99 @@ public static void registerNativeSequenceStorage(NativeSequenceStorage storage)
355358
@TruffleBoundary
356359
@SuppressWarnings("try")
357360
public static void pollReferenceQueue() {
358-
HandleContext context = getContext();
359-
if (!context.referenceQueuePollActive) {
361+
PythonContext context = PythonContext.get(null);
362+
HandleContext handleContext = context.nativeContext;
363+
if (!handleContext.referenceQueuePollActive) {
360364
try (GilNode.UncachedAcquire ignored = GilNode.uncachedAcquire()) {
361-
ReferenceQueue<Object> queue = context.referenceQueue;
365+
ReferenceQueue<Object> queue = handleContext.referenceQueue;
362366
int count = 0;
363367
long start = 0;
364-
NativeObjectReferenceArrayWrapper referencesToBeFreed = context.referencesToBeFreed;
365-
while (true) {
366-
Object entry = queue.poll();
367-
if (entry == null) {
368-
if (count > 0) {
369-
assert context.referenceQueuePollActive;
370-
releaseNativeObjects(referencesToBeFreed);
371-
context.referenceQueuePollActive = false;
372-
LOGGER.fine("collected " + count + " references from native reference queue in " + ((System.nanoTime() - start) / 1000000) + "ms");
373-
}
374-
return;
375-
}
376-
if (count == 0) {
377-
assert !context.referenceQueuePollActive;
378-
context.referenceQueuePollActive = true;
379-
start = System.nanoTime();
380-
} else {
381-
assert context.referenceQueuePollActive;
368+
NativeObjectReferenceArrayWrapper referencesToBeFreed = handleContext.referencesToBeFreed;
369+
PythonContext.PythonThreadState threadState = context.getThreadState(context.getLanguage());
370+
/*
371+
* There can be an active exception. Since we might be calling arbitary python, we
372+
* need to stash it.
373+
*/
374+
PException savedException = null;
375+
LazyTraceback savedTraceback = null;
376+
Object savedNativeException = null;
377+
if (threadState.getCurrentException() != null) {
378+
savedException = threadState.getCurrentException();
379+
savedTraceback = threadState.getCurrentTraceback();
380+
threadState.clearCurrentException();
381+
Object nativeThreadState = PThreadState.getNativeThreadState(threadState);
382+
if (nativeThreadState != null) {
383+
savedNativeException = CStructAccess.ReadPointerNode.getUncached().read(nativeThreadState, CFields.PyThreadState__curexc_type);
384+
CStructAccess.WritePointerNode.getUncached().write(nativeThreadState, CFields.PyThreadState__curexc_type, 0L);
382385
}
383-
count++;
384-
if (entry instanceof PythonObjectReference reference) {
385-
LOGGER.fine(() -> PythonUtils.formatJString("releasing %s", reference.toString()));
386-
if (HandlePointerConverter.pointsToPyHandleSpace(reference.pointer)) {
387-
assert nativeStubLookupGet(context, reference.pointer, reference.handleTableIndex) != null : Long.toHexString(reference.pointer);
388-
nativeStubLookupRemove(context, reference);
389-
/*
390-
* We may only free native object stubs if their reference count is
391-
* zero. We cannot free other structs (e.g. PyDateTime_CAPI) because we
392-
* don't know if they are still used from native code. Those must be
393-
* free'd at context finalization.
394-
*/
395-
long stubPointer = HandlePointerConverter.pointerToStub(reference.pointer);
396-
if (subNativeRefCount(stubPointer, MANAGED_REFCNT) == 0) {
397-
freeNativeStub(stubPointer);
398-
} else {
399-
/*
400-
* In this case, the object is no longer referenced from managed but
401-
* still from native code (since the reference count is greater 0).
402-
* We therefore need to overwrite the 'CFields.GraalPyObject__id'
403-
* field because there may be referenced from managed in the future
404-
* and then we would incorrectly reuse the ID.
405-
*/
406-
CStructAccess.WriteIntNode.writeUncached(reference.pointer, CFields.GraalPyObject__handle_table_index, 0);
386+
}
387+
try {
388+
while (true) {
389+
Object entry = queue.poll();
390+
if (entry == null) {
391+
if (count > 0) {
392+
assert handleContext.referenceQueuePollActive;
393+
releaseNativeObjects(referencesToBeFreed);
394+
handleContext.referenceQueuePollActive = false;
395+
LOGGER.fine("collected " + count + " references from native reference queue in " + ((System.nanoTime() - start) / 1000000) + "ms");
407396
}
397+
return;
398+
}
399+
if (count == 0) {
400+
assert !handleContext.referenceQueuePollActive;
401+
handleContext.referenceQueuePollActive = true;
402+
start = System.nanoTime();
408403
} else {
409-
assert nativeLookupGet(context, reference.pointer) != null : Long.toHexString(reference.pointer);
410-
nativeLookupRemove(context, reference.pointer);
411-
if (reference.freeAtCollection) {
412-
freeNativeStruct(reference);
404+
assert handleContext.referenceQueuePollActive;
405+
}
406+
count++;
407+
if (entry instanceof PythonObjectReference reference) {
408+
LOGGER.fine(() -> PythonUtils.formatJString("releasing %s", reference.toString()));
409+
if (HandlePointerConverter.pointsToPyHandleSpace(reference.pointer)) {
410+
assert nativeStubLookupGet(handleContext, reference.pointer, reference.handleTableIndex) != null : Long.toHexString(reference.pointer);
411+
nativeStubLookupRemove(handleContext, reference);
412+
/*
413+
* We may only free native object stubs if their reference count is
414+
* zero. We cannot free other structs (e.g. PyDateTime_CAPI) because
415+
* we don't know if they are still used from native code. Those must
416+
* be free'd at context finalization.
417+
*/
418+
long stubPointer = HandlePointerConverter.pointerToStub(reference.pointer);
419+
if (subNativeRefCount(stubPointer, MANAGED_REFCNT) == 0) {
420+
freeNativeStub(stubPointer);
421+
} else {
422+
/*
423+
* In this case, the object is no longer referenced from managed
424+
* but still from native code (since the reference count is
425+
* greater 0). We therefore need to overwrite the
426+
* 'CFields.GraalPyObject__id' field because there may be
427+
* referenced from managed in the future and then we would
428+
* incorrectly reuse the ID.
429+
*/
430+
CStructAccess.WriteIntNode.writeUncached(reference.pointer, CFields.GraalPyObject__handle_table_index, 0);
431+
}
432+
} else {
433+
assert nativeLookupGet(handleContext, reference.pointer) != null : Long.toHexString(reference.pointer);
434+
nativeLookupRemove(handleContext, reference.pointer);
435+
if (reference.freeAtCollection) {
436+
freeNativeStruct(reference);
437+
}
413438
}
439+
} else if (entry instanceof NativeObjectReference reference) {
440+
nativeLookupRemove(handleContext, reference.pointer);
441+
processNativeObjectReference(reference, referencesToBeFreed);
442+
} else if (entry instanceof NativeStorageReference reference) {
443+
handleContext.nativeStorageReferences.remove(reference);
444+
processNativeStorageReference(reference);
445+
}
446+
}
447+
} finally {
448+
if (savedException != null) {
449+
threadState.setCurrentException(savedException, savedTraceback);
450+
Object nativeThreadState = PThreadState.getNativeThreadState(threadState);
451+
if (nativeThreadState != null) {
452+
CStructAccess.WritePointerNode.getUncached().write(nativeThreadState, CFields.PyThreadState__curexc_type, savedNativeException);
414453
}
415-
} else if (entry instanceof NativeObjectReference reference) {
416-
nativeLookupRemove(context, reference.pointer);
417-
processNativeObjectReference(reference, referencesToBeFreed);
418-
} else if (entry instanceof NativeStorageReference reference) {
419-
context.nativeStorageReferences.remove(reference);
420-
processNativeStorageReference(reference);
421454
}
422455
}
423456
}

0 commit comments

Comments
 (0)