Blame sysdeps/htl/pt-mutex-timedlock.c

Packit 6c4009
/* Lock a mutex with a timeout.  Generic version.
Packit 6c4009
   Copyright (C) 2000-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 <pthread.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
Packit 6c4009
#include <pt-internal.h>
Packit 6c4009
Packit 6c4009
#define LOSE do { * (int *) 0 = 0; } while (1)
Packit 6c4009
Packit 6c4009
/* Try to lock MUTEX, block until *ABSTIME if it is already held.  As
Packit 6c4009
   a GNU extension, if TIMESPEC is NULL then wait forever.  */
Packit 6c4009
int
Packit 6c4009
__pthread_mutex_timedlock_internal (struct __pthread_mutex *mutex,
Packit 6c4009
				    const struct timespec *abstime)
Packit 6c4009
{
Packit 6c4009
  error_t err;
Packit 6c4009
  int drain;
Packit 6c4009
  struct __pthread *self;
Packit 6c4009
  const struct __pthread_mutexattr *attr = mutex->__attr;
Packit 6c4009
Packit 6c4009
  if (attr == __PTHREAD_ERRORCHECK_MUTEXATTR)
Packit 6c4009
    attr = &__pthread_errorcheck_mutexattr;
Packit 6c4009
  if (attr == __PTHREAD_RECURSIVE_MUTEXATTR)
Packit 6c4009
    attr = &__pthread_recursive_mutexattr;
Packit 6c4009
Packit 6c4009
  __pthread_spin_lock (&mutex->__lock);
Packit 6c4009
  if (__pthread_spin_trylock (&mutex->__held) == 0)
Packit 6c4009
    /* Successfully acquired the lock.  */
Packit 6c4009
    {
Packit 6c4009
#ifdef ALWAYS_TRACK_MUTEX_OWNER
Packit 6c4009
# ifndef NDEBUG
Packit 6c4009
      self = _pthread_self ();
Packit 6c4009
      if (self != NULL)
Packit 6c4009
	/* The main thread may take a lock before the library is fully
Packit 6c4009
	   initialized, in particular, before the main thread has a
Packit 6c4009
	   TCB.  */
Packit 6c4009
	{
Packit 6c4009
	  assert (mutex->__owner == NULL);
Packit 6c4009
	  mutex->__owner = _pthread_self ();
Packit 6c4009
	}
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
      if (attr != NULL)
Packit 6c4009
	switch (attr->__mutex_type)
Packit 6c4009
	  {
Packit 6c4009
	  case PTHREAD_MUTEX_NORMAL:
Packit 6c4009
	    break;
Packit 6c4009
Packit 6c4009
	  case PTHREAD_MUTEX_RECURSIVE:
Packit 6c4009
	    mutex->__locks = 1;
Packit 6c4009
	  case PTHREAD_MUTEX_ERRORCHECK:
Packit 6c4009
	    mutex->__owner = _pthread_self ();
Packit 6c4009
	    break;
Packit 6c4009
Packit 6c4009
	  default:
Packit 6c4009
	    LOSE;
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
      __pthread_spin_unlock (&mutex->__lock);
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* The lock is busy.  */
Packit 6c4009
Packit 6c4009
  self = _pthread_self ();
Packit 6c4009
  assert (self);
Packit 6c4009
Packit 6c4009
  if (attr == NULL || attr->__mutex_type == PTHREAD_MUTEX_NORMAL)
Packit 6c4009
    {
Packit 6c4009
#if defined(ALWAYS_TRACK_MUTEX_OWNER)
Packit 6c4009
      assert (mutex->__owner != self);
Packit 6c4009
#endif
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      switch (attr->__mutex_type)
Packit 6c4009
	{
Packit 6c4009
	case PTHREAD_MUTEX_ERRORCHECK:
Packit 6c4009
	  if (mutex->__owner == self)
Packit 6c4009
	    {
Packit 6c4009
	      __pthread_spin_unlock (&mutex->__lock);
Packit 6c4009
	      return EDEADLK;
Packit 6c4009
	    }
Packit 6c4009
	  break;
Packit 6c4009
Packit 6c4009
	case PTHREAD_MUTEX_RECURSIVE:
Packit 6c4009
	  if (mutex->__owner == self)
Packit 6c4009
	    {
Packit 6c4009
	      mutex->__locks++;
Packit 6c4009
	      __pthread_spin_unlock (&mutex->__lock);
Packit 6c4009
	      return 0;
Packit 6c4009
	    }
Packit 6c4009
	  break;
Packit 6c4009
Packit 6c4009
	default:
Packit 6c4009
	  LOSE;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
#if !defined(ALWAYS_TRACK_MUTEX_OWNER)
Packit 6c4009
  if (attr != NULL && attr->__mutex_type != PTHREAD_MUTEX_NORMAL)
Packit 6c4009
#endif
Packit 6c4009
    assert (mutex->__owner);
Packit 6c4009
Packit 6c4009
  if (abstime != NULL && (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000))
Packit 6c4009
    return EINVAL;
Packit 6c4009
Packit 6c4009
  /* Add ourselves to the queue.  */
Packit 6c4009
  __pthread_enqueue (&mutex->__queue, self);
Packit 6c4009
  __pthread_spin_unlock (&mutex->__lock);
Packit 6c4009
Packit 6c4009
  /* Block the thread.  */
Packit 6c4009
  if (abstime != NULL)
Packit 6c4009
    err = __pthread_timedblock (self, abstime, 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 (&mutex->__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 mutex 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 (&mutex->__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
      return err;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
#if !defined(ALWAYS_TRACK_MUTEX_OWNER)
Packit 6c4009
  if (attr != NULL && attr->__mutex_type != PTHREAD_MUTEX_NORMAL)
Packit 6c4009
#endif
Packit 6c4009
    {
Packit 6c4009
      assert (mutex->__owner == self);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (attr != NULL)
Packit 6c4009
    switch (attr->__mutex_type)
Packit 6c4009
      {
Packit 6c4009
      case PTHREAD_MUTEX_NORMAL:
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      case PTHREAD_MUTEX_RECURSIVE:
Packit 6c4009
	assert (mutex->__locks == 0);
Packit 6c4009
	mutex->__locks = 1;
Packit 6c4009
      case PTHREAD_MUTEX_ERRORCHECK:
Packit 6c4009
	mutex->__owner = self;
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      default:
Packit 6c4009
	LOSE;
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
pthread_mutex_timedlock (struct __pthread_mutex *mutex,
Packit 6c4009
			 const struct timespec *abstime)
Packit 6c4009
{
Packit 6c4009
  return __pthread_mutex_timedlock_internal (mutex, abstime);
Packit 6c4009
}