Blame sysdeps/htl/pt-cond-timedwait.c

Packit Service 82fcde
/* Wait on a condition.  Generic version.
Packit Service 82fcde
   Copyright (C) 2000-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library;  if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <pthread.h>
Packit Service 82fcde
Packit Service 82fcde
#include <pt-internal.h>
Packit Service 82fcde
#include <pthreadP.h>
Packit Service 82fcde
Packit Service 82fcde
extern int __pthread_cond_timedwait_internal (pthread_cond_t *cond,
Packit Service 82fcde
					      pthread_mutex_t *mutex,
Packit Service 82fcde
					      const struct timespec *abstime);
Packit Service 82fcde
Packit Service 82fcde
int
Packit Service 82fcde
__pthread_cond_timedwait (pthread_cond_t *cond,
Packit Service 82fcde
			  pthread_mutex_t *mutex,
Packit Service 82fcde
			  const struct timespec *abstime)
Packit Service 82fcde
{
Packit Service 82fcde
  return __pthread_cond_timedwait_internal (cond, mutex, abstime);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
strong_alias (__pthread_cond_timedwait, pthread_cond_timedwait);
Packit Service 82fcde
Packit Service 82fcde
struct cancel_ctx
Packit Service 82fcde
{
Packit Service 82fcde
  struct __pthread *wakeup;
Packit Service 82fcde
  pthread_cond_t *cond;
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
cancel_hook (void *arg)
Packit Service 82fcde
{
Packit Service 82fcde
  struct cancel_ctx *ctx = arg;
Packit Service 82fcde
  struct __pthread *wakeup = ctx->wakeup;
Packit Service 82fcde
  pthread_cond_t *cond = ctx->cond;
Packit Service 82fcde
  int unblock;
Packit Service 82fcde
Packit Service 82fcde
  __pthread_spin_lock (&cond->__lock);
Packit Service 82fcde
  /* The thread only needs to be awaken if it's blocking or about to block.
Packit Service 82fcde
     If it was already unblocked, it's not queued any more.  */
Packit Service 82fcde
  unblock = wakeup->prevp != NULL;
Packit Service 82fcde
  if (unblock)
Packit Service 82fcde
    __pthread_dequeue (wakeup);
Packit Service 82fcde
  __pthread_spin_unlock (&cond->__lock);
Packit Service 82fcde
Packit Service 82fcde
  if (unblock)
Packit Service 82fcde
    __pthread_wakeup (wakeup);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Block on condition variable COND until ABSTIME.  As a GNU
Packit Service 82fcde
   extension, if ABSTIME is NULL, then wait forever.  MUTEX should be
Packit Service 82fcde
   held by the calling thread.  On return, MUTEX will be held by the
Packit Service 82fcde
   calling thread.  */
Packit Service 82fcde
int
Packit Service 82fcde
__pthread_cond_timedwait_internal (pthread_cond_t *cond,
Packit Service 82fcde
				   pthread_mutex_t *mutex,
Packit Service 82fcde
				   const struct timespec *abstime)
Packit Service 82fcde
{
Packit Service 82fcde
  error_t err;
Packit Service 82fcde
  int cancelled, oldtype, drain;
Packit Service 82fcde
  clockid_t clock_id = __pthread_default_condattr.__clock;
Packit Service 82fcde
Packit Service 82fcde
  if (abstime && (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000))
Packit Service 82fcde
    return EINVAL;
Packit Service 82fcde
Packit Service 82fcde
  struct __pthread *self = _pthread_self ();
Packit Service 82fcde
  struct cancel_ctx ctx;
Packit Service 82fcde
  ctx.wakeup = self;
Packit Service 82fcde
  ctx.cond = cond;
Packit Service 82fcde
Packit Service 82fcde
  /* Test for a pending cancellation request, switch to deferred mode for
Packit Service 82fcde
     safer resource handling, and prepare the hook to call in case we're
Packit Service 82fcde
     cancelled while blocking.  Once CANCEL_LOCK is released, the cancellation
Packit Service 82fcde
     hook can be called by another thread at any time.  Whatever happens,
Packit Service 82fcde
     this function must exit with MUTEX locked.
Packit Service 82fcde
Packit Service 82fcde
     This function contains inline implementations of pthread_testcancel and
Packit Service 82fcde
     pthread_setcanceltype to reduce locking overhead.  */
Packit Service 82fcde
  __pthread_mutex_lock (&self->cancel_lock);
Packit Service 82fcde
  cancelled = (self->cancel_state == PTHREAD_CANCEL_ENABLE)
Packit Service 82fcde
      && self->cancel_pending;
Packit Service 82fcde
Packit Service 82fcde
  if (!cancelled)
Packit Service 82fcde
    {
Packit Service 82fcde
      self->cancel_hook = cancel_hook;
Packit Service 82fcde
      self->cancel_hook_arg = &ctx;
Packit Service 82fcde
      oldtype = self->cancel_type;
Packit Service 82fcde
Packit Service 82fcde
      if (oldtype != PTHREAD_CANCEL_DEFERRED)
Packit Service 82fcde
	self->cancel_type = PTHREAD_CANCEL_DEFERRED;
Packit Service 82fcde
Packit Service 82fcde
      /* Add ourselves to the list of waiters.  This is done while setting
Packit Service 82fcde
         the cancellation hook to simplify the cancellation procedure, i.e.
Packit Service 82fcde
         if the thread is queued, it can be cancelled, otherwise it is
Packit Service 82fcde
         already unblocked, progressing on the return path.  */
Packit Service 82fcde
      __pthread_spin_lock (&cond->__lock);
Packit Service 82fcde
      __pthread_enqueue (&cond->__queue, self);
Packit Service 82fcde
      if (cond->__attr != NULL)
Packit Service 82fcde
	clock_id = cond->__attr->__clock;
Packit Service 82fcde
      __pthread_spin_unlock (&cond->__lock);
Packit Service 82fcde
    }
Packit Service 82fcde
  __pthread_mutex_unlock (&self->cancel_lock);
Packit Service 82fcde
Packit Service 82fcde
  if (cancelled)
Packit Service 82fcde
    __pthread_exit (PTHREAD_CANCELED);
Packit Service 82fcde
Packit Service 82fcde
  /* Release MUTEX before blocking.  */
Packit Service 82fcde
  __pthread_mutex_unlock (mutex);
Packit Service 82fcde
Packit Service 82fcde
  /* Block the thread.  */
Packit Service 82fcde
  if (abstime != NULL)
Packit Service 82fcde
    err = __pthread_timedblock (self, abstime, clock_id);
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      err = 0;
Packit Service 82fcde
      __pthread_block (self);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  __pthread_spin_lock (&cond->__lock);
Packit Service 82fcde
  if (self->prevp == NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Another thread removed us from the list of waiters, which means a
Packit Service 82fcde
         wakeup message has been sent.  It was either consumed while we were
Packit Service 82fcde
         blocking, or queued after we timed out and before we acquired the
Packit Service 82fcde
         condition lock, in which case the message queue must be drained.  */
Packit Service 82fcde
      if (!err)
Packit Service 82fcde
	drain = 0;
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  assert (err == ETIMEDOUT);
Packit Service 82fcde
	  drain = 1;
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      /* We're still in the list of waiters.  Noone attempted to wake us up,
Packit Service 82fcde
         i.e. we timed out.  */
Packit Service 82fcde
      assert (err == ETIMEDOUT);
Packit Service 82fcde
      __pthread_dequeue (self);
Packit Service 82fcde
      drain = 0;
Packit Service 82fcde
    }
Packit Service 82fcde
  __pthread_spin_unlock (&cond->__lock);
Packit Service 82fcde
Packit Service 82fcde
  if (drain)
Packit Service 82fcde
    __pthread_block (self);
Packit Service 82fcde
Packit Service 82fcde
  /* We're almost done.  Remove the unblock hook, restore the previous
Packit Service 82fcde
     cancellation type, and check for a pending cancellation request.  */
Packit Service 82fcde
  __pthread_mutex_lock (&self->cancel_lock);
Packit Service 82fcde
  self->cancel_hook = NULL;
Packit Service 82fcde
  self->cancel_hook_arg = NULL;
Packit Service 82fcde
  self->cancel_type = oldtype;
Packit Service 82fcde
  cancelled = (self->cancel_state == PTHREAD_CANCEL_ENABLE)
Packit Service 82fcde
      && self->cancel_pending;
Packit Service 82fcde
  __pthread_mutex_unlock (&self->cancel_lock);
Packit Service 82fcde
Packit Service 82fcde
  /* Reacquire MUTEX before returning/cancelling.  */
Packit Service 82fcde
  __pthread_mutex_lock (mutex);
Packit Service 82fcde
Packit Service 82fcde
  if (cancelled)
Packit Service 82fcde
    __pthread_exit (PTHREAD_CANCELED);
Packit Service 82fcde
Packit Service 82fcde
  return err;
Packit Service 82fcde
}