hjl / source-git / glibc

Forked from source-git/glibc 3 years ago
Clone

Blame nptl/pthread_mutex_unlock.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 <stdlib.h>
Packit 6c4009
#include "pthreadP.h"
Packit 6c4009
#include <lowlevellock.h>
Packit 6c4009
#include <stap-probe.h>
Packit 6c4009
Packit 6c4009
#ifndef lll_unlock_elision
Packit 6c4009
#define lll_unlock_elision(a,b,c) ({ lll_unlock (a,c); 0; })
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
__pthread_mutex_unlock_full (pthread_mutex_t *mutex, int decr)
Packit 6c4009
     __attribute_noinline__;
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
attribute_hidden
Packit 6c4009
__pthread_mutex_unlock_usercnt (pthread_mutex_t *mutex, int decr)
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
  int type = PTHREAD_MUTEX_TYPE_ELISION (mutex);
Packit 6c4009
  if (__builtin_expect (type &
Packit 6c4009
		~(PTHREAD_MUTEX_KIND_MASK_NP|PTHREAD_MUTEX_ELISION_FLAGS_NP), 0))
Packit 6c4009
    return __pthread_mutex_unlock_full (mutex, decr);
Packit 6c4009
Packit 6c4009
  if (__builtin_expect (type, PTHREAD_MUTEX_TIMED_NP)
Packit 6c4009
      == PTHREAD_MUTEX_TIMED_NP)
Packit 6c4009
    {
Packit 6c4009
      /* Always reset the owner field.  */
Packit 6c4009
    normal:
Packit 6c4009
      mutex->__data.__owner = 0;
Packit 6c4009
      if (decr)
Packit 6c4009
	/* One less user.  */
Packit 6c4009
	--mutex->__data.__nusers;
Packit 6c4009
Packit 6c4009
      /* Unlock.  */
Packit 6c4009
      lll_unlock (mutex->__data.__lock, PTHREAD_MUTEX_PSHARED (mutex));
Packit 6c4009
Packit 6c4009
      LIBC_PROBE (mutex_release, 1, mutex);
Packit 6c4009
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
  else if (__glibc_likely (type == PTHREAD_MUTEX_TIMED_ELISION_NP))
Packit 6c4009
    {
Packit 6c4009
      /* Don't reset the owner/users fields for elision.  */
Packit 6c4009
      return lll_unlock_elision (mutex->__data.__lock, mutex->__data.__elision,
Packit 6c4009
				      PTHREAD_MUTEX_PSHARED (mutex));
Packit 6c4009
    }
Packit 6c4009
  else if (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex)
Packit 6c4009
			      == PTHREAD_MUTEX_RECURSIVE_NP, 1))
Packit 6c4009
    {
Packit 6c4009
      /* Recursive mutex.  */
Packit 6c4009
      if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid))
Packit 6c4009
	return EPERM;
Packit 6c4009
Packit 6c4009
      if (--mutex->__data.__count != 0)
Packit 6c4009
	/* We still hold the mutex.  */
Packit 6c4009
	return 0;
Packit 6c4009
      goto normal;
Packit 6c4009
    }
Packit 6c4009
  else if (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex)
Packit 6c4009
			      == PTHREAD_MUTEX_ADAPTIVE_NP, 1))
Packit 6c4009
    goto normal;
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* Error checking mutex.  */
Packit 6c4009
      assert (type == PTHREAD_MUTEX_ERRORCHECK_NP);
Packit 6c4009
      if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid)
Packit 6c4009
	  || ! lll_islocked (mutex->__data.__lock))
Packit 6c4009
	return EPERM;
Packit 6c4009
      goto normal;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
__pthread_mutex_unlock_full (pthread_mutex_t *mutex, int decr)
Packit 6c4009
{
Packit 6c4009
  int newowner = 0;
Packit 6c4009
  int private;
Packit 6c4009
Packit 6c4009
  switch (PTHREAD_MUTEX_TYPE (mutex))
Packit 6c4009
    {
Packit 6c4009
    case PTHREAD_MUTEX_ROBUST_RECURSIVE_NP:
Packit 6c4009
      /* Recursive mutex.  */
Packit 6c4009
      if ((mutex->__data.__lock & FUTEX_TID_MASK)
Packit 6c4009
	  == THREAD_GETMEM (THREAD_SELF, tid)
Packit 6c4009
	  && __builtin_expect (mutex->__data.__owner
Packit 6c4009
			       == PTHREAD_MUTEX_INCONSISTENT, 0))
Packit 6c4009
	{
Packit 6c4009
	  if (--mutex->__data.__count != 0)
Packit 6c4009
	    /* We still hold the mutex.  */
Packit 6c4009
	    return ENOTRECOVERABLE;
Packit 6c4009
Packit 6c4009
	  goto notrecoverable;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid))
Packit 6c4009
	return EPERM;
Packit 6c4009
Packit 6c4009
      if (--mutex->__data.__count != 0)
Packit 6c4009
	/* We still hold the mutex.  */
Packit 6c4009
	return 0;
Packit 6c4009
Packit 6c4009
      goto robust;
Packit 6c4009
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
      if ((mutex->__data.__lock & FUTEX_TID_MASK)
Packit 6c4009
	  != THREAD_GETMEM (THREAD_SELF, tid)
Packit 6c4009
	  || ! lll_islocked (mutex->__data.__lock))
Packit 6c4009
	return EPERM;
Packit 6c4009
Packit 6c4009
      /* If the previous owner died and the caller did not succeed in
Packit 6c4009
	 making the state consistent, mark the mutex as unrecoverable
Packit 6c4009
	 and make all waiters.  */
Packit 6c4009
      if (__builtin_expect (mutex->__data.__owner
Packit 6c4009
			    == PTHREAD_MUTEX_INCONSISTENT, 0))
Packit 6c4009
      notrecoverable:
Packit 6c4009
	newowner = PTHREAD_MUTEX_NOTRECOVERABLE;
Packit 6c4009
Packit 6c4009
    robust:
Packit 6c4009
      /* Remove mutex from the list.  */
Packit 6c4009
      THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
Packit 6c4009
		     &mutex->__data.__list.__next);
Packit 6c4009
      /* We must set op_pending before we dequeue the mutex.  Also see
Packit 6c4009
	 comments at ENQUEUE_MUTEX.  */
Packit 6c4009
      __asm ("" ::: "memory");
Packit 6c4009
      DEQUEUE_MUTEX (mutex);
Packit 6c4009
Packit 6c4009
      mutex->__data.__owner = newowner;
Packit 6c4009
      if (decr)
Packit 6c4009
	/* One less user.  */
Packit 6c4009
	--mutex->__data.__nusers;
Packit 6c4009
Packit 6c4009
      /* Unlock by setting the lock to 0 (not acquired); if the lock had
Packit 6c4009
	 FUTEX_WAITERS set previously, then wake any waiters.
Packit 6c4009
         The unlock operation must be the last access to the mutex to not
Packit 6c4009
         violate the mutex destruction requirements (see __lll_unlock).  */
Packit 6c4009
      private = PTHREAD_ROBUST_MUTEX_PSHARED (mutex);
Packit 6c4009
      if (__glibc_unlikely ((atomic_exchange_rel (&mutex->__data.__lock, 0)
Packit 6c4009
			     & FUTEX_WAITERS) != 0))
Packit 6c4009
	lll_futex_wake (&mutex->__data.__lock, 1, private);
Packit 6c4009
Packit 6c4009
      /* We must clear op_pending after we release the mutex.
Packit 6c4009
	 FIXME However, this violates the mutex destruction requirements
Packit 6c4009
	 because another thread could acquire the mutex, destroy it, and
Packit 6c4009
	 reuse the memory for something else; then, if this thread crashes,
Packit 6c4009
	 and the memory happens to have a value equal to the TID, the kernel
Packit 6c4009
	 will believe it is still related to the mutex (which has been
Packit 6c4009
	 destroyed already) and will modify some other random object.  */
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
      /* Recursive mutex.  */
Packit 6c4009
      if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid))
Packit 6c4009
	return EPERM;
Packit 6c4009
Packit 6c4009
      if (--mutex->__data.__count != 0)
Packit 6c4009
	/* We still hold the mutex.  */
Packit 6c4009
	return 0;
Packit 6c4009
      goto continue_pi_non_robust;
Packit 6c4009
Packit 6c4009
    case PTHREAD_MUTEX_PI_ROBUST_RECURSIVE_NP:
Packit 6c4009
      /* Recursive mutex.  */
Packit 6c4009
      if ((mutex->__data.__lock & FUTEX_TID_MASK)
Packit 6c4009
	  == THREAD_GETMEM (THREAD_SELF, tid)
Packit 6c4009
	  && __builtin_expect (mutex->__data.__owner
Packit 6c4009
			       == PTHREAD_MUTEX_INCONSISTENT, 0))
Packit 6c4009
	{
Packit 6c4009
	  if (--mutex->__data.__count != 0)
Packit 6c4009
	    /* We still hold the mutex.  */
Packit 6c4009
	    return ENOTRECOVERABLE;
Packit 6c4009
Packit 6c4009
	  goto pi_notrecoverable;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid))
Packit 6c4009
	return EPERM;
Packit 6c4009
Packit 6c4009
      if (--mutex->__data.__count != 0)
Packit 6c4009
	/* We still hold the mutex.  */
Packit 6c4009
	return 0;
Packit 6c4009
Packit 6c4009
      goto continue_pi_robust;
Packit 6c4009
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_ERRORCHECK_NP:
Packit 6c4009
    case PTHREAD_MUTEX_PI_ROBUST_NORMAL_NP:
Packit 6c4009
    case PTHREAD_MUTEX_PI_ROBUST_ADAPTIVE_NP:
Packit 6c4009
      if ((mutex->__data.__lock & FUTEX_TID_MASK)
Packit 6c4009
	  != THREAD_GETMEM (THREAD_SELF, tid)
Packit 6c4009
	  || ! lll_islocked (mutex->__data.__lock))
Packit 6c4009
	return EPERM;
Packit 6c4009
Packit 6c4009
      /* If the previous owner died and the caller did not succeed in
Packit 6c4009
	 making the state consistent, mark the mutex as unrecoverable
Packit 6c4009
	 and make all waiters.  */
Packit 5e5144
      /* See concurrency notes regarding __kind in struct __pthread_mutex_s
Packit 5e5144
	 in sysdeps/nptl/bits/thread-shared-types.h.  */
Packit 5e5144
      if ((atomic_load_relaxed (&(mutex->__data.__kind))
Packit 5e5144
	   & PTHREAD_MUTEX_ROBUST_NORMAL_NP) != 0
Packit 6c4009
	  && __builtin_expect (mutex->__data.__owner
Packit 6c4009
			       == PTHREAD_MUTEX_INCONSISTENT, 0))
Packit 6c4009
      pi_notrecoverable:
Packit 6c4009
       newowner = PTHREAD_MUTEX_NOTRECOVERABLE;
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
      if ((atomic_load_relaxed (&(mutex->__data.__kind))
Packit 5e5144
	   & PTHREAD_MUTEX_ROBUST_NORMAL_NP) != 0)
Packit 6c4009
	{
Packit 6c4009
	continue_pi_robust:
Packit 6c4009
	  /* Remove mutex from the list.
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 must set op_pending before we dequeue the mutex.  Also see
Packit 6c4009
	     comments at ENQUEUE_MUTEX.  */
Packit 6c4009
	  __asm ("" ::: "memory");
Packit 6c4009
	  DEQUEUE_MUTEX (mutex);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
    continue_pi_non_robust:
Packit 6c4009
      mutex->__data.__owner = newowner;
Packit 6c4009
      if (decr)
Packit 6c4009
	/* One less user.  */
Packit 6c4009
	--mutex->__data.__nusers;
Packit 6c4009
Packit 6c4009
      /* Unlock.  Load all necessary mutex data before releasing the mutex
Packit 6c4009
	 to not violate the mutex destruction requirements (see
Packit 6c4009
	 lll_unlock).  */
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 robust = atomic_load_relaxed (&(mutex->__data.__kind))
Packit 5e5144
	& PTHREAD_MUTEX_ROBUST_NORMAL_NP;
Packit 6c4009
      private = (robust
Packit 6c4009
		 ? PTHREAD_ROBUST_MUTEX_PSHARED (mutex)
Packit 6c4009
		 : PTHREAD_MUTEX_PSHARED (mutex));
Packit 6c4009
      /* Unlock the mutex using a CAS unless there are futex waiters or our
Packit 6c4009
	 TID is not the value of __lock anymore, in which case we let the
Packit 6c4009
	 kernel take care of the situation.  Use release MO in the CAS to
Packit 6c4009
	 synchronize with acquire MO in lock acquisitions.  */
Packit 6c4009
      int l = atomic_load_relaxed (&mutex->__data.__lock);
Packit 6c4009
      do
Packit 6c4009
	{
Packit 6c4009
	  if (((l & FUTEX_WAITERS) != 0)
Packit 6c4009
	      || (l != THREAD_GETMEM (THREAD_SELF, tid)))
Packit 6c4009
	    {
Packit 6c4009
	      INTERNAL_SYSCALL_DECL (__err);
Packit 6c4009
	      INTERNAL_SYSCALL (futex, __err, 2, &mutex->__data.__lock,
Packit 6c4009
				__lll_private_flag (FUTEX_UNLOCK_PI, private));
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
      while (!atomic_compare_exchange_weak_release (&mutex->__data.__lock,
Packit 6c4009
						    &l, 0));
Packit 6c4009
Packit 6c4009
      /* This happens after the kernel releases the mutex but violates the
Packit 6c4009
	 mutex destruction requirements; see comments in the code handling
Packit 6c4009
	 PTHREAD_MUTEX_ROBUST_NORMAL_NP.  */
Packit 6c4009
      THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
Packit 6c4009
      break;
Packit 6c4009
#endif  /* __NR_futex.  */
Packit 6c4009
Packit 6c4009
    case PTHREAD_MUTEX_PP_RECURSIVE_NP:
Packit 6c4009
      /* Recursive mutex.  */
Packit 6c4009
      if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid))
Packit 6c4009
	return EPERM;
Packit 6c4009
Packit 6c4009
      if (--mutex->__data.__count != 0)
Packit 6c4009
	/* We still hold the mutex.  */
Packit 6c4009
	return 0;
Packit 6c4009
      goto pp;
Packit 6c4009
Packit 6c4009
    case PTHREAD_MUTEX_PP_ERRORCHECK_NP:
Packit 6c4009
      /* Error checking mutex.  */
Packit 6c4009
      if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid)
Packit 6c4009
	  || (mutex->__data.__lock & ~ PTHREAD_MUTEX_PRIO_CEILING_MASK) == 0)
Packit 6c4009
	return EPERM;
Packit 6c4009
      /* FALLTHROUGH */
Packit 6c4009
Packit 6c4009
    case PTHREAD_MUTEX_PP_NORMAL_NP:
Packit 6c4009
    case PTHREAD_MUTEX_PP_ADAPTIVE_NP:
Packit 6c4009
      /* Always reset the owner field.  */
Packit 6c4009
    pp:
Packit 6c4009
      mutex->__data.__owner = 0;
Packit 6c4009
Packit 6c4009
      if (decr)
Packit 6c4009
	/* One less user.  */
Packit 6c4009
	--mutex->__data.__nusers;
Packit 6c4009
Packit 6c4009
      /* Unlock.  Use release MO in the CAS to synchronize with acquire MO in
Packit 6c4009
	 lock acquisitions.  */
Packit 6c4009
      int newval;
Packit 6c4009
      int oldval = atomic_load_relaxed (&mutex->__data.__lock);
Packit 6c4009
      do
Packit 6c4009
	{
Packit 6c4009
	  newval = oldval & PTHREAD_MUTEX_PRIO_CEILING_MASK;
Packit 6c4009
	}
Packit 6c4009
      while (!atomic_compare_exchange_weak_release (&mutex->__data.__lock,
Packit 6c4009
						    &oldval, newval));
Packit 6c4009
Packit 6c4009
      if ((oldval & ~PTHREAD_MUTEX_PRIO_CEILING_MASK) > 1)
Packit 6c4009
	lll_futex_wake (&mutex->__data.__lock, 1,
Packit 6c4009
			PTHREAD_MUTEX_PSHARED (mutex));
Packit 6c4009
Packit 6c4009
      int oldprio = newval >> PTHREAD_MUTEX_PRIO_CEILING_SHIFT;
Packit 6c4009
Packit 6c4009
      LIBC_PROBE (mutex_release, 1, mutex);
Packit 6c4009
Packit 6c4009
      return __pthread_tpp_change_priority (oldprio, -1);
Packit 6c4009
Packit 6c4009
    default:
Packit 6c4009
      /* Correct code cannot set any other type.  */
Packit 6c4009
      return EINVAL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  LIBC_PROBE (mutex_release, 1, mutex);
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__pthread_mutex_unlock (pthread_mutex_t *mutex)
Packit 6c4009
{
Packit 6c4009
  return __pthread_mutex_unlock_usercnt (mutex, 1);
Packit 6c4009
}
Packit 6c4009
weak_alias (__pthread_mutex_unlock, pthread_mutex_unlock)
Packit 6c4009
hidden_def (__pthread_mutex_unlock)