97
97
#define LW_FLAG_BITS 3
98
98
#define LW_FLAG_MASK (((1<<LW_FLAG_BITS)-1)<<(32-LW_FLAG_BITS))
99
99
100
- /* assumes MAX_BACKENDS is a (power of 2) - 1, checked below */
101
- #define LW_VAL_EXCLUSIVE (MAX_BACKENDS + 1)
100
+ /*
101
+ * already (power of 2)-1, i.e. suitable for a mask
102
+ *
103
+ * Originally, the LW_SHARED lock reference count was maintained in bits
104
+ * [MAX_BACKEND_BITS-1:0] of LWLock.state, with a theoretical maximum of
105
+ * MAX_BACKENDS (when all MAX_BACKENDS processes hold the lock concurrently).
106
+ *
107
+ * To reduce lock acquisition overhead, we optimized LWLockAttemptLock by
108
+ * merging the read and update operations for the LW_SHARED lock's state.
109
+ * This eliminates the need for separate atomic instructions - a critical
110
+ * improvement given the high cost of atomic operations on high-core-count
111
+ * systems.
112
+ *
113
+ * This optimization introduces a scenario where the reference count may
114
+ * temporarily increment even when a reader fails to acquire an exclusive lock.
115
+ * However, since each process retries lock acquisition up to *twice* before
116
+ * waiting on a semaphore, the reference count is bounded by MAX_BACKENDS * 2.
117
+ *
118
+ * To ensure compatibility with this upper bound:
119
+ * 1. LW_SHARED_MASK has been extended by 1 bit
120
+ * 2. LW_VAL_EXCLUSIVE is left-shifted by 1 bit
121
+ */
122
+ #define LW_SHARED_MASK ((MAX_BACKENDS << 1) + 1)
123
+ #define LW_VAL_EXCLUSIVE (LW_SHARED_MASK + 1)
124
+ #define LW_LOCK_MASK (LW_SHARED_MASK | LW_VAL_EXCLUSIVE)
102
125
#define LW_VAL_SHARED 1
103
126
104
- /* already (power of 2)-1, i.e. suitable for a mask */
105
- #define LW_SHARED_MASK MAX_BACKENDS
106
- #define LW_LOCK_MASK (MAX_BACKENDS | LW_VAL_EXCLUSIVE)
127
+ /* assumes MAX_BACKENDS is a (power of 2) - 1, checked below */
107
128
108
129
109
130
StaticAssertDecl (((MAX_BACKENDS + 1 ) & MAX_BACKENDS ) == 0 ,
110
131
"MAX_BACKENDS + 1 needs to be a power of 2" );
111
132
112
- StaticAssertDecl ((MAX_BACKENDS & LW_FLAG_MASK ) == 0 ,
113
- "MAX_BACKENDS and LW_FLAG_MASK overlap" );
133
+ StaticAssertDecl ((LW_SHARED_MASK & LW_FLAG_MASK ) == 0 ,
134
+ "LW_SHARED_MASK and LW_FLAG_MASK overlap" );
114
135
115
136
StaticAssertDecl ((LW_VAL_EXCLUSIVE & LW_FLAG_MASK ) == 0 ,
116
137
"LW_VAL_EXCLUSIVE and LW_FLAG_MASK overlap" );
@@ -277,15 +298,17 @@ PRINT_LWDEBUG(const char *where, LWLock *lock, LWLockMode mode)
277
298
if (Trace_lwlocks )
278
299
{
279
300
uint32 state = pg_atomic_read_u32 (& lock -> state );
301
+ uint32 excl = (state & LW_VAL_EXCLUSIVE ) != 0 ;
302
+ uint32 shared = excl ? 0 : state & LW_SHARED_MASK ;
280
303
281
304
ereport (LOG ,
282
305
(errhidestmt (true),
283
306
errhidecontext (true),
284
307
errmsg_internal ("%d: %s(%s %p): excl %u shared %u haswaiters %u waiters %u rOK %d" ,
285
308
MyProcPid ,
286
309
where , T_NAME (lock ), lock ,
287
- ( state & LW_VAL_EXCLUSIVE ) != 0 ,
288
- state & LW_SHARED_MASK ,
310
+ excl ,
311
+ shared ,
289
312
(state & LW_FLAG_HAS_WAITERS ) != 0 ,
290
313
pg_atomic_read_u32 (& lock -> nwaiters ),
291
314
(state & LW_FLAG_RELEASE_OK ) != 0 )));
@@ -790,15 +813,30 @@ GetLWLockIdentifier(uint32 classId, uint16 eventId)
790
813
* This function will not block waiting for a lock to become free - that's the
791
814
* caller's job.
792
815
*
816
+ * willwait: true if the caller is willing to wait for the lock to become free
817
+ * false if the caller is not willing to wait.
818
+ *
793
819
* Returns true if the lock isn't free and we need to wait.
794
820
*/
795
821
static bool
796
- LWLockAttemptLock (LWLock * lock , LWLockMode mode )
822
+ LWLockAttemptLock (LWLock * lock , LWLockMode mode , bool willwait )
797
823
{
798
824
uint32 old_state ;
799
825
800
826
Assert (mode == LW_EXCLUSIVE || mode == LW_SHARED );
801
827
828
+ /*
829
+ * To avoid conflicts between the reference count and the LW_VAL_EXCLUSIVE
830
+ * flag, this optimization is disabled when willwait is false. See detailed
831
+ * comments in this file where LW_SHARED_MASK is defined for more explaination.
832
+ */
833
+ if (willwait && mode == LW_SHARED )
834
+ {
835
+ old_state = pg_atomic_fetch_add_u32 (& lock -> state , LW_VAL_SHARED );
836
+ Assert ((old_state & LW_LOCK_MASK ) != LW_LOCK_MASK );
837
+ return (old_state & LW_VAL_EXCLUSIVE ) != 0 ;
838
+ }
839
+
802
840
/*
803
841
* Read once outside the loop, later iterations will get the newer value
804
842
* via compare & exchange.
@@ -1242,7 +1280,7 @@ LWLockAcquire(LWLock *lock, LWLockMode mode)
1242
1280
* Try to grab the lock the first time, we're not in the waitqueue
1243
1281
* yet/anymore.
1244
1282
*/
1245
- mustwait = LWLockAttemptLock (lock , mode );
1283
+ mustwait = LWLockAttemptLock (lock , mode , true );
1246
1284
1247
1285
if (!mustwait )
1248
1286
{
@@ -1265,7 +1303,7 @@ LWLockAcquire(LWLock *lock, LWLockMode mode)
1265
1303
LWLockQueueSelf (lock , mode );
1266
1304
1267
1305
/* we're now guaranteed to be woken up if necessary */
1268
- mustwait = LWLockAttemptLock (lock , mode );
1306
+ mustwait = LWLockAttemptLock (lock , mode , true );
1269
1307
1270
1308
/* ok, grabbed the lock the second time round, need to undo queueing */
1271
1309
if (!mustwait )
@@ -1368,7 +1406,7 @@ LWLockConditionalAcquire(LWLock *lock, LWLockMode mode)
1368
1406
HOLD_INTERRUPTS ();
1369
1407
1370
1408
/* Check for the lock */
1371
- mustwait = LWLockAttemptLock (lock , mode );
1409
+ mustwait = LWLockAttemptLock (lock , mode , false );
1372
1410
1373
1411
if (mustwait )
1374
1412
{
@@ -1435,13 +1473,13 @@ LWLockAcquireOrWait(LWLock *lock, LWLockMode mode)
1435
1473
* NB: We're using nearly the same twice-in-a-row lock acquisition
1436
1474
* protocol as LWLockAcquire(). Check its comments for details.
1437
1475
*/
1438
- mustwait = LWLockAttemptLock (lock , mode );
1476
+ mustwait = LWLockAttemptLock (lock , mode , true );
1439
1477
1440
1478
if (mustwait )
1441
1479
{
1442
1480
LWLockQueueSelf (lock , LW_WAIT_UNTIL_FREE );
1443
1481
1444
- mustwait = LWLockAttemptLock (lock , mode );
1482
+ mustwait = LWLockAttemptLock (lock , mode , true );
1445
1483
1446
1484
if (mustwait )
1447
1485
{
@@ -1843,7 +1881,10 @@ LWLockReleaseInternal(LWLock *lock, LWLockMode mode)
1843
1881
* others, even if we still have to wakeup other waiters.
1844
1882
*/
1845
1883
if (mode == LW_EXCLUSIVE )
1846
- oldstate = pg_atomic_sub_fetch_u32 (& lock -> state , LW_VAL_EXCLUSIVE );
1884
+ {
1885
+ oldstate = pg_atomic_fetch_and_u32 (& lock -> state , ~LW_LOCK_MASK );
1886
+ oldstate &= ~LW_LOCK_MASK ;
1887
+ }
1847
1888
else
1848
1889
oldstate = pg_atomic_sub_fetch_u32 (& lock -> state , LW_VAL_SHARED );
1849
1890
0 commit comments