From dc933c023baa857f9d802ef517e36be25da2290f Mon Sep 17 00:00:00 2001 From: abdoulrasheed Date: Fri, 1 Aug 2025 23:17:02 +0100 Subject: [PATCH 1/4] gh-137017: Ensure is_alive() remains True until the underlying OS thread is fully cleaned up --- .../Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst | 3 +++ Modules/_threadmodule.c | 4 ++++ 2 files changed, 7 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst diff --git a/Misc/NEWS.d/next/Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst b/Misc/NEWS.d/next/Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst new file mode 100644 index 00000000000000..a3119158830f5e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst @@ -0,0 +1,3 @@ +Fix `threading.Thread.is_alive()` to remain `True` until the underlying OS +thread is fully cleaned up. This avoids false negatives in edge cases +involving thread monitoring or premature `is_alive()` calls. diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index f82ad6870f850f..548c29b1de848f 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -704,6 +704,10 @@ PyThreadHandleObject_is_done(PyObject *op, PyObject *Py_UNUSED(dummy)) { PyThreadHandleObject *self = PyThreadHandleObject_CAST(op); if (_PyEvent_IsSet(&self->handle->thread_is_exiting)) { + if (_PyOnceFlag_CallOnce(&self->handle->once, join_thread, self->handle) == -1) { + PyErr_SetString(PyExc_RuntimeError, "failed to join thread"); + return NULL; + } Py_RETURN_TRUE; } else { From 8c2b76ee54f304985ab006250e8d71f8212231e0 Mon Sep 17 00:00:00 2001 From: abdoulrasheed Date: Sat, 2 Aug 2025 02:18:59 +0100 Subject: [PATCH 2/4] Fix Sphinx formatting in NEWS entry --- .../Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst b/Misc/NEWS.d/next/Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst index a3119158830f5e..726cae3157a289 100644 --- a/Misc/NEWS.d/next/Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst +++ b/Misc/NEWS.d/next/Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst @@ -1,3 +1,3 @@ -Fix `threading.Thread.is_alive()` to remain `True` until the underlying OS +Fix :obj:`threading.Thread.is_alive` to remain ``True`` until the underlying OS thread is fully cleaned up. This avoids false negatives in edge cases -involving thread monitoring or premature `is_alive()` calls. +involving thread monitoring or premature :obj:`threading.Thread.is_alive` calls. \ No newline at end of file From 7b2ddf32f65e3fced5f320518097c8a23f786ba7 Mon Sep 17 00:00:00 2001 From: Abdul Date: Tue, 9 Sep 2025 12:56:06 +0100 Subject: [PATCH 3/4] Add new line --- .../next/Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst b/Misc/NEWS.d/next/Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst index 726cae3157a289..7c2c013016d72e 100644 --- a/Misc/NEWS.d/next/Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst +++ b/Misc/NEWS.d/next/Library/2025-08-01-23-11-25.gh-issue-137017.0yGcNc.rst @@ -1,3 +1,3 @@ Fix :obj:`threading.Thread.is_alive` to remain ``True`` until the underlying OS thread is fully cleaned up. This avoids false negatives in edge cases -involving thread monitoring or premature :obj:`threading.Thread.is_alive` calls. \ No newline at end of file +involving thread monitoring or premature :obj:`threading.Thread.is_alive` calls. From ea5518ede557086e159784297090c5fb3e29ed2c Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Mon, 15 Sep 2025 11:48:37 +0100 Subject: [PATCH 4/4] Update Modules/_threadmodule.c --- Modules/_threadmodule.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index bc1096315aff0a..c6d07b1360711c 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -712,7 +712,6 @@ PyThreadHandleObject_is_done(PyObject *op, PyObject *Py_UNUSED(dummy)) PyThreadHandleObject *self = PyThreadHandleObject_CAST(op); if (_PyEvent_IsSet(&self->handle->thread_is_exiting)) { if (_PyOnceFlag_CallOnce(&self->handle->once, join_thread, self->handle) == -1) { - PyErr_SetString(PyExc_RuntimeError, "failed to join thread"); return NULL; } Py_RETURN_TRUE;