hjl / source-git / glibc

Forked from source-git/glibc 3 years ago
Clone

Blame nptl/pthread_mutex_timedlock.c

Packit 6c4009
/* Copyright (C) 2002-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
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 <assert.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <time.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
#include <sys/time.h>
Packit 6c4009
#include "pthreadP.h"
Packit 6c4009
#include <atomic.h>
Packit 6c4009
#include <lowlevellock.h>
Packit 6c4009
#include <not-cancel.h>
Packit 6c4009
Packit 6c4009
#include <stap-probe.h>
Packit 6c4009
Packit 6c4009
#ifndef lll_timedlock_elision
Packit 6c4009
#define lll_timedlock_elision(a,dummy,b,c) lll_timedlock(a, b, c)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifndef lll_trylock_elision
Packit 6c4009
#define lll_trylock_elision(a,t) lll_trylock(a)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifndef FORCE_ELISION
Packit 6c4009
#define FORCE_ELISION(m, s)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__pthread_mutex_timedlock (pthread_mutex_t *mutex,
Packit 6c4009
			   const struct timespec *abstime)
Packit 6c4009
{
Packit 6c4009
  int oldval;
Packit 6c4009
  pid_t id = THREAD_GETMEM (THREAD_SELF, tid);
Packit 6c4009
  int result = 0;
Packit 6c4009
Packit 6c4009
  LIBC_PROBE (mutex_timedlock_entry, 2, mutex, abstime);
Packit 6c4009
Packit 6c4009
  /* We must not check ABSTIME here.  If the thread does not block
Packit 6c4009
     abstime must not be checked for a valid value.  */
Packit 6c4009
Packit 5e5144
  /* See concurrency notes regarding mutex type which is loaded from __kind
Packit 5e5144
     in struct __pthread_mutex_s in sysdeps/nptl/bits/thread-shared-types.h.  */
Packit 6c4009
  switch (__builtin_expect (PTHREAD_MUTEX_TYPE_ELISION (mutex),
Packit 6c4009
			    PTHREAD_MUTEX_TIMED_NP))
Packit 6c4009
    {
Packit 6c4009
      /* Recursive mutex.  */
Packit 6c4009
    case PTHREAD_MUTEX_RECURSIVE_NP|PTHREAD_MUTEX_ELISION_NP:
Packit 6c4009
    case PTHREAD_MUTEX_RECURSIVE_NP:
Packit 6c4009
      /* Check whether we already hold the mutex.  */
Packit 6c4009
      if (mutex->__data.__owner == id)
Packit 6c4009
	{
Packit 6c4009
	  /* Just bump the counter.  */
Packit 6c4009
	  if (__glibc_unlikely (mutex->__data.__count + 1 == 0))
Packit 6c4009
	    /* Overflow of the counter.  */
Packit 6c4009
	    return EAGAIN;
Packit 6c4009
Packit 6c4009
	  ++mutex->__data.__count;
Packit 6c4009
Packit 6c4009
	  goto out;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* We have to get the mutex.  */
Packit 6c4009
      result = lll_timedlock (mutex->__data.__lock, abstime,
Packit 6c4009
			      PTHREAD_MUTEX_PSHARED (mutex));
Packit 6c4009
Packit 6c4009
      if (result != 0)
Packit 6c4009
	goto out;
Packit 6c4009
Packit 6c4009
      /* Only locked once so far.  */
Packit 6c4009
      mutex->__data.__count = 1;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
      /* Error checking mutex.  */
Packit 6c4009
    case PTHREAD_MUTEX_ERRORCHECK_NP:
Packit 6c4009
      /* Check whether we already hold the mutex.  */
Packit 6c4009
      if (__glibc_unlikely (mutex->__data.__owner == id))
Packit 6c4009
	return EDEADLK;
Packit 6c4009
Packit 6c4009
      /* Don't do lock elision on an error checking mutex.  */
Packit 6c4009
      goto simple;
Packit 6c4009
Packit 6c4009
    case PTHREAD_MUTEX_TIMED_NP:
Packit 6c4009
      FORCE_ELISION (mutex, goto elision);
Packit 6c4009
    simple:
Packit 6c4009
      /* Normal mutex.  */
Packit 6c4009
      result = lll_timedlock (mutex->__data.__lock, abstime,
Packit 6c4009
			      PTHREAD_MUTEX_PSHARED (mutex));
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case PTHREAD_MUTEX_TIMED_ELISION_NP:
Packit 6c4009
    elision: __attribute__((unused))
Packit 6c4009
      /* Don't record ownership */
Packit 6c4009
      return lll_timedlock_elision (mutex->__data.__lock,
Packit 6c4009
				    mutex->__data.__spins,
Packit 6c4009
				    abstime,
Packit 6c4009
				    PTHREAD_MUTEX_PSHARED (mutex));
Packit 6c4009
Packit 6c4009
Packit 6c4009
    case PTHREAD_MUTEX_ADAPTIVE_NP:
Packit 6c4009
      if (! __is_smp)
Packit 6c4009
	goto simple;
Packit 6c4009
Packit 6c4009
      if (lll_trylock (mutex->__data.__lock) != 0)
Packit 6c4009
	{
Packit 6c4009
	  int cnt = 0;
Packit 6c4009
	  int max_cnt = MIN (MAX_ADAPTIVE_COUNT,
Packit 6c4009
			     mutex->__data.__spins * 2 + 10);
Packit 6c4009
	  do
Packit 6c4009
	    {
Packit 6c4009
	      if (cnt++ >= max_cnt)
Packit 6c4009
		{
Packit 6c4009
		  result = lll_timedlock (mutex->__data.__lock, abstime,
Packit 6c4009
					  PTHREAD_MUTEX_PSHARED (mutex));
Packit 6c4009
		  break;
Packit 6c4009
		}
Packit 6c4009
	      atomic_spin_nop ();
Packit 6c4009
	    }
Packit 6c4009
	  while (lll_trylock (mutex->__data.__lock) != 0);
Packit 6c4009
Packit 6c4009
	  mutex->__data.__spins += (cnt - mutex->__data.__spins) / 8;
Packit 6c4009
	}
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case PTHREAD_MUTEX_ROBUST_RECURSIVE_NP:
Packit 6c4009
    case PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP:
Packit 6c4009
    case PTHREAD_MUTEX_ROBUST_NORMAL_NP:
Packit 6c4009
    case PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP:
Packit 6c4009
      THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
Packit 6c4009
		     &mutex->__data.__list.__next);
Packit 6c4009
      /* We need to set op_pending before starting the operation.  Also
Packit 6c4009
	 see comments at ENQUEUE_MUTEX.  */
Packit 6c4009
      __asm ("" ::: "memory");
Packit 6c4009
Packit 6c4009
      oldval = mutex->__data.__lock;
Packit 6c4009
      /* This is set to FUTEX_WAITERS iff we might have shared the
Packit 6c4009
	 FUTEX_WAITERS flag with other threads, and therefore need to keep it
Packit 6c4009
	 set to avoid lost wake-ups.  We have the same requirement in the
Packit 6c4009
	 simple mutex algorithm.  */
Packit 6c4009
      unsigned int assume_other_futex_waiters = 0;
Packit 6c4009
      while (1)
Packit 6c4009
	{
Packit 6c4009
	  /* Try to acquire the lock through a CAS from 0 (not acquired) to
Packit 6c4009
	     our TID | assume_other_futex_waiters.  */
Packit 6c4009
	  if (__glibc_likely (oldval == 0))
Packit 6c4009
	    {
Packit 6c4009
	      oldval
Packit 6c4009
	        = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
Packit 6c4009
	            id | assume_other_futex_waiters, 0);
Packit 6c4009
	      if (__glibc_likely (oldval == 0))
Packit 6c4009
		break;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  if ((oldval & FUTEX_OWNER_DIED) != 0)
Packit 6c4009
	    {
Packit 6c4009
	      /* The previous owner died.  Try locking the mutex.  */
Packit 6c4009
	      int newval = id | (oldval & FUTEX_WAITERS)
Packit 6c4009
		  | assume_other_futex_waiters;
Packit 6c4009
Packit 6c4009
	      newval
Packit 6c4009
		= atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
Packit 6c4009
						       newval, oldval);
Packit 6c4009
	      if (newval != oldval)
Packit 6c4009
		{
Packit 6c4009
		  oldval = newval;
Packit 6c4009
		  continue;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      /* We got the mutex.  */
Packit 6c4009
	      mutex->__data.__count = 1;
Packit 6c4009
	      /* But it is inconsistent unless marked otherwise.  */
Packit 6c4009
	      mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
Packit 6c4009
Packit 6c4009
	      /* We must not enqueue the mutex before we have acquired it.
Packit 6c4009
		 Also see comments at ENQUEUE_MUTEX.  */
Packit 6c4009
	      __asm ("" ::: "memory");
Packit 6c4009
	      ENQUEUE_MUTEX (mutex);
Packit 6c4009
	      /* We need to clear op_pending after we enqueue the mutex.  */
Packit 6c4009
	      __asm ("" ::: "memory");
Packit 6c4009
	      THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
Packit 6c4009
Packit 6c4009
	      /* Note that we deliberately exit here.  If we fall
Packit 6c4009
		 through to the end of the function __nusers would be
Packit 6c4009
		 incremented which is not correct because the old
Packit 6c4009
		 owner has to be discounted.  */
Packit 6c4009
	      return EOWNERDEAD;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Check whether we already hold the mutex.  */
Packit 6c4009
	  if (__glibc_unlikely ((oldval & FUTEX_TID_MASK) == id))
Packit 6c4009
	    {
Packit 6c4009
	      int kind = PTHREAD_MUTEX_TYPE (mutex);
Packit 6c4009
	      if (kind == PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP)
Packit 6c4009
		{
Packit 6c4009
		  /* We do not need to ensure ordering wrt another memory
Packit 6c4009
		     access.  Also see comments at ENQUEUE_MUTEX. */
Packit 6c4009
		  THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
Packit 6c4009
				 NULL);
Packit 6c4009
		  return EDEADLK;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      if (kind == PTHREAD_MUTEX_ROBUST_RECURSIVE_NP)
Packit 6c4009
		{
Packit 6c4009
		  /* We do not need to ensure ordering wrt another memory
Packit 6c4009
		     access.  */
Packit 6c4009
		  THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
Packit 6c4009
				 NULL);
Packit 6c4009
Packit 6c4009
		  /* Just bump the counter.  */
Packit 6c4009
		  if (__glibc_unlikely (mutex->__data.__count + 1 == 0))
Packit 6c4009
		    /* Overflow of the counter.  */
Packit 6c4009
		    return EAGAIN;
Packit 6c4009
Packit 6c4009
		  ++mutex->__data.__count;
Packit 6c4009
Packit 6c4009
		  LIBC_PROBE (mutex_timedlock_acquired, 1, mutex);
Packit 6c4009
Packit 6c4009
		  return 0;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* We are about to block; check whether the timeout is invalid.  */
Packit 6c4009
	  if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
Packit 6c4009
	    return EINVAL;
Packit 6c4009
	  /* Work around the fact that the kernel rejects negative timeout
Packit 6c4009
	     values despite them being valid.  */
Packit 6c4009
	  if (__glibc_unlikely (abstime->tv_sec < 0))
Packit 6c4009
	    return ETIMEDOUT;
Packit 6c4009
#if (!defined __ASSUME_FUTEX_CLOCK_REALTIME \
Packit 6c4009
     || !defined lll_futex_timed_wait_bitset)
Packit 6c4009
	  struct timeval tv;
Packit 6c4009
	  struct timespec rt;
Packit 6c4009
Packit 6c4009
	  /* Get the current time.  */
Packit 6c4009
	  (void) __gettimeofday (&tv, NULL);
Packit 6c4009
Packit 6c4009
	  /* Compute relative timeout.  */
Packit 6c4009
	  rt.tv_sec = abstime->tv_sec - tv.tv_sec;
Packit 6c4009
	  rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
Packit 6c4009
	  if (rt.tv_nsec < 0)
Packit 6c4009
	    {
Packit 6c4009
	      rt.tv_nsec += 1000000000;
Packit 6c4009
	      --rt.tv_sec;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Already timed out?  */
Packit 6c4009
	  if (rt.tv_sec < 0)
Packit 6c4009
	    return ETIMEDOUT;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
	  /* We cannot acquire the mutex nor has its owner died.  Thus, try
Packit 6c4009
	     to block using futexes.  Set FUTEX_WAITERS if necessary so that
Packit 6c4009
	     other threads are aware that there are potentially threads
Packit 6c4009
	     blocked on the futex.  Restart if oldval changed in the
Packit 6c4009
	     meantime.  */
Packit 6c4009
	  if ((oldval & FUTEX_WAITERS) == 0)
Packit 6c4009
	    {
Packit 6c4009
	      if (atomic_compare_and_exchange_bool_acq (&mutex->__data.__lock,
Packit 6c4009
							oldval | FUTEX_WAITERS,
Packit 6c4009
							oldval)
Packit 6c4009
		  != 0)
Packit 6c4009
		{
Packit 6c4009
		  oldval = mutex->__data.__lock;
Packit 6c4009
		  continue;
Packit 6c4009
		}
Packit 6c4009
	      oldval |= FUTEX_WAITERS;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* It is now possible that we share the FUTEX_WAITERS flag with
Packit 6c4009
	     another thread; therefore, update assume_other_futex_waiters so
Packit 6c4009
	     that we do not forget about this when handling other cases
Packit 6c4009
	     above and thus do not cause lost wake-ups.  */
Packit 6c4009
	  assume_other_futex_waiters |= FUTEX_WAITERS;
Packit 6c4009
Packit 6c4009
	  /* Block using the futex.  */
Packit 6c4009
#if (!defined __ASSUME_FUTEX_CLOCK_REALTIME \
Packit 6c4009
     || !defined lll_futex_timed_wait_bitset)
Packit 6c4009
	  lll_futex_timed_wait (&mutex->__data.__lock, oldval,
Packit 6c4009
				&rt, PTHREAD_ROBUST_MUTEX_PSHARED (mutex));
Packit 6c4009
#else
Packit 6c4009
	  int err = lll_futex_timed_wait_bitset (&mutex->__data.__lock,
Packit 6c4009
	      oldval, abstime, FUTEX_CLOCK_REALTIME,
Packit 6c4009
	      PTHREAD_ROBUST_MUTEX_PSHARED (mutex));
Packit 6c4009
	  /* The futex call timed out.  */
Packit 6c4009
	  if (err == -ETIMEDOUT)
Packit 6c4009
	    return -err;
Packit 6c4009
#endif
Packit 6c4009
	  /* Reload current lock value.  */
Packit 6c4009
	  oldval = mutex->__data.__lock;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* We have acquired the mutex; check if it is still consistent.  */
Packit 6c4009
      if (__builtin_expect (mutex->__data.__owner
Packit 6c4009
			    == PTHREAD_MUTEX_NOTRECOVERABLE, 0))
Packit 6c4009
	{
Packit 6c4009
	  /* This mutex is now not recoverable.  */
Packit 6c4009
	  mutex->__data.__count = 0;
Packit 6c4009
	  int private = PTHREAD_ROBUST_MUTEX_PSHARED (mutex);
Packit 6c4009
	  lll_unlock (mutex->__data.__lock, private);
Packit 6c4009
	  /* FIXME This violates the mutex destruction requirements.  See
Packit 6c4009
	     __pthread_mutex_unlock_full.  */
Packit 6c4009
	  THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
Packit 6c4009
	  return ENOTRECOVERABLE;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      mutex->__data.__count = 1;
Packit 6c4009
      /* We must not enqueue the mutex before we have acquired it.
Packit 6c4009
	 Also see comments at ENQUEUE_MUTEX.  */
Packit 6c4009
      __asm ("" ::: "memory");
Packit 6c4009
      ENQUEUE_MUTEX (mutex);
Packit 6c4009
      /* We need to clear op_pending after we enqueue the mutex.  */
Packit 6c4009
      __asm ("" ::: "memory");
Packit 6c4009
      THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    /* The PI support requires the Linux futex system call.  If that's not
Packit 6c4009
       available, pthread_mutex_init should never have allowed the type to
Packit 6c4009
       be set.  So it will get the default case for an invalid type.  */
Packit 6c4009
#ifdef __NR_futex
Packit 6c4009
    case PTHREAD_MUTEX_PI_RECURSIVE_NP:
Packit 6c4009
    case PTHREAD_MUTEX_PI_ERRORCHECK_NP:
Packit 6c4009
    case PTHREAD_MUTEX_PI_NORMAL_NP:
Packit 6c4009
    case PTHREAD_MUTEX_PI_ADAPTIVE_NP:
Packit 6c4009
    case PTHREAD_MUTEX_PI_ROBUST_RECURSIVE_NP:
Packit 6c4009
    case PTHREAD_MUTEX_PI_ROBUST_ERRORCHECK_NP:
Packit 6c4009
    case PTHREAD_MUTEX_PI_ROBUST_NORMAL_NP:
Packit 6c4009
    case PTHREAD_MUTEX_PI_ROBUST_ADAPTIVE_NP:
Packit 6c4009
      {
Packit 5e5144
	int kind, robust;
Packit 5e5144
	{
Packit 5e5144
	  /* See concurrency notes regarding __kind in struct __pthread_mutex_s
Packit 5e5144
	     in sysdeps/nptl/bits/thread-shared-types.h.  */
Packit 5e5144
	  int mutex_kind = atomic_load_relaxed (&(mutex->__data.__kind));
Packit 5e5144
	  kind = mutex_kind & PTHREAD_MUTEX_KIND_MASK_NP;
Packit 5e5144
	  robust = mutex_kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP;
Packit 5e5144
	}
Packit 6c4009
Packit 6c4009
	if (robust)
Packit 6c4009
	  {
Packit 6c4009
	    /* Note: robust PI futexes are signaled by setting bit 0.  */
Packit 6c4009
	    THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
Packit 6c4009
			   (void *) (((uintptr_t) &mutex->__data.__list.__next)
Packit 6c4009
				     | 1));
Packit 6c4009
	    /* We need to set op_pending before starting the operation.  Also
Packit 6c4009
	       see comments at ENQUEUE_MUTEX.  */
Packit 6c4009
	    __asm ("" ::: "memory");
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	oldval = mutex->__data.__lock;
Packit 6c4009
Packit 6c4009
	/* Check whether we already hold the mutex.  */
Packit 6c4009
	if (__glibc_unlikely ((oldval & FUTEX_TID_MASK) == id))
Packit 6c4009
	  {
Packit 6c4009
	    if (kind == PTHREAD_MUTEX_ERRORCHECK_NP)
Packit 6c4009
	      {
Packit 6c4009
		/* We do not need to ensure ordering wrt another memory
Packit 6c4009
		   access.  */
Packit 6c4009
		THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
Packit 6c4009
		return EDEADLK;
Packit 6c4009
	      }
Packit 6c4009
Packit 6c4009
	    if (kind == PTHREAD_MUTEX_RECURSIVE_NP)
Packit 6c4009
	      {
Packit 6c4009
		/* We do not need to ensure ordering wrt another memory
Packit 6c4009
		   access.  */
Packit 6c4009
		THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
Packit 6c4009
Packit 6c4009
		/* Just bump the counter.  */
Packit 6c4009
		if (__glibc_unlikely (mutex->__data.__count + 1 == 0))
Packit 6c4009
		  /* Overflow of the counter.  */
Packit 6c4009
		  return EAGAIN;
Packit 6c4009
Packit 6c4009
		++mutex->__data.__count;
Packit 6c4009
Packit 6c4009
		LIBC_PROBE (mutex_timedlock_acquired, 1, mutex);
Packit 6c4009
Packit 6c4009
		return 0;
Packit 6c4009
	      }
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	oldval = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
Packit 6c4009
						      id, 0);
Packit 6c4009
Packit 6c4009
	if (oldval != 0)
Packit 6c4009
	  {
Packit 6c4009
	    /* The mutex is locked.  The kernel will now take care of
Packit 6c4009
	       everything.  The timeout value must be a relative value.
Packit 6c4009
	       Convert it.  */
Packit 6c4009
	    int private = (robust
Packit 6c4009
			   ? PTHREAD_ROBUST_MUTEX_PSHARED (mutex)
Packit 6c4009
			   : PTHREAD_MUTEX_PSHARED (mutex));
Packit 6c4009
	    INTERNAL_SYSCALL_DECL (__err);
Packit 6c4009
Packit 6c4009
	    int e = INTERNAL_SYSCALL (futex, __err, 4, &mutex->__data.__lock,
Packit 6c4009
				      __lll_private_flag (FUTEX_LOCK_PI,
Packit 6c4009
							  private), 1,
Packit 6c4009
				      abstime);
Packit 6c4009
	    if (INTERNAL_SYSCALL_ERROR_P (e, __err))
Packit 6c4009
	      {
Packit 6c4009
		if (INTERNAL_SYSCALL_ERRNO (e, __err) == ETIMEDOUT)
Packit 6c4009
		  return ETIMEDOUT;
Packit 6c4009
Packit 6c4009
		if (INTERNAL_SYSCALL_ERRNO (e, __err) == ESRCH
Packit 6c4009
		    || INTERNAL_SYSCALL_ERRNO (e, __err) == EDEADLK)
Packit 6c4009
		  {
Packit 6c4009
		    assert (INTERNAL_SYSCALL_ERRNO (e, __err) != EDEADLK
Packit 6c4009
			    || (kind != PTHREAD_MUTEX_ERRORCHECK_NP
Packit 6c4009
				&& kind != PTHREAD_MUTEX_RECURSIVE_NP));
Packit 6c4009
		    /* ESRCH can happen only for non-robust PI mutexes where
Packit 6c4009
		       the owner of the lock died.  */
Packit 6c4009
		    assert (INTERNAL_SYSCALL_ERRNO (e, __err) != ESRCH
Packit 6c4009
			    || !robust);
Packit 6c4009
Packit 6c4009
		    /* Delay the thread until the timeout is reached.
Packit 6c4009
		       Then return ETIMEDOUT.  */
Packit 6c4009
		    struct timespec reltime;
Packit 6c4009
		    struct timespec now;
Packit 6c4009
Packit 6c4009
		    INTERNAL_SYSCALL (clock_gettime, __err, 2, CLOCK_REALTIME,
Packit 6c4009
				      &now;;
Packit 6c4009
		    reltime.tv_sec = abstime->tv_sec - now.tv_sec;
Packit 6c4009
		    reltime.tv_nsec = abstime->tv_nsec - now.tv_nsec;
Packit 6c4009
		    if (reltime.tv_nsec < 0)
Packit 6c4009
		      {
Packit 6c4009
			reltime.tv_nsec += 1000000000;
Packit 6c4009
			--reltime.tv_sec;
Packit 6c4009
		      }
Packit 6c4009
		    if (reltime.tv_sec >= 0)
Packit 6c4009
		      while (__nanosleep_nocancel (&reltime, &reltime) != 0)
Packit 6c4009
			continue;
Packit 6c4009
Packit 6c4009
		    return ETIMEDOUT;
Packit 6c4009
		  }
Packit 6c4009
Packit 6c4009
		return INTERNAL_SYSCALL_ERRNO (e, __err);
Packit 6c4009
	      }
Packit 6c4009
Packit 6c4009
	    oldval = mutex->__data.__lock;
Packit 6c4009
Packit 6c4009
	    assert (robust || (oldval & FUTEX_OWNER_DIED) == 0);
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	if (__glibc_unlikely (oldval & FUTEX_OWNER_DIED))
Packit 6c4009
	  {
Packit 6c4009
	    atomic_and (&mutex->__data.__lock, ~FUTEX_OWNER_DIED);
Packit 6c4009
Packit 6c4009
	    /* We got the mutex.  */
Packit 6c4009
	    mutex->__data.__count = 1;
Packit 6c4009
	    /* But it is inconsistent unless marked otherwise.  */
Packit 6c4009
	    mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
Packit 6c4009
Packit 6c4009
	    /* We must not enqueue the mutex before we have acquired it.
Packit 6c4009
	       Also see comments at ENQUEUE_MUTEX.  */
Packit 6c4009
	    __asm ("" ::: "memory");
Packit 6c4009
	    ENQUEUE_MUTEX_PI (mutex);
Packit 6c4009
	    /* We need to clear op_pending after we enqueue the mutex.  */
Packit 6c4009
	    __asm ("" ::: "memory");
Packit 6c4009
	    THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
Packit 6c4009
Packit 6c4009
	    /* Note that we deliberately exit here.  If we fall
Packit 6c4009
	       through to the end of the function __nusers would be
Packit 6c4009
	       incremented which is not correct because the old owner
Packit 6c4009
	       has to be discounted.  */
Packit 6c4009
	    return EOWNERDEAD;
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	if (robust
Packit 6c4009
	    && __builtin_expect (mutex->__data.__owner
Packit 6c4009
				 == PTHREAD_MUTEX_NOTRECOVERABLE, 0))
Packit 6c4009
	  {
Packit 6c4009
	    /* This mutex is now not recoverable.  */
Packit 6c4009
	    mutex->__data.__count = 0;
Packit 6c4009
Packit 6c4009
	    INTERNAL_SYSCALL_DECL (__err);
Packit 6c4009
	    INTERNAL_SYSCALL (futex, __err, 4, &mutex->__data.__lock,
Packit 6c4009
			      __lll_private_flag (FUTEX_UNLOCK_PI,
Packit 6c4009
						  PTHREAD_ROBUST_MUTEX_PSHARED (mutex)),
Packit 6c4009
			      0, 0);
Packit 6c4009
Packit 6c4009
	    /* To the kernel, this will be visible after the kernel has
Packit 6c4009
	       acquired the mutex in the syscall.  */
Packit 6c4009
	    THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
Packit 6c4009
	    return ENOTRECOVERABLE;
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	mutex->__data.__count = 1;
Packit 6c4009
	if (robust)
Packit 6c4009
	  {
Packit 6c4009
	    /* We must not enqueue the mutex before we have acquired it.
Packit 6c4009
	       Also see comments at ENQUEUE_MUTEX.  */
Packit 6c4009
	    __asm ("" ::: "memory");
Packit 6c4009
	    ENQUEUE_MUTEX_PI (mutex);
Packit 6c4009
	    /* We need to clear op_pending after we enqueue the mutex.  */
Packit 6c4009
	    __asm ("" ::: "memory");
Packit 6c4009
	    THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
Packit 6c4009
	  }
Packit 6c4009
	}
Packit 6c4009
      break;
Packit 6c4009
#endif  /* __NR_futex.  */
Packit 6c4009
Packit 6c4009
    case PTHREAD_MUTEX_PP_RECURSIVE_NP:
Packit 6c4009
    case PTHREAD_MUTEX_PP_ERRORCHECK_NP:
Packit 6c4009
    case PTHREAD_MUTEX_PP_NORMAL_NP:
Packit 6c4009
    case PTHREAD_MUTEX_PP_ADAPTIVE_NP:
Packit 6c4009
      {
Packit 5e5144
	/* See concurrency notes regarding __kind in struct __pthread_mutex_s
Packit 5e5144
	   in sysdeps/nptl/bits/thread-shared-types.h.  */
Packit 5e5144
	int kind = atomic_load_relaxed (&(mutex->__data.__kind))
Packit 5e5144
	  & PTHREAD_MUTEX_KIND_MASK_NP;
Packit 6c4009
Packit 6c4009
	oldval = mutex->__data.__lock;
Packit 6c4009
Packit 6c4009
	/* Check whether we already hold the mutex.  */
Packit 6c4009
	if (mutex->__data.__owner == id)
Packit 6c4009
	  {
Packit 6c4009
	    if (kind == PTHREAD_MUTEX_ERRORCHECK_NP)
Packit 6c4009
	      return EDEADLK;
Packit 6c4009
Packit 6c4009
	    if (kind == PTHREAD_MUTEX_RECURSIVE_NP)
Packit 6c4009
	      {
Packit 6c4009
		/* Just bump the counter.  */
Packit 6c4009
		if (__glibc_unlikely (mutex->__data.__count + 1 == 0))
Packit 6c4009
		  /* Overflow of the counter.  */
Packit 6c4009
		  return EAGAIN;
Packit 6c4009
Packit 6c4009
		++mutex->__data.__count;
Packit 6c4009
Packit 6c4009
		LIBC_PROBE (mutex_timedlock_acquired, 1, mutex);
Packit 6c4009
Packit 6c4009
		return 0;
Packit 6c4009
	      }
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	int oldprio = -1, ceilval;
Packit 6c4009
	do
Packit 6c4009
	  {
Packit 6c4009
	    int ceiling = (oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK)
Packit 6c4009
			  >> PTHREAD_MUTEX_PRIO_CEILING_SHIFT;
Packit 6c4009
Packit 6c4009
	    if (__pthread_current_priority () > ceiling)
Packit 6c4009
	      {
Packit 6c4009
		result = EINVAL;
Packit 6c4009
	      failpp:
Packit 6c4009
		if (oldprio != -1)
Packit 6c4009
		  __pthread_tpp_change_priority (oldprio, -1);
Packit 6c4009
		return result;
Packit 6c4009
	      }
Packit 6c4009
Packit 6c4009
	    result = __pthread_tpp_change_priority (oldprio, ceiling);
Packit 6c4009
	    if (result)
Packit 6c4009
	      return result;
Packit 6c4009
Packit 6c4009
	    ceilval = ceiling << PTHREAD_MUTEX_PRIO_CEILING_SHIFT;
Packit 6c4009
	    oldprio = ceiling;
Packit 6c4009
Packit 6c4009
	    oldval
Packit 6c4009
	      = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
Packit 6c4009
						     ceilval | 1, ceilval);
Packit 6c4009
Packit 6c4009
	    if (oldval == ceilval)
Packit 6c4009
	      break;
Packit 6c4009
Packit 6c4009
	    do
Packit 6c4009
	      {
Packit 6c4009
		oldval
Packit 6c4009
		  = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
Packit 6c4009
							 ceilval | 2,
Packit 6c4009
							 ceilval | 1);
Packit 6c4009
Packit 6c4009
		if ((oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) != ceilval)
Packit 6c4009
		  break;
Packit 6c4009
Packit 6c4009
		if (oldval != ceilval)
Packit 6c4009
		  {
Packit 6c4009
		    /* Reject invalid timeouts.  */
Packit 6c4009
		    if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
Packit 6c4009
		      {
Packit 6c4009
			result = EINVAL;
Packit 6c4009
			goto failpp;
Packit 6c4009
		      }
Packit 6c4009
Packit 6c4009
		    struct timeval tv;
Packit 6c4009
		    struct timespec rt;
Packit 6c4009
Packit 6c4009
		    /* Get the current time.  */
Packit 6c4009
		    (void) __gettimeofday (&tv, NULL);
Packit 6c4009
Packit 6c4009
		    /* Compute relative timeout.  */
Packit 6c4009
		    rt.tv_sec = abstime->tv_sec - tv.tv_sec;
Packit 6c4009
		    rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
Packit 6c4009
		    if (rt.tv_nsec < 0)
Packit 6c4009
		      {
Packit 6c4009
			rt.tv_nsec += 1000000000;
Packit 6c4009
			--rt.tv_sec;
Packit 6c4009
		      }
Packit 6c4009
Packit 6c4009
		    /* Already timed out?  */
Packit 6c4009
		    if (rt.tv_sec < 0)
Packit 6c4009
		      {
Packit 6c4009
			result = ETIMEDOUT;
Packit 6c4009
			goto failpp;
Packit 6c4009
		      }
Packit 6c4009
Packit 6c4009
		    lll_futex_timed_wait (&mutex->__data.__lock,
Packit 6c4009
					  ceilval | 2, &rt,
Packit 6c4009
					  PTHREAD_MUTEX_PSHARED (mutex));
Packit 6c4009
		  }
Packit 6c4009
	      }
Packit 6c4009
	    while (atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
Packit 6c4009
							ceilval | 2, ceilval)
Packit 6c4009
		   != ceilval);
Packit 6c4009
	  }
Packit 6c4009
	while ((oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK) != ceilval);
Packit 6c4009
Packit 6c4009
	assert (mutex->__data.__owner == 0);
Packit 6c4009
	mutex->__data.__count = 1;
Packit 6c4009
      }
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    default:
Packit 6c4009
      /* Correct code cannot set any other type.  */
Packit 6c4009
      return EINVAL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (result == 0)
Packit 6c4009
    {
Packit 6c4009
      /* Record the ownership.  */
Packit 6c4009
      mutex->__data.__owner = id;
Packit 6c4009
      ++mutex->__data.__nusers;
Packit 6c4009
Packit 6c4009
      LIBC_PROBE (mutex_timedlock_acquired, 1, mutex);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
 out:
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
weak_alias (__pthread_mutex_timedlock, pthread_mutex_timedlock)