bpo-39877: Fix PyEval_RestoreThread() for daemon threads (GH-18811) · python/cpython@eb4e2ae (original) (raw)
`@@ -188,6 +188,25 @@ static size_t opcache_global_misses = 0;
`
188
188
`#include "pythread.h"
`
189
189
`#include "ceval_gil.h"
`
190
190
``
``
191
`+
static void
`
``
192
`+
ensure_tstate_not_null(const char *func, PyThreadState *tstate)
`
``
193
`+
{
`
``
194
`+
if (tstate == NULL) {
`
``
195
`+
_Py_FatalErrorFunc(func, "current thread state is NULL");
`
``
196
`+
}
`
``
197
`+
}
`
``
198
+
``
199
+
``
200
`+
#ifndef NDEBUG
`
``
201
`+
static int is_tstate_valid(PyThreadState *tstate)
`
``
202
`+
{
`
``
203
`+
assert(!_PyMem_IsPtrFreed(tstate));
`
``
204
`+
assert(!_PyMem_IsPtrFreed(tstate->interp));
`
``
205
`+
return 1;
`
``
206
`+
}
`
``
207
`+
#endif
`
``
208
+
``
209
+
191
210
`int
`
192
211
`PyEval_ThreadsInitialized(void)
`
193
212
`{
`
`@@ -208,6 +227,7 @@ PyEval_InitThreads(void)
`
208
227
`PyThread_init_thread();
`
209
228
`create_gil(gil);
`
210
229
`PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
`
``
230
`+
ensure_tstate_not_null(func, tstate);
`
211
231
`take_gil(ceval, tstate);
`
212
232
``
213
233
`struct _pending_calls *pending = &ceval->pending;
`
`@@ -235,14 +255,26 @@ _PyEval_FiniThreads(struct _ceval_runtime_state *ceval)
`
235
255
` }
`
236
256
`}
`
237
257
``
``
258
`+
/* This function is designed to exit daemon threads immediately rather than
`
``
259
`+
taking the GIL if Py_Finalize() has been called.
`
``
260
+
``
261
`+
The caller must not hold the GIL, since this function does not release
`
``
262
`+
the GIL before exiting the thread.
`
``
263
+
``
264
`+
When this function is called by a daemon thread after Py_Finalize() has been
`
``
265
`+
called, the GIL does no longer exist.
`
``
266
+
``
267
`+
tstate must be non-NULL. */
`
238
268
`static inline void
`
239
269
`exit_thread_if_finalizing(PyThreadState *tstate)
`
240
270
`{
`
241
``
`-
_PyRuntimeState *runtime = tstate->interp->runtime;
`
242
``
`-
/* _Py_Finalizing is protected by the GIL */
`
``
271
`+
/* bpo-39877: Access _PyRuntime directly rather than using
`
``
272
`+
tstate->interp->runtime to support calls from Python daemon threads.
`
``
273
`+
After Py_Finalize() has been called, tstate can be a dangling pointer:
`
``
274
`+
point to PyThreadState freed memory. */
`
``
275
`+
_PyRuntimeState *runtime = &_PyRuntime;
`
243
276
`PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(runtime);
`
244
277
`if (finalizing != NULL && finalizing != tstate) {
`
245
``
`-
drop_gil(&runtime->ceval, tstate);
`
246
278
`PyThread_exit_thread();
`
247
279
` }
`
248
280
`}
`
`@@ -280,13 +312,14 @@ void
`
280
312
`PyEval_AcquireLock(void)
`
281
313
`{
`
282
314
`_PyRuntimeState *runtime = &_PyRuntime;
`
283
``
`-
struct _ceval_runtime_state *ceval = &runtime->ceval;
`
284
315
`PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
`
285
``
`-
if (tstate == NULL) {
`
286
``
`-
Py_FatalError("current thread state is NULL");
`
287
``
`-
}
`
288
``
`-
take_gil(ceval, tstate);
`
``
316
`+
ensure_tstate_not_null(func, tstate);
`
``
317
+
289
318
`exit_thread_if_finalizing(tstate);
`
``
319
`+
assert(is_tstate_valid(tstate));
`
``
320
+
``
321
`+
struct _ceval_runtime_state *ceval = &runtime->ceval;
`
``
322
`+
take_gil(ceval, tstate);
`
290
323
`}
`
291
324
``
292
325
`void
`
`@@ -304,15 +337,18 @@ PyEval_ReleaseLock(void)
`
304
337
`void
`
305
338
`PyEval_AcquireThread(PyThreadState *tstate)
`
306
339
`{
`
307
``
`-
assert(tstate != NULL);
`
``
340
`+
ensure_tstate_not_null(func, tstate);
`
``
341
+
``
342
`+
exit_thread_if_finalizing(tstate);
`
``
343
`+
assert(is_tstate_valid(tstate));
`
308
344
``
309
345
`_PyRuntimeState *runtime = tstate->interp->runtime;
`
310
346
`struct _ceval_runtime_state *ceval = &runtime->ceval;
`
311
347
``
312
348
`/* Check someone has called PyEval_InitThreads() to create the lock */
`
313
349
`assert(gil_created(&ceval->gil));
`
``
350
+
314
351
`take_gil(ceval, tstate);
`
315
``
`-
exit_thread_if_finalizing(tstate);
`
316
352
`if (_PyThreadState_Swap(&runtime->gilstate, tstate) != NULL) {
`
317
353
`Py_FatalError("non-NULL old thread state");
`
318
354
` }
`
`@@ -344,8 +380,9 @@ _PyEval_ReInitThreads(_PyRuntimeState *runtime)
`
344
380
`return;
`
345
381
` }
`
346
382
`recreate_gil(&ceval->gil);
`
347
``
`-
PyThreadState *current_tstate = _PyRuntimeState_GetThreadState(runtime);
`
348
``
`-
take_gil(ceval, current_tstate);
`
``
383
`+
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
`
``
384
`+
ensure_tstate_not_null(func, tstate);
`
``
385
`+
take_gil(ceval, tstate);
`
349
386
``
350
387
`struct _pending_calls *pending = &ceval->pending;
`
351
388
`pending->lock = PyThread_allocate_lock();
`
`@@ -354,7 +391,7 @@ _PyEval_ReInitThreads(_PyRuntimeState *runtime)
`
354
391
` }
`
355
392
``
356
393
`/* Destroy all threads except the current one */
`
357
``
`-
_PyThreadState_DeleteExcept(runtime, current_tstate);
`
``
394
`+
_PyThreadState_DeleteExcept(runtime, tstate);
`
358
395
`}
`
359
396
``
360
397
`/* This function is used to signal that async exceptions are waiting to be
`
`@@ -383,16 +420,16 @@ PyEval_SaveThread(void)
`
383
420
`void
`
384
421
`PyEval_RestoreThread(PyThreadState *tstate)
`
385
422
`{
`
386
``
`-
assert(tstate != NULL);
`
``
423
`+
ensure_tstate_not_null(func, tstate);
`
``
424
+
``
425
`+
exit_thread_if_finalizing(tstate);
`
``
426
`+
assert(is_tstate_valid(tstate));
`
387
427
``
388
428
`_PyRuntimeState *runtime = tstate->interp->runtime;
`
389
429
`struct _ceval_runtime_state *ceval = &runtime->ceval;
`
390
430
`assert(gil_created(&ceval->gil));
`
391
431
``
392
``
`-
int err = errno;
`
393
432
`take_gil(ceval, tstate);
`
394
``
`-
exit_thread_if_finalizing(tstate);
`
395
``
`-
errno = err;
`
396
433
``
397
434
`_PyThreadState_Swap(&runtime->gilstate, tstate);
`
398
435
`}
`
`@@ -750,11 +787,14 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
`
750
787
`PyObject **fastlocals, **freevars;
`
751
788
`PyObject retval = NULL; / Return value */
`
752
789
`_PyRuntimeState * const runtime = &_PyRuntime;
`
753
``
`-
PyThreadState * const tstate = _PyRuntimeState_GetThreadState(runtime);
`
754
790
`struct _ceval_runtime_state * const ceval = &runtime->ceval;
`
755
791
`_Py_atomic_int * const eval_breaker = &ceval->eval_breaker;
`
756
792
`PyCodeObject *co;
`
757
793
``
``
794
`+
PyThreadState * const tstate = _PyRuntimeState_GetThreadState(runtime);
`
``
795
`+
ensure_tstate_not_null(func, tstate);
`
``
796
`+
assert(is_tstate_valid(tstate));
`
``
797
+
758
798
`/* when tracing we set things up so that
`
759
799
``
760
800
` not (instr_lb <= current_bytecode_offset < instr_ub)
`
`@@ -1242,11 +1282,11 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
`
1242
1282
``
1243
1283
`/* Other threads may run now */
`
1244
1284
``
1245
``
`-
take_gil(ceval, tstate);
`
1246
``
-
1247
1285
`/* Check if we should make a quick exit. */
`
1248
1286
`exit_thread_if_finalizing(tstate);
`
1249
1287
``
``
1288
`+
take_gil(ceval, tstate);
`
``
1289
+
1250
1290
`if (_PyThreadState_Swap(&runtime->gilstate, tstate) != NULL) {
`
1251
1291
`Py_FatalError("orphan tstate");
`
1252
1292
` }
`