Skip to content

Commit e2f42c4

Browse files
committed
Fix using PyGILState_Check in unattached thread
1 parent d7bafe8 commit e2f42c4

File tree

4 files changed

+48
-18
lines changed

4 files changed

+48
-18
lines changed

graalpython/com.oracle.graal.python.cext/include/pystate.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2018, 2023, Oracle and/or its affiliates.
1+
/* Copyright (c) 2018, 2024, Oracle and/or its affiliates.
22
* Copyright (C) 1996-2020 Python Software Foundation
33
*
44
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -39,6 +39,7 @@ PyAPI_FUNC(PyObject *) PyInterpreterState_GetDict(PyInterpreterState *);
3939
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000
4040
/* New in 3.7 */
4141
PyAPI_FUNC(int64_t) PyInterpreterState_GetID(PyInterpreterState *);
42+
// GraalPy-specific
4243
PyAPI_FUNC(int64_t) PyInterpreterState_GetIDFromThreadState(PyThreadState *);
4344
#endif
4445
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000
@@ -79,8 +80,9 @@ PyAPI_FUNC(PyFrameObject*) PyThreadState_GetFrame(PyThreadState *tstate);
7980
PyAPI_FUNC(uint64_t) PyThreadState_GetID(PyThreadState *tstate);
8081
#endif
8182

82-
/* GraalVM change: we need more state bits */
83-
typedef int PyGILState_STATE;
83+
typedef
84+
enum {PyGILState_LOCKED, PyGILState_UNLOCKED}
85+
PyGILState_STATE;
8486

8587

8688
/* Ensure that the current thread is ready to call the Python

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

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -137,29 +137,56 @@ int PyState_RemoveModule(struct PyModuleDef* def) {
137137
return 0;
138138
}
139139

140-
#define _PYGILSTATE_LOCKED 0x1
141-
#define _PYGILSTATE_ATTACHED 0x2
142-
143-
PyAPI_FUNC(PyGILState_STATE) PyGILState_Ensure() {
144-
int result = 0;
140+
int
141+
PyGILState_Check()
142+
{
143+
int attached = 0;
144+
/*
145+
* PyGILState_Check is allowed to be called from a new thread that didn't yet setup the GIL.
146+
* If we don't attach the thread ourselves, the upcall will work because NFI will attach
147+
* the thread automatically, but it won't create the context which would then break
148+
* subsequent PyGILState_Ensure.
149+
*/
145150
if (TRUFFLE_CONTEXT) {
146151
if ((*TRUFFLE_CONTEXT)->getTruffleEnv(TRUFFLE_CONTEXT) == NULL) {
147152
(*TRUFFLE_CONTEXT)->attachCurrentThread(TRUFFLE_CONTEXT);
148-
result |= _PYGILSTATE_ATTACHED;
153+
attached = 1;
149154
}
150155
}
151-
int locked = GraalPyTruffleGILState_Ensure();
152-
if (locked) {
153-
result |= _PYGILSTATE_LOCKED;
156+
int ret = GraalPyTruffleGILState_Check();
157+
if (attached) {
158+
(*TRUFFLE_CONTEXT)->detachCurrentThread(TRUFFLE_CONTEXT);
159+
}
160+
return ret;
161+
}
162+
163+
static THREAD_LOCAL int graalpy_attached_thread = 0;
164+
static THREAD_LOCAL int graalpy_gilstate_counter = 0;
165+
166+
PyGILState_STATE
167+
PyGILState_Ensure(void)
168+
{
169+
if (TRUFFLE_CONTEXT) {
170+
if ((*TRUFFLE_CONTEXT)->getTruffleEnv(TRUFFLE_CONTEXT) == NULL) {
171+
(*TRUFFLE_CONTEXT)->attachCurrentThread(TRUFFLE_CONTEXT);
172+
graalpy_attached_thread = 1;
173+
}
174+
graalpy_gilstate_counter++;
154175
}
155-
return result;
176+
return GraalPyTruffleGILState_Ensure() ? PyGILState_UNLOCKED : PyGILState_LOCKED;
156177
}
157178

158-
PyAPI_FUNC(void) PyGILState_Release(PyGILState_STATE state) {
159-
if (state & _PYGILSTATE_LOCKED) {
179+
void
180+
PyGILState_Release(PyGILState_STATE oldstate)
181+
{
182+
if (oldstate == PyGILState_UNLOCKED) {
160183
GraalPyTruffleGILState_Release();
161184
}
162-
if (TRUFFLE_CONTEXT && (state & _PYGILSTATE_ATTACHED)) {
163-
(*TRUFFLE_CONTEXT)->detachCurrentThread(TRUFFLE_CONTEXT);
185+
if (TRUFFLE_CONTEXT) {
186+
graalpy_gilstate_counter--;
187+
if (graalpy_gilstate_counter == 0 && graalpy_attached_thread) {
188+
(*TRUFFLE_CONTEXT)->detachCurrentThread(TRUFFLE_CONTEXT);
189+
graalpy_attached_thread = 0;
190+
}
164191
}
165192
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
public final class PythonCextPyStateBuiltins {
7272

7373
@CApiBuiltin(ret = Int, args = {}, acquiresGIL = false, call = Direct)
74-
abstract static class PyGILState_Check extends CApiNullaryBuiltinNode {
74+
abstract static class PyTruffleGILState_Check extends CApiNullaryBuiltinNode {
7575

7676
@Specialization
7777
Object check() {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ public final class CApiFunction {
186186
/*
187187
* Functions that are implemented as C code that can be executed both in native and in Sulong:
188188
*/
189+
@CApiBuiltin(name = "PyGILState_Check", ret = Int, args = {}, acquiresGIL = false, call = CImpl)
189190
@CApiBuiltin(name = "PyArg_Parse", ret = Int, args = {PyObject, ConstCharPtrAsTruffleString, VARARGS}, call = CImpl)
190191
@CApiBuiltin(name = "PyArg_ParseTuple", ret = Int, args = {PyObject, ConstCharPtrAsTruffleString, VARARGS}, call = CImpl)
191192
@CApiBuiltin(name = "PyArg_ParseTupleAndKeywords", ret = Int, args = {PyObject, PyObject, ConstCharPtrAsTruffleString, CHAR_PTR_LIST, VARARGS}, call = CImpl)

0 commit comments

Comments
 (0)