1515 * Portions Copyright (c) 1994, Regents of the University of California
1616 *
1717 * 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 $
1919 *
2020 *-------------------------------------------------------------------------
2121 */
195195LWLockAcquire (LWLockId lockid ,LWLockMode mode )
196196{
197197volatile LWLock * lock = LWLockArray + lockid ;
198- bool mustwait ;
198+ PROC * proc = MyProc ;
199+ int extraWaits = 0 ;
199200
200201PRINT_LWDEBUG ("LWLockAcquire" ,lockid ,lock );
201202
@@ -206,43 +207,57 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
206207 */
207208HOLD_INTERRUPTS ();
208209
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 (;;)
214227{
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 )
216235{
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;
219243}
220244else
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 )
231245{
232- lock -> shared ++ ;
233- mustwait = false;
246+ if (lock -> exclusive == 0 )
247+ {
248+ lock -> shared ++ ;
249+ mustwait = false;
250+ }
251+ else
252+ mustwait = true;
234253}
235- else
236- mustwait = true;
237- }
238254
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 */
244257
245258/*
259+ * Add myself to wait queue.
260+ *
246261 * If we don't have a PROC structure, there's no way to wait. This
247262 * should never occur, since MyProc should only be null during
248263 * shared memory initialization.
@@ -267,9 +282,9 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
267282 *
268283 * Since we share the process wait semaphore with the regular lock
269284 * 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 ,
273288 * re-increment the sema by the number of additional signals
274289 * received, so that the lock manager or signal manager will see
275290 * the received signal when it next waits.
@@ -287,23 +302,21 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
287302
288303LOG_LWDEBUG ("LWLockAcquire" ,lockid ,"awakened" );
289304
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. */
302306}
303307
308+ /* We are done updating shared state of the lock itself. */
309+ SpinLockRelease_NoHoldoff (& lock -> mutex );
310+
304311/* Add lock to list of locks held by this backend */
305312Assert (num_held_lwlocks < MAX_SIMUL_LWLOCKS );
306313held_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 );
307320}
308321
309322/*
@@ -344,12 +357,7 @@ LWLockConditionalAcquire(LWLockId lockid, LWLockMode mode)
344357}
345358else
346359{
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 )
353361{
354362lock -> shared ++ ;
355363mustwait = false;
@@ -427,20 +435,17 @@ LWLockRelease(LWLockId lockid)
427435if (lock -> exclusive == 0 && lock -> shared == 0 )
428436{
429437/*
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.
432441 */
433442proc = head ;
434- if (proc -> lwExclusive )
435- lock -> exclusive ++ ;
436- else
443+ if (!proc -> lwExclusive )
437444{
438- lock -> shared ++ ;
439445while (proc -> lwWaitLink != NULL &&
440446 !proc -> lwWaitLink -> lwExclusive )
441447{
442448proc = proc -> lwWaitLink ;
443- lock -> shared ++ ;
444449}
445450}
446451/* proc is now the last PROC to be released */