diff --git a/Lib/threading.py b/Lib/threading.py --- a/Lib/threading.py +++ b/Lib/threading.py @@ -181,13 +181,10 @@ saved_state = self._release_save() try: # restore state no matter what (e.g., KeyboardInterrupt) if timeout is None: - waiter.acquire() + waiter._acquire_condition(-1, self._lock, saved_state) gotit = True else: - if timeout > 0: - gotit = waiter.acquire(True, timeout) - else: - gotit = waiter.acquire(False) + gotit = waiter._acquire_condition(timeout, self._lock, saved_state) if not gotit: try: self._waiters.remove(waiter) diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -190,6 +190,34 @@ \n\ Return whether the lock is in the locked state."); +PyDoc_STRVAR(lock_release_save_doc, +"_release_save() -> state\n\ +\n\ +For internal use by `threading.Condition`."); + +/* restore does nothing, since _acquire_condition was used which did it + * automatically + */ +static PyObject * +lock_acquire_restore(lockobject *self, PyObject *args) +{ + Py_RETURN_NONE; +} + +PyDoc_STRVAR(lock_acquire_restore_doc, +"_acquire_restore(state) -> None\n\ +\n\ +For internal use by `threading.Condition`."); + +/* forward decleration */ +static PyObject * +lock_acquire_condition(lockobject *self, PyObject *args, PyObject *kwds); + +PyDoc_STRVAR(lock_acquire_condition_doc, +"_acquire_condition(timeout, outer, state) -> None\n\ +\n\ +For internal use by `threading.Condition`."); + static PyMethodDef lock_methods[] = { {"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock, METH_VARARGS | METH_KEYWORDS, acquire_doc}, @@ -207,6 +235,12 @@ METH_VARARGS | METH_KEYWORDS, acquire_doc}, {"__exit__", (PyCFunction)lock_PyThread_release_lock, METH_VARARGS, release_doc}, + {"_release_save", (PyCFunction)lock_PyThread_release_lock, + METH_NOARGS, lock_release_save_doc}, + {"_acquire_restore", (PyCFunction)lock_acquire_restore, + METH_VARARGS, lock_acquire_restore_doc}, + {"_acquire_condition", (PyCFunction)lock_acquire_condition, + METH_VARARGS, lock_acquire_condition_doc}, {NULL, NULL} /* sentinel */ }; @@ -374,36 +408,6 @@ to be available for other threads."); 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; @@ -481,8 +485,8 @@ 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}, + {"_acquire_restore", (PyCFunction)lock_acquire_restore, + METH_O, lock_acquire_restore_doc}, {"_release_save", (PyCFunction)rlock_release_save, METH_NOARGS, rlock_release_save_doc}, {"__enter__", (PyCFunction)rlock_acquire, @@ -553,6 +557,79 @@ return self; } +static PyObject * +lock_acquire_condition(lockobject *self, PyObject *args, PyObject *kwds) +{ + double timeout; + PyObject *outer_lock; + PyObject *outer_arg; + unsigned long count; + long owner; + PY_TIMEOUT_T microseconds; + PyLockStatus r; + + /* if we can't even parse the arguments, then we cannot re-acquire the outer lock. But that + * is the same if passing wrong arguments to a _aquire_restore method. + */ + if (!PyArg_ParseTuple(args, "dOO:_acquire_condition", &timeout, &outer_lock, &outer_arg)) + return NULL; + if (Py_TYPE(outer_lock) == &RLocktype) + if (!PyArg_ParseTuple(outer_arg, "kl", &count, &owner)) + return NULL; + + /* these checks are kept as in "acquire" but shouldn't really be necessary + * for an "internal" method + */ + if (timeout < 0 && timeout != -1) { + PyErr_SetString(PyExc_ValueError, "timeout value must be " + "strictly positive"); + goto ERR; + } + if (timeout == -1) + microseconds = -1; + else { + timeout *= 1e6; + if (timeout >= (double) PY_TIMEOUT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "timeout value is too large"); + goto ERR; + } + microseconds = (PY_TIMEOUT_T) timeout; + } + + r = PY_LOCK_ACQUIRED; + goto ACQUIRE; +ERR: + r = PY_LOCK_INTR; /* just acquire the outer lock and exit with error */ +ACQUIRE: + /* acquire the condition lock _and_ the outer lock before reacquiring the GIL */ + Py_BEGIN_ALLOW_THREADS + if (r == PY_LOCK_ACQUIRED) + r = PyThread_acquire_lock_timed(self->lock_lock, microseconds, 1); + /* acquire the outer lock if we know it */ + if (Py_TYPE(outer_lock) == &Locktype) { + PyThread_acquire_lock_timed(((lockobject*)outer_lock)->lock_lock, -1, 0); + } else if(Py_TYPE(outer_lock) == &RLocktype) { + rlockobject *rlock = (rlockobject*)outer_lock; + PyThread_acquire_lock_timed(rlock->rlock_lock, -1, 0); + } + Py_END_ALLOW_THREADS + /* and restore the state of the locks */ + if (Py_TYPE(outer_lock) == &Locktype) { + ((lockobject*)outer_lock)->locked = 1; + } else if (Py_TYPE(outer_lock) == &RLocktype) { + rlockobject *rlock = (rlockobject*)outer_lock; + rlock->rlock_count = count; + rlock->rlock_owner = owner; + } + + if (r == PY_LOCK_INTR) { + return NULL; + } + + return PyBool_FromLong(r == PY_LOCK_ACQUIRED); +} + /* Thread-local objects */ #include "structmember.h"