Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

gh-104341: Fix threading Module Shutdown #104560

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 12 commits into
base: main
Choose a base branch
Loading
from
Prev Previous commit
Next Next commit
Add module_thread.lifetime_mutex.
  • Loading branch information
ericsnowcurrently committed May 16, 2023
commit da78235d48060b5846e4d3172b44df397bdac910
56 changes: 56 additions & 0 deletions 56 Modules/_threadmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ static struct PyModuleDef thread_module;
struct module_thread {
PyThreadState *tstate;
int daemonic;
PyThread_type_lock lifetime_mutex;
int lifetime_mutex_held;
struct module_thread *prev;
struct module_thread *next;
};
Expand All @@ -51,13 +53,28 @@ module_threads_init(struct module_threads *threads)
}

#ifdef HAVE_FORK
static int module_thread_reinit(struct module_thread *);

static int
module_threads_reinit(struct module_threads *threads)
{
if (_PyThread_at_fork_reinit(threads->mutex) < 0) {
PyErr_SetString(ThreadError, "failed to reinitialize lock at fork");
return -1;
}

PyThread_acquire_lock(threads->mutex, WAIT_LOCK);

struct module_thread *mt = threads->head;
while (mt != NULL) {
if (module_thread_reinit(mt) < 0) {
return -1;
}
mt = mt->next;
}

PyThread_release_lock(threads->mutex);

return 0;
}
#endif
Expand Down Expand Up @@ -124,18 +141,48 @@ add_module_thread(struct module_threads *threads,
mt->prev = NULL;
mt->next = NULL;

// Create the lifetime lock.
mt->lifetime_mutex = PyThread_allocate_lock();
if (mt->lifetime_mutex == NULL) {
PyMem_Free(mt);
return NULL;
}
mt->lifetime_mutex_held = 0;

// Add the entry to the end of the list.
module_threads_add(threads, mt);

return mt;
}

#ifdef HAVE_FORK
static int
module_thread_reinit(struct module_thread *mt)
{
if (_PyThread_at_fork_reinit(mt->lifetime_mutex) < 0) {
PyErr_SetString(ThreadError, "failed to reinitialize lock at fork");
return -1;
}
if (mt->lifetime_mutex_held) {
PyThread_acquire_lock(mt->lifetime_mutex, WAIT_LOCK);
}

return 0;
}
#endif

static void
module_thread_starting(struct module_thread *mt)
{
assert(mt->tstate == PyThreadState_Get());

mt->tstate->interp->threads.count++;

// We acquire the lifetime lock here instead of in add_module_thread()
// because we must do it in the actual thread, which wasn't started yet
// when add_module_thread() was called.
PyThread_acquire_lock(mt->lifetime_mutex, WAIT_LOCK);
mt->lifetime_mutex_held = 1;
}

static void
Expand All @@ -150,10 +197,19 @@ module_thread_finished(struct module_thread *mt)
static void
remove_module_thread(struct module_threads *threads, struct module_thread *mt)
{
// Mark the thread as truly dead now.
if (mt->lifetime_mutex_held) {
PyThread_release_lock(mt->lifetime_mutex);
}

// Remove it from the list.
module_threads_remove(threads, mt);

// Deallocate everything.
if (mt->lifetime_mutex != NULL) {
PyThread_free_lock(mt->lifetime_mutex);
mt->lifetime_mutex = NULL;
}
PyMem_RawFree(mt);
}

Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.