Skip to content

Commit

Permalink
pythongh-114315: Make threading.Lock a real class
Browse files Browse the repository at this point in the history
  • Loading branch information
sobolevn committed Jan 23, 2024
1 parent 3620fa4 commit 4b1c4f6
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 9 deletions.
8 changes: 5 additions & 3 deletions Doc/library/threading.rst
Expand Up @@ -534,9 +534,11 @@ All methods are executed atomically.
lock, subsequent attempts to acquire it block, until it is released; any
thread may release it.

Note that ``Lock`` is actually a factory function which returns an instance
of the most efficient version of the concrete Lock class that is supported
by the platform.
.. versionchanged:: 3.13
Prior to 3.13 ``Lock`` actually used to be a factory
function which returned an instance
of the most efficient version of the concrete Lock class that is supported
by the platform.


.. method:: acquire(blocking=True, timeout=-1)
Expand Down
14 changes: 9 additions & 5 deletions Lib/test/test_threading.py
Expand Up @@ -170,11 +170,15 @@ def test_args_argument(self):
t.start()
t.join()

@cpython_only
def test_disallow_instantiation(self):
# Ensure that the type disallows instantiation (bpo-43916)
lock = threading.Lock()
test.support.check_disallow_instantiation(self, type(lock))
def test_lock_no_args(self):
threading.Lock() # works
self.assertRaises(TypeError, threading.Lock, 1)
self.assertRaises(TypeError, threading.Lock, a=1)
self.assertRaises(TypeError, threading.Lock, 1, 2, a=1, b=2)

def test_lock_or_none(self):
import types
self.assertIsInstance(threading.Lock | None, types.UnionType)

# Create a bunch of threads, let each do some work, wait until all are
# done.
Expand Down
3 changes: 2 additions & 1 deletion Lib/threading.py
Expand Up @@ -36,6 +36,7 @@
_start_joinable_thread = _thread.start_joinable_thread
_daemon_threads_allowed = _thread.daemon_threads_allowed
_allocate_lock = _thread.allocate_lock
_LockType = _thread.LockType
_set_sentinel = _thread._set_sentinel
get_ident = _thread.get_ident
_is_main_interpreter = _thread._is_main_interpreter
Expand Down Expand Up @@ -107,7 +108,7 @@ def gettrace():

# Synchronization classes

Lock = type(_allocate_lock())
Lock = _LockType

def RLock(*args, **kwargs):
"""Factory function that returns a new reentrant lock.
Expand Down
@@ -0,0 +1,2 @@
Make :class:`threading.Lock` a real class, not a factory function. Add
``__new__`` to ``_thread.lock`` type.
12 changes: 12 additions & 0 deletions Modules/_threadmodule.c
Expand Up @@ -5,6 +5,7 @@
#include "Python.h"
#include "pycore_interp.h" // _PyInterpreterState.threads.count
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
#include "pycore_pylifecycle.h"
#include "pycore_pystate.h" // _PyThreadState_SetCurrent()
#include "pycore_sysmodule.h" // _PySys_GetAttr()
Expand Down Expand Up @@ -354,11 +355,22 @@ static lockobject *newlockobject(PyObject *module);
static PyObject *
lock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
// convert to AC?
if (!_PyArg_NoKeywords("lock", kwargs)) {
goto error;
}
if (!_PyArg_CheckPositional("lock", PyTuple_GET_SIZE(args), 0, 0)) {
goto error;
}

PyObject *module = PyType_GetModuleByDef(type, &thread_module);
if (module == NULL) {
return NULL;
}
return (PyObject *)newlockobject(module);

error:
return NULL;
}


Expand Down

0 comments on commit 4b1c4f6

Please sign in to comment.