Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 5be45a6

Browse files
bpo-33608: Minor cleanup related to pending calls. (pythongh-12247)
1 parent 7bda9de commit 5be45a6

File tree

5 files changed

+81
-68
lines changed

5 files changed

+81
-68
lines changed

Include/internal/pycore_ceval.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ extern "C" {
1212
#include "pythread.h"
1313

1414
struct _pending_calls {
15-
unsigned long main_thread;
1615
PyThread_type_lock lock;
1716
/* Request for running pending calls. */
1817
_Py_atomic_int calls_to_do;

Include/internal/pycore_pystate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ struct _is {
3131
int64_t id_refcount;
3232
PyThread_type_lock id_mutex;
3333

34+
int finalizing;
35+
3436
PyObject *modules;
3537
PyObject *modules_by_index;
3638
PyObject *sysdict;
@@ -207,6 +209,8 @@ typedef struct pyruntimestate {
207209
struct _xidregitem *head;
208210
} xidregistry;
209211

212+
unsigned long main_thread;
213+
210214
#define NEXITFUNCS 32
211215
void (*exitfuncs[NEXITFUNCS])(void);
212216
int nexitfuncs;

Python/ceval.c

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,11 @@ PyEval_InitThreads(void)
174174
PyThread_init_thread();
175175
create_gil();
176176
take_gil(_PyThreadState_GET());
177-
_PyRuntime.ceval.pending.main_thread = PyThread_get_thread_ident();
178-
if (!_PyRuntime.ceval.pending.lock)
177+
// Set it to the ID of the main thread of the main interpreter.
178+
_PyRuntime.main_thread = PyThread_get_thread_ident();
179+
if (!_PyRuntime.ceval.pending.lock) {
179180
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
181+
}
180182
}
181183

182184
void
@@ -243,9 +245,9 @@ PyEval_ReInitThreads(void)
243245
if (!gil_created())
244246
return;
245247
recreate_gil();
246-
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
247248
take_gil(current_tstate);
248-
_PyRuntime.ceval.pending.main_thread = PyThread_get_thread_ident();
249+
_PyRuntime.main_thread = PyThread_get_thread_ident();
250+
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
249251

250252
/* Destroy all threads except the current one */
251253
_PyThreadState_DeleteExcept(current_tstate);
@@ -323,6 +325,35 @@ _PyEval_SignalReceived(void)
323325
SIGNAL_PENDING_SIGNALS();
324326
}
325327

328+
/* Push one item onto the queue while holding the lock. */
329+
static int
330+
_push_pending_call(int (*func)(void *), void *arg)
331+
{
332+
int i = _PyRuntime.ceval.pending.last;
333+
int j = (i + 1) % NPENDINGCALLS;
334+
if (j == _PyRuntime.ceval.pending.first) {
335+
return -1; /* Queue full */
336+
}
337+
_PyRuntime.ceval.pending.calls[i].func = func;
338+
_PyRuntime.ceval.pending.calls[i].arg = arg;
339+
_PyRuntime.ceval.pending.last = j;
340+
return 0;
341+
}
342+
343+
/* Pop one item off the queue while holding the lock. */
344+
static void
345+
_pop_pending_call(int (**func)(void *), void **arg)
346+
{
347+
int i = _PyRuntime.ceval.pending.first;
348+
if (i == _PyRuntime.ceval.pending.last) {
349+
return; /* Queue empty */
350+
}
351+
352+
*func = _PyRuntime.ceval.pending.calls[i].func;
353+
*arg = _PyRuntime.ceval.pending.calls[i].arg;
354+
_PyRuntime.ceval.pending.first = (i + 1) % NPENDINGCALLS;
355+
}
356+
326357
/* This implementation is thread-safe. It allows
327358
scheduling to be made from any thread, and even from an executing
328359
callback.
@@ -331,7 +362,6 @@ _PyEval_SignalReceived(void)
331362
int
332363
Py_AddPendingCall(int (*func)(void *), void *arg)
333364
{
334-
int i, j, result=0;
335365
PyThread_type_lock lock = _PyRuntime.ceval.pending.lock;
336366

337367
/* try a few times for the lock. Since this mechanism is used
@@ -346,6 +376,7 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
346376
* this function is called before any bytecode evaluation takes place.
347377
*/
348378
if (lock != NULL) {
379+
int i;
349380
for (i = 0; i<100; i++) {
350381
if (PyThread_acquire_lock(lock, NOWAIT_LOCK))
351382
break;
@@ -354,15 +385,8 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
354385
return -1;
355386
}
356387

357-
i = _PyRuntime.ceval.pending.last;
358-
j = (i + 1) % NPENDINGCALLS;
359-
if (j == _PyRuntime.ceval.pending.first) {
360-
result = -1; /* Queue full */
361-
} else {
362-
_PyRuntime.ceval.pending.calls[i].func = func;
363-
_PyRuntime.ceval.pending.calls[i].arg = arg;
364-
_PyRuntime.ceval.pending.last = j;
365-
}
388+
int result = _push_pending_call(func, arg);
389+
366390
/* signal main loop */
367391
SIGNAL_PENDING_CALLS();
368392
if (lock != NULL)
@@ -373,10 +397,10 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
373397
static int
374398
handle_signals(void)
375399
{
376-
/* Only handle signals on main thread. */
377-
if (_PyRuntime.ceval.pending.main_thread &&
378-
PyThread_get_thread_ident() != _PyRuntime.ceval.pending.main_thread)
379-
{
400+
/* Only handle signals on main thread. PyEval_InitThreads must
401+
* have been called already.
402+
*/
403+
if (PyThread_get_thread_ident() != _PyRuntime.main_thread) {
380404
return 0;
381405
}
382406
/*
@@ -401,9 +425,7 @@ make_pending_calls(void)
401425
static int busy = 0;
402426

403427
/* only service pending calls on main thread */
404-
if (_PyRuntime.ceval.pending.main_thread &&
405-
PyThread_get_thread_ident() != _PyRuntime.ceval.pending.main_thread)
406-
{
428+
if (PyThread_get_thread_ident() != _PyRuntime.main_thread) {
407429
return 0;
408430
}
409431

@@ -428,24 +450,18 @@ make_pending_calls(void)
428450

429451
/* perform a bounded number of calls, in case of recursion */
430452
for (int i=0; i<NPENDINGCALLS; i++) {
431-
int j;
432-
int (*func)(void *);
453+
int (*func)(void *) = NULL;
433454
void *arg = NULL;
434455

435456
/* pop one item off the queue while holding the lock */
436457
PyThread_acquire_lock(_PyRuntime.ceval.pending.lock, WAIT_LOCK);
437-
j = _PyRuntime.ceval.pending.first;
438-
if (j == _PyRuntime.ceval.pending.last) {
439-
func = NULL; /* Queue empty */
440-
} else {
441-
func = _PyRuntime.ceval.pending.calls[j].func;
442-
arg = _PyRuntime.ceval.pending.calls[j].arg;
443-
_PyRuntime.ceval.pending.first = (j + 1) % NPENDINGCALLS;
444-
}
458+
_pop_pending_call(&func, &arg);
445459
PyThread_release_lock(_PyRuntime.ceval.pending.lock);
460+
446461
/* having released the lock, perform the callback */
447-
if (func == NULL)
462+
if (func == NULL) {
448463
break;
464+
}
449465
res = func(arg);
450466
if (res) {
451467
goto error;

Python/pylifecycle.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,7 @@ Py_EndInterpreter(PyThreadState *tstate)
14601460
Py_FatalError("Py_EndInterpreter: thread is not current");
14611461
if (tstate->frame != NULL)
14621462
Py_FatalError("Py_EndInterpreter: thread still has a frame");
1463+
interp->finalizing = 1;
14631464

14641465
wait_for_thread_shutdown();
14651466

Python/pystate.c

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ _PyRuntimeState_Init_impl(_PyRuntimeState *runtime)
6060
return _Py_INIT_ERR("Can't initialize threads for cross-interpreter data registry");
6161
}
6262

63+
// runtime->main_thread is set in PyEval_InitThreads().
64+
6365
return _Py_INIT_OK();
6466
}
6567

@@ -133,42 +135,19 @@ PyInterpreterState_New(void)
133135
return NULL;
134136
}
135137

138+
memset(interp, 0, sizeof(*interp));
136139
interp->id_refcount = -1;
137-
interp->id_mutex = NULL;
138-
interp->modules = NULL;
139-
interp->modules_by_index = NULL;
140-
interp->sysdict = NULL;
141-
interp->builtins = NULL;
142-
interp->builtins_copy = NULL;
143-
interp->tstate_head = NULL;
144140
interp->check_interval = 100;
145-
interp->num_threads = 0;
146-
interp->pythread_stacksize = 0;
147-
interp->codec_search_path = NULL;
148-
interp->codec_search_cache = NULL;
149-
interp->codec_error_registry = NULL;
150-
interp->codecs_initialized = 0;
151-
interp->fscodec_initialized = 0;
152141
interp->core_config = _PyCoreConfig_INIT;
153142
interp->config = _PyMainInterpreterConfig_INIT;
154-
interp->importlib = NULL;
155-
interp->import_func = NULL;
156143
interp->eval_frame = _PyEval_EvalFrameDefault;
157-
interp->co_extra_user_count = 0;
158144
#ifdef HAVE_DLOPEN
159145
#if HAVE_DECL_RTLD_NOW
160146
interp->dlopenflags = RTLD_NOW;
161147
#else
162148
interp->dlopenflags = RTLD_LAZY;
163149
#endif
164150
#endif
165-
#ifdef HAVE_FORK
166-
interp->before_forkers = NULL;
167-
interp->after_forkers_parent = NULL;
168-
interp->after_forkers_child = NULL;
169-
#endif
170-
interp->pyexitfunc = NULL;
171-
interp->pyexitmodule = NULL;
172151

173152
HEAD_LOCK();
174153
if (_PyRuntime.interpreters.next_id < 0) {
@@ -223,6 +202,9 @@ PyInterpreterState_Clear(PyInterpreterState *interp)
223202
Py_CLEAR(interp->after_forkers_parent);
224203
Py_CLEAR(interp->after_forkers_child);
225204
#endif
205+
// XXX Once we have one allocator per interpreter (i.e.
206+
// per-interpreter GC) we must ensure that all of the interpreter's
207+
// objects have been cleaned up at the point.
226208
}
227209

228210

@@ -334,28 +316,39 @@ PyInterpreterState_GetID(PyInterpreterState *interp)
334316
}
335317

336318

337-
PyInterpreterState *
338-
_PyInterpreterState_LookUpID(PY_INT64_T requested_id)
319+
static PyInterpreterState *
320+
interp_look_up_id(PY_INT64_T requested_id)
339321
{
340-
if (requested_id < 0)
341-
goto error;
342-
343322
PyInterpreterState *interp = PyInterpreterState_Head();
344323
while (interp != NULL) {
345324
PY_INT64_T id = PyInterpreterState_GetID(interp);
346-
if (id < 0)
325+
if (id < 0) {
347326
return NULL;
348-
if (requested_id == id)
327+
}
328+
if (requested_id == id) {
349329
return interp;
330+
}
350331
interp = PyInterpreterState_Next(interp);
351332
}
352-
353-
error:
354-
PyErr_Format(PyExc_RuntimeError,
355-
"unrecognized interpreter ID %lld", requested_id);
356333
return NULL;
357334
}
358335

336+
PyInterpreterState *
337+
_PyInterpreterState_LookUpID(PY_INT64_T requested_id)
338+
{
339+
PyInterpreterState *interp = NULL;
340+
if (requested_id >= 0) {
341+
HEAD_LOCK();
342+
interp = interp_look_up_id(requested_id);
343+
HEAD_UNLOCK();
344+
}
345+
if (interp == NULL && !PyErr_Occurred()) {
346+
PyErr_Format(PyExc_RuntimeError,
347+
"unrecognized interpreter ID %lld", requested_id);
348+
}
349+
return interp;
350+
}
351+
359352

360353
int
361354
_PyInterpreterState_IDInitref(PyInterpreterState *interp)

0 commit comments

Comments
 (0)