diff options
Diffstat (limited to 'sysdeps/generic/sem-timedwait.c')
-rw-r--r-- | sysdeps/generic/sem-timedwait.c | 51 |
1 files changed, 30 insertions, 21 deletions
diff --git a/sysdeps/generic/sem-timedwait.c b/sysdeps/generic/sem-timedwait.c index 94e6dee..7ab1583 100644 --- a/sysdeps/generic/sem-timedwait.c +++ b/sysdeps/generic/sem-timedwait.c @@ -27,6 +27,8 @@ int __sem_timedwait_internal (sem_t *restrict sem, const struct timespec *restrict timeout) { + error_t err; + int drain; struct __pthread *self; __pthread_spin_lock (&sem->__lock); @@ -52,32 +54,39 @@ __sem_timedwait_internal (sem_t *restrict sem, /* Block the thread. */ if (timeout) + err = __pthread_timedblock (self, timeout, CLOCK_REALTIME); + else { - error_t err; - - err = __pthread_timedblock (self, timeout, CLOCK_REALTIME); - if (err) - /* We timed out. We may need to disconnect ourself from the - waiter queue. - - FIXME: What do we do if we get a wakeup message before we - disconnect ourself? It may remain until the next time we - block. */ - { - assert (err == ETIMEDOUT); - - __pthread_spin_lock (&sem->__lock); - if (self->prevp) - __pthread_dequeue (self); - __pthread_spin_unlock (&sem->__lock); - - errno = err; - return -1; - } + err = 0; + __pthread_block (self); } + + __pthread_spin_lock (&sem->__lock); + if (! self->prevp) + /* Another thread removed us from the queue, which means a wakeup message + has been sent. It was either consumed while we were blocking, or + queued after we timed out and before we acquired the semaphore lock, in + which case the message queue must be drained. */ + drain = err ? 1 : 0; else + { + /* We're still in the queue. Noone attempted to wake us up, i.e. we + timed out. */ + __pthread_dequeue (self); + drain = 0; + } + __pthread_spin_unlock (&sem->__lock); + + if (drain) __pthread_block (self); + if (err) + { + assert (err == ETIMEDOUT); + errno = err; + return -1; + } + return 0; } |