cpython icon indicating copy to clipboard operation
cpython copied to clipboard

Change RLock from factory function to actual type, remove dead Python RLock implementation

Open larryhastings opened this issue 3 months ago • 3 comments

Feature or enhancement

Proposal:

Currently in CPython threading.RLock is a factory function that returns a new RLock instance. It either returns an instance of _CRLock, if it's defined, otherwise it returns an instance of _PyRLock. I'm kidding--in practice, it never returns _PyRLock. You'd have to do something silly (threading._CRLock = None) in order to get it to return an instance of _PyRLock, because these days _CRLock is always defined.

I haven't gone reading through old versions / git blame / etc, but my guess is that CPython used to support platforms that didn't have a native RLock, or the C implementation of RLock was unsuitable for some reason on some platforms. Here in 2025 I find the C implementation of RLock is unguarded by any #if / #ifdef statement. All platforms currently supported by CPython must support threading, the C version of RLock (_thread.RLock) is always defined, and the threading.RLock factory function always returns an instance of that object (unless you do something silly).

Assuming I'm right about all that:

  • The Python implementation of RLock (class _RLock) in Lib/threading.py is dead code and should be removed.
  • There's no longer any point in making threading.RLock a factory function. (Was there ever a point?) threading.RLock should simply be an alias for the RLock implementation in C (_thread.RLock).

Has this already been discussed elsewhere?

No response given

Links to previous discussion of this feature:

No response

Linked PRs

  • gh-141462

larryhastings avatar Nov 12 '25 10:11 larryhastings

IMO, it sounds good.

And I'd like to add something on related topic. Today I've realized that _thread.lock can not be used as base type, but _thread.RLock can be used. Is it intentional?

>>> import _thread
>>> class C(_thread.lock): pass
... 
Traceback (most recent call last):
  File "<python-input-54>", line 1, in <module>
    class C(_thread.lock): pass
TypeError: type '_thread.lock' is not an acceptable base type
>>> class C(_thread.RLock): pass
... 
>>> 

efimov-mikhail avatar Nov 12 '25 10:11 efimov-mikhail

IMO, it sounds good.

And I'd like to add something on related topic. Today I've realized that _thread.lock can not be used as base type, but _thread.RLock can be used. Is it intentional?

import _thread class C(_thread.lock): pass ... Traceback (most recent call last): File "", line 1, in class C(_thread.lock): pass TypeError: type '_thread.lock' is not an acceptable base type class C(_thread.RLock): pass ...

I might be wrong, but I think _thread.lock is defined in without Py_TPFLAGS_BASETYPE (and with Py_TPFLAGS_IMMUTABLETYPE), so it’s not subclassable. It’s meant to be a minimal, fast primitive with tight invariants. On the other hand _thread.RLock is defined with Py_TPFLAGS_BASETYPE, so it is subclassable. It also exposes the private hooks (_acquire_restore, _release_save, _recursion_count, _is_owned, _at_fork_reinit) that threading.Condition uses.

mohsinm-dev avatar Nov 12 '25 11:11 mohsinm-dev

Py_TPFLAGS_BASETYPE

I found this commit: https://github.com/python/cpython/pull/114479/commits/b33a71ae609385d5e368ab98088ca2f0e6cf6aa9

# Intentionally disallow subclasses of threading.Lock because they have
# never been allowed, so why start now just because the type is public?

So that it's intentional.

I'll raise a PR to remove dead RLock code.

chaope avatar Dec 16 '25 19:12 chaope