This is the mail archive of the
libc-help@sourceware.org
mailing list for the glibc project.
Re: Race unlocking-locking mutex
- From: Ángel González <keisial at gmail dot com>
- To: David Ahern <dsahern at gmail dot com>
- Cc: Carlos O'Donell <carlos at systemhalted dot org>, Godmar Back <godmar at gmail dot com>, "libc-help at sourceware dot org" <libc-help at sourceware dot org>
- Date: Sun, 15 Sep 2013 02:31:57 +0200
- Subject: Re: Race unlocking-locking mutex
- Authentication-results: sourceware.org; auth=none
- References: <5231ECBE dot 4030006 at gmail dot com> <CAB4+JY+dBDVhk9UJWXRXrxF5BhoTFXv==_v+ibdEBU5Noj4aEw at mail dot gmail dot com> <5231F2C3 dot 6080305 at gmail dot com> <CAE2sS1gp9Z5b5qoSqgBXbbL-S+_fHaL1PN7z=CfMsqSTA=7LFg at mail dot gmail dot com> <52336631 dot 5030402 at gmail dot com> <5234CB4C dot 3060305 at gmail dot com>
On 14/09/13 22:47, David Ahern wrote:
David problem should be solved if the unlock atomically assigned it to
the waken thread (instead of waiting for it to reacquire the mutex).
However, as it is a kernel decision, I think it would require a new
futex operation, which stored in uaddr2 the waken tids. Then
mutex->__data.__owner could be passed as uaddr2 and the mutex considered
also locked if owner != 0.
Not necessarily proposing this for libc, but the product I work on
needs a solution for this problem that invokes some kind of fairness
-- and without adding too much complexity.
One option that comes to mind is adding a new element in the mutex --
a __prev_owner. On the unlock path set __prev_owner to thread id,
release lock, awaken a waiter. If it is an uncontended lock (no tasks
awakened or waiting), set __prev_owner to 0.
On the lock path check if __prev_owner is equal to thread id and if so
take the slow path into the kernel.
Any particular worrisome race conditions to be wary of?
David
Hmm, yes, it seems doable.
I think it could even be implemented without __prev_owner.
This may be such an implementation (to replace the nptl/lowlevellock.h
__generic_mutex_lock).
static inline void
__generic_mutex_lock (int *mutex)
{
unsigned int v;
/* Mutex available and no waiters, we got the mutex. (this is the
fastpath). */
if (!atomic_compare_and_exchange_bool_acq(mutex, 1 << 31, 0))
return;
atomic_increment (mutex);
while (1)
{
if (!atomic_compare_and_exchange_bool_acq(mutex, 1 << 31, 1))
{
return;
}
/* We have to wait now. First make sure the futex value we are
monitoring is locked or with waiters. */
v = *mutex;
assert(v != 0);
if (__builtin_expect(v == 0, 0))
continue;
lll_futex_wait (mutex, v,
// XYZ check mutex flag
LLL_SHARED);
}
while (1)
{
if (atomic_bit_test_set (mutex, 31) == 0)
{
atomic_decrement (mutex);
return;
}
/* We have to wait now. First make sure the futex value we are
monitoring is truly negative (i.e. locked). */
v = *mutex;
if (v >= 0)
continue;
lll_futex_wait (mutex, v,
// XYZ check mutex flag
LLL_SHARED);
}
}