From: Andres Freund Date: Sat, 22 Feb 2025 01:55:23 +0000 (-0500) Subject: Allow lwlocks to be disowned X-Git-Tag: REL_18_BETA1~791 X-Git-Url: http://git.postgresql.org/gitweb/?a=commitdiff_plain;h=f8d7f29b3e81db59b95e4b5baaa6943178c89fd8;p=postgresql.git Allow lwlocks to be disowned To implement AIO writes, the backend initiating writes needs to transfer the lock ownership to the AIO subsystem, so the lock held during the write can be released in another backend. Other backends need to be able to "complete" an asynchronously started IO to avoid deadlocks (consider e.g. one backend starting IO for a buffer and then waiting for a heavyweight lock held by another relation followed by the current holder of the heavyweight lock waiting for the IO to complete). To that end, this commit adds LWLockDisown() and LWLockReleaseDisowned(). If code uses LWLockDisown() it's the code's responsibility to ensure that the lock is released in case of errors. Reviewed-by: Heikki Linnakangas Discussion: https://postgr.es/m/1f6b50a7-38ef-4d87-8246-786d39f46ab9@iki.fi --- diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c index f1e74f184f1..8a7fb6a22c6 100644 --- a/src/backend/storage/lmgr/lwlock.c +++ b/src/backend/storage/lmgr/lwlock.c @@ -1775,14 +1775,25 @@ LWLockUpdateVar(LWLock *lock, pg_atomic_uint64 *valptr, uint64 val) /* - * LWLockRelease - release a previously acquired lock + * Stop treating lock as held by current backend. + * + * This is the code that can be shared between actually releasing a lock + * (LWLockRelease()) and just not tracking ownership of the lock anymore + * without releasing the lock (LWLockDisown()). + * + * Returns the mode in which the lock was held by the current backend. + * + * NB: This does not call RESUME_INTERRUPTS(), but leaves that responsibility + * of the caller. + * + * NB: This will leave lock->owner pointing to the current backend (if + * LOCK_DEBUG is set). This is somewhat intentional, as it makes it easier to + * debug cases of missing wakeups during lock release. */ -void -LWLockRelease(LWLock *lock) +static inline LWLockMode +LWLockDisownInternal(LWLock *lock) { LWLockMode mode; - uint32 oldstate; - bool check_waiters; int i; /* @@ -1802,7 +1813,18 @@ LWLockRelease(LWLock *lock) for (; i < num_held_lwlocks; i++) held_lwlocks[i] = held_lwlocks[i + 1]; - PRINT_LWDEBUG("LWLockRelease", lock, mode); + return mode; +} + +/* + * Helper function to release lock, shared between LWLockRelease() and + * LWLockeleaseDisowned(). + */ +static void +LWLockReleaseInternal(LWLock *lock, LWLockMode mode) +{ + uint32 oldstate; + bool check_waiters; /* * Release my hold on lock, after that it can immediately be acquired by @@ -1840,6 +1862,38 @@ LWLockRelease(LWLock *lock) LOG_LWDEBUG("LWLockRelease", lock, "releasing waiters"); LWLockWakeup(lock); } +} + + +/* + * Stop treating lock as held by current backend. + * + * After calling this function it's the callers responsibility to ensure that + * the lock gets released (via LWLockReleaseDisowned()), even in case of an + * error. This only is desirable if the lock is going to be released in a + * different process than the process that acquired it. + */ +void +LWLockDisown(LWLock *lock) +{ + LWLockDisownInternal(lock); + + RESUME_INTERRUPTS(); +} + +/* + * LWLockRelease - release a previously acquired lock + */ +void +LWLockRelease(LWLock *lock) +{ + LWLockMode mode; + + mode = LWLockDisownInternal(lock); + + PRINT_LWDEBUG("LWLockRelease", lock, mode); + + LWLockReleaseInternal(lock, mode); /* * Now okay to allow cancel/die interrupts. @@ -1847,6 +1901,15 @@ LWLockRelease(LWLock *lock) RESUME_INTERRUPTS(); } +/* + * Release lock previously disowned with LWLockDisown(). + */ +void +LWLockReleaseDisowned(LWLock *lock, LWLockMode mode) +{ + LWLockReleaseInternal(lock, mode); +} + /* * LWLockReleaseClearVar - release a previously acquired lock, reset variable */ diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h index 2aa46fd50da..13a7dc89980 100644 --- a/src/include/storage/lwlock.h +++ b/src/include/storage/lwlock.h @@ -129,6 +129,8 @@ extern bool LWLockAcquireOrWait(LWLock *lock, LWLockMode mode); extern void LWLockRelease(LWLock *lock); extern void LWLockReleaseClearVar(LWLock *lock, pg_atomic_uint64 *valptr, uint64 val); extern void LWLockReleaseAll(void); +extern void LWLockDisown(LWLock *l); +extern void LWLockReleaseDisowned(LWLock *l, LWLockMode mode); extern bool LWLockHeldByMe(LWLock *lock); extern bool LWLockAnyHeldByMe(LWLock *lock, int nlocks, size_t stride); extern bool LWLockHeldByMeInMode(LWLock *lock, LWLockMode mode);