NPTL lowlevellock.h description
A rough outline of how the mutex locking fuctions of the NPTL work is given below.
lowlevellock.h
--------------
** FUTEX OPERATIONS **
Call the futex syscall with the passed in parameters.
-------------------------------------------
lll_futex_wait(int *futex, int val)
-------------------------------------------
syscall(SYS_futex, futex, FUTEX_WAIT, val, NULL);
FUTEX_WAIT checks that the futex value is still equal to val, and
sleeps. If put to sleep, the process will sleep until another process
calls FUTEX_WAIT
---------------------------------------------------------------------------
lll_futex_timed_wait(int *futex, int val, const struct timespec * timespec)
---------------------------------------------------------------------------
syscall(SYS_futex,futex, FUTEX_WAIT, val, timespec);
As above, but with a specified timeout
-------------------------------------------
lll_futex_wake(int *futex, int nr)
-------------------------------------------
syscall(SYS_futex,futex, FUTEX_WAKE, nr, NULL);
FUTEX_WAKE wakes nr processes waiting on futex.
** MUTEX OPERATIONS **
Operate on mutex's
in this context, futex represents these mutex values :
0 : lock is available
1 : lock taken
>1: n waiters
-------------------------------------------
lll_mutex_trylock (int *futex)
-------------------------------------------
if ( futex == 0 )
atomic_increment futex;
return false;
else
return true;
note : the if/increment step needs to be atomic, so use a compare and
exchange.
-------------------------------------------
lll_mutex_lock (int *futex)
-------------------------------------------
increment the lock. if we have it, continue, otherwise wait on the
futex until we can take it. when we exit, set futex to 2.
The reason is that lll_mutex_unlock sets the lock unconditionally to
0. Consider 3 users, the first gets the lock, the second and third
waiting in ___lll_mutex_lock. The mutex value is 3. The first user
releases the lock and wakes up the second waiter. If the second waiter
sets the lock value to 1 then on release of the lock it will NOT wake
up the third waiter. With only two users, one holding the lock and
one waiting for it, the second user will do an unnecessary futex call
after it got the lock and releases it again. But this is built in the
mutex design.
- Martin Schwidefsky <schwidefsky@de.ibm.com>
atomic_increment(futex)
if (futex > 1)
while {
lll_futex_wait(futex)
atomic_increment(futex)
} futex != 1
futex = 2
----------------------------------------------------------
lll_mutex_timedlock (int *futex, struct timespec *abstime)
----------------------------------------------------------
as above, but if we time out return ETIMEDOUT
-------------------------------------------
lll_mutex_unlock (int *futex)
-------------------------------------------
decrease the futex, if someone is waiting then wake them up. Set the
lock value to zero (see description above).
atomic_decrement(futex)
if ( *futex > 0 ) /* someone is waiting */
lll_futex_wake( futex , 1 )
*futex = 0
-------------------------------------------
lll_mutex_islocked(futex)
-------------------------------------------
simply report if the lock is taken or not.
if ( futex == 0 )
return false
else
return true
** INTERNAL MUTEX IMPLEMENTATION **
These operations are used inside libc.
In this context, futex represents
1 - untaken
0 - taken by one user
<0 - taken by more users
-------------------------------------------
lll_trylock (int *futex)
-------------------------------------------
if ( *futex == 1 )
atomic_decrement(futex);
return false
else
return true
as above, use a compare and swap for the if/dec operation.
-------------------------------------------
lll_lock (int *futex)
-------------------------------------------
lock the futex. on exit set futex value to -1 (see note above)
atomic_decrement(futex);
if ( *futex < 0 )
do {
lll_futex_wait(futex, *futex)
atomic_decrement(futex)
}
while ( *futex != 0 )
*futex = -1
-------------------------------------------
lll_unlock (int *futex)
-------------------------------------------
increment the futex. if we have a waiter, wake them up. unlock the
futex
atomic_increment(futex)
if ( *futex < 1 )
lll_futex_wake(futex, 1)
*futex = 1
-------------------------------------------
lll_islocked(int *futex)
-------------------------------------------
check if the futex is locked.
if ( *futex == 1 )
return false
else
return true
*** TID FUNCTIONS **
Waiting on kernel signals for threads.
-------------------------------------------
lll_wait_tid (int *ptid)
-------------------------------------------
The kernel notifies a process with uses CLONE_CLEARTID via futex
wakeup when the clone terminates. The memory location contains the
thread ID while the clone is running and is reset to zero afterwards.
int tid;
while ((tid = *ptid) != 0)
lll_futex_wait (ptid, tid);
-------------------------------------------
lll_timedwait_tid (int *ptid, const struct timespec *abstime)
-------------------------------------------
as above but used a timed futex wait.