Blame sysdeps/nptl/lowlevellock.h

Packit 6c4009
/* Low-level lock implementation.  Generic futex-based 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
#ifndef _LOWLEVELLOCK_H
Packit 6c4009
#define _LOWLEVELLOCK_H	1
Packit 6c4009
Packit 6c4009
#include <atomic.h>
Packit 6c4009
#include <lowlevellock-futex.h>
Packit 6c4009
Packit 6c4009
/* Low-level locks use a combination of atomic operations (to acquire and
Packit 6c4009
   release lock ownership) and futex operations (to block until the state
Packit 6c4009
   of a lock changes).  A lock can be in one of three states:
Packit 6c4009
   0:  not acquired,
Packit 6c4009
   1:  acquired with no waiters; no other threads are blocked or about to block
Packit 6c4009
       for changes to the lock state,
Packit 6c4009
   >1: acquired, possibly with waiters; there may be other threads blocked or
Packit 6c4009
       about to block for changes to the lock state.
Packit 6c4009
Packit 6c4009
   We expect that the common case is an uncontended lock, so we just need
Packit 6c4009
   to transition the lock between states 0 and 1; releasing the lock does
Packit 6c4009
   not need to wake any other blocked threads.  If the lock is contended
Packit 6c4009
   and a thread decides to block using a futex operation, then this thread
Packit 6c4009
   needs to first change the state to >1; if this state is observed during
Packit 6c4009
   lock release, the releasing thread will wake one of the potentially
Packit 6c4009
   blocked threads.
Packit 6c4009
Packit 6c4009
   Much of this code takes a 'private' parameter.  This may be:
Packit 6c4009
   LLL_PRIVATE: lock only shared within a process
Packit 6c4009
   LLL_SHARED:  lock may be shared across processes.
Packit 6c4009
Packit 6c4009
   Condition variables contain an optimization for broadcasts that requeues
Packit 6c4009
   waiting threads on a lock's futex.  Therefore, there is a special
Packit 6c4009
   variant of the locks (whose name contains "cond") that makes sure to
Packit 6c4009
   always set the lock state to >1 and not just 1.
Packit 6c4009
Packit 6c4009
   Robust locks set the lock to the id of the owner.  This allows detection
Packit 6c4009
   of the case where the owner exits without releasing the lock.  Flags are
Packit 6c4009
   OR'd with the owner id to record additional information about lock state.
Packit 6c4009
   Therefore the states of robust locks are:
Packit 6c4009
    0: not acquired
Packit 6c4009
   id: acquired (by user identified by id & FUTEX_TID_MASK)
Packit 6c4009
Packit 6c4009
   The following flags may be set in the robust lock value:
Packit 6c4009
   FUTEX_WAITERS     - possibly has waiters
Packit 6c4009
   FUTEX_OWNER_DIED  - owning user has exited without releasing the futex.  */
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* If LOCK is 0 (not acquired), set to 1 (acquired with no waiters) and return
Packit 6c4009
   0.  Otherwise leave lock unchanged and return non-zero to indicate that the
Packit 6c4009
   lock was not acquired.  */
Packit 6c4009
#define lll_trylock(lock)	\
Packit 6c4009
  __glibc_unlikely (atomic_compare_and_exchange_bool_acq (&(lock), 1, 0))
Packit 6c4009
Packit 6c4009
/* If LOCK is 0 (not acquired), set to 2 (acquired, possibly with waiters) and
Packit 6c4009
   return 0.  Otherwise leave lock unchanged and return non-zero to indicate
Packit 6c4009
   that the lock was not acquired.  */
Packit 6c4009
#define lll_cond_trylock(lock)	\
Packit 6c4009
  __glibc_unlikely (atomic_compare_and_exchange_bool_acq (&(lock), 2, 0))
Packit 6c4009
Packit 6c4009
extern void __lll_lock_wait_private (int *futex) attribute_hidden;
Packit 6c4009
extern void __lll_lock_wait (int *futex, int private) attribute_hidden;
Packit 6c4009
Packit 6c4009
/* This is an expression rather than a statement even though its value is
Packit 6c4009
   void, so that it can be used in a comma expression or as an expression
Packit 6c4009
   that's cast to void.  */
Packit 6c4009
/* The inner conditional compiles to a call to __lll_lock_wait_private if
Packit 6c4009
   private is known at compile time to be LLL_PRIVATE, and to a call to
Packit 6c4009
   __lll_lock_wait otherwise.  */
Packit 6c4009
/* If FUTEX is 0 (not acquired), set to 1 (acquired with no waiters) and
Packit 6c4009
   return.  Otherwise, ensure that it is >1 (acquired, possibly with waiters)
Packit 6c4009
   and then block until we acquire the lock, at which point FUTEX will still be
Packit 6c4009
   >1.  The lock is always acquired on return.  */
Packit 6c4009
#define __lll_lock(futex, private)                                      \
Packit 6c4009
  ((void)                                                               \
Packit 6c4009
   ({                                                                   \
Packit 6c4009
     int *__futex = (futex);                                            \
Packit 6c4009
     if (__glibc_unlikely                                               \
Packit 6c4009
         (atomic_compare_and_exchange_bool_acq (__futex, 1, 0)))        \
Packit 6c4009
       {                                                                \
Packit 6c4009
         if (__builtin_constant_p (private) && (private) == LLL_PRIVATE) \
Packit 6c4009
           __lll_lock_wait_private (__futex);                           \
Packit 6c4009
         else                                                           \
Packit 6c4009
           __lll_lock_wait (__futex, private);                          \
Packit 6c4009
       }                                                                \
Packit 6c4009
   }))
Packit 6c4009
#define lll_lock(futex, private)	\
Packit 6c4009
  __lll_lock (&(futex), private)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* This is an expression rather than a statement even though its value is
Packit 6c4009
   void, so that it can be used in a comma expression or as an expression
Packit 6c4009
   that's cast to void.  */
Packit 6c4009
/* Unconditionally set FUTEX to 2 (acquired, possibly with waiters).  If FUTEX
Packit 6c4009
   was 0 (not acquired) then return.  Otherwise, block until the lock is
Packit 6c4009
   acquired, at which point FUTEX is 2 (acquired, possibly with waiters).  The
Packit 6c4009
   lock is always acquired on return.  */
Packit 6c4009
#define __lll_cond_lock(futex, private)                                 \
Packit 6c4009
  ((void)                                                               \
Packit 6c4009
   ({                                                                   \
Packit 6c4009
     int *__futex = (futex);                                            \
Packit 6c4009
     if (__glibc_unlikely (atomic_exchange_acq (__futex, 2) != 0))      \
Packit 6c4009
       __lll_lock_wait (__futex, private);                              \
Packit 6c4009
   }))
Packit 6c4009
#define lll_cond_lock(futex, private) __lll_cond_lock (&(futex), private)
Packit 6c4009
Packit 6c4009
Packit 6c4009
extern int __lll_timedlock_wait (int *futex, const struct timespec *,
Packit 6c4009
				 int private) attribute_hidden;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* As __lll_lock, but with a timeout.  If the timeout occurs then return
Packit 6c4009
   ETIMEDOUT.  If ABSTIME is invalid, return EINVAL.  */
Packit 6c4009
#define __lll_timedlock(futex, abstime, private)                \
Packit 6c4009
  ({                                                            \
Packit 6c4009
    int *__futex = (futex);                                     \
Packit 6c4009
    int __val = 0;                                              \
Packit 6c4009
                                                                \
Packit 6c4009
    if (__glibc_unlikely                                        \
Packit 6c4009
        (atomic_compare_and_exchange_bool_acq (__futex, 1, 0))) \
Packit 6c4009
      __val = __lll_timedlock_wait (__futex, abstime, private); \
Packit 6c4009
    __val;                                                      \
Packit 6c4009
  })
Packit 6c4009
#define lll_timedlock(futex, abstime, private)  \
Packit 6c4009
  __lll_timedlock (&(futex), abstime, private)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* This is an expression rather than a statement even though its value is
Packit 6c4009
   void, so that it can be used in a comma expression or as an expression
Packit 6c4009
   that's cast to void.  */
Packit 6c4009
/* Unconditionally set FUTEX to 0 (not acquired), releasing the lock.  If FUTEX
Packit 6c4009
   was >1 (acquired, possibly with waiters), then wake any waiters.  The waiter
Packit 6c4009
   that acquires the lock will set FUTEX to >1.
Packit 6c4009
   Evaluate PRIVATE before releasing the lock so that we do not violate the
Packit 6c4009
   mutex destruction requirements.  Specifically, we need to ensure that
Packit 6c4009
   another thread can destroy the mutex (and reuse its memory) once it
Packit 6c4009
   acquires the lock and when there will be no further lock acquisitions;
Packit 6c4009
   thus, we must not access the lock after releasing it, or those accesses
Packit 6c4009
   could be concurrent with mutex destruction or reuse of the memory.  */
Packit 6c4009
#define __lll_unlock(futex, private)                    \
Packit 6c4009
  ((void)                                               \
Packit 6c4009
   ({                                                   \
Packit 6c4009
     int *__futex = (futex);                            \
Packit 6c4009
     int __private = (private);                         \
Packit 6c4009
     int __oldval = atomic_exchange_rel (__futex, 0);   \
Packit 6c4009
     if (__glibc_unlikely (__oldval > 1))               \
Packit 6c4009
       lll_futex_wake (__futex, 1, __private);          \
Packit 6c4009
   }))
Packit 6c4009
#define lll_unlock(futex, private)	\
Packit 6c4009
  __lll_unlock (&(futex), private)
Packit 6c4009
Packit 6c4009
Packit 6c4009
#define lll_islocked(futex) \
Packit 6c4009
  ((futex) != LLL_LOCK_INITIALIZER)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Our internal lock implementation is identical to the binary-compatible
Packit 6c4009
   mutex implementation. */
Packit 6c4009
Packit 6c4009
/* Initializers for lock.  */
Packit 6c4009
#define LLL_LOCK_INITIALIZER		(0)
Packit 6c4009
#define LLL_LOCK_INITIALIZER_LOCKED	(1)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* The kernel notifies a process which uses CLONE_CHILD_CLEARTID via futex
Packit 6c4009
   wake-up when the clone terminates.  The memory location contains the
Packit 6c4009
   thread ID while the clone is running and is reset to zero by the kernel
Packit 6c4009
   afterwards.  The kernel up to version 3.16.3 does not use the private futex
Packit 6c4009
   operations for futex wake-up when the clone terminates.  */
Packit 6c4009
#define lll_wait_tid(tid)				\
Packit 6c4009
  do {							\
Packit 6c4009
    __typeof (tid) __tid;				\
Packit 6c4009
    /* We need acquire MO here so that we synchronize	\
Packit 6c4009
       with the kernel's store to 0 when the clone	\
Packit 6c4009
       terminates. (see above)  */			\
Packit 6c4009
    while ((__tid = atomic_load_acquire (&(tid))) != 0)	\
Packit 6c4009
      lll_futex_wait (&(tid), __tid, LLL_SHARED);	\
Packit 6c4009
  } while (0)
Packit 6c4009
Packit 6c4009
extern int __lll_timedwait_tid (int *, const struct timespec *)
Packit 6c4009
     attribute_hidden;
Packit 6c4009
Packit 6c4009
/* As lll_wait_tid, but with a timeout.  If the timeout occurs then return
Packit 6c4009
   ETIMEDOUT.  If ABSTIME is invalid, return EINVAL.  */
Packit 6c4009
#define lll_timedwait_tid(tid, abstime) \
Packit 6c4009
  ({							\
Packit 6c4009
    int __res = 0;					\
Packit 6c4009
    if ((tid) != 0)					\
Packit 6c4009
      __res = __lll_timedwait_tid (&(tid), (abstime));	\
Packit 6c4009
    __res;						\
Packit 6c4009
  })
Packit 6c4009
Packit 6c4009
Packit 6c4009
#endif	/* lowlevellock.h */