Issue #7316: the acquire() method of lock objects in the :mod:`threading`

module now takes an optional timeout argument in seconds.  Timeout support
relies on the system threading library, so as to avoid a semi-busy wait
loop.
This commit is contained in:
Antoine Pitrou 2010-04-14 15:44:10 +00:00
parent e53de3dc4a
commit 7c3e577395
11 changed files with 326 additions and 77 deletions

View File

@ -28,7 +28,7 @@ implementation. For systems lacking the :mod:`_thread` module, the
:mod:`_dummy_thread` module is available. It duplicates this module's interface
and can be used as a drop-in replacement.
It defines the following constant and functions:
It defines the following constants and functions:
.. exception:: error
@ -103,19 +103,34 @@ It defines the following constant and functions:
Availability: Windows, systems with POSIX threads.
.. data:: TIMEOUT_MAX
The maximum value allowed for the *timeout* parameter of
:meth:`Lock.acquire`. Specifiying a timeout greater than this value will
raise an :exc:`OverflowError`.
Lock objects have the following methods:
.. method:: lock.acquire([waitflag])
.. method:: lock.acquire(waitflag=1, timeout=-1)
Without the optional argument, this method acquires the lock unconditionally, if
Without any optional argument, this method acquires the lock unconditionally, if
necessary waiting until it is released by another thread (only one thread at a
time can acquire a lock --- that's their reason for existence). If the integer
*waitflag* argument is present, the action depends on its value: if it is zero,
the lock is only acquired if it can be acquired immediately without waiting,
while if it is nonzero, the lock is acquired unconditionally as before. The
return value is ``True`` if the lock is acquired successfully, ``False`` if not.
time can acquire a lock --- that's their reason for existence).
If the integer *waitflag* argument is present, the action depends on its
value: if it is zero, the lock is only acquired if it can be acquired
immediately without waiting, while if it is nonzero, the lock is acquired
unconditionally as above.
If the floating-point *timeout* argument is present and positive, it
specifies the maximum wait time in seconds before returning. A negative
*timeout* argument specifies an unbounded wait. You cannot specify
a *timeout* if *waitflag* is zero.
The return value is ``True`` if the lock is acquired successfully,
``False`` if not.
.. method:: lock.release()

View File

@ -155,6 +155,16 @@ This module defines the following functions and objects:
Availability: Windows, systems with POSIX threads.
This module also defines the following constant:
.. data:: TIMEOUT_MAX
The maximum value allowed for the *timeout* parameter of blocking functions
(:meth:`Lock.acquire`, :meth:`RLock.acquire`, :meth:`Condition.wait`, etc.).
Specifiying a timeout greater than this value will raise an
:exc:`OverflowError`.
Detailed interfaces for the objects are documented below.
The design of this module is loosely based on Java's threading model. However,
@ -349,7 +359,7 @@ and may vary across implementations.
All methods are executed atomically.
.. method:: Lock.acquire(blocking=True)
.. method:: Lock.acquire(blocking=True, timeout=-1)
Acquire a lock, blocking or non-blocking.
@ -363,6 +373,15 @@ All methods are executed atomically.
without an argument would block, return false immediately; otherwise, do the
same thing as when called without arguments, and return true.
When invoked with the floating-point *timeout* argument set to a positive
value, block for at most the number of seconds specified by *timeout*
and as long as the lock cannot be acquired. A negative *timeout* argument
specifies an unbounded wait. It is forbidden to specify a *timeout*
when *blocking* is false.
The return value is ``True`` if the lock is acquired successfully,
``False`` if not (for example if the *timeout* expired).
.. method:: Lock.release()
@ -396,7 +415,7 @@ pair) resets the lock to unlocked and allows another thread blocked in
:meth:`acquire` to proceed.
.. method:: RLock.acquire(blocking=True)
.. method:: RLock.acquire(blocking=True, timeout=-1)
Acquire a lock, blocking or non-blocking.
@ -415,6 +434,11 @@ pair) resets the lock to unlocked and allows another thread blocked in
without an argument would block, return false immediately; otherwise, do the
same thing as when called without arguments, and return true.
When invoked with the floating-point *timeout* argument set to a positive
value, block for at most the number of seconds specified by *timeout*
and as long as the lock cannot be acquired. Return true if the lock has
been acquired, false if the timeout has elapsed.
.. method:: RLock.release()

View File

@ -19,6 +19,41 @@ PyAPI_FUNC(void) PyThread_free_lock(PyThread_type_lock);
PyAPI_FUNC(int) PyThread_acquire_lock(PyThread_type_lock, int);
#define WAIT_LOCK 1
#define NOWAIT_LOCK 0
/* PY_TIMEOUT_T is the integral type used to specify timeouts when waiting
on a lock (see PyThread_acquire_lock_timed() below).
PY_TIMEOUT_MAX is the highest usable value (in microseconds) of that
type, and depends on the system threading API.
NOTE: this isn't the same value as `_thread.TIMEOUT_MAX`. The _thread
module exposes a higher-level API, with timeouts expressed in seconds
and floating-point numbers allowed.
*/
#if defined(HAVE_LONG_LONG)
#define PY_TIMEOUT_T PY_LONG_LONG
#define PY_TIMEOUT_MAX PY_LLONG_MAX
#else
#define PY_TIMEOUT_T long
#define PY_TIMEOUT_MAX LONG_MAX
#endif
/* In the NT API, the timeout is a DWORD and is expressed in milliseconds */
#if defined (NT_THREADS)
#if (0xFFFFFFFFLL * 1000 < PY_TIMEOUT_MAX)
#undef PY_TIMEOUT_MAX
#define PY_TIMEOUT_MAX (0xFFFFFFFFLL * 1000)
#endif
#endif
/* If microseconds == 0, the call is non-blocking: it returns immediately
even when the lock can't be acquired.
If microseconds > 0, the call waits up to the specified duration.
If microseconds < 0, the call waits until success (or abnormal failure)
microseconds must be less than PY_TIMEOUT_MAX. Behaviour otherwise is
undefined. */
PyAPI_FUNC(int) PyThread_acquire_lock_timed(PyThread_type_lock,
PY_TIMEOUT_T microseconds);
PyAPI_FUNC(void) PyThread_release_lock(PyThread_type_lock);
PyAPI_FUNC(size_t) PyThread_get_stacksize(void);

View File

@ -17,6 +17,10 @@ __all__ = ['error', 'start_new_thread', 'exit', 'get_ident', 'allocate_lock',
'interrupt_main', 'LockType']
import traceback as _traceback
import time
# A dummy value
TIMEOUT_MAX = 2**31
class error(Exception):
"""Dummy implementation of _thread.error."""
@ -92,7 +96,7 @@ class LockType(object):
def __init__(self):
self.locked_status = False
def acquire(self, waitflag=None):
def acquire(self, waitflag=None, timeout=-1):
"""Dummy implementation of acquire().
For blocking calls, self.locked_status is automatically set to
@ -111,6 +115,8 @@ class LockType(object):
self.locked_status = True
return True
else:
if timeout > 0:
time.sleep(timeout)
return False
__enter__ = acquire

View File

@ -440,10 +440,10 @@ class Pool(object):
p.terminate()
debug('joining task handler')
task_handler.join(1e100)
task_handler.join()
debug('joining result handler')
result_handler.join(1e100)
task_handler.join()
if pool and hasattr(pool[0], 'terminate'):
debug('joining pool workers')

View File

@ -4,7 +4,7 @@ Various tests for synchronization primitives.
import sys
import time
from _thread import start_new_thread, get_ident
from _thread import start_new_thread, get_ident, TIMEOUT_MAX
import threading
import unittest
@ -62,6 +62,14 @@ class BaseTestCase(unittest.TestCase):
support.threading_cleanup(*self._threads)
support.reap_children()
def assertTimeout(self, actual, expected):
# The waiting and/or time.time() can be imprecise, which
# is why comparing to the expected value would sometimes fail
# (especially under Windows).
self.assertGreaterEqual(actual, expected * 0.6)
# Test nothing insane happened
self.assertLess(actual, expected * 10.0)
class BaseLockTests(BaseTestCase):
"""
@ -143,6 +151,32 @@ class BaseLockTests(BaseTestCase):
Bunch(f, 15).wait_for_finished()
self.assertEqual(n, len(threading.enumerate()))
def test_timeout(self):
lock = self.locktype()
# Can't set timeout if not blocking
self.assertRaises(ValueError, lock.acquire, 0, 1)
# Invalid timeout values
self.assertRaises(ValueError, lock.acquire, timeout=-100)
self.assertRaises(OverflowError, lock.acquire, timeout=1e100)
self.assertRaises(OverflowError, lock.acquire, timeout=TIMEOUT_MAX + 1)
# TIMEOUT_MAX is ok
lock.acquire(timeout=TIMEOUT_MAX)
lock.release()
t1 = time.time()
self.assertTrue(lock.acquire(timeout=5))
t2 = time.time()
# Just a sanity test that it didn't actually wait for the timeout.
self.assertLess(t2 - t1, 5)
results = []
def f():
t1 = time.time()
results.append(lock.acquire(timeout=0.5))
t2 = time.time()
results.append(t2 - t1)
Bunch(f, 1).wait_for_finished()
self.assertFalse(results[0])
self.assertTimeout(results[1], 0.5)
class LockTests(BaseLockTests):
"""
@ -284,14 +318,14 @@ class EventTests(BaseTestCase):
def f():
results1.append(evt.wait(0.0))
t1 = time.time()
r = evt.wait(0.2)
r = evt.wait(0.5)
t2 = time.time()
results2.append((r, t2 - t1))
Bunch(f, N).wait_for_finished()
self.assertEqual(results1, [False] * N)
for r, dt in results2:
self.assertFalse(r)
self.assertTrue(dt >= 0.2, dt)
self.assertTimeout(dt, 0.5)
# The event is set
results1 = []
results2 = []
@ -397,14 +431,14 @@ class ConditionTests(BaseTestCase):
def f():
cond.acquire()
t1 = time.time()
cond.wait(0.2)
cond.wait(0.5)
t2 = time.time()
cond.release()
results.append(t2 - t1)
Bunch(f, N).wait_for_finished()
self.assertEqual(len(results), 5)
for dt in results:
self.assertTrue(dt >= 0.2, dt)
self.assertTimeout(dt, 0.5)
class BaseSemaphoreTests(BaseTestCase):

View File

@ -31,6 +31,7 @@ try:
_CRLock = _thread.RLock
except AttributeError:
_CRLock = None
TIMEOUT_MAX = _thread.TIMEOUT_MAX
del _thread
@ -107,14 +108,14 @@ class _RLock(_Verbose):
return "<%s owner=%r count=%d>" % (
self.__class__.__name__, owner, self._count)
def acquire(self, blocking=True):
def acquire(self, blocking=True, timeout=-1):
me = _get_ident()
if self._owner == me:
self._count = self._count + 1
if __debug__:
self._note("%s.acquire(%s): recursive success", self, blocking)
return 1
rc = self._block.acquire(blocking)
rc = self._block.acquire(blocking, timeout)
if rc:
self._owner = me
self._count = 1
@ -234,22 +235,10 @@ class _Condition(_Verbose):
if __debug__:
self._note("%s.wait(): got it", self)
else:
# Balancing act: We can't afford a pure busy loop, so we
# have to sleep; but if we sleep the whole timeout time,
# we'll be unresponsive. The scheme here sleeps very
# little at first, longer as time goes on, but never longer
# than 20 times per second (or the timeout time remaining).
endtime = _time() + timeout
delay = 0.0005 # 500 us -> initial delay of 1 ms
while True:
gotit = waiter.acquire(0)
if gotit:
break
remaining = endtime - _time()
if remaining <= 0:
break
delay = min(delay * 2, remaining, .05)
_sleep(delay)
if timeout > 0:
gotit = waiter.acquire(True, timeout)
else:
gotit = waiter.acquire(False)
if not gotit:
if __debug__:
self._note("%s.wait(%s): timed out", self, timeout)

View File

@ -312,6 +312,11 @@ C-API
Library
-------
- Issue #7316: the acquire() method of lock objects in the :mod:`threading`
module now takes an optional timeout argument in seconds. Timeout support
relies on the system threading library, so as to avoid a semi-busy wait
loop.
- Issue #8383: pickle and pickletools use surrogatepass error handler when
encoding unicode as utf8 to support lone surrogates and stay compatible with
Python 2.x and 3.0

View File

@ -40,18 +40,47 @@ lock_dealloc(lockobject *self)
}
static PyObject *
lock_PyThread_acquire_lock(lockobject *self, PyObject *args)
lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds)
{
int i = 1;
char *kwlist[] = {"blocking", "timeout", NULL};
int blocking = 1;
double timeout = -1;
PY_TIMEOUT_T microseconds;
int r;
if (!PyArg_ParseTuple(args, "|i:acquire", &i))
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|id:acquire", kwlist,
&blocking, &timeout))
return NULL;
if (!blocking && timeout != -1) {
PyErr_SetString(PyExc_ValueError, "can't specify a timeout "
"for a non-blocking call");
return NULL;
}
if (timeout < 0 && timeout != -1) {
PyErr_SetString(PyExc_ValueError, "timeout value must be "
"strictly positive");
return NULL;
}
if (!blocking)
microseconds = 0;
else if (timeout == -1)
microseconds = -1;
else {
timeout *= 1e6;
if (timeout >= (double) PY_TIMEOUT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"timeout value is too large");
return NULL;
}
microseconds = (PY_TIMEOUT_T) timeout;
}
Py_BEGIN_ALLOW_THREADS
i = PyThread_acquire_lock(self->lock_lock, i);
r = PyThread_acquire_lock_timed(self->lock_lock, microseconds);
Py_END_ALLOW_THREADS
return PyBool_FromLong((long)i);
return PyBool_FromLong(r);
}
PyDoc_STRVAR(acquire_doc,
@ -106,9 +135,9 @@ Return whether the lock is in the locked state.");
static PyMethodDef lock_methods[] = {
{"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock,
METH_VARARGS, acquire_doc},
METH_VARARGS | METH_KEYWORDS, acquire_doc},
{"acquire", (PyCFunction)lock_PyThread_acquire_lock,
METH_VARARGS, acquire_doc},
METH_VARARGS | METH_KEYWORDS, acquire_doc},
{"release_lock", (PyCFunction)lock_PyThread_release_lock,
METH_NOARGS, release_doc},
{"release", (PyCFunction)lock_PyThread_release_lock,
@ -118,7 +147,7 @@ static PyMethodDef lock_methods[] = {
{"locked", (PyCFunction)lock_locked_lock,
METH_NOARGS, locked_doc},
{"__enter__", (PyCFunction)lock_PyThread_acquire_lock,
METH_VARARGS, acquire_doc},
METH_VARARGS | METH_KEYWORDS, acquire_doc},
{"__exit__", (PyCFunction)lock_PyThread_release_lock,
METH_VARARGS, release_doc},
{NULL, NULL} /* sentinel */
@ -183,15 +212,41 @@ rlock_dealloc(rlockobject *self)
static PyObject *
rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds)
{
char *kwlist[] = {"blocking", NULL};
char *kwlist[] = {"blocking", "timeout", NULL};
int blocking = 1;
double timeout = -1;
PY_TIMEOUT_T microseconds;
long tid;
int r = 1;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:acquire", kwlist,
&blocking))
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|id:acquire", kwlist,
&blocking, &timeout))
return NULL;
if (!blocking && timeout != -1) {
PyErr_SetString(PyExc_ValueError, "can't specify a timeout "
"for a non-blocking call");
return NULL;
}
if (timeout < 0 && timeout != -1) {
PyErr_SetString(PyExc_ValueError, "timeout value must be "
"strictly positive");
return NULL;
}
if (!blocking)
microseconds = 0;
else if (timeout == -1)
microseconds = -1;
else {
timeout *= 1e6;
if (timeout >= (double) PY_TIMEOUT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"timeout value is too large");
return NULL;
}
microseconds = (PY_TIMEOUT_T) timeout;
}
tid = PyThread_get_thread_ident();
if (self->rlock_count > 0 && tid == self->rlock_owner) {
unsigned long count = self->rlock_count + 1;
@ -206,11 +261,11 @@ rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds)
if (self->rlock_count > 0 ||
!PyThread_acquire_lock(self->rlock_lock, 0)) {
if (!blocking) {
if (microseconds == 0) {
Py_RETURN_FALSE;
}
Py_BEGIN_ALLOW_THREADS
r = PyThread_acquire_lock(self->rlock_lock, blocking);
r = PyThread_acquire_lock_timed(self->rlock_lock, microseconds);
Py_END_ALLOW_THREADS
}
if (r) {
@ -1005,7 +1060,7 @@ static struct PyModuleDef threadmodule = {
PyMODINIT_FUNC
PyInit__thread(void)
{
PyObject *m, *d;
PyObject *m, *d, *timeout_max;
/* Initialize types: */
if (PyType_Ready(&localtype) < 0)
@ -1020,6 +1075,12 @@ PyInit__thread(void)
if (m == NULL)
return NULL;
timeout_max = PyFloat_FromDouble(PY_TIMEOUT_MAX / 1000000);
if (!timeout_max)
return NULL;
if (PyModule_AddObject(m, "TIMEOUT_MAX", timeout_max) < 0)
return NULL;
/* Add a symbolic constant */
d = PyModule_GetDict(m);
ThreadError = PyErr_NewException("_thread.error", NULL, NULL);

View File

@ -34,13 +34,13 @@ DeleteNonRecursiveMutex(PNRMUTEX mutex)
}
DWORD
EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait)
EnterNonRecursiveMutex(PNRMUTEX mutex, DWORD milliseconds)
{
/* Assume that the thread waits successfully */
DWORD ret ;
/* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */
if (!wait)
if (milliseconds == 0)
{
if (InterlockedCompareExchange(&mutex->owned, 0, -1) != -1)
return WAIT_TIMEOUT ;
@ -49,7 +49,7 @@ EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait)
else
ret = InterlockedIncrement(&mutex->owned) ?
/* Some thread owns the mutex, let's wait... */
WaitForSingleObject(mutex->hevent, INFINITE) : WAIT_OBJECT_0 ;
WaitForSingleObject(mutex->hevent, milliseconds) : WAIT_OBJECT_0 ;
mutex->thread_id = GetCurrentThreadId() ; /* We own it */
return ret ;
@ -239,18 +239,37 @@ PyThread_free_lock(PyThread_type_lock aLock)
* if the lock has already been acquired by this thread!
*/
int
PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag)
PyThread_acquire_lock_timed(PyThread_type_lock aLock, PY_TIMEOUT_T microseconds)
{
int success ;
PY_TIMEOUT_T milliseconds;
dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag));
if (microseconds >= 0) {
milliseconds = microseconds / 1000;
if (microseconds % 1000 > 0)
++milliseconds;
if ((DWORD) milliseconds != milliseconds)
Py_FatalError("Timeout too large for a DWORD, "
"please check PY_TIMEOUT_MAX");
}
else
milliseconds = INFINITE;
success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag ? INFINITE : 0)) == WAIT_OBJECT_0 ;
dprintf(("%ld: PyThread_acquire_lock_timed(%p, %lld) called\n",
PyThread_get_thread_ident(), aLock, microseconds));
dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success));
success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (DWORD) milliseconds) == WAIT_OBJECT_0 ;
dprintf(("%ld: PyThread_acquire_lock(%p, %lld) -> %d\n",
PyThread_get_thread_ident(), aLock, microseconds, success));
return success;
}
int
PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag)
{
return PyThread_acquire_lock_timed(aLock, waitflag ? -1 : 0);
}
void
PyThread_release_lock(PyThread_type_lock aLock)

View File

@ -83,6 +83,26 @@
#endif
/* We assume all modern POSIX systems have gettimeofday() */
#ifdef GETTIMEOFDAY_NO_TZ
#define GETTIMEOFDAY(ptv) gettimeofday(ptv)
#else
#define GETTIMEOFDAY(ptv) gettimeofday(ptv, (struct timezone *)NULL)
#endif
#define MICROSECONDS_TO_TIMESPEC(microseconds, ts) \
do { \
struct timeval tv; \
GETTIMEOFDAY(&tv); \
tv.tv_usec += microseconds % 1000000; \
tv.tv_sec += microseconds / 1000000; \
tv.tv_sec += tv.tv_usec / 1000000; \
tv.tv_usec %= 1000000; \
ts.tv_sec = tv.tv_sec; \
ts.tv_nsec = tv.tv_usec * 1000; \
} while(0)
/* A pthread mutex isn't sufficient to model the Python lock type
* because, according to Draft 5 of the docs (P1003.4a/D5), both of the
* following are undefined:
@ -295,34 +315,53 @@ fix_status(int status)
return (status == -1) ? errno : status;
}
int
PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
int
PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
{
int success;
sem_t *thelock = (sem_t *)lock;
int status, error = 0;
struct timespec ts;
dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag));
dprintf(("PyThread_acquire_lock_timed(%p, %lld) called\n",
lock, microseconds));
if (microseconds > 0)
MICROSECONDS_TO_TIMESPEC(microseconds, ts);
do {
if (waitflag)
status = fix_status(sem_wait(thelock));
else
if (microseconds > 0)
status = fix_status(sem_timedwait(thelock, &ts));
else if (microseconds == 0)
status = fix_status(sem_trywait(thelock));
else
status = fix_status(sem_wait(thelock));
} while (status == EINTR); /* Retry if interrupted by a signal */
if (waitflag) {
if (microseconds > 0) {
if (status != ETIMEDOUT)
CHECK_STATUS("sem_timedwait");
}
else if (microseconds == 0) {
if (status != EAGAIN)
CHECK_STATUS("sem_trywait");
}
else {
CHECK_STATUS("sem_wait");
} else if (status != EAGAIN) {
CHECK_STATUS("sem_trywait");
}
success = (status == 0) ? 1 : 0;
dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success));
dprintf(("PyThread_acquire_lock_timed(%p, %lld) -> %d\n",
lock, microseconds, success));
return success;
}
int
PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
{
return PyThread_acquire_lock_timed(lock, waitflag ? -1 : 0);
}
void
PyThread_release_lock(PyThread_type_lock lock)
{
@ -390,40 +429,62 @@ PyThread_free_lock(PyThread_type_lock lock)
free((void *)thelock);
}
int
PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
int
PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
{
int success;
pthread_lock *thelock = (pthread_lock *)lock;
int status, error = 0;
dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag));
dprintf(("PyThread_acquire_lock_timed(%p, %lld) called\n",
lock, microseconds));
status = pthread_mutex_lock( &thelock->mut );
CHECK_STATUS("pthread_mutex_lock[1]");
success = thelock->locked == 0;
if ( !success && waitflag ) {
if (!success && microseconds != 0) {
struct timespec ts;
if (microseconds > 0)
MICROSECONDS_TO_TIMESPEC(microseconds, ts);
/* continue trying until we get the lock */
/* mut must be locked by me -- part of the condition
* protocol */
while ( thelock->locked ) {
status = pthread_cond_wait(&thelock->lock_released,
&thelock->mut);
CHECK_STATUS("pthread_cond_wait");
while (thelock->locked) {
if (microseconds > 0) {
status = pthread_cond_timedwait(
&thelock->lock_released,
&thelock->mut, &ts);
if (status == ETIMEDOUT)
break;
CHECK_STATUS("pthread_cond_timed_wait");
}
else {
status = pthread_cond_wait(
&thelock->lock_released,
&thelock->mut);
CHECK_STATUS("pthread_cond_wait");
}
}
success = 1;
success = (status == 0);
}
if (success) thelock->locked = 1;
status = pthread_mutex_unlock( &thelock->mut );
CHECK_STATUS("pthread_mutex_unlock[1]");
if (error) success = 0;
dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success));
dprintf(("PyThread_acquire_lock_timed(%p, %lld) -> %d\n",
lock, microseconds, success));
return success;
}
int
PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
{
return PyThread_acquire_lock_timed(lock, waitflag ? -1 : 0);
}
void
PyThread_release_lock(PyThread_type_lock lock)
{