@ -23,36 +23,98 @@
# include <pt-internal.h>
struct cancel_ctx
{
struct __pthread * wakeup ;
sem_t * sem ;
} ;
static void
cancel_hook ( void * arg )
{
struct cancel_ctx * ctx = arg ;
struct __pthread * wakeup = ctx - > wakeup ;
sem_t * sem = ctx - > sem ;
int unblock ;
__pthread_spin_wait ( & sem - > __lock ) ;
/* The thread only needs to be awaken if it's blocking or about to block.
If it was already unblocked , it ' s not queued any more . */
unblock = wakeup - > prevp ! = NULL ;
if ( unblock )
__pthread_dequeue ( wakeup ) ;
__pthread_spin_unlock ( & sem - > __lock ) ;
if ( unblock )
__pthread_wakeup ( wakeup ) ;
}
int
__sem_timedwait_internal ( sem_t * restrict sem ,
clockid_t clock_id ,
const struct timespec * restrict timeout )
{
error_t err ;
int drain ;
struct __pthread * self ;
int cancelled , oldtype , drain ;
int ret = 0 ;
struct __pthread * self = _pthread_self ( ) ;
struct cancel_ctx ctx ;
ctx . wakeup = self ;
ctx . sem = sem ;
/* Test for a pending cancellation request, switch to deferred mode for
safer resource handling , and prepare the hook to call in case we ' re
cancelled while blocking . Once CANCEL_LOCK is released , the cancellation
hook can be called by another thread at any time . Whatever happens ,
this function must exit with MUTEX locked .
This function contains inline implementations of pthread_testcancel and
pthread_setcanceltype to reduce locking overhead . */
__pthread_mutex_lock ( & self - > cancel_lock ) ;
cancelled = ( self - > cancel_state = = PTHREAD_CANCEL_ENABLE )
& & self - > cancel_pending ;
if ( cancelled )
{
__pthread_mutex_unlock ( & self - > cancel_lock ) ;
__pthread_exit ( PTHREAD_CANCELED ) ;
}
self - > cancel_hook = cancel_hook ;
self - > cancel_hook_arg = & ctx ;
oldtype = self - > cancel_type ;
if ( oldtype ! = PTHREAD_CANCEL_DEFERRED )
self - > cancel_type = PTHREAD_CANCEL_DEFERRED ;
/* Add ourselves to the list of waiters. This is done while setting
the cancellation hook to simplify the cancellation procedure , i . e .
if the thread is queued , it can be cancelled , otherwise it is
already unblocked , progressing on the return path . */
__pthread_spin_wait ( & sem - > __lock ) ;
if ( sem - > __value > 0 )
/* Successful down. */
{
sem - > __value - - ;
__pthread_spin_unlock ( & sem - > __lock ) ;
return 0 ;
goto out_locked ;
}
if ( timeout ! = NULL & & ! valid_nanoseconds ( timeout - > tv_nsec ) )
{
errno = EINVAL ;
return - 1 ;
ret = - 1 ;
__pthread_spin_unlock ( & sem - > __lock ) ;
goto out_locked ;
}
/* Add ourselves to the queue. */
self = _pthread_self ( ) ;
__pthread_enqueue ( & sem - > __queue , self ) ;
__pthread_spin_unlock ( & sem - > __lock ) ;
__pthread_mutex_unlock ( & self - > cancel_lock ) ;
/* Block the thread. */
if ( timeout ! = NULL )
err = __pthread_timedblock_intr ( self , timeout , clock_id ) ;
@ -82,10 +144,24 @@ __sem_timedwait_internal (sem_t *restrict sem,
{
assert ( err = = ETIMEDOUT | | err = = EINTR ) ;
errno = err ;
return - 1 ;
ret = - 1 ;
}
return 0 ;
/* We're almost done. Remove the unblock hook, restore the previous
cancellation type , and check for a pending cancellation request . */
__pthread_mutex_lock ( & self - > cancel_lock ) ;
out_locked :
self - > cancel_hook = NULL ;
self - > cancel_hook_arg = NULL ;
self - > cancel_type = oldtype ;
cancelled = ( self - > cancel_state = = PTHREAD_CANCEL_ENABLE )
& & self - > cancel_pending ;
__pthread_mutex_unlock ( & self - > cancel_lock ) ;
if ( cancelled )
__pthread_exit ( PTHREAD_CANCELED ) ;
return ret ;
}
int