mirror of
https://github.com/python/cpython.git
synced 2025-10-17 04:08:28 +00:00
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:
parent
e53de3dc4a
commit
7c3e577395
11 changed files with 326 additions and 77 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue