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

@ -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)
{