15
15
* Portions Copyright (c) 1994, Regents of the University of California
16
16
*
17
17
* IDENTIFICATION
18
- * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lwlock.c,v 1.5 2001/12/28 23:26:04 tgl Exp $
18
+ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lwlock.c,v 1.6 2001/12/29 21:28:18 momjian Exp $
19
19
*
20
20
*-------------------------------------------------------------------------
21
21
*/
195
195
LWLockAcquire (LWLockId lockid ,LWLockMode mode )
196
196
{
197
197
volatile LWLock * lock = LWLockArray + lockid ;
198
- bool mustwait ;
198
+ PROC * proc = MyProc ;
199
+ int extraWaits = 0 ;
199
200
200
201
PRINT_LWDEBUG ("LWLockAcquire" ,lockid ,lock );
201
202
@@ -206,43 +207,57 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
206
207
*/
207
208
HOLD_INTERRUPTS ();
208
209
209
- /* Acquire mutex. Time spent holding mutex should be short! */
210
- SpinLockAcquire_NoHoldoff (& lock -> mutex );
211
-
212
- /* If I can get the lock, do so quickly. */
213
- if (mode == LW_EXCLUSIVE )
210
+ /*
211
+ * Loop here to try to acquire lock after each time we are signaled
212
+ * by LWLockRelease.
213
+ *
214
+ * NOTE: it might seem better to have LWLockRelease actually grant us
215
+ * the lock, rather than retrying and possibly having to go back to
216
+ * sleep. But in practice that is no good because it means a process
217
+ * swap for every lock acquisition when two or more processes are
218
+ * contending for the same lock. Since LWLocks are normally used to
219
+ * protect not-very-long sections of computation, a process needs to
220
+ * be able to acquire and release the same lock many times during a
221
+ * single process dispatch cycle, even in the presence of contention.
222
+ * The efficiency of being able to do that outweighs the inefficiency of
223
+ * sometimes wasting a dispatch cycle because the lock is not free when a
224
+ * released waiter gets to run. See pgsql-hackers archives for 29-Dec-01.
225
+ */
226
+ for (;;)
214
227
{
215
- if (lock -> exclusive == 0 && lock -> shared == 0 )
228
+ bool mustwait ;
229
+
230
+ /* Acquire mutex. Time spent holding mutex should be short! */
231
+ SpinLockAcquire_NoHoldoff (& lock -> mutex );
232
+
233
+ /* If I can get the lock, do so quickly. */
234
+ if (mode == LW_EXCLUSIVE )
216
235
{
217
- lock -> exclusive ++ ;
218
- mustwait = false;
236
+ if (lock -> exclusive == 0 && lock -> shared == 0 )
237
+ {
238
+ lock -> exclusive ++ ;
239
+ mustwait = false;
240
+ }
241
+ else
242
+ mustwait = true;
219
243
}
220
244
else
221
- mustwait = true;
222
- }
223
- else
224
- {
225
- /*
226
- * If there is someone waiting (presumably for exclusive access),
227
- * queue up behind him even though I could get the lock. This
228
- * prevents a stream of read locks from starving a writer.
229
- */
230
- if (lock -> exclusive == 0 && lock -> head == NULL )
231
245
{
232
- lock -> shared ++ ;
233
- mustwait = false;
246
+ if (lock -> exclusive == 0 )
247
+ {
248
+ lock -> shared ++ ;
249
+ mustwait = false;
250
+ }
251
+ else
252
+ mustwait = true;
234
253
}
235
- else
236
- mustwait = true;
237
- }
238
254
239
- if (mustwait )
240
- {
241
- /* Add myself to wait queue */
242
- PROC * proc = MyProc ;
243
- int extraWaits = 0 ;
255
+ if (!mustwait )
256
+ break ;/* got the lock */
244
257
245
258
/*
259
+ * Add myself to wait queue.
260
+ *
246
261
* If we don't have a PROC structure, there's no way to wait. This
247
262
* should never occur, since MyProc should only be null during
248
263
* shared memory initialization.
@@ -267,9 +282,9 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
267
282
*
268
283
* Since we share the process wait semaphore with the regular lock
269
284
* manager and ProcWaitForSignal, and we may need to acquire an
270
- * LWLock while one of those is pending, it is possible that we
271
- *get awakened for a reason other than beinggranted the LWLock .
272
- * If so, loop back and wait again. Once we've gotten thelock ,
285
+ * LWLock while one of those is pending, it is possible that we get
286
+ * awakened for a reason other than beingsignaled by LWLockRelease .
287
+ * If so, loop back and wait again. Once we've gotten theLWLock ,
273
288
* re-increment the sema by the number of additional signals
274
289
* received, so that the lock manager or signal manager will see
275
290
* the received signal when it next waits.
@@ -287,23 +302,21 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
287
302
288
303
LOG_LWDEBUG ("LWLockAcquire" ,lockid ,"awakened" );
289
304
290
- /*
291
- * The awakener already updated the lock struct's state, so we
292
- * don't need to do anything more to it. Just need to fix the
293
- * semaphore count.
294
- */
295
- while (extraWaits -- > 0 )
296
- IpcSemaphoreUnlock (proc -> sem .semId ,proc -> sem .semNum );
297
- }
298
- else
299
- {
300
- /* Got the lock without waiting */
301
- SpinLockRelease_NoHoldoff (& lock -> mutex );
305
+ /* Now loop back and try to acquire lock again. */
302
306
}
303
307
308
+ /* We are done updating shared state of the lock itself. */
309
+ SpinLockRelease_NoHoldoff (& lock -> mutex );
310
+
304
311
/* Add lock to list of locks held by this backend */
305
312
Assert (num_held_lwlocks < MAX_SIMUL_LWLOCKS );
306
313
held_lwlocks [num_held_lwlocks ++ ]= lockid ;
314
+
315
+ /*
316
+ * Fix the process wait semaphore's count for any absorbed wakeups.
317
+ */
318
+ while (extraWaits -- > 0 )
319
+ IpcSemaphoreUnlock (proc -> sem .semId ,proc -> sem .semNum );
307
320
}
308
321
309
322
/*
@@ -344,12 +357,7 @@ LWLockConditionalAcquire(LWLockId lockid, LWLockMode mode)
344
357
}
345
358
else
346
359
{
347
- /*
348
- * If there is someone waiting (presumably for exclusive access),
349
- * queue up behind him even though I could get the lock. This
350
- * prevents a stream of read locks from starving a writer.
351
- */
352
- if (lock -> exclusive == 0 && lock -> head == NULL )
360
+ if (lock -> exclusive == 0 )
353
361
{
354
362
lock -> shared ++ ;
355
363
mustwait = false;
@@ -427,20 +435,17 @@ LWLockRelease(LWLockId lockid)
427
435
if (lock -> exclusive == 0 && lock -> shared == 0 )
428
436
{
429
437
/*
430
- * Remove the to-be-awakened PROCs from the queue, and update
431
- * the lock state to show them as holding the lock.
438
+ * Remove the to-be-awakened PROCs from the queue. If the
439
+ * front waiter wants exclusive lock, awaken him only.
440
+ * Otherwise awaken as many waiters as want shared access.
432
441
*/
433
442
proc = head ;
434
- if (proc -> lwExclusive )
435
- lock -> exclusive ++ ;
436
- else
443
+ if (!proc -> lwExclusive )
437
444
{
438
- lock -> shared ++ ;
439
445
while (proc -> lwWaitLink != NULL &&
440
446
!proc -> lwWaitLink -> lwExclusive )
441
447
{
442
448
proc = proc -> lwWaitLink ;
443
- lock -> shared ++ ;
444
449
}
445
450
}
446
451
/* proc is now the last PROC to be released */