diff options
author | mpage <mpage@meta.com> | 2024-03-01 21:43:12 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-01 21:43:12 (GMT) |
commit | 9e88173d363fb22c2c7bf3da3a266817db6bf24b (patch) | |
tree | dec22e7ab1b7d7815490b8a3fecbe68392ad42e4 /Lib/threading.py | |
parent | 5e0c7bc1d311048e8252bae6fc91cb51c556f807 (diff) | |
download | cpython-9e88173d363fb22c2c7bf3da3a266817db6bf24b.zip cpython-9e88173d363fb22c2c7bf3da3a266817db6bf24b.tar.gz cpython-9e88173d363fb22c2c7bf3da3a266817db6bf24b.tar.bz2 |
gh-114271: Make `_thread.ThreadHandle` thread-safe in free-threaded builds (GH-115190)
Make `_thread.ThreadHandle` thread-safe in free-threaded builds
We protect the mutable state of `ThreadHandle` using a `_PyOnceFlag`.
Concurrent operations (i.e. `join` or `detach`) on `ThreadHandle` block
until it is their turn to execute or an earlier operation succeeds.
Once an operation has been applied successfully all future operations
complete immediately.
The `join()` method is now idempotent. It may be called multiple times
but the underlying OS thread will only be joined once. After `join()`
succeeds, any future calls to `join()` will succeed immediately.
The internal thread handle `detach()` method has been removed.
Diffstat (limited to 'Lib/threading.py')
-rw-r--r-- | Lib/threading.py | 24 |
1 files changed, 7 insertions, 17 deletions
diff --git a/Lib/threading.py b/Lib/threading.py index b6ff00a..ec89550 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -931,7 +931,6 @@ class Thread: if _HAVE_THREAD_NATIVE_ID: self._native_id = None self._tstate_lock = None - self._join_lock = None self._handle = None self._started = Event() self._is_stopped = False @@ -956,14 +955,11 @@ class Thread: if self._tstate_lock is not None: self._tstate_lock._at_fork_reinit() self._tstate_lock.acquire() - if self._join_lock is not None: - self._join_lock._at_fork_reinit() else: # This thread isn't alive after fork: it doesn't have a tstate # anymore. self._is_stopped = True self._tstate_lock = None - self._join_lock = None self._handle = None def __repr__(self): @@ -996,8 +992,6 @@ class Thread: if self._started.is_set(): raise RuntimeError("threads can only be started once") - self._join_lock = _allocate_lock() - with _active_limbo_lock: _limbo[self] = self try: @@ -1167,17 +1161,9 @@ class Thread: self._join_os_thread() def _join_os_thread(self): - join_lock = self._join_lock - if join_lock is None: - return - with join_lock: - # Calling join() multiple times would raise an exception - # in one of the callers. - if self._handle is not None: - self._handle.join() - self._handle = None - # No need to keep this around - self._join_lock = None + # self._handle may be cleared post-fork + if self._handle is not None: + self._handle.join() def _wait_for_tstate_lock(self, block=True, timeout=-1): # Issue #18808: wait for the thread state to be gone. @@ -1478,6 +1464,10 @@ class _MainThread(Thread): with _active_limbo_lock: _active[self._ident] = self + def _join_os_thread(self): + # No ThreadHandle for main thread + pass + # Helper thread-local instance to detect when a _DummyThread # is collected. Not a part of the public API. |