bpo-33608: Deal with pending calls relative to runtime shutdown. (gh-… · python/cpython@842a2f0 (original) (raw)
`@@ -330,31 +330,33 @@ _PyEval_SignalReceived(void)
`
330
330
``
331
331
`/* Push one item onto the queue while holding the lock. */
`
332
332
`static int
`
333
``
`-
_push_pending_call(int (*func)(void *), void *arg)
`
``
333
`+
_push_pending_call(struct _pending_calls *pending,
`
``
334
`+
int (*func)(void *), void *arg)
`
334
335
`{
`
335
``
`-
int i = _PyRuntime.ceval.pending.last;
`
``
336
`+
int i = pending->last;
`
336
337
`int j = (i + 1) % NPENDINGCALLS;
`
337
``
`-
if (j == _PyRuntime.ceval.pending.first) {
`
``
338
`+
if (j == pending->first) {
`
338
339
`return -1; /* Queue full */
`
339
340
` }
`
340
``
`-
_PyRuntime.ceval.pending.calls[i].func = func;
`
341
``
`-
_PyRuntime.ceval.pending.calls[i].arg = arg;
`
342
``
`-
_PyRuntime.ceval.pending.last = j;
`
``
341
`+
pending->calls[i].func = func;
`
``
342
`+
pending->calls[i].arg = arg;
`
``
343
`+
pending->last = j;
`
343
344
`return 0;
`
344
345
`}
`
345
346
``
346
347
`/* Pop one item off the queue while holding the lock. */
`
347
348
`static void
`
348
``
`-
_pop_pending_call(int (**func)(void *), void **arg)
`
``
349
`+
_pop_pending_call(struct _pending_calls *pending,
`
``
350
`+
int (**func)(void *), void **arg)
`
349
351
`{
`
350
``
`-
int i = _PyRuntime.ceval.pending.first;
`
351
``
`-
if (i == _PyRuntime.ceval.pending.last) {
`
``
352
`+
int i = pending->first;
`
``
353
`+
if (i == pending->last) {
`
352
354
`return; /* Queue empty */
`
353
355
` }
`
354
356
``
355
``
`-
*func = _PyRuntime.ceval.pending.calls[i].func;
`
356
``
`-
*arg = _PyRuntime.ceval.pending.calls[i].arg;
`
357
``
`-
_PyRuntime.ceval.pending.first = (i + 1) % NPENDINGCALLS;
`
``
357
`+
*func = pending->calls[i].func;
`
``
358
`+
*arg = pending->calls[i].arg;
`
``
359
`+
pending->first = (i + 1) % NPENDINGCALLS;
`
358
360
`}
`
359
361
``
360
362
`/* This implementation is thread-safe. It allows
`
`@@ -365,9 +367,23 @@ _pop_pending_call(int (**func)(void *), void **arg)
`
365
367
`int
`
366
368
`Py_AddPendingCall(int (*func)(void *), void *arg)
`
367
369
`{
`
368
``
`-
PyThread_acquire_lock(_PyRuntime.ceval.pending.lock, WAIT_LOCK);
`
369
``
`-
int result = _push_pending_call(func, arg);
`
370
``
`-
PyThread_release_lock(_PyRuntime.ceval.pending.lock);
`
``
370
`+
struct _pending_calls *pending = &_PyRuntime.ceval.pending;
`
``
371
+
``
372
`+
PyThread_acquire_lock(pending->lock, WAIT_LOCK);
`
``
373
`+
if (pending->finishing) {
`
``
374
`+
PyThread_release_lock(pending->lock);
`
``
375
+
``
376
`+
PyObject *exc, *val, *tb;
`
``
377
`+
PyErr_Fetch(&exc, &val, &tb);
`
``
378
`+
PyErr_SetString(PyExc_SystemError,
`
``
379
`+
"Py_AddPendingCall: cannot add pending calls "
`
``
380
`+
"(Python shutting down)");
`
``
381
`+
PyErr_Print();
`
``
382
`+
PyErr_Restore(exc, val, tb);
`
``
383
`+
return -1;
`
``
384
`+
}
`
``
385
`+
int result = _push_pending_call(pending, func, arg);
`
``
386
`+
PyThread_release_lock(pending->lock);
`
371
387
``
372
388
`/* signal main loop */
`
373
389
`SIGNAL_PENDING_CALLS();
`
`@@ -400,7 +416,7 @@ handle_signals(void)
`
400
416
`}
`
401
417
``
402
418
`static int
`
403
``
`-
make_pending_calls(void)
`
``
419
`+
make_pending_calls(struct _pending_calls* pending)
`
404
420
`{
`
405
421
`static int busy = 0;
`
406
422
``
`@@ -425,9 +441,9 @@ make_pending_calls(void)
`
425
441
`void *arg = NULL;
`
426
442
``
427
443
`/* pop one item off the queue while holding the lock */
`
428
``
`-
PyThread_acquire_lock(_PyRuntime.ceval.pending.lock, WAIT_LOCK);
`
429
``
`-
_pop_pending_call(&func, &arg);
`
430
``
`-
PyThread_release_lock(_PyRuntime.ceval.pending.lock);
`
``
444
`+
PyThread_acquire_lock(pending->lock, WAIT_LOCK);
`
``
445
`+
_pop_pending_call(pending, &func, &arg);
`
``
446
`+
PyThread_release_lock(pending->lock);
`
431
447
``
432
448
`/* having released the lock, perform the callback */
`
433
449
`if (func == NULL) {
`
`@@ -448,6 +464,30 @@ make_pending_calls(void)
`
448
464
`return res;
`
449
465
`}
`
450
466
``
``
467
`+
void
`
``
468
`+
_Py_FinishPendingCalls(void)
`
``
469
`+
{
`
``
470
`+
struct _pending_calls *pending = &_PyRuntime.ceval.pending;
`
``
471
+
``
472
`+
assert(PyGILState_Check());
`
``
473
+
``
474
`+
PyThread_acquire_lock(pending->lock, WAIT_LOCK);
`
``
475
`+
pending->finishing = 1;
`
``
476
`+
PyThread_release_lock(pending->lock);
`
``
477
+
``
478
`+
if (!_Py_atomic_load_relaxed(&(pending->calls_to_do))) {
`
``
479
`+
return;
`
``
480
`+
}
`
``
481
+
``
482
`+
if (make_pending_calls(pending) < 0) {
`
``
483
`+
PyObject *exc, *val, *tb;
`
``
484
`+
PyErr_Fetch(&exc, &val, &tb);
`
``
485
`+
PyErr_BadInternalCall();
`
``
486
`+
_PyErr_ChainExceptions(exc, val, tb);
`
``
487
`+
PyErr_Print();
`
``
488
`+
}
`
``
489
`+
}
`
``
490
+
451
491
`/* Py_MakePendingCalls() is a simple wrapper for the sake
`
452
492
` of backward-compatibility. */
`
453
493
`int
`
`@@ -462,7 +502,7 @@ Py_MakePendingCalls(void)
`
462
502
`return res;
`
463
503
` }
`
464
504
``
465
``
`-
res = make_pending_calls();
`
``
505
`+
res = make_pending_calls(&_PyRuntime.ceval.pending);
`
466
506
`if (res != 0) {
`
467
507
`return res;
`
468
508
` }
`
`@@ -1012,7 +1052,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
`
1012
1052
`if (_Py_atomic_load_relaxed(
`
1013
1053
`&_PyRuntime.ceval.pending.calls_to_do))
`
1014
1054
` {
`
1015
``
`-
if (make_pending_calls() != 0) {
`
``
1055
`+
if (make_pending_calls(&_PyRuntime.ceval.pending) != 0) {
`
1016
1056
` goto error;
`
1017
1057
` }
`
1018
1058
` }
`