On 2015-07-29 14:22:23 +0200, Andres Freund wrote: > On 2015-07-29 15:14:23 +0300, Heikki Linnakangas wrote: > > Ah, ok, that should work, as long as you also re-check the variable's value > > after queueing. Want to write the patch, or should I? > > I'll try. Shouldn't be too hard.
What do you think about something roughly like the attached? - Andres
>From 2879408d8da7714ab6af6238dfe1c8a535800af3 Mon Sep 17 00:00:00 2001 From: Andres Freund <and...@anarazel.de> Date: Wed, 29 Jul 2015 15:06:54 +0200 Subject: [PATCH] Other way to fix the issues around the broken LWLock variable support. --- src/backend/storage/lmgr/lwlock.c | 105 +++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 36 deletions(-) diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c index e5566d1..c64f20d 100644 --- a/src/backend/storage/lmgr/lwlock.c +++ b/src/backend/storage/lmgr/lwlock.c @@ -1066,7 +1066,15 @@ LWLockAcquireCommon(LWLock *lock, LWLockMode mode, uint64 *valptr, uint64 val) /* If there's a variable associated with this lock, initialize it */ if (valptr) + { +#ifdef LWLOCK_STATS + lwstats->spin_delay_count += SpinLockAcquire(&lock->mutex); +#else + SpinLockAcquire(&lock->mutex); +#endif *valptr = val; + SpinLockRelease(&lock->mutex); + } TRACE_POSTGRESQL_LWLOCK_ACQUIRE(T_NAME(lock), T_ID(lock), mode); @@ -1259,6 +1267,60 @@ LWLockAcquireOrWait(LWLock *lock, LWLockMode mode) } /* + * Does the lwlock in its current state need to wait for the variable value to + * change? + * + * If we don't need to wait, and it's because the value of the variable has + * changed, store the current value in newval. + * + * *result is set to true if the lock was free, and false otherwise. + */ +static bool +LWLockConflictsWithVar(LWLock *lock, + uint64 *valptr, uint64 oldval, uint64 *newval, + bool *result) +{ + bool mustwait; + uint64 value; + + mustwait = (pg_atomic_read_u32(&lock->state) & LW_VAL_EXCLUSIVE) != 0; + + if (!mustwait) + { + *result = true; + return false; + } + + /* + * Perform comparison using spinlock as we can't rely on atomic 64 bit + * reads/stores. On platforms with a way to do atomic 64 bit reads/writes + * the spinlock can be optimized away. + */ +#ifdef LWLOCK_STATS + lwstats->spin_delay_count += SpinLockAcquire(&lock->mutex); +#else + SpinLockAcquire(&lock->mutex); +#endif + + *result = false; + + value = *valptr; + if (value != oldval) + { + mustwait = false; + *newval = value; + } + else + { + mustwait = true; + } + + SpinLockRelease(&lock->mutex); + + return mustwait; +} + +/* * LWLockWaitForVar - Wait until lock is free, or a variable is updated. * * If the lock is held and *valptr equals oldval, waits until the lock is @@ -1313,39 +1375,9 @@ LWLockWaitForVar(LWLock *lock, uint64 *valptr, uint64 oldval, uint64 *newval) for (;;) { bool mustwait; - uint64 value; - - mustwait = (pg_atomic_read_u32(&lock->state) & LW_VAL_EXCLUSIVE) != 0; - - if (mustwait) - { - /* - * Perform comparison using spinlock as we can't rely on atomic 64 - * bit reads/stores. - */ -#ifdef LWLOCK_STATS - lwstats->spin_delay_count += SpinLockAcquire(&lock->mutex); -#else - SpinLockAcquire(&lock->mutex); -#endif - /* - * XXX: We can significantly optimize this on platforms with 64bit - * atomics. - */ - value = *valptr; - if (value != oldval) - { - result = false; - mustwait = false; - *newval = value; - } - else - mustwait = true; - SpinLockRelease(&lock->mutex); - } - else - mustwait = false; + mustwait = LWLockConflictsWithVar(lock, valptr, oldval, newval, + &result); if (!mustwait) break; /* the lock was free or value didn't match */ @@ -1365,12 +1397,13 @@ LWLockWaitForVar(LWLock *lock, uint64 *valptr, uint64 oldval, uint64 *newval) pg_atomic_fetch_or_u32(&lock->state, LW_FLAG_RELEASE_OK); /* - * We're now guaranteed to be woken up if necessary. Recheck the - * lock's state. + * We're now guaranteed to be woken up if necessary. Recheck the lock + * and variables state. */ - mustwait = (pg_atomic_read_u32(&lock->state) & LW_VAL_EXCLUSIVE) != 0; + mustwait = LWLockConflictsWithVar(lock, valptr, oldval, newval, + &result); - /* Ok, lock is free after we queued ourselves. Undo queueing. */ + /* Ok, no conflict after we queued ourselves. Undo queueing. */ if (!mustwait) { LOG_LWDEBUG("LWLockWaitForVar", lock, "free, undoing queue"); -- 2.4.0.rc2.1.g3d6bc9a
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers