mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 21:51:50 +00:00 
			
		
		
		
	gh-125139: use _PyRecursiveMutex in _thread.RLock (#125144)
				
					
				
			This commit is contained in:
		
							parent
							
								
									5217328f93
								
							
						
					
					
						commit
						67f6e08147
					
				
					 3 changed files with 63 additions and 122 deletions
				
			
		|  | @ -160,8 +160,9 @@ typedef struct { | ||||||
| 
 | 
 | ||||||
| PyAPI_FUNC(int) _PyRecursiveMutex_IsLockedByCurrentThread(_PyRecursiveMutex *m); | PyAPI_FUNC(int) _PyRecursiveMutex_IsLockedByCurrentThread(_PyRecursiveMutex *m); | ||||||
| PyAPI_FUNC(void) _PyRecursiveMutex_Lock(_PyRecursiveMutex *m); | PyAPI_FUNC(void) _PyRecursiveMutex_Lock(_PyRecursiveMutex *m); | ||||||
|  | extern PyLockStatus _PyRecursiveMutex_LockTimed(_PyRecursiveMutex *m, PyTime_t timeout, _PyLockFlags flags); | ||||||
| PyAPI_FUNC(void) _PyRecursiveMutex_Unlock(_PyRecursiveMutex *m); | PyAPI_FUNC(void) _PyRecursiveMutex_Unlock(_PyRecursiveMutex *m); | ||||||
| 
 | extern int _PyRecursiveMutex_TryUnlock(_PyRecursiveMutex *m); | ||||||
| 
 | 
 | ||||||
| // A readers-writer (RW) lock. The lock supports multiple concurrent readers or
 | // A readers-writer (RW) lock. The lock supports multiple concurrent readers or
 | ||||||
| // a single writer. The lock is write-preferring: if a writer is waiting while
 | // a single writer. The lock is write-preferring: if a writer is waiting while
 | ||||||
|  |  | ||||||
|  | @ -726,11 +726,6 @@ lock_dealloc(PyObject *op) | ||||||
|     Py_DECREF(tp); |     Py_DECREF(tp); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline PyLockStatus |  | ||||||
| acquire_timed(PyThread_type_lock lock, PyTime_t timeout) |  | ||||||
| { |  | ||||||
|     return PyThread_acquire_lock_timed_with_retries(lock, timeout); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| static int | static int | ||||||
| lock_acquire_parse_args(PyObject *args, PyObject *kwds, | lock_acquire_parse_args(PyObject *args, PyObject *kwds, | ||||||
|  | @ -973,10 +968,7 @@ static PyType_Spec lock_type_spec = { | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     PyObject_HEAD |     PyObject_HEAD | ||||||
|     PyThread_type_lock rlock_lock; |     _PyRecursiveMutex lock; | ||||||
|     PyThread_ident_t rlock_owner; |  | ||||||
|     unsigned long rlock_count; |  | ||||||
|     PyObject *in_weakreflist; |  | ||||||
| } rlockobject; | } rlockobject; | ||||||
| 
 | 
 | ||||||
| static int | static int | ||||||
|  | @ -992,59 +984,26 @@ rlock_dealloc(PyObject *op) | ||||||
| { | { | ||||||
|     rlockobject *self = (rlockobject*)op; |     rlockobject *self = (rlockobject*)op; | ||||||
|     PyObject_GC_UnTrack(self); |     PyObject_GC_UnTrack(self); | ||||||
|     if (self->in_weakreflist != NULL) |     PyObject_ClearWeakRefs((PyObject *) self); | ||||||
|         PyObject_ClearWeakRefs((PyObject *) self); |  | ||||||
|     /* self->rlock_lock can be NULL if PyThread_allocate_lock() failed
 |  | ||||||
|        in rlock_new() */ |  | ||||||
|     if (self->rlock_lock != NULL) { |  | ||||||
|         /* Unlock the lock so it's safe to free it */ |  | ||||||
|         if (self->rlock_count > 0) |  | ||||||
|             PyThread_release_lock(self->rlock_lock); |  | ||||||
| 
 |  | ||||||
|         PyThread_free_lock(self->rlock_lock); |  | ||||||
|     } |  | ||||||
|     PyTypeObject *tp = Py_TYPE(self); |     PyTypeObject *tp = Py_TYPE(self); | ||||||
|     tp->tp_free(self); |     tp->tp_free(self); | ||||||
|     Py_DECREF(tp); |     Py_DECREF(tp); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool |  | ||||||
| rlock_is_owned_by(rlockobject *self, PyThread_ident_t tid) |  | ||||||
| { |  | ||||||
|     PyThread_ident_t owner_tid = |  | ||||||
|         _Py_atomic_load_ullong_relaxed(&self->rlock_owner); |  | ||||||
|     return owner_tid == tid && self->rlock_count > 0; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| static PyObject * | static PyObject * | ||||||
| rlock_acquire(PyObject *op, PyObject *args, PyObject *kwds) | rlock_acquire(PyObject *op, PyObject *args, PyObject *kwds) | ||||||
| { | { | ||||||
|     rlockobject *self = (rlockobject*)op; |     rlockobject *self = (rlockobject*)op; | ||||||
|     PyTime_t timeout; |     PyTime_t timeout; | ||||||
|     PyThread_ident_t tid; |  | ||||||
|     PyLockStatus r = PY_LOCK_ACQUIRED; |  | ||||||
| 
 | 
 | ||||||
|     if (lock_acquire_parse_args(args, kwds, &timeout) < 0) |     if (lock_acquire_parse_args(args, kwds, &timeout) < 0) { | ||||||
|         return NULL; |         return NULL; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     tid = PyThread_get_thread_ident_ex(); |     PyLockStatus r = _PyRecursiveMutex_LockTimed(&self->lock, timeout, | ||||||
|     if (rlock_is_owned_by(self, tid)) { |                                                  _PY_LOCK_HANDLE_SIGNALS | _PY_LOCK_DETACH); | ||||||
|         unsigned long count = self->rlock_count + 1; |     if (r == PY_LOCK_INTR) { | ||||||
|         if (count <= self->rlock_count) { |  | ||||||
|             PyErr_SetString(PyExc_OverflowError, |  | ||||||
|                             "Internal lock count overflowed"); |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|         self->rlock_count = count; |  | ||||||
|         Py_RETURN_TRUE; |  | ||||||
|     } |  | ||||||
|     r = acquire_timed(self->rlock_lock, timeout); |  | ||||||
|     if (r == PY_LOCK_ACQUIRED) { |  | ||||||
|         assert(self->rlock_count == 0); |  | ||||||
|         _Py_atomic_store_ullong_relaxed(&self->rlock_owner, tid); |  | ||||||
|         self->rlock_count = 1; |  | ||||||
|     } |  | ||||||
|     else if (r == PY_LOCK_INTR) { |  | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1078,17 +1037,12 @@ static PyObject * | ||||||
| rlock_release(PyObject *op, PyObject *Py_UNUSED(ignored)) | rlock_release(PyObject *op, PyObject *Py_UNUSED(ignored)) | ||||||
| { | { | ||||||
|     rlockobject *self = (rlockobject*)op; |     rlockobject *self = (rlockobject*)op; | ||||||
|     PyThread_ident_t tid = PyThread_get_thread_ident_ex(); |  | ||||||
| 
 | 
 | ||||||
|     if (!rlock_is_owned_by(self, tid)) { |     if (_PyRecursiveMutex_TryUnlock(&self->lock) < 0) { | ||||||
|         PyErr_SetString(PyExc_RuntimeError, |         PyErr_SetString(PyExc_RuntimeError, | ||||||
|                         "cannot release un-acquired lock"); |                         "cannot release un-acquired lock"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     if (--self->rlock_count == 0) { |  | ||||||
|         _Py_atomic_store_ullong_relaxed(&self->rlock_owner, 0); |  | ||||||
|         PyThread_release_lock(self->rlock_lock); |  | ||||||
|     } |  | ||||||
|     Py_RETURN_NONE; |     Py_RETURN_NONE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1116,25 +1070,15 @@ rlock_acquire_restore(PyObject *op, PyObject *args) | ||||||
| { | { | ||||||
|     rlockobject *self = (rlockobject*)op; |     rlockobject *self = (rlockobject*)op; | ||||||
|     PyThread_ident_t owner; |     PyThread_ident_t owner; | ||||||
|     unsigned long count; |     Py_ssize_t count; | ||||||
|     int r = 1; |  | ||||||
| 
 | 
 | ||||||
|     if (!PyArg_ParseTuple(args, "(k" Py_PARSE_THREAD_IDENT_T "):_acquire_restore", |     if (!PyArg_ParseTuple(args, "(n" Py_PARSE_THREAD_IDENT_T "):_acquire_restore", | ||||||
|             &count, &owner)) |             &count, &owner)) | ||||||
|         return NULL; |         return NULL; | ||||||
| 
 | 
 | ||||||
|     if (!PyThread_acquire_lock(self->rlock_lock, 0)) { |     _PyRecursiveMutex_Lock(&self->lock); | ||||||
|         Py_BEGIN_ALLOW_THREADS |     _Py_atomic_store_ullong_relaxed(&self->lock.thread, owner); | ||||||
|         r = PyThread_acquire_lock(self->rlock_lock, 1); |     self->lock.level = (size_t)count - 1; | ||||||
|         Py_END_ALLOW_THREADS |  | ||||||
|     } |  | ||||||
|     if (!r) { |  | ||||||
|         PyErr_SetString(ThreadError, "couldn't acquire lock"); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|     assert(self->rlock_count == 0); |  | ||||||
|     _Py_atomic_store_ullong_relaxed(&self->rlock_owner, owner); |  | ||||||
|     self->rlock_count = count; |  | ||||||
|     Py_RETURN_NONE; |     Py_RETURN_NONE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1148,21 +1092,18 @@ static PyObject * | ||||||
| rlock_release_save(PyObject *op, PyObject *Py_UNUSED(ignored)) | rlock_release_save(PyObject *op, PyObject *Py_UNUSED(ignored)) | ||||||
| { | { | ||||||
|     rlockobject *self = (rlockobject*)op; |     rlockobject *self = (rlockobject*)op; | ||||||
|     PyThread_ident_t owner; |  | ||||||
|     unsigned long count; |  | ||||||
| 
 | 
 | ||||||
|     if (self->rlock_count == 0) { |     if (!_PyRecursiveMutex_IsLockedByCurrentThread(&self->lock)) { | ||||||
|         PyErr_SetString(PyExc_RuntimeError, |         PyErr_SetString(PyExc_RuntimeError, | ||||||
|                         "cannot release un-acquired lock"); |                         "cannot release un-acquired lock"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     owner = self->rlock_owner; |     PyThread_ident_t owner = self->lock.thread; | ||||||
|     count = self->rlock_count; |     Py_ssize_t count = self->lock.level + 1; | ||||||
|     self->rlock_count = 0; |     self->lock.level = 0;  // ensure the unlock releases the lock
 | ||||||
|     _Py_atomic_store_ullong_relaxed(&self->rlock_owner, 0); |     _PyRecursiveMutex_Unlock(&self->lock); | ||||||
|     PyThread_release_lock(self->rlock_lock); |     return Py_BuildValue("n" Py_PARSE_THREAD_IDENT_T, count, owner); | ||||||
|     return Py_BuildValue("k" Py_PARSE_THREAD_IDENT_T, count, owner); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| PyDoc_STRVAR(rlock_release_save_doc, | PyDoc_STRVAR(rlock_release_save_doc, | ||||||
|  | @ -1175,10 +1116,10 @@ static PyObject * | ||||||
| rlock_recursion_count(PyObject *op, PyObject *Py_UNUSED(ignored)) | rlock_recursion_count(PyObject *op, PyObject *Py_UNUSED(ignored)) | ||||||
| { | { | ||||||
|     rlockobject *self = (rlockobject*)op; |     rlockobject *self = (rlockobject*)op; | ||||||
|     PyThread_ident_t tid = PyThread_get_thread_ident_ex(); |     if (_PyRecursiveMutex_IsLockedByCurrentThread(&self->lock)) { | ||||||
|     PyThread_ident_t owner = |         return PyLong_FromSize_t(self->lock.level + 1); | ||||||
|         _Py_atomic_load_ullong_relaxed(&self->rlock_owner); |     } | ||||||
|     return PyLong_FromUnsignedLong(owner == tid ? self->rlock_count : 0UL); |     return PyLong_FromLong(0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| PyDoc_STRVAR(rlock_recursion_count_doc, | PyDoc_STRVAR(rlock_recursion_count_doc, | ||||||
|  | @ -1191,12 +1132,8 @@ static PyObject * | ||||||
| rlock_is_owned(PyObject *op, PyObject *Py_UNUSED(ignored)) | rlock_is_owned(PyObject *op, PyObject *Py_UNUSED(ignored)) | ||||||
| { | { | ||||||
|     rlockobject *self = (rlockobject*)op; |     rlockobject *self = (rlockobject*)op; | ||||||
|     PyThread_ident_t tid = PyThread_get_thread_ident_ex(); |     long owned = _PyRecursiveMutex_IsLockedByCurrentThread(&self->lock); | ||||||
| 
 |     return PyBool_FromLong(owned); | ||||||
|     if (rlock_is_owned_by(self, tid)) { |  | ||||||
|         Py_RETURN_TRUE; |  | ||||||
|     } |  | ||||||
|     Py_RETURN_FALSE; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| PyDoc_STRVAR(rlock_is_owned_doc, | PyDoc_STRVAR(rlock_is_owned_doc, | ||||||
|  | @ -1212,16 +1149,7 @@ rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||||||
|     if (self == NULL) { |     if (self == NULL) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     self->in_weakreflist = NULL; |     self->lock = (_PyRecursiveMutex){0}; | ||||||
|     self->rlock_owner = 0; |  | ||||||
|     self->rlock_count = 0; |  | ||||||
| 
 |  | ||||||
|     self->rlock_lock = PyThread_allocate_lock(); |  | ||||||
|     if (self->rlock_lock == NULL) { |  | ||||||
|         Py_DECREF(self); |  | ||||||
|         PyErr_SetString(ThreadError, "can't allocate lock"); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|     return (PyObject *) self; |     return (PyObject *) self; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1229,13 +1157,13 @@ static PyObject * | ||||||
| rlock_repr(PyObject *op) | rlock_repr(PyObject *op) | ||||||
| { | { | ||||||
|     rlockobject *self = (rlockobject*)op; |     rlockobject *self = (rlockobject*)op; | ||||||
|     PyThread_ident_t owner = |     PyThread_ident_t owner = self->lock.thread; | ||||||
|             _Py_atomic_load_ullong_relaxed(&self->rlock_owner); |     size_t count = self->lock.level + 1; | ||||||
|     return PyUnicode_FromFormat( |     return PyUnicode_FromFormat( | ||||||
|         "<%s %s object owner=%" PY_FORMAT_THREAD_IDENT_T " count=%lu at %p>", |         "<%s %s object owner=%" PY_FORMAT_THREAD_IDENT_T " count=%zu at %p>", | ||||||
|         self->rlock_count ? "locked" : "unlocked", |         owner ? "locked" : "unlocked", | ||||||
|         Py_TYPE(self)->tp_name, owner, |         Py_TYPE(self)->tp_name, owner, | ||||||
|         self->rlock_count, self); |         count, self); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -1243,14 +1171,7 @@ rlock_repr(PyObject *op) | ||||||
| static PyObject * | static PyObject * | ||||||
| rlock__at_fork_reinit(rlockobject *self, PyObject *Py_UNUSED(args)) | rlock__at_fork_reinit(rlockobject *self, PyObject *Py_UNUSED(args)) | ||||||
| { | { | ||||||
|     if (_PyThread_at_fork_reinit(&self->rlock_lock) < 0) { |     self->lock = (_PyRecursiveMutex){0}; | ||||||
|         PyErr_SetString(ThreadError, "failed to reinitialize lock at fork"); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     self->rlock_owner = 0; |  | ||||||
|     self->rlock_count = 0; |  | ||||||
| 
 |  | ||||||
|     Py_RETURN_NONE; |     Py_RETURN_NONE; | ||||||
| } | } | ||||||
| #endif  /* HAVE_FORK */ | #endif  /* HAVE_FORK */ | ||||||
|  | @ -1281,18 +1202,12 @@ static PyMethodDef rlock_methods[] = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static PyMemberDef rlock_type_members[] = { |  | ||||||
|     {"__weaklistoffset__", Py_T_PYSSIZET, offsetof(rlockobject, in_weakreflist), Py_READONLY}, |  | ||||||
|     {NULL}, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static PyType_Slot rlock_type_slots[] = { | static PyType_Slot rlock_type_slots[] = { | ||||||
|     {Py_tp_dealloc, rlock_dealloc}, |     {Py_tp_dealloc, rlock_dealloc}, | ||||||
|     {Py_tp_repr, rlock_repr}, |     {Py_tp_repr, rlock_repr}, | ||||||
|     {Py_tp_methods, rlock_methods}, |     {Py_tp_methods, rlock_methods}, | ||||||
|     {Py_tp_alloc, PyType_GenericAlloc}, |     {Py_tp_alloc, PyType_GenericAlloc}, | ||||||
|     {Py_tp_new, rlock_new}, |     {Py_tp_new, rlock_new}, | ||||||
|     {Py_tp_members, rlock_type_members}, |  | ||||||
|     {Py_tp_traverse, rlock_traverse}, |     {Py_tp_traverse, rlock_traverse}, | ||||||
|     {0, 0}, |     {0, 0}, | ||||||
| }; | }; | ||||||
|  | @ -1301,7 +1216,7 @@ static PyType_Spec rlock_type_spec = { | ||||||
|     .name = "_thread.RLock", |     .name = "_thread.RLock", | ||||||
|     .basicsize = sizeof(rlockobject), |     .basicsize = sizeof(rlockobject), | ||||||
|     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | |     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | | ||||||
|               Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), |               Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_MANAGED_WEAKREF), | ||||||
|     .slots = rlock_type_slots, |     .slots = rlock_type_slots, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -377,21 +377,46 @@ _PyRecursiveMutex_Lock(_PyRecursiveMutex *m) | ||||||
|     assert(m->level == 0); |     assert(m->level == 0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | PyLockStatus | ||||||
|  | _PyRecursiveMutex_LockTimed(_PyRecursiveMutex *m, PyTime_t timeout, _PyLockFlags flags) | ||||||
|  | { | ||||||
|  |     PyThread_ident_t thread = PyThread_get_thread_ident_ex(); | ||||||
|  |     if (recursive_mutex_is_owned_by(m, thread)) { | ||||||
|  |         m->level++; | ||||||
|  |         return PY_LOCK_ACQUIRED; | ||||||
|  |     } | ||||||
|  |     PyLockStatus s = _PyMutex_LockTimed(&m->mutex, timeout, flags); | ||||||
|  |     if (s == PY_LOCK_ACQUIRED) { | ||||||
|  |         _Py_atomic_store_ullong_relaxed(&m->thread, thread); | ||||||
|  |         assert(m->level == 0); | ||||||
|  |     } | ||||||
|  |     return s; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void | void | ||||||
| _PyRecursiveMutex_Unlock(_PyRecursiveMutex *m) | _PyRecursiveMutex_Unlock(_PyRecursiveMutex *m) | ||||||
|  | { | ||||||
|  |     if (_PyRecursiveMutex_TryUnlock(m) < 0) { | ||||||
|  |         Py_FatalError("unlocking a recursive mutex that is not " | ||||||
|  |                       "owned by the current thread"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int | ||||||
|  | _PyRecursiveMutex_TryUnlock(_PyRecursiveMutex *m) | ||||||
| { | { | ||||||
|     PyThread_ident_t thread = PyThread_get_thread_ident_ex(); |     PyThread_ident_t thread = PyThread_get_thread_ident_ex(); | ||||||
|     if (!recursive_mutex_is_owned_by(m, thread)) { |     if (!recursive_mutex_is_owned_by(m, thread)) { | ||||||
|         Py_FatalError("unlocking a recursive mutex that is not owned by the" |         return -1; | ||||||
|                       " current thread"); |  | ||||||
|     } |     } | ||||||
|     if (m->level > 0) { |     if (m->level > 0) { | ||||||
|         m->level--; |         m->level--; | ||||||
|         return; |         return 0; | ||||||
|     } |     } | ||||||
|     assert(m->level == 0); |     assert(m->level == 0); | ||||||
|     _Py_atomic_store_ullong_relaxed(&m->thread, 0); |     _Py_atomic_store_ullong_relaxed(&m->thread, 0); | ||||||
|     PyMutex_Unlock(&m->mutex); |     PyMutex_Unlock(&m->mutex); | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #define _Py_WRITE_LOCKED 1 | #define _Py_WRITE_LOCKED 1 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Kumar Aditya
						Kumar Aditya