Blame sysdeps/htl/sem-timedwait.c

Packit 6c4009
/* Wait on a semaphore with a timeout.  Generic version.
Packit 6c4009
   Copyright (C) 2005-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library;  if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <semaphore.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
Packit 6c4009
#include <pt-internal.h>
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__sem_timedwait_internal (sem_t *restrict sem,
Packit 6c4009
			  const struct timespec *restrict timeout)
Packit 6c4009
{
Packit 6c4009
  error_t err;
Packit 6c4009
  int drain;
Packit 6c4009
  struct __pthread *self;
Packit 6c4009
Packit 6c4009
  __pthread_spin_lock (&sem->__lock);
Packit 6c4009
  if (sem->__value > 0)
Packit 6c4009
    /* Successful down.  */
Packit 6c4009
    {
Packit 6c4009
      sem->__value--;
Packit 6c4009
      __pthread_spin_unlock (&sem->__lock);
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (timeout != NULL && (timeout->tv_nsec < 0 || timeout->tv_nsec >= 1000000000))
Packit 6c4009
    {
Packit 6c4009
      errno = EINVAL;
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Add ourselves to the queue.  */
Packit 6c4009
  self = _pthread_self ();
Packit 6c4009
Packit 6c4009
  __pthread_enqueue (&sem->__queue, self);
Packit 6c4009
  __pthread_spin_unlock (&sem->__lock);
Packit 6c4009
Packit 6c4009
  /* Block the thread.  */
Packit 6c4009
  if (timeout != NULL)
Packit 6c4009
    err = __pthread_timedblock (self, timeout, CLOCK_REALTIME);
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      err = 0;
Packit 6c4009
      __pthread_block (self);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  __pthread_spin_lock (&sem->__lock);
Packit 6c4009
  if (self->prevp == NULL)
Packit 6c4009
    /* Another thread removed us from the queue, which means a wakeup message
Packit 6c4009
       has been sent.  It was either consumed while we were blocking, or
Packit 6c4009
       queued after we timed out and before we acquired the semaphore lock, in
Packit 6c4009
       which case the message queue must be drained.  */
Packit 6c4009
    drain = err ? 1 : 0;
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* We're still in the queue.  Noone attempted to wake us up, i.e. we
Packit 6c4009
         timed out.  */
Packit 6c4009
      __pthread_dequeue (self);
Packit 6c4009
      drain = 0;
Packit 6c4009
    }
Packit 6c4009
  __pthread_spin_unlock (&sem->__lock);
Packit 6c4009
Packit 6c4009
  if (drain)
Packit 6c4009
    __pthread_block (self);
Packit 6c4009
Packit 6c4009
  if (err)
Packit 6c4009
    {
Packit 6c4009
      assert (err == ETIMEDOUT);
Packit 6c4009
      errno = err;
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__sem_timedwait (sem_t *restrict sem, const struct timespec *restrict timeout)
Packit 6c4009
{
Packit 6c4009
  return __sem_timedwait_internal (sem, timeout);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
weak_alias (__sem_timedwait, sem_timedwait);