]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
bufmgr: Use atomic sub for unpinning buffers
authorAndres Freund <andres@anarazel.de>
Thu, 6 Nov 2025 21:43:16 +0000 (16:43 -0500)
committerAndres Freund <andres@anarazel.de>
Thu, 6 Nov 2025 21:43:16 +0000 (16:43 -0500)
The prior commit made it legal to modify BufferDesc.state while the buffer
header spinlock is held. This allows us to replace the CAS loop
inUnpinBufferNoOwner() with an atomic sub. This improves scalability
significantly. See the prior commits for more background.

Reviewed-by: Matthias van de Meent <boekewurm+postgres@gmail.com>
Discussion: https://postgr.es/m/fvfmkr5kk4nyex56ejgxj3uzi63isfxovp2biecb4bspbjrze7@az2pljabhnff

src/backend/storage/buffer/bufmgr.c

index 4b9fd76294d4d43663b3427cf209b4928d06389c..327ddb7adc88dd1f2cf01ac37e439be0f9387181 100644 (file)
@@ -3267,7 +3267,6 @@ UnpinBufferNoOwner(BufferDesc *buf)
        ref->refcount--;
        if (ref->refcount == 0)
        {
-               uint32          buf_state;
                uint32          old_buf_state;
 
                /*
@@ -3285,33 +3284,11 @@ UnpinBufferNoOwner(BufferDesc *buf)
                 */
                Assert(!LWLockHeldByMe(BufferDescriptorGetContentLock(buf)));
 
-               /*
-                * Decrement the shared reference count.
-                *
-                * Since buffer spinlock holder can update status using just write,
-                * it's not safe to use atomic decrement here; thus use a CAS loop.
-                *
-                * TODO: The above requirement does not hold anymore, in a future
-                * commit this will be rewritten to release the pin in a single atomic
-                * operation.
-                */
-               old_buf_state = pg_atomic_read_u32(&buf->state);
-               for (;;)
-               {
-                       if (old_buf_state & BM_LOCKED)
-                               old_buf_state = WaitBufHdrUnlocked(buf);
-
-                       buf_state = old_buf_state;
-
-                       buf_state -= BUF_REFCOUNT_ONE;
-
-                       if (pg_atomic_compare_exchange_u32(&buf->state, &old_buf_state,
-                                                                                          buf_state))
-                               break;
-               }
+               /* decrement the shared reference count */
+               old_buf_state = pg_atomic_fetch_sub_u32(&buf->state, BUF_REFCOUNT_ONE);
 
                /* Support LockBufferForCleanup() */
-               if (buf_state & BM_PIN_COUNT_WAITER)
+               if (old_buf_state & BM_PIN_COUNT_WAITER)
                        WakePinCountWaiter(buf);
 
                ForgetPrivateRefCountEntry(ref);