Skip to content

Commit 859070f

Browse files
committed
Use an RW lock instead of a counter.
1 parent cfd62b8 commit 859070f

File tree

7 files changed

+50
-53
lines changed

7 files changed

+50
-53
lines changed

Include/internal/pycore_interp_structs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,10 @@ struct _is {
971971
# endif
972972
#endif
973973

974+
/* The "pre-finalization" lock, which protects against things like starting
975+
* threads. The exclusive writer is only used when the interpreter finalizes. */
976+
_PyRWMutex prefini_mutex;
977+
974978
/* the initial PyInterpreterState.threads.head */
975979
_PyThreadStateImpl _initial_thread;
976980
// _initial_thread should be the last field of PyInterpreterState.

Include/internal/pycore_pylifecycle.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ extern void _PyErr_DisplayException(PyObject *file, PyObject *exc);
8585

8686
extern void _PyThreadState_DeleteCurrent(PyThreadState *tstate);
8787

88-
extern int _PyAtExit_Call(PyInterpreterState *interp);
88+
extern void _PyAtExit_Call(PyInterpreterState *interp);
8989

9090
extern int _Py_IsCoreInitialized(void);
9191

Lib/threading.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1573,7 +1573,7 @@ def _shutdown():
15731573
_main_thread._os_thread_handle._set_done()
15741574

15751575
# Wait for all non-daemon threads to exit.
1576-
return _thread_shutdown()
1576+
_thread_shutdown()
15771577

15781578

15791579
def main_thread():

Modules/_threadmodule.c

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,13 @@ ThreadHandle_get_os_handle(ThreadHandle *handle, PyThread_handle_t *os_handle)
176176
}
177177

178178
static void
179-
add_to_shutdown_handles(thread_module_state *state, ThreadHandle *handle)
179+
add_to_shutdown_handles(thread_module_state *state, ThreadHandle *handle, PyInterpreterState *interp)
180180
{
181+
_PyRWMutex_RLock(&interp->prefini_mutex);
181182
HEAD_LOCK(&_PyRuntime);
182183
llist_insert_tail(&state->shutdown_handles, &handle->shutdown_node);
183184
HEAD_UNLOCK(&_PyRuntime);
185+
_PyRWMutex_RUnlock(&interp->prefini_mutex);
184186
}
185187

186188
static void
@@ -195,13 +197,16 @@ clear_shutdown_handles(thread_module_state *state)
195197
}
196198

197199
static void
198-
remove_from_shutdown_handles(ThreadHandle *handle)
200+
remove_from_shutdown_handles(ThreadHandle *handle, PyInterpreterState *interp)
199201
{
202+
assert(interp != NULL);
203+
_PyRWMutex_RLock(&interp->prefini_mutex);
200204
HEAD_LOCK(&_PyRuntime);
201205
if (handle->shutdown_node.next != NULL) {
202206
llist_remove(&handle->shutdown_node);
203207
}
204208
HEAD_UNLOCK(&_PyRuntime);
209+
_PyRWMutex_RUnlock(&interp->prefini_mutex);
205210
}
206211

207212
static ThreadHandle *
@@ -309,7 +314,7 @@ _PyThread_AfterFork(struct _pythread_runtime_state *state)
309314
handle->mutex = (PyMutex){_Py_UNLOCKED};
310315
_PyEvent_Notify(&handle->thread_is_exiting);
311316
llist_remove(node);
312-
remove_from_shutdown_handles(handle);
317+
remove_from_shutdown_handles(handle, _PyInterpreterState_GET());
313318
}
314319
}
315320

@@ -392,7 +397,7 @@ thread_run(void *boot_raw)
392397

393398
exit:
394399
// Don't need to wait for this thread anymore
395-
remove_from_shutdown_handles(handle);
400+
remove_from_shutdown_handles(handle, _PyInterpreterState_GET());
396401

397402
_PyEvent_Notify(&handle->thread_is_exiting);
398403
ThreadHandle_decref(handle);
@@ -1863,12 +1868,12 @@ do_start_new_thread(thread_module_state *state, PyObject *func, PyObject *args,
18631868
// Add the handle before starting the thread to avoid adding a handle
18641869
// to a thread that has already finished (i.e. if the thread finishes
18651870
// before the call to `ThreadHandle_start()` below returns).
1866-
add_to_shutdown_handles(state, handle);
1871+
add_to_shutdown_handles(state, handle, interp);
18671872
}
18681873

18691874
if (ThreadHandle_start(handle, func, args, kwargs) < 0) {
18701875
if (!daemon) {
1871-
remove_from_shutdown_handles(handle);
1876+
remove_from_shutdown_handles(handle, _PyInterpreterState_GET());
18721877
}
18731878
return -1;
18741879
}
@@ -2345,7 +2350,6 @@ thread_shutdown(PyObject *self, PyObject *args)
23452350
{
23462351
PyThread_ident_t ident = PyThread_get_thread_ident_ex();
23472352
thread_module_state *state = get_thread_state(self);
2348-
int found_thread = 0;
23492353

23502354
for (;;) {
23512355
ThreadHandle *handle = NULL;
@@ -2354,7 +2358,6 @@ thread_shutdown(PyObject *self, PyObject *args)
23542358
HEAD_LOCK(&_PyRuntime);
23552359
struct llist_node *node;
23562360
llist_for_each_safe(node, &state->shutdown_handles) {
2357-
found_thread = 1;
23582361
ThreadHandle *cur = llist_data(node, ThreadHandle, shutdown_node);
23592362
if (cur->ident != ident) {
23602363
ThreadHandle_incref(cur);
@@ -2375,13 +2378,13 @@ thread_shutdown(PyObject *self, PyObject *args)
23752378
PyErr_FormatUnraisable("Exception ignored while joining a thread "
23762379
"in _thread._shutdown()");
23772380
ThreadHandle_decref(handle);
2378-
return PyBool_FromLong(found_thread);
2381+
Py_RETURN_NONE;
23792382
}
23802383

23812384
ThreadHandle_decref(handle);
23822385
}
23832386

2384-
return PyBool_FromLong(found_thread);
2387+
Py_RETURN_NONE;
23852388
}
23862389

23872390
PyDoc_STRVAR(shutdown_doc,

Modules/atexitmodule.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ _PyAtExit_Fini(PyInterpreterState *interp)
9999
}
100100
}
101101

102-
static int
102+
static void
103103
atexit_callfuncs(struct atexit_state *state)
104104
{
105105
assert(!PyErr_Occurred());
@@ -112,13 +112,10 @@ atexit_callfuncs(struct atexit_state *state)
112112
{
113113
PyErr_FormatUnraisable("Exception ignored while "
114114
"copying atexit callbacks");
115-
return 0;
115+
return;
116116
}
117117

118-
int called = 0;
119-
120118
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(copy); ++i) {
121-
called = 1;
122119
// We don't have to worry about evil borrowed references, because
123120
// no other threads can access this list.
124121
PyObject *tuple = PyList_GET_ITEM(copy, i);
@@ -144,15 +141,14 @@ atexit_callfuncs(struct atexit_state *state)
144141
atexit_cleanup(state);
145142

146143
assert(!PyErr_Occurred());
147-
return called;
148144
}
149145

150146

151-
int
147+
void
152148
_PyAtExit_Call(PyInterpreterState *interp)
153149
{
154150
struct atexit_state *state = &interp->atexit;
155-
return atexit_callfuncs(state);
151+
atexit_callfuncs(state);
156152
}
157153

158154

@@ -200,9 +196,15 @@ atexit_register(PyObject *module, PyObject *args, PyObject *kwargs)
200196
return NULL;
201197
}
202198

199+
PyInterpreterState *interp = _PyInterpreterState_GET();
200+
assert(interp != NULL);
203201
struct atexit_state *state = get_atexit_state();
202+
_PyRWMutex_RLock(&interp->prefini_mutex);
204203
// atexit callbacks go in a LIFO order
205-
if (PyList_Insert(state->callbacks, 0, callback) < 0)
204+
int res = PyList_Insert(state->callbacks, 0, callback);
205+
_PyRWMutex_RUnlock(&interp->prefini_mutex);
206+
207+
if (res < 0)
206208
{
207209
Py_DECREF(callback);
208210
return NULL;

Python/ceval_gil.c

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -785,8 +785,10 @@ _PyEval_AddPendingCall(PyInterpreterState *interp,
785785
}
786786

787787
PyMutex_Lock(&pending->mutex);
788+
_PyRWMutex_RLock(&interp->prefini_mutex);
788789
_Py_add_pending_call_result result =
789790
_push_pending_call(pending, func, arg, flags);
791+
_PyRWMutex_RUnlock(&interp->prefini_mutex);
790792
PyMutex_Unlock(&pending->mutex);
791793

792794
if (main_only) {
@@ -837,11 +839,10 @@ handle_signals(PyThreadState *tstate)
837839
}
838840

839841
static int
840-
_make_pending_calls(struct _pending_calls *pending, int32_t *p_npending, int *p_called)
842+
_make_pending_calls(struct _pending_calls *pending, int32_t *p_npending)
841843
{
842844
int res = 0;
843845
int32_t npending = -1;
844-
int called = 0;
845846

846847
assert(sizeof(pending->max) <= sizeof(size_t)
847848
&& ((size_t)pending->max) <= Py_ARRAY_LENGTH(pending->calls));
@@ -869,8 +870,6 @@ _make_pending_calls(struct _pending_calls *pending, int32_t *p_npending, int *p_
869870
break;
870871
}
871872

872-
called = 1;
873-
874873
/* having released the lock, perform the callback */
875874
res = func(arg);
876875
if ((flags & _Py_PENDING_RAWFREE) && arg != NULL) {
@@ -884,7 +883,6 @@ _make_pending_calls(struct _pending_calls *pending, int32_t *p_npending, int *p_
884883

885884
finally:
886885
*p_npending = npending;
887-
*p_called = called;
888886
return res;
889887
}
890888

@@ -921,7 +919,7 @@ clear_pending_handling_thread(struct _pending_calls *pending)
921919
}
922920

923921
static int
924-
make_pending_calls_with_count(PyThreadState *tstate)
922+
make_pending_calls_lock_held(PyThreadState *tstate)
925923
{
926924
PyInterpreterState *interp = tstate->interp;
927925
struct _pending_calls *pending = &interp->ceval.pending;
@@ -951,8 +949,7 @@ make_pending_calls_with_count(PyThreadState *tstate)
951949
unsignal_pending_calls(tstate, interp);
952950

953951
int32_t npending;
954-
int called;
955-
if (_make_pending_calls(pending, &npending, &called) != 0) {
952+
if (_make_pending_calls(pending, &npending) != 0) {
956953
clear_pending_handling_thread(pending);
957954
/* There might not be more calls to make, but we play it safe. */
958955
signal_pending_calls(tstate, interp);
@@ -963,9 +960,8 @@ make_pending_calls_with_count(PyThreadState *tstate)
963960
signal_pending_calls(tstate, interp);
964961
}
965962

966-
int main_called = 0;
967963
if (_Py_IsMainThread() && _Py_IsMainInterpreter(interp)) {
968-
if (_make_pending_calls(pending_main, &npending, &main_called) != 0) {
964+
if (_make_pending_calls(pending_main, &npending) != 0) {
969965
clear_pending_handling_thread(pending);
970966
/* There might not be more calls to make, but we play it safe. */
971967
signal_pending_calls(tstate, interp);
@@ -978,17 +974,16 @@ make_pending_calls_with_count(PyThreadState *tstate)
978974
}
979975

980976
clear_pending_handling_thread(pending);
981-
return Py_MAX(called, main_called);
977+
return 0;
982978
}
983979

984980
static int
985981
make_pending_calls(PyThreadState *tstate)
986982
{
987-
if (make_pending_calls_with_count(tstate) < 0) {
988-
return -1;
989-
}
990-
991-
return 0;
983+
_PyRWMutex_RLock(&tstate->interp->prefini_mutex);
984+
int res = make_pending_calls_lock_held(tstate);
985+
_PyRWMutex_RUnlock(&tstate->interp->prefini_mutex);
986+
return res;
992987
}
993988

994989

Python/pylifecycle.c

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ static PyStatus init_android_streams(PyThreadState *tstate);
9696
#if defined(__APPLE__) && HAS_APPLE_SYSTEM_LOG
9797
static PyStatus init_apple_streams(PyThreadState *tstate);
9898
#endif
99-
static int wait_for_thread_shutdown(PyThreadState *tstate);
99+
static void wait_for_thread_shutdown(PyThreadState *tstate);
100100
static void finalize_subinterpreters(void);
101101
static void call_ll_exitfuncs(_PyRuntimeState *runtime);
102102

@@ -2010,15 +2010,11 @@ make_pre_finalization_calls(PyThreadState *tstate)
20102010
* could start a thread or vice versa. To ensure that we properly clean
20112011
* call everything, we run these in a loop until none of them run anything. */
20122012
for (;;) {
2013-
int called = 0;
2014-
20152013
// Wrap up existing "threading"-module-created, non-daemon threads.
2016-
int threads_joined = wait_for_thread_shutdown(tstate);
2017-
called = Py_MAX(called, threads_joined);
2014+
wait_for_thread_shutdown(tstate);
20182015

20192016
// Make any remaining pending calls.
2020-
int made_pending_calls = _Py_FinishPendingCalls(tstate);
2021-
called = Py_MAX(called, made_pending_calls);
2017+
_Py_FinishPendingCalls(tstate);
20222018

20232019
/* The interpreter is still entirely intact at this point, and the
20242020
* exit funcs may be relying on that. In particular, if some thread
@@ -2030,9 +2026,9 @@ make_pre_finalization_calls(PyThreadState *tstate)
20302026
* the threads created via Threading.
20312027
*/
20322028

2033-
int called_atexit = _PyAtExit_Call(tstate->interp);
2034-
called = Py_MAX(called, called_atexit);
2029+
_PyAtExit_Call(tstate->interp);
20352030

2031+
_PyRWMutex_Unlock(&tstate->interp->prefini_mutex);
20362032
if (called == 0) {
20372033
break;
20382034
}
@@ -3456,7 +3452,7 @@ Py_ExitStatusException(PyStatus status)
34563452
the threading module was imported in the first place.
34573453
The shutdown routine will wait until all non-daemon
34583454
"threading" threads have completed. */
3459-
static int
3455+
static void
34603456
wait_for_thread_shutdown(PyThreadState *tstate)
34613457
{
34623458
PyObject *result;
@@ -3466,19 +3462,16 @@ wait_for_thread_shutdown(PyThreadState *tstate)
34663462
PyErr_FormatUnraisable("Exception ignored on threading shutdown");
34673463
}
34683464
/* else: threading not imported */
3469-
return 0;
3465+
return;
34703466
}
34713467
int called = 0;
34723468
result = PyObject_CallMethodNoArgs(threading, &_Py_ID(_shutdown));
34733469
if (result == NULL) {
34743470
PyErr_FormatUnraisable("Exception ignored on threading shutdown");
34753471
}
3476-
else {
3477-
assert(PyBool_Check(result) && _Py_IsImmortal(result));
3478-
called = result == Py_True;
3479-
}
3472+
Py_XDECREF(result);
34803473
Py_DECREF(threading);
3481-
return called;
3474+
return;
34823475
}
34833476

34843477
int Py_AtExit(void (*func)(void))

0 commit comments

Comments
 (0)