Skip to content

Commit e5b6cb4

Browse files
Respect the GIL.
1 parent 99ffe4b commit e5b6cb4

File tree

4 files changed

+203
-62
lines changed

4 files changed

+203
-62
lines changed

Include/cpython/pystate.h

+47-24
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ struct _py_trashcan {
112112
PyObject *delete_later;
113113
};
114114

115+
// We can't include pythread.h yet (due to include cycles)
116+
// so we duplicate PyThread_type_lock here.
117+
typedef void *PyThread_type_lock;
118+
115119
struct _ts {
116120
/* See Python/ceval.c for comments explaining most fields */
117121

@@ -188,30 +192,49 @@ struct _ts {
188192

189193
struct _py_trashcan trash;
190194

191-
/* Called when a thread state is deleted normally, but not when it
192-
* is destroyed after fork().
193-
* Pain: to prevent rare but fatal shutdown errors (issue 18808),
194-
* Thread.join() must wait for the join'ed thread's tstate to be unlinked
195-
* from the tstate chain. That happens at the end of a thread's life,
196-
* in pystate.c.
197-
* The obvious way doesn't quite work: create a lock which the tstate
198-
* unlinking code releases, and have Thread.join() wait to acquire that
199-
* lock. The problem is that we _are_ at the end of the thread's life:
200-
* if the thread holds the last reference to the lock, decref'ing the
201-
* lock will delete the lock, and that may trigger arbitrary Python code
202-
* if there's a weakref, with a callback, to the lock. But by this time
203-
* _PyRuntime.gilstate.tstate_current is already NULL, so only the simplest
204-
* of C code can be allowed to run (in particular it must not be possible to
205-
* release the GIL).
206-
* So instead of holding the lock directly, the tstate holds a weakref to
207-
* the lock: that's the value of on_delete_data below. Decref'ing a
208-
* weakref is harmless.
209-
* on_delete points to _threadmodule.c's static release_sentinel() function.
210-
* After the tstate is unlinked, release_sentinel is called with the
211-
* weakref-to-lock (on_delete_data) argument, and release_sentinel releases
212-
* the indirectly held lock.
213-
*/
214-
int (*on_delete)(void *);
195+
struct {
196+
/* Called when a thread state is deleted normally, but not when
197+
* it is destroyed after fork().
198+
*
199+
* Pain: to prevent rare but fatal shutdown errors (issue 18808),
200+
* Thread.join() must wait for the join'ed thread's tstate to be
201+
* unlinked from the tstate chain. That happens at the end of a
202+
* thread's life, in pystate.c.
203+
*
204+
* The obvious way doesn't quite work: create a lock which the
205+
* tstate unlinking code releases, and have Thread.join() wait
206+
* to acquire that lock. The problem is that we _are_ at the end
207+
* of the thread's life: if the thread holds the last reference
208+
* to the lock, decref'ing the lock will delete the lock, and
209+
* that may trigger arbitrary Python code if there's a weakref,
210+
* with a callback, to the lock. But by this time the current
211+
* tstate is already NULL, so only the simplest of C code can be
212+
* allowed to run (in particular it must not be possible to
213+
* release the GIL).
214+
*
215+
* So instead of holding the lock directly, the tstate holds
216+
* a weakref to the lock: that's the value of .lock_weakref
217+
* below. Decref'ing a weakref is harmless.
218+
*
219+
* .pre_delete points to the thread_prepare_delete static function
220+
* in _threadmodule.c. It gets called right _before_ the tstate
221+
* is deleted, with the GIL held, and returns the lock to release.
222+
* It also adds a pending call to delete the lock once the lock
223+
* has been released. This works because the pending call won't
224+
* run until another thread of the interpreter takes the GIL.
225+
*
226+
* It is important that the lock be released _after_ the GIL
227+
* is released to avoid a race with threading._shutdown().
228+
*/
229+
// XXX Does all of the above explanation still hold?
230+
PyThread_type_lock (*pre_delete)(PyThreadState *);
231+
PyObject *lock_weakref;
232+
} _threading_thread;
233+
/* These weren't ever meant to be used except internally,
234+
* but we're keeping them around just in case. Internally,
235+
* we use the _threading_thread field. */
236+
/* XXX Drop them! */
237+
void (*on_delete)(void *);
215238
void *on_delete_data;
216239

217240
int coroutine_origin_tracking_depth;

Modules/_threadmodule.c

+108-29
Original file line numberDiff line numberDiff line change
@@ -1307,57 +1307,136 @@ yet finished.\n\
13071307
This function is meant for internal and specialized purposes only.\n\
13081308
In most applications `threading.enumerate()` should be used instead.");
13091309

1310+
1311+
/* Here we're essentinally cleaning up after a thread that finished
1312+
and has aleady been deallocated (both the threading.Thread and
1313+
the tstate). Thus it will run in a different thread with the
1314+
same interpreter (via a "pending call"). */
13101315
static int
1311-
release_sentinel(void *wr_raw)
1316+
clean_up_sentinel(void *data)
13121317
{
1313-
PyObject *wr = _PyObject_CAST(wr_raw);
1314-
/* Tricky: this function is called when the current thread state
1315-
is being deleted. Therefore, only simple C code can safely
1316-
execute here. */
1317-
PyObject *obj = PyWeakref_GET_OBJECT(wr);
1318-
lockobject *lock;
1319-
if (obj != Py_None) {
1320-
lock = (lockobject *) obj;
1321-
if (lock->locked) {
1322-
PyThread_release_lock(lock->lock_lock);
1323-
lock->locked = 0;
1318+
PyObject *lockobj = (PyObject *)data;
1319+
assert(lockobj != NULL);
1320+
PyThread_type_lock lock = ((lockobject *)lockobj)->lock_lock;
1321+
1322+
/* Wait here until we know for sure that he thread running
1323+
_PyThreadState_DeleteCurrent() has released the lock. */
1324+
if (acquire_timed(lock, 0) == PY_LOCK_ACQUIRED) {
1325+
/* _PyThreadState_DeleteCurrent() finished, so we can proceed. */
1326+
PyThread_release_lock(lock);
1327+
((lockobject *)lockobj)->locked = 0;
1328+
}
1329+
else if (((lockobject *)lockobj)->locked == 2) {
1330+
/* _PyThreadState_DeleteCurrent() is still holding
1331+
the lock, so we will wait here until it is released.
1332+
We don't need to hold the GIL while we wait though. */
1333+
((lockobject *)lockobj)->locked = 1;
1334+
1335+
PyLockStatus r = acquire_timed(lock, -1);
1336+
// XXX Why do we have to loop?
1337+
while (r == PY_LOCK_FAILURE) {
1338+
r = acquire_timed(lock, -1);
13241339
}
1340+
PyThread_release_lock(lock);
1341+
((lockobject *)lockobj)->locked = 0;
13251342
}
1343+
/* Otherwise the current thread acquired the lock right before
1344+
its eval loop was interrupted to run this pending call.
1345+
We can simply let it proceed. */
1346+
1347+
/* In all cases, at this point we are done with the lock. */
1348+
Py_DECREF(lockobj);
1349+
return 0;
1350+
}
1351+
1352+
static PyThread_type_lock
1353+
thread_prepare_delete(PyThreadState *tstate)
1354+
{
1355+
assert(tstate->_threading_thread.pre_delete != NULL);
1356+
PyThread_type_lock lock = NULL;
1357+
1358+
/* Tricky: this function is called when the current thread state
1359+
is being deleted. Therefore, only simple C code can safely
1360+
execute here. The GIL is still held. */
1361+
1362+
PyObject *wr = tstate->_threading_thread.lock_weakref;
1363+
assert(wr != NULL);
1364+
PyObject *lockobj = PyWeakref_GET_OBJECT(wr);
1365+
if (lockobj == Py_None) {
1366+
/* The thread has already been destroyed, so we can clean up now. */
1367+
goto done;
1368+
}
1369+
if (_PyThreadState_GET() != tstate) {
1370+
assert(PyThread_get_thread_ident() != tstate->thread_id);
1371+
/* It must be a daemon thread that was killed during
1372+
* interp/runtime finalization, so there's nothing to do. */
1373+
goto done;
1374+
}
1375+
assert(((lockobject *)lockobj)->locked == 1);
1376+
assert(acquire_timed(((lockobject *)lockobj)->lock_lock, 0) == PY_LOCK_FAILURE);
1377+
/* We cheat a little here to allow clean_up_sentinel() to know
1378+
that this thread is still holding the lock. The value will be
1379+
reset to the normal 0 or 1 as soon as any other thread
1380+
uses the lock. */
1381+
((lockobject *)lockobj)->locked = 2;
1382+
1383+
/* We need to prevent the underlying PyThread_type_lock from getting
1384+
destroyed before we release it in _PyThreadState_DeleteCurrent(),
1385+
However, we don't need the weakref any more. */
1386+
Py_INCREF(lockobj);
1387+
1388+
/* The pending call will be run the next time the GIL is taken
1389+
by one of this interpreter's threads. */
1390+
void *data = (void *) lockobj;
1391+
if (Py_AddPendingCall(clean_up_sentinel, data) < 0) {
1392+
Py_DECREF(lockobj);
1393+
/* We otherwise ignore the error. A non-zero value means
1394+
there were too many pending calls already queued up.
1395+
This case is unlikely, and, at worst,
1396+
we'll just leak the lock.
1397+
*/
1398+
goto done;
1399+
}
1400+
1401+
lock = ((lockobject *)lockobj)->lock_lock;
1402+
1403+
done:
13261404
/* Deallocating a weakref with a NULL callback only calls
13271405
PyObject_GC_Del(), which can't call any Python code. */
13281406
Py_DECREF(wr);
1329-
return 0;
1407+
tstate->_threading_thread.pre_delete = NULL;
1408+
tstate->_threading_thread.lock_weakref = NULL;
1409+
return lock;
13301410
}
13311411

13321412
static PyObject *
13331413
thread__set_sentinel(PyObject *module, PyObject *Py_UNUSED(ignored))
13341414
{
1335-
PyObject *wr;
13361415
PyThreadState *tstate = _PyThreadState_GET();
1337-
lockobject *lock;
13381416

1339-
if (tstate->on_delete_data != NULL) {
1417+
if (tstate->_threading_thread.lock_weakref != NULL) {
13401418
/* We must support the re-creation of the lock from a
13411419
fork()ed child. */
1342-
assert(tstate->on_delete == &release_sentinel);
1343-
wr = (PyObject *) tstate->on_delete_data;
1344-
tstate->on_delete = NULL;
1345-
tstate->on_delete_data = NULL;
1346-
Py_DECREF(wr);
1347-
}
1348-
lock = newlockobject(module);
1349-
if (lock == NULL)
1420+
assert(tstate->_threading_thread.pre_delete == &thread_prepare_delete);
1421+
tstate->_threading_thread.pre_delete = NULL;
1422+
tstate->_threading_thread.lock_weakref = NULL;
1423+
Py_DECREF(tstate->_threading_thread.lock_weakref);
1424+
}
1425+
1426+
PyObject *lockobj = (PyObject *) newlockobject(module);
1427+
if (lockobj == NULL) {
13501428
return NULL;
1429+
}
13511430
/* The lock is owned by whoever called _set_sentinel(), but the weakref
13521431
hangs to the thread state. */
1353-
wr = PyWeakref_NewRef((PyObject *) lock, NULL);
1432+
PyObject *wr = PyWeakref_NewRef(lockobj, NULL);
13541433
if (wr == NULL) {
1355-
Py_DECREF(lock);
1434+
Py_DECREF(lockobj);
13561435
return NULL;
13571436
}
1358-
tstate->on_delete_data = (void *) wr;
1359-
tstate->on_delete = &release_sentinel;
1360-
return (PyObject *) lock;
1437+
tstate->_threading_thread.pre_delete = &thread_prepare_delete;
1438+
tstate->_threading_thread.lock_weakref = wr;
1439+
return lockobj;
13611440
}
13621441

13631442
PyDoc_STRVAR(_set_sentinel_doc,

Python/pystate.c

+40-8
Original file line numberDiff line numberDiff line change
@@ -1490,6 +1490,22 @@ PyThreadState_Clear(PyThreadState *tstate)
14901490

14911491
Py_CLEAR(tstate->context);
14921492

1493+
if (tstate != current_fast_get(&_PyRuntime)) {
1494+
/* The "current" case is handled in _PyThreadState_DeleteCurrent(). */
1495+
if (tstate->_threading_thread.pre_delete != NULL) {
1496+
#ifdef NDEBUG
1497+
(void) tstate->_threading_thread.pre_delete(tstate);
1498+
#else
1499+
PyThread_type_lock lock;
1500+
lock = tstate->_threading_thread.pre_delete(tstate);
1501+
assert(lock == NULL);
1502+
#endif
1503+
}
1504+
}
1505+
if (tstate->on_delete != NULL) {
1506+
tstate->on_delete(tstate->on_delete_data);
1507+
}
1508+
14931509
tstate->_status.cleared = 1;
14941510

14951511
// XXX Call _PyThreadStateSwap(runtime, NULL) here if "current".
@@ -1501,6 +1517,7 @@ static void
15011517
tstate_delete_common(PyThreadState *tstate)
15021518
{
15031519
assert(tstate->_status.cleared && !tstate->_status.finalized);
1520+
// XXX assert(tstate->_threading_thread.pre_delete == NULL);
15041521

15051522
PyInterpreterState *interp = tstate->interp;
15061523
if (interp == NULL) {
@@ -1561,18 +1578,33 @@ void
15611578
_PyThreadState_DeleteCurrent(PyThreadState *tstate)
15621579
{
15631580
_Py_EnsureTstateNotNULL(tstate);
1564-
/* We ignore the return value. A non-zero value means there were
1565-
too many pending calls already queued up. This case is unlikely,
1566-
and, at worst, we'll leak on_delete_data.
1567-
Also, note that the pending call will be run the next time the
1568-
GIL is taken by one of this interpreter's threads. So it won't
1569-
happen until after the _PyEval_ReleaseLock() call below. */
1570-
(void)_PyEval_AddPendingCall(tstate->interp,
1571-
tstate->on_delete, tstate->on_delete_data);
1581+
1582+
PyThread_type_lock lock = NULL;
1583+
if (tstate->_threading_thread.pre_delete != NULL) {
1584+
/* This may queue up a pending call that will run in a
1585+
different thread in the same interpreter. It will only
1586+
run _after_ we've released the GIL below.
1587+
1588+
lock will be NULL if the threading.Thread has already been
1589+
destroyed, if there are too many pending calls already queued
1590+
up, or if _PyThreadState_DeleteCurrent() wasn't called by
1591+
thread_run() (in _threadmodule.c).
1592+
*/
1593+
lock = tstate->_threading_thread.pre_delete(tstate);
1594+
}
15721595

15731596
tstate_delete_common(tstate);
15741597
current_fast_clear(tstate->interp->runtime);
15751598
_PyEval_ReleaseLock(tstate);
1599+
1600+
if (lock != NULL) {
1601+
/* Notify threading._shutdown() that this thread has been finalized.
1602+
This must happen *after* the GIL is released,
1603+
to avoid a race with threading._shutdown()
1604+
(via wait_for_thread_shutdown() in pylifecycle.c).. */
1605+
PyThread_release_lock(lock);
1606+
}
1607+
15761608
free_threadstate(tstate);
15771609
}
15781610

Tools/c-analyzer/c_analyzer/analyze.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,14 @@ def find_typedecl(decl, typespec, typespecs):
174174
# If the decl is in a source file then we expect the
175175
# type to be in the same file or in a header file.
176176
continue
177-
candidates.append(typedecl)
177+
for c in candidates:
178+
if c.name == typedecl.name and c.data == typedecl.data:
179+
# The type was duplicated in another file.
180+
assert c.parent == typedecl.parent
181+
break
182+
else:
183+
candidates.append(typedecl)
184+
178185
if not candidates:
179186
return None
180187
elif len(candidates) == 1:

0 commit comments

Comments
 (0)