Skip to content

Commit 68628e0

Browse files
author
Rémy Voet (ryv)
committed
Fixes of my previous try:
- Add a new type of ThreadHandleState for failing bootstrap (avoid using THREAD_HANDLE_DONE for two different case, which is incorrect for join()) - is_bootstraped instead of is_running having the same behavior than before (True when the thread is `THREAD_HANDLE_DONE` state) - Renamming stuff
1 parent 4a75fe7 commit 68628e0

3 files changed

Lines changed: 45 additions & 24 deletions

File tree

Lib/test/test_threading.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,6 +1476,7 @@ def _set_ident_memory_error():
14761476
mock.patch.object(thread, '_set_ident', _set_ident_memory_error)
14771477
):
14781478
thread.start()
1479+
thread.join()
14791480
self.assertFalse(thread.is_alive())
14801481
self.assertFalse(thread in threading._limbo)
14811482
self.assertFalse(thread in threading._active)

Lib/threading.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,7 @@ def _after_fork(self, new_ident=None):
953953
def __repr__(self):
954954
assert self._initialized, "Thread.__init__() was not called"
955955
status = "initial"
956-
if self._os_thread_handle.is_running():
956+
if self._os_thread_handle.is_bootstraped():
957957
status = "started"
958958
if self._os_thread_handle.is_done():
959959
status = "stopped"
@@ -976,7 +976,7 @@ def start(self):
976976
if not self._initialized:
977977
raise RuntimeError("thread.__init__() not called")
978978

979-
if self._os_thread_handle.is_running():
979+
if self._os_thread_handle.is_bootstraped():
980980
raise RuntimeError("threads can only be started once")
981981

982982
with _active_limbo_lock:
@@ -999,13 +999,15 @@ def start(self):
999999
with _active_limbo_lock:
10001000
del _limbo[self]
10011001
raise
1002-
self._os_thread_handle.wait_bootstrap_done()
1002+
self._os_thread_handle.wait_bootstraped()
10031003

10041004
# It's possible that the _started event never occurs from the new Thread;
10051005
# e.g., it didn't have enough memory to call the initialization part of _bootstrap_inner.
1006-
if self._os_thread_handle.is_done():
1006+
if self._os_thread_handle.is_failed():
10071007
with _active_limbo_lock:
10081008
_limbo.pop(self, None)
1009+
if self._ident:
1010+
_active.pop(self._ident, None)
10091011

10101012
def run(self):
10111013
"""Method representing the thread's activity.
@@ -1065,7 +1067,7 @@ def _bootstrap_inner(self):
10651067
if _HAVE_THREAD_NATIVE_ID:
10661068
self._set_native_id()
10671069
self._set_os_name()
1068-
self._os_thread_handle.set_bootstrap_done()
1070+
self._os_thread_handle.set_bootstraped()
10691071
with _active_limbo_lock:
10701072
_active[self._ident] = self
10711073
del _limbo[self]
@@ -1113,7 +1115,7 @@ def join(self, timeout=None):
11131115
"""
11141116
if not self._initialized:
11151117
raise RuntimeError("Thread.__init__() not called")
1116-
if not self._os_thread_handle.is_done() and not self._os_thread_handle.is_running():
1118+
if not self._os_thread_handle.is_done() and not self._os_thread_handle.is_bootstraped():
11171119
raise RuntimeError("cannot join thread before it is started")
11181120
if self is current_thread():
11191121
raise RuntimeError("cannot join current thread")
@@ -1176,7 +1178,7 @@ def is_alive(self):
11761178
11771179
"""
11781180
assert self._initialized, "Thread.__init__() not called"
1179-
return self._os_thread_handle.is_running() and not self._os_thread_handle.is_done()
1181+
return self._os_thread_handle.is_bootstraped() and not self._os_thread_handle.is_done()
11801182

11811183
@property
11821184
def daemon(self):
@@ -1199,7 +1201,7 @@ def daemon(self, daemonic):
11991201
raise RuntimeError("Thread.__init__() not called")
12001202
if daemonic and not _daemon_threads_allowed():
12011203
raise RuntimeError('daemon threads are disabled in this interpreter')
1202-
if self._os_thread_handle.is_running() or self._os_thread_handle.is_done():
1204+
if self._os_thread_handle.is_bootstraped() or self._os_thread_handle.is_done():
12031205
raise RuntimeError("cannot set daemon status of active thread")
12041206
self._daemonic = daemonic
12051207

@@ -1441,7 +1443,7 @@ def __init__(self):
14411443
_DeleteDummyThreadOnDel(self)
14421444

14431445
def is_alive(self):
1444-
if self._os_thread_handle.is_running() and not self._os_thread_handle.is_done():
1446+
if self._os_thread_handle.is_bootstraped() and not self._os_thread_handle.is_done():
14451447
return True
14461448
raise RuntimeError("thread is not alive")
14471449

Modules/_threadmodule.c

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ typedef enum {
103103
THREAD_HANDLE_NOT_STARTED = 1,
104104
THREAD_HANDLE_STARTING = 2,
105105
THREAD_HANDLE_RUNNING = 3,
106-
THREAD_HANDLE_DONE = 4,
106+
THREAD_HANDLE_FAILED = 4,
107+
THREAD_HANDLE_DONE = 5,
107108
} ThreadHandleState;
108109

109110
// A handle to wait for thread completion.
@@ -288,7 +289,8 @@ ThreadHandle_decref(ThreadHandle *self)
288289
// 1. This is the destructor; nothing else holds a reference.
289290
// 2. The refcount going to zero is a "synchronizes-with" event; all
290291
// changes from other threads are visible.
291-
if (self->state == THREAD_HANDLE_RUNNING && !detach_thread(self)) {
292+
if ((self->state == THREAD_HANDLE_RUNNING || self->state == THREAD_HANDLE_FAILED)
293+
&& !detach_thread(self)) {
292294
self->state = THREAD_HANDLE_DONE;
293295
}
294296

@@ -396,8 +398,8 @@ thread_run(void *boot_raw)
396398
PyErr_FormatUnraisable(
397399
"Exception ignored in thread started by %R", boot->func);
398400
}
399-
// TODO: should we use a new state for failed bootsrap Thread
400-
set_thread_handle_state(handle, THREAD_HANDLE_DONE);
401+
set_thread_handle_state(handle, THREAD_HANDLE_FAILED);
402+
// Notify that the bootstraped is done, in case the Python call didn't manage too (e.g. Memory error)
401403
_PyEvent_Notify(&handle->thread_is_bootstraped);
402404
}
403405
else {
@@ -509,7 +511,10 @@ static int
509511
join_thread(void *arg)
510512
{
511513
ThreadHandle *handle = (ThreadHandle*)arg;
512-
assert(get_thread_handle_state(handle) >= THREAD_HANDLE_RUNNING);
514+
assert(
515+
get_thread_handle_state(handle) == THREAD_HANDLE_RUNNING ||
516+
get_thread_handle_state(handle) == THREAD_HANDLE_FAILED
517+
);
513518
PyThread_handle_t os_handle;
514519
if (ThreadHandle_get_os_handle(handle, &os_handle)) {
515520
int err = 0;
@@ -629,7 +634,7 @@ ThreadHandle_set_done(ThreadHandle *self)
629634
}
630635

631636
static void
632-
ThreadHandle_set_bootstrap_done(ThreadHandle *self)
637+
ThreadHandle_set_bootstraped(ThreadHandle *self)
633638
{
634639
_PyEvent_Notify(&self->thread_is_bootstraped);
635640
}
@@ -721,11 +726,10 @@ PyThreadHandleObject_join(PyObject *op, PyObject *args)
721726
}
722727

723728
static PyObject *
724-
PyThreadHandleObject_is_running(PyObject *op, PyObject *Py_UNUSED(dummy))
729+
PyThreadHandleObject_is_bootstraped(PyObject *op, PyObject *Py_UNUSED(dummy))
725730
{
726731
PyThreadHandleObject *self = PyThreadHandleObject_CAST(op);
727-
ThreadHandleState state = get_thread_handle_state(self->handle);
728-
if (state == THREAD_HANDLE_RUNNING) {
732+
if (_PyEvent_IsSet(&self->handle->thread_is_bootstraped)) {
729733
Py_RETURN_TRUE;
730734
}
731735
else {
@@ -734,21 +738,33 @@ PyThreadHandleObject_is_running(PyObject *op, PyObject *Py_UNUSED(dummy))
734738
}
735739

736740
static PyObject *
737-
PyThreadHandleObject_wait_bootstrap_done(PyObject *op, PyObject *Py_UNUSED(dummy))
741+
PyThreadHandleObject_wait_bootstraped(PyObject *op, PyObject *Py_UNUSED(dummy))
738742
{
739743
PyThreadHandleObject *self = PyThreadHandleObject_CAST(op);
740744
PyEvent_Wait(&self->handle->thread_is_bootstraped);
741745
Py_RETURN_NONE;
742746
}
743747

744748
static PyObject *
745-
PyThreadHandleObject_set_bootstrap_done(PyObject *op, PyObject *Py_UNUSED(dummy))
749+
PyThreadHandleObject_set_bootstraped(PyObject *op, PyObject *Py_UNUSED(dummy))
746750
{
747751
PyThreadHandleObject *self = PyThreadHandleObject_CAST(op);
748-
ThreadHandle_set_bootstrap_done(self->handle);
752+
ThreadHandle_set_bootstraped(self->handle);
749753
Py_RETURN_NONE;
750754
}
751755

756+
static PyObject *
757+
PyThreadHandleObject_is_failed(PyObject *op, PyObject *Py_UNUSED(dummy))
758+
{
759+
PyThreadHandleObject *self = PyThreadHandleObject_CAST(op);
760+
if (get_thread_handle_state(self->handle) == THREAD_HANDLE_FAILED) {
761+
Py_RETURN_TRUE;
762+
}
763+
else {
764+
Py_RETURN_FALSE;
765+
}
766+
}
767+
752768
static PyObject *
753769
PyThreadHandleObject_is_done(PyObject *op, PyObject *Py_UNUSED(dummy))
754770
{
@@ -782,9 +798,10 @@ static PyGetSetDef ThreadHandle_getsetlist[] = {
782798
static PyMethodDef ThreadHandle_methods[] = {
783799
{"join", PyThreadHandleObject_join, METH_VARARGS, NULL},
784800
{"_set_done", PyThreadHandleObject_set_done, METH_NOARGS, NULL},
785-
{"wait_bootstrap_done", PyThreadHandleObject_wait_bootstrap_done, METH_NOARGS, NULL},
786-
{"set_bootstrap_done", PyThreadHandleObject_set_bootstrap_done, METH_NOARGS, NULL},
787-
{"is_running", PyThreadHandleObject_is_running, METH_NOARGS, NULL},
801+
{"wait_bootstraped", PyThreadHandleObject_wait_bootstraped, METH_NOARGS, NULL},
802+
{"set_bootstraped", PyThreadHandleObject_set_bootstraped, METH_NOARGS, NULL},
803+
{"is_bootstraped", PyThreadHandleObject_is_bootstraped, METH_NOARGS, NULL},
804+
{"is_failed", PyThreadHandleObject_is_failed, METH_NOARGS, NULL},
788805
{"is_done", PyThreadHandleObject_is_done, METH_NOARGS, NULL},
789806
{0, 0}
790807
};
@@ -2511,6 +2528,7 @@ thread__make_thread_handle(PyObject *module, PyObject *identobj)
25112528
hobj->handle->ident = ident;
25122529
hobj->handle->state = THREAD_HANDLE_RUNNING;
25132530
PyMutex_Unlock(&hobj->handle->mutex);
2531+
_PyEvent_Notify(&hobj->handle->thread_is_bootstraped);
25142532
return (PyObject*) hobj;
25152533
}
25162534

0 commit comments

Comments
 (0)