# Should we deprecate \`threading.\_PyRLock\` in 3.13?

**URL:** <https://discuss.python.org/t/should-we-deprecate-threading-pyrlock-in-3-13/27297>\
**Category:** Core Development\
**Created:** [June 1, 2023, 8:22am UTC](https://discuss.python.org/t/should-we-deprecate-threading-pyrlock-in-3-13/27297 "2023-06-01T08:22:20Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![sobolevn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/sobolevn/32/9274_2.png) [@sobolevn](https://discuss.python.org/u/sobolevn)\
**Post date:** [June 1, 2023, 8:22am UTC](https://discuss.python.org/t/should-we-deprecate-threading-pyrlock-in-3-13/27297/1 "2023-06-01T08:22:20Z")

</div>

While working on [`threading.RLock` must not support `*args, **kwargs` arguments · Issue #102029 · python/cpython · GitHub](https://github.com/python/cpython/issues/102029) I was thinking about whether this is good idea or not. Right now I think that deprecating `threading._PyRLock` is good idea indeed, here are my points.

For quite a long time `threading._PyRlock` does not work as it should, see [https://github.com/python/cpython/issues/57906](https://github.com/python/cpython/issues/57906)  
TLDR:

> If a signal occurs during \_Py\_RLock.acquire() or release() and then operates on the same lock to acquire() or release() it, process hangs or assertions can be triggered.  
> This bug has existed for ages. People who want sane behaviour should just switch to Python 3. Closing.

Moreover, since Python3 we have `_thread.RLock` that is used in several places of stdlib directly:

- [https://github.com/python/cpython/blob/7d07e5891d2843f269fac00dc8847abfe3671765/Lib/functools.py#L21](https://github.com/python/cpython/blob/7d07e5891d2843f269fac00dc8847abfe3671765/Lib/functools.py#L21)
- [https://github.com/python/cpython/blob/7d07e5891d2843f269fac00dc8847abfe3671765/Lib/importlib/\_bootstrap.py#L165](https://github.com/python/cpython/blob/7d07e5891d2843f269fac00dc8847abfe3671765/Lib/importlib/_bootstrap.py#L165)

So, any other interpreters that are using CPython’s stdlib already have to adapt `_thread` module. Example from `RustPython`: [https://github.com/RustPython/RustPython/blob/058f8c55005a8ae93275957952e20a0ae9bf6968/Lib/functools.py#L21](https://github.com/RustPython/RustPython/blob/058f8c55005a8ae93275957952e20a0ae9bf6968/Lib/functools.py#L21) and [https://github.com/RustPython/RustPython/blob/058f8c55005a8ae93275957952e20a0ae9bf6968/vm/Lib/python\_builtins/\_thread.py](https://github.com/RustPython/RustPython/blob/058f8c55005a8ae93275957952e20a0ae9bf6968/vm/Lib/python_builtins/_thread.py) (they are using `_dummy_thread` for now).

And the last thing: for CPython users won’t feel anything, because `_thread` module is a required module for CPython: [https://github.com/python/cpython/blob/201440e97a3433ad7691163c4a17d4c2721fd7c6/Modules/Setup.bootstrap.in#L22](https://github.com/python/cpython/blob/201440e97a3433ad7691163c4a17d4c2721fd7c6/Modules/Setup.bootstrap.in#L22) and since it is always present, regular usages of `threading.RLock` won’t be changed.

The only thing that will change with new deprecation notice is direct creation of `_PyRLock` (right now the stdlib does not have any usages of it). However, there are some usages of `_PyRLock` in the wild, but mostly they are used to monkeypatch `_PyRLock`: [https://github.com/search?type=code&q=\_PyRLock()](https://github.com/search?type=code&q=_PyRLock%28%29) and they can easily silence the warning / ignore the missing class.

So, to sum things up, we have:

1. Legacy implementation with known problems
2. That almost no one uses
3. With a working default substitution that people rely on
4. While legacy implementation can be easily (from the technical point of view) deprecated

Are there any arguments to keep things as-is?  
Are there any valid use-cases for `_PyRLock` that we should continue to support?

Please, share your opinions 🙂

---

<div class="post-metadata">

**Author:** ![vstinner](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/vstinner/32/15130_2.png) [@vstinner](https://discuss.python.org/u/vstinner)\
**Post date:** [June 1, 2023, 11:21am UTC](https://discuss.python.org/t/should-we-deprecate-threading-pyrlock-in-3-13/27297/2 "2023-06-01T11:21:57Z")

</div>

I suggest to remove it, it’s impossible to handle properly signals with locks **in pure Python code**. There are still race conditions around `_tstate_lock` in the threading module because of that, whereas it’s implemented in C.

See [Thread spuriously marked dead after interrupting a join call · Issue #90882 · python/cpython · GitHub](https://github.com/python/cpython/issues/90882) and [Race condition in Thread.\_wait\_for\_tstate\_lock() · Issue #89437 · python/cpython · GitHub](https://github.com/python/cpython/issues/89437)

Currently, theading.py uses the following code:

```auto
        try:
            if lock.acquire(block, timeout):
                lock.release()
                self._stop()
        except:
            if lock.locked():
                # bpo-45274: lock.acquire() acquired the lock, but the function
                # was interrupted with an exception before reaching the
                # lock.release(). It can happen if a signal handler raises an
                # exception, like CTRL+C which raises KeyboardInterrupt.
                lock.release()
                self._stop()
            raise

```

IMO it should be rewritten in C to handle properly signals and make sure that the thread and its \_tstate\_lock remain in a consistent state.

---

<div class="post-metadata">

**Author:** ![eric.snow](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/eric.snow/32/31_2.png) [@eric.snow](https://discuss.python.org/u/eric.snow)\
**Post date:** [June 1, 2023, 3:35pm UTC](https://discuss.python.org/t/should-we-deprecate-threading-pyrlock-in-3-13/27297/3 "2023-06-01T15:35:33Z")

</div>

> [@vstinner](#):
>
> IMO it should be rewritten in C

This might be a broader question. How much of threading.py is the way it is because we used to support “dummy” threads (for platforms without thread support)? Threads-less builds haven’t been supported since 2017 (see [https://github.com/python/cpython/pull/3385](https://github.com/python/cpython/pull/3385)), so should we consider generally what parts of threading.py would be more appropriately implemented in C at this point? (I’m only suggesting we think about it, not that we jump into a blanket a re-write.)
