diff -r 4637ad8d0ad6 Lib/test/test_threading.py --- a/Lib/test/test_threading.py Fri Nov 06 23:41:14 2009 +0100 +++ b/Lib/test/test_threading.py Sat Nov 07 02:04:57 2009 +0100 @@ -141,9 +141,9 @@ class ThreadTests(BaseTestCase): def test_foreign_thread(self): # Check that a "foreign" thread can use the threading module. def f(mutex): - # Acquiring an RLock forces an entry for the foreign + # Acquiring a _PyRLock forces an entry for the foreign # thread to get made in the threading._active map. - r = threading.RLock() + r = threading._PyRLock() r.acquire() r.release() mutex.release() @@ -506,8 +506,11 @@ class ThreadingExceptionTests(BaseTestCa class LockTests(lock_tests.LockTests): locktype = staticmethod(threading.Lock) -class RLockTests(lock_tests.RLockTests): - locktype = staticmethod(threading.RLock) +class PyRLockTests(lock_tests.RLockTests): + locktype = staticmethod(threading._PyRLock) + +class CRLockTests(lock_tests.RLockTests): + locktype = staticmethod(threading._CRLock) class EventTests(lock_tests.EventTests): eventtype = staticmethod(threading.Event) @@ -527,7 +530,7 @@ class BoundedSemaphoreTests(lock_tests.B def test_main(): - test.support.run_unittest(LockTests, RLockTests, EventTests, + test.support.run_unittest(LockTests, PyRLockTests, CRLockTests, EventTests, ConditionAsRLockTests, ConditionTests, SemaphoreTests, BoundedSemaphoreTests, ThreadTests, diff -r 4637ad8d0ad6 Lib/threading.py --- a/Lib/threading.py Fri Nov 06 23:41:14 2009 +0100 +++ b/Lib/threading.py Sat Nov 07 02:04:57 2009 +0100 @@ -27,6 +27,10 @@ _start_new_thread = _thread.start_new_th _allocate_lock = _thread.allocate_lock _get_ident = _thread.get_ident ThreadError = _thread.error +try: + _CRLock = _thread.RLock +except AttributeError: + _CRLock = None del _thread @@ -79,8 +83,12 @@ def settrace(func): Lock = _allocate_lock -def RLock(*args, **kwargs): - return _RLock(*args, **kwargs) +def RLock(verbose=None, *args, **kwargs): + if verbose is None: + verbose = _VERBOSE + if (__debug__ and verbose) or _CRLock is None: + return _PyRLock(verbose, *args, **kwargs) + return _CRLock(*args, **kwargs) class _RLock(_Verbose): @@ -154,6 +162,8 @@ class _RLock(_Verbose): def _is_owned(self): return self._owner is current_thread() +_PyRLock = _RLock + def Condition(*args, **kwargs): return _Condition(*args, **kwargs) diff -r 4637ad8d0ad6 Modules/_threadmodule.c --- a/Modules/_threadmodule.c Fri Nov 06 23:41:14 2009 +0100 +++ b/Modules/_threadmodule.c Sat Nov 07 02:04:57 2009 +0100 @@ -155,6 +155,253 @@ static PyTypeObject Locktype = { lock_methods, /*tp_methods*/ }; +/* Recursive lock objects */ + +typedef struct { + PyObject_HEAD + PyThread_type_lock rlock_lock; + long rlock_owner; + unsigned long rlock_count; + PyObject *in_weakreflist; +} rlockobject; + +static void +rlock_dealloc(rlockobject *self) +{ + assert(self->rlock_lock); + if (self->in_weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) self); + /* 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); + Py_TYPE(self)->tp_free(self); +} + +static PyObject * +rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds) +{ + char *kwlist[] = {"blocking", NULL}; + int blocking = 1; + long tid; + int r = 1; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:acquire", kwlist, + &blocking)) + return NULL; + + tid = PyThread_get_thread_ident(); + if (self->rlock_count > 0 && tid == self->rlock_owner) { + unsigned long count = self->rlock_count + 1; + if (count <= self->rlock_count) { + PyErr_SetString(PyExc_OverflowError, + "Internal lock count overflowed"); + return NULL; + } + self->rlock_count = count; + Py_RETURN_TRUE; + } + + if (self->rlock_count > 0 || + !PyThread_acquire_lock(self->rlock_lock, 0)) { + if (!blocking) { + Py_RETURN_FALSE; + } + Py_BEGIN_ALLOW_THREADS + r = PyThread_acquire_lock(self->rlock_lock, blocking); + Py_END_ALLOW_THREADS + } + if (r) { + assert(self->rlock_count == 0); + self->rlock_owner = tid; + self->rlock_count = 1; + } + + return PyBool_FromLong((long) r); +} + +PyDoc_STRVAR(rlock_acquire_doc, +"acquire([wait]) -> bool\n\ +\n\ +Lock the lock. Without argument, this blocks if the lock is locked\n\ +by another thread, waiting for another thread to release\n\ +the lock, and return None once the lock is acquired.\n\ +With an argument, this will only block if the argument is true,\n\ +and the return value reflects whether the lock is acquired.\n\ +The blocking operation is not interruptible."); + +static PyObject * +rlock_release(rlockobject *self) +{ + long tid = PyThread_get_thread_ident(); + + if (self->rlock_count == 0 || self->rlock_owner != tid) { + PyErr_SetString(PyExc_RuntimeError, + "cannot release un-acquired lock"); + return NULL; + } + assert(self->rlock_count > 0); + if (--self->rlock_count == 0) { + PyThread_release_lock(self->rlock_lock); + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(rlock_release_doc, +"release()\n\ +\n\ +Release the lock, allowing another thread that is blocked waiting for\n\ +the lock to acquire the lock. The lock must be in the locked state,\n\ +and must be locked by the same thread that unlocks it."); + +static PyObject * +rlock_acquire_restore(rlockobject *self, PyObject *arg) +{ + long owner; + unsigned long count; + int r = 1; + + if (!PyArg_ParseTuple(arg, "kl:_acquire_restore", &count, &owner)) + return NULL; + + if (!PyThread_acquire_lock(self->rlock_lock, 0)) { + Py_BEGIN_ALLOW_THREADS + r = PyThread_acquire_lock(self->rlock_lock, 1); + Py_END_ALLOW_THREADS + } + if (!r) { + PyErr_SetString(ThreadError, "couldn't acquire lock"); + return NULL; + } + assert(self->rlock_count == 0); + self->rlock_owner = owner; + self->rlock_count = count; + Py_RETURN_NONE; +} + +PyDoc_STRVAR(rlock_acquire_restore_doc, +"_acquire_restore(state) -> None\n\ +\n\ +For internal use by `threading.Condition`."); + +static PyObject * +rlock_release_save(rlockobject *self) +{ + long owner; + unsigned long count; + + owner = self->rlock_owner; + count = self->rlock_count; + self->rlock_count = 0; + PyThread_release_lock(self->rlock_lock); + return Py_BuildValue("kl", count, owner); +} + +PyDoc_STRVAR(rlock_release_save_doc, +"_release_save() -> tuple\n\ +\n\ +For internal use by `threading.Condition`."); + + +static PyObject * +rlock_is_owned(rlockobject *self) +{ + long tid = PyThread_get_thread_ident(); + + if (self->rlock_count > 0 && self->rlock_owner == tid) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +PyDoc_STRVAR(rlock_is_owned_doc, +"_is_owned() -> bool\n\ +\n\ +For internal use by `threading.Condition`."); + +static PyObject * +rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + rlockobject *self; + + self = (rlockobject *) type->tp_alloc(type, 0); + if (self != NULL) { + self->rlock_lock = PyThread_allocate_lock(); + if (self->rlock_lock == NULL) { + Py_TYPE(self)->tp_free(self); + PyErr_SetString(ThreadError, "can't allocate lock"); + return NULL; + } + self->in_weakreflist = NULL; + self->rlock_owner = 0; + self->rlock_count = 0; + } + + return (PyObject *) self; +} + +static PyMethodDef rlock_methods[] = { + {"acquire", (PyCFunction)rlock_acquire, + METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc}, + {"release", (PyCFunction)rlock_release, + METH_NOARGS, rlock_release_doc}, + {"_is_owned", (PyCFunction)rlock_is_owned, + METH_NOARGS, rlock_is_owned_doc}, + {"_acquire_restore", (PyCFunction)rlock_acquire_restore, + METH_O, rlock_acquire_restore_doc}, + {"_release_save", (PyCFunction)rlock_release_save, + METH_NOARGS, rlock_release_save_doc}, + {"__enter__", (PyCFunction)rlock_acquire, + METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc}, + {"__exit__", (PyCFunction)rlock_release, + METH_VARARGS, rlock_release_doc}, + {NULL, NULL} /* sentinel */ +}; + + +static PyTypeObject RLocktype = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "_thread.RLock", /*tp_name*/ + sizeof(rlockobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)rlock_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + offsetof(rlockobject, in_weakreflist), /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + rlock_methods, /*tp_methods*/ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + rlock_new /* tp_new */ +}; + static lockobject * newlockobject(void) { @@ -752,6 +999,8 @@ PyInit__thread(void) return NULL; if (PyType_Ready(&Locktype) < 0) return NULL; + if (PyType_Ready(&RLocktype) < 0) + return NULL; /* Create the module and add the functions */ m = PyModule_Create(&threadmodule); @@ -766,6 +1015,10 @@ PyInit__thread(void) Py_INCREF(&Locktype); PyDict_SetItemString(d, "LockType", (PyObject *)&Locktype); + Py_INCREF(&RLocktype); + if (PyModule_AddObject(m, "RLock", (PyObject *)&RLocktype) < 0) + return NULL; + Py_INCREF(&localtype); if (PyModule_AddObject(m, "_local", (PyObject *)&localtype) < 0) return NULL;