Index: Modules/_threadmodule.c =================================================================== --- Modules/_threadmodule.c (révision 65906) +++ Modules/_threadmodule.c (copie de travail) @@ -22,15 +22,28 @@ PyThread_type_lock lock_lock; } lockobject; +typedef struct { + PyObject_HEAD + lockobject* rlock_lock; + int rlock_has_owner; + long rlock_owner; + unsigned int rlock_count; +} rlockobject; + static void -lock_dealloc(lockobject *self) +PyThread_dealloc(PyThread_type_lock lock) { - assert(self->lock_lock); /* Unlock the lock so it's safe to free it */ - PyThread_acquire_lock(self->lock_lock, 0); - PyThread_release_lock(self->lock_lock); + PyThread_acquire_lock(lock, 0); + PyThread_release_lock(lock); + PyThread_free_lock(lock); +} - PyThread_free_lock(self->lock_lock); +static void +lock_dealloc(lockobject *self) +{ + assert(self->lock_lock); + PyThread_dealloc(self->lock_lock); PyObject_Del(self); } @@ -49,6 +62,20 @@ return PyBool_FromLong((long)i); } +static int +PyThread_try_release_lock(PyThread_type_lock lock) +{ + /* Sanity check: the lock must be locked */ + if (PyThread_acquire_lock(lock, 0)) { + PyThread_release_lock(lock); + PyErr_SetString(ThreadError, "release unlocked lock"); + return 0; + } + + PyThread_release_lock(lock); + return 1; +} + PyDoc_STRVAR(acquire_doc, "acquire([wait]) -> None or bool\n\ (acquire_lock() is an obsolete synonym)\n\ @@ -63,14 +90,9 @@ static PyObject * lock_PyThread_release_lock(lockobject *self) { - /* Sanity check: the lock must be locked */ - if (PyThread_acquire_lock(self->lock_lock, 0)) { - PyThread_release_lock(self->lock_lock); - PyErr_SetString(ThreadError, "release unlocked lock"); + if (!PyThread_try_release_lock(self->lock_lock)) { return NULL; } - - PyThread_release_lock(self->lock_lock); Py_INCREF(Py_None); return Py_None; } @@ -93,6 +115,137 @@ return PyBool_FromLong(1L); } +static void +rlock_dealloc(rlockobject *self) +{ + if (self->rlock_lock) + PyThread_dealloc(self->rlock_lock); + PyObject_Del(self); +} + +static PyObject * +rlock_PyThread_acquire_lock(rlockobject *self, PyObject *args) +{ + int blocking = 1; + int success; + long me; + + if (!PyArg_ParseTuple(args, "|i:acquire", &blocking)) + return NULL; + + me = PyThread_get_thread_ident(); + if (self->rlock_has_owner && self->rlock_owner == me) { + self->rlock_count += 1; +#if 0 + if __debug__: + self._note("%s.acquire(%s): recursive success", self, blocking) +#endif + success = 1; + } else { + Py_BEGIN_ALLOW_THREADS + success = PyThread_acquire_lock(self->rlock_lock, blocking); + Py_END_ALLOW_THREADS + + if (success) { + self->rlock_has_owner = 1; + self->rlock_owner = me; + self->rlock_count = 1; +#if 0 + if __debug__: + self._note("%s.acquire(%s): initial success", self, blocking) +#endif + } else { +#if 0 + if __debug__: + self._note("%s.acquire(%s): failure", self, blocking) +#endif + } + } + + return PyBool_FromLong(success); +} + +static PyObject * +rlock_PyThread_release_lock(rlockobject *self, PyObject *args) +{ + long me; + me = PyThread_get_thread_ident(); + if (!self->rlock_has_owner || self->rlock_owner != me) { + PyErr_SetString(PyExc_RuntimeError, + "cannot release un-aquired lock"); + return NULL; + } + self->rlock_count -= 1; + if (!self->rlock_count) { + self->rlock_has_owner = 0; + if (!PyThread_try_release_lock(self->rlock_lock)) + return NULL; +#if 0 + if __debug__: + self._note("%s.release(): final release", self) +#endif + } else { +#if 0 + if __debug__: + self._note("%s.release(): non-final release", self) +#endif + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +rlock_PyThread_acquire_restore(rlockobject *self, PyObject *args) +{ + unsigned long count; + PyObject* owner; + if (!PyArg_ParseTuple(args, "kO", &count, &owner)) + return NULL; + PyThread_acquire_lock(self->rlock_lock, 1); + self->rlock_count = count; + if (PyLong_Check(owner)) { + self->rlock_has_owner = 1; + self->rlock_owner = PyLong_AsLong(owner); + } else { + self->rlock_has_owner = 0; + } +#if 0 + if __debug__: + self._note("%s._acquire_restore()", self) +#endif + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +rlock_PyThread_release_save(rlockobject *self, PyObject *args) +{ + PyObject *ret; +#if 0 + if __debug__: + self._note("%s._release_save()", self) +#endif + if (!PyThread_try_release_lock(self->rlock_lock)) + return NULL; + if (self->rlock_has_owner) + ret = Py_BuildValue("(kl)", self->rlock_count, self->rlock_owner); + else + ret = Py_BuildValue("(kO)", self->rlock_count, Py_None); + if (!ret) + return NULL; + self->rlock_has_owner = 0; + self->rlock_count = 0; + return ret; +} + +static PyObject * +rlock_PyThread_is_owned(rlockobject *self, PyObject *args) +{ + long me = PyThread_get_thread_ident(); + int owned = (self->rlock_has_owner && self->rlock_owner == me); + return PyBool_FromLong(owned); +} + PyDoc_STRVAR(locked_doc, "locked() -> bool\n\ (locked_lock() is an obsolete synonym)\n\ @@ -151,6 +304,57 @@ lock_methods, /*tp_methods*/ }; +static PyMethodDef rlock_methods[] = { + {"acquire", (PyCFunction)rlock_PyThread_acquire_lock, + METH_VARARGS, NULL}, + {"release", (PyCFunction)rlock_PyThread_release_lock, + METH_NOARGS, NULL}, + {"__enter__", (PyCFunction)rlock_PyThread_acquire_lock, + METH_VARARGS, NULL}, + {"__exit__", (PyCFunction)rlock_PyThread_release_lock, + METH_VARARGS, NULL}, + {"_acquire_restore", (PyCFunction)rlock_PyThread_acquire_restore, + METH_VARARGS, NULL}, + {"_release_save", (PyCFunction)rlock_PyThread_release_save, + METH_VARARGS, NULL}, + {"_is_owned", (PyCFunction)rlock_PyThread_is_owned, + METH_VARARGS, NULL}, + {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_compare*/ + 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, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + rlock_methods, /*tp_methods*/ +}; + static lockobject * newlockobject(void) { @@ -167,6 +371,25 @@ return self; } +static rlockobject* +newrlockobject(void) +{ + rlockobject *self; + self = PyObject_New(rlockobject, &RLocktype); + if (self == NULL) + return NULL; + self->rlock_count = 0; + self->rlock_has_owner = 0; + self->rlock_owner = 0; + self->rlock_lock = PyThread_allocate_lock(); + if (self->rlock_lock == NULL) { + PyObject_Del(self); + self = NULL; + PyErr_SetString(ThreadError, "can't allocate lock"); + } + return self; +} + /* Thread-local objects */ #include "structmember.h" @@ -564,6 +787,7 @@ #endif static lockobject *newlockobject(void); +static rlockobject *newrlockobject(void); static PyObject * thread_PyThread_allocate_lock(PyObject *self) @@ -571,12 +795,23 @@ return (PyObject *) newlockobject(); } +static PyObject * +thread_PyThread_allocate_rlock(PyObject *self) +{ + return (PyObject *) newrlockobject(); +} + PyDoc_STRVAR(allocate_doc, "allocate_lock() -> lock object\n\ (allocate() is an obsolete synonym)\n\ \n\ Create a new lock object. See LockType.__doc__ for information about locks."); +PyDoc_STRVAR(allocate_rlock_doc, +"RLock() -> rlock object\n\ +\n\ +Create a new rlock object. See RLock.__doc__ for information about recursive locks."); + static PyObject * thread_get_ident(PyObject *self) { @@ -676,6 +911,8 @@ {"stack_size", (PyCFunction)thread_stack_size, METH_VARARGS, stack_size_doc}, + {"RLock", (PyCFunction)thread_PyThread_allocate_rlock, + METH_NOARGS, allocate_rlock_doc}, #ifndef NO_EXIT_PROG {"exit_prog", (PyCFunction)thread_PyThread_exit_prog, METH_VARARGS}, @@ -725,6 +962,8 @@ 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); @@ -739,6 +978,9 @@ Py_INCREF(&Locktype); PyDict_SetItemString(d, "LockType", (PyObject *)&Locktype); + Py_INCREF(&RLocktype); + PyDict_SetItemString(d, "RLockType", (PyObject *)&RLocktype); + Py_INCREF(&localtype); if (PyModule_AddObject(m, "_local", (PyObject *)&localtype) < 0) return NULL;