Skip to content

Commit a680be5

Browse files
committed
Tighten native thread state fallback
1 parent 34d72e4 commit a680be5

6 files changed

Lines changed: 53 additions & 23 deletions

File tree

graalpython/com.oracle.graal.python.cext/src/pystate.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,16 @@ extern "C" {
8383
static inline PyThreadState *
8484
_get_thread_state() {
8585
PyThreadState *ts = tstate_current;
86+
if (UNLIKELY(ts == NULL)) {
87+
/*
88+
* Very unlikely fallback: this can happen if another thread initializes the C API while
89+
* the current thread is attached to Python but blocked and therefore misses eager
90+
* initialization of its native 'tstate_current' TLS slot.
91+
*/
92+
ts = GraalPyPrivate_ThreadState_Get(&tstate_current);
93+
assert(ts != NULL);
94+
tstate_current = ts;
95+
}
8696
assert(ts != NULL);
8797
return ts;
8898
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPyStateBuiltins.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Direct;
4444
import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Ignored;
4545
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Int;
46+
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Pointer;
4647
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyFrameObjectTransfer;
4748
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObject;
4849
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectBorrowed;
@@ -112,6 +113,42 @@ static Object restore(
112113
}
113114
}
114115

116+
/**
117+
* Very unlikely fallback for threads that were already attached when another thread initialized
118+
* the C API, but were blocked at that time and therefore could not process the thread-local
119+
* action that eagerly initializes their native 'tstate_current' TLS slot.
120+
*/
121+
@CApiBuiltin(ret = PyThreadState, args = {Pointer}, acquireGil = false, call = Ignored)
122+
abstract static class GraalPyPrivate_ThreadState_Get extends CApiUnaryBuiltinNode {
123+
private static final TruffleLogger LOGGER = CApiContext.getLogger(GraalPyPrivate_ThreadState_Get.class);
124+
125+
@Specialization
126+
@TruffleBoundary
127+
static Object get(Object tstateCurrentPtr) {
128+
PythonContext context = PythonContext.get(null);
129+
PythonThreadState threadState = context.getThreadState(context.getLanguage());
130+
131+
/*
132+
* The C caller may have observed 'tstate_current == NULL' before entering this upcall.
133+
* While entering this builtin, the same thread may process a queued thread-local action
134+
* from C API initialization and initialize its native thread state eagerly. So the
135+
* fallback decision made in C can be stale by the time we get here.
136+
*/
137+
if (threadState.isNativeThreadStateInitialized()) {
138+
LOGGER.fine(() -> String.format("Lazy initialization attempt of native thread state for thread %s aborted. Was initialized in the meantime.", Thread.currentThread()));
139+
Object nativeThreadState = PThreadState.getNativeThreadState(threadState);
140+
assert nativeThreadState != null;
141+
return nativeThreadState;
142+
}
143+
144+
LOGGER.fine(() -> "Lazy (fallback) initialization of native thread state for thread " + Thread.currentThread());
145+
assert PThreadState.getNativeThreadState(threadState) == null;
146+
Object nativeThreadState = PThreadState.getOrCreateNativeThreadState(threadState);
147+
threadState.setNativeThreadLocalVarPointer(tstateCurrentPtr);
148+
return nativeThreadState;
149+
}
150+
}
151+
115152
@CApiBuiltin(ret = Void, args = {}, call = Ignored)
116153
abstract static class GraalPyPrivate_BeforeThreadDetach extends CApiNullaryBuiltinNode {
117154
@Specialization

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ protected void perform(ThreadLocalAction.Access access) {
859859
context.initializeNativeThreadState();
860860
}
861861
};
862-
submitThreadLocalActions(context, threads, action);
862+
context.getEnv().submitThreadLocal(threads, action);
863863
}
864864

865865
private static Thread[] getOtherAliveAttachedThreads(PythonContext context) {
@@ -873,12 +873,6 @@ private static Thread[] getOtherAliveAttachedThreads(PythonContext context) {
873873
return threads.toArray(Thread[]::new);
874874
}
875875

876-
private static void submitThreadLocalActions(PythonContext context, Thread[] threads, ThreadLocalAction action) {
877-
for (Thread thread : threads) {
878-
context.getEnv().submitThreadLocal(new Thread[]{thread}, action);
879-
}
880-
}
881-
882876
private static CApiContext loadCApi(Node node, PythonContext context, TruffleString name, TruffleString path, String reason) throws IOException, ImportException, ApiInitException {
883877
Env env = context.getEnv();
884878
InteropLibrary U = InteropLibrary.getUncached();

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -853,15 +853,6 @@ static Object doWithoutContext(NativeCAPISymbol symbol, Object[] args,
853853
if (capiState != PythonContext.CApiState.INITIALIZING && capiState != PythonContext.CApiState.INITIALIZED) {
854854
CompilerDirectives.transferToInterpreterAndInvalidate();
855855
CApiContext.ensureCapiWasLoaded("call internal native GraalPy function");
856-
capiState = pythonContext.getCApiState();
857-
}
858-
if (capiState == PythonContext.CApiState.INITIALIZED) {
859-
PythonContext.PythonThreadState threadState = pythonContext.getThreadState(pythonContext.getLanguage(inliningTarget));
860-
// Native thread-state bootstrap may itself call internal C API helpers before
861-
// 'tstate_current' has been published for this thread.
862-
if (!threadState.isNativeThreadStateInitializationInProgress()) {
863-
pythonContext.ensureNativeThreadStateInitialized(threadState);
864-
}
865856
}
866857
// TODO review EnsureTruffleStringNode with GR-37896
867858
Object callable = CApiContext.getNativeSymbol(inliningTarget, symbol);

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,6 @@ private static Object invoke(VirtualFrame frame, PythonContext ctx, CApiTiming t
804804
CheckFunctionResultNode checkResultNode, CExtToJavaNode convertReturnValue, PForeignToPTypeNode fromForeign, GetThreadStateNode getThreadStateNode,
805805
ExternalFunctionInvokeNode invokeNode) {
806806
PythonThreadState threadState = getThreadStateNode.execute(inliningTarget, ctx);
807-
ctx.ensureNativeThreadStateInitialized(threadState);
808807
Object result = invokeNode.execute(frame, inliningTarget, threadState, timing, name, callable, cArguments);
809808
result = checkResultNode.execute(threadState, name, result);
810809
if (convertReturnValue != null) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,10 @@ public void setNativeThreadLocalVarPointer(Object ptr) {
593593
this.nativeThreadLocalVarPointer = ptr;
594594
}
595595

596+
public Object getNativeThreadLocalVarPointer() {
597+
return nativeThreadLocalVarPointer;
598+
}
599+
596600
public boolean isNativeThreadStateInitialized() {
597601
return nativeThreadLocalVarPointer != null;
598602
}
@@ -2604,15 +2608,10 @@ public synchronized void attachThread(Thread thread, ContextThreadLocal<PythonTh
26042608

26052609
@TruffleBoundary
26062610
public void initializeNativeThreadState() {
2611+
LOGGER.fine(() -> "Initializing native thread state for thread " + Thread.currentThread());
26072612
initializeNativeThreadState(getThreadState(getLanguage()));
26082613
}
26092614

2610-
public void ensureNativeThreadStateInitialized(PythonThreadState pythonThreadState) {
2611-
if (getCApiState() == CApiState.INITIALIZED && !pythonThreadState.isNativeThreadStateInitialized()) {
2612-
initializeNativeThreadState(pythonThreadState);
2613-
}
2614-
}
2615-
26162615
@SuppressWarnings("try")
26172616
public void initializeNativeThreadState(PythonThreadState pythonThreadState) {
26182617
CompilerAsserts.neverPartOfCompilation();

0 commit comments

Comments
 (0)