]> granicus.if.org Git - postgresql/blob - src/backend/storage/lmgr/proc.c
Remove duplocate lock_timeout value.
[postgresql] / src / backend / storage / lmgr / proc.c
1 /*-------------------------------------------------------------------------
2  *
3  * proc.c--
4  *        routines to manage per-process shared memory data structure
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.23 1997/10/30 17:23:59 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 /*
15  *      Each postgres backend gets one of these.  We'll use it to
16  *      clean up after the process should the process suddenly die.
17  *
18  *
19  * Interface (a):
20  *              ProcSleep(), ProcWakeup(), ProcWakeupNext(),
21  *              ProcQueueAlloc() -- create a shm queue for sleeping processes
22  *              ProcQueueInit() -- create a queue without allocing memory
23  *
24  * Locking and waiting for buffers can cause the backend to be
25  * put to sleep.  Whoever releases the lock, etc. wakes the
26  * process up again (and gives it an error code so it knows
27  * whether it was awoken on an error condition).
28  *
29  * Interface (b):
30  *
31  * ProcReleaseLocks -- frees the locks associated with this process,
32  * ProcKill -- destroys the shared memory state (and locks)
33  *              associated with the process.
34  *
35  * 5/15/91 -- removed the buffer pool based lock chain in favor
36  *              of a shared memory lock chain.  The write-protection is
37  *              more expensive if the lock chain is in the buffer pool.
38  *              The only reason I kept the lock chain in the buffer pool
39  *              in the first place was to allow the lock table to grow larger
40  *              than available shared memory and that isn't going to work
41  *              without a lot of unimplemented support anyway.
42  *
43  * 4/7/95 -- instead of allocating a set of 1 semaphore per process, we
44  *              allocate a semaphore from a set of PROC_NSEMS_PER_SET semaphores
45  *              shared among backends (we keep a few sets of semaphores around).
46  *              This is so that we can support more backends. (system-wide semaphore
47  *              sets run out pretty fast.)                                -ay 4/95
48  *
49  * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.23 1997/10/30 17:23:59 momjian Exp $
50  */
51 #include <sys/time.h>
52 #include <unistd.h>
53 #include <string.h>
54 #include <signal.h>
55 #include <sys/types.h>
56
57 #if defined(sparc_solaris)
58 #include <sys/ipc.h>
59 #include <sys/sem.h>
60 #endif
61
62 #include "postgres.h"
63 #include "miscadmin.h"
64 #include "libpq/pqsignal.h"
65
66 #include "access/xact.h"
67 #include "utils/hsearch.h"
68
69 #include "storage/ipc.h"
70 /* In Ultrix, sem.h must be included after ipc.h */
71 #include <sys/sem.h>
72 #include "storage/buf.h"
73 #include "storage/lock.h"
74 #include "storage/lmgr.h"
75 #include "storage/shmem.h"
76 #include "storage/spin.h"
77 #include "storage/proc.h"
78
79 static void HandleDeadLock(int sig);
80 static PROC *ProcWakeup(PROC *proc, int errType);
81
82 /* --------------------
83  * Spin lock for manipulating the shared process data structure:
84  * ProcGlobal.... Adding an extra spin lock seemed like the smallest
85  * hack to get around reading and updating this structure in shared
86  * memory. -mer 17 July 1991
87  * --------------------
88  */
89 SPINLOCK        ProcStructLock;
90
91 /*
92  * For cleanup routines.  Don't cleanup if the initialization
93  * has not happened.
94  */
95 static bool ProcInitialized = FALSE;
96
97 static PROC_HDR *ProcGlobal = NULL;
98
99 PROC       *MyProc = NULL;
100
101 static void ProcKill(int exitStatus, int pid);
102 static void ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum);
103 static void ProcFreeSem(IpcSemaphoreKey semKey, int semNum);
104
105 /*
106  * InitProcGlobal -
107  *        initializes the global process table. We put it here so that
108  *        the postmaster can do this initialization. (ProcFreeAllSem needs
109  *        to read this table on exiting the postmaster. If we have the first
110  *        backend do this, starting up and killing the postmaster without
111  *        starting any backends will be a problem.)
112  */
113 void
114 InitProcGlobal(IPCKey key)
115 {
116         bool            found = false;
117
118         /* attach to the free list */
119         ProcGlobal = (PROC_HDR *)
120                 ShmemInitStruct("Proc Header", (unsigned) sizeof(PROC_HDR), &found);
121
122         /* --------------------
123          * We're the first - initialize.
124          * --------------------
125          */
126         if (!found)
127         {
128                 int                     i;
129
130                 ProcGlobal->numProcs = 0;
131                 ProcGlobal->freeProcs = INVALID_OFFSET;
132                 ProcGlobal->currKey = IPCGetProcessSemaphoreInitKey(key);
133                 for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++)
134                         ProcGlobal->freeSemMap[i] = 0;
135         }
136 }
137
138 /* ------------------------
139  * InitProc -- create a per-process data structure for this process
140  * used by the lock manager on semaphore queues.
141  * ------------------------
142  */
143 void
144 InitProcess(IPCKey key)
145 {
146         bool            found = false;
147         int                     pid;
148         int                     semstat;
149         unsigned long location,
150                                 myOffset;
151
152         /* ------------------
153          * Routine called if deadlock timer goes off. See ProcSleep()
154          * ------------------
155          */
156         pqsignal(SIGALRM, HandleDeadLock);
157
158         SpinAcquire(ProcStructLock);
159
160         /* attach to the free list */
161         ProcGlobal = (PROC_HDR *)
162                 ShmemInitStruct("Proc Header", (unsigned) sizeof(PROC_HDR), &found);
163         if (!found)
164         {
165                 /* this should not happen. InitProcGlobal() is called before this. */
166                 elog(WARN, "InitProcess: Proc Header uninitialized");
167         }
168
169         if (MyProc != NULL)
170         {
171                 SpinRelease(ProcStructLock);
172                 elog(WARN, "ProcInit: you already exist");
173                 return;
174         }
175
176         /* try to get a proc from the free list first */
177
178         myOffset = ProcGlobal->freeProcs;
179
180         if (myOffset != INVALID_OFFSET)
181         {
182                 MyProc = (PROC *) MAKE_PTR(myOffset);
183                 ProcGlobal->freeProcs = MyProc->links.next;
184         }
185         else
186         {
187
188                 /*
189                  * have to allocate one.  We can't use the normal binding table
190                  * mechanism because the proc structure is stored by PID instead
191                  * of by a global name (need to look it up by PID when we cleanup
192                  * dead processes).
193                  */
194
195                 MyProc = (PROC *) ShmemAlloc((unsigned) sizeof(PROC));
196                 if (!MyProc)
197                 {
198                         SpinRelease(ProcStructLock);
199                         elog(FATAL, "cannot create new proc: out of memory");
200                 }
201
202                 /* this cannot be initialized until after the buffer pool */
203                 SHMQueueInit(&(MyProc->lockQueue));
204                 MyProc->procId = ProcGlobal->numProcs;
205                 ProcGlobal->numProcs++;
206         }
207
208         /*
209          * zero out the spin lock counts and set the sLocks field for
210          * ProcStructLock to 1 as we have acquired this spinlock above but
211          * didn't record it since we didn't have MyProc until now.
212          */
213         MemSet(MyProc->sLocks, 0, sizeof(MyProc->sLocks));
214         MyProc->sLocks[ProcStructLock] = 1;
215
216
217         if (IsUnderPostmaster)
218         {
219                 IPCKey          semKey;
220                 int                     semNum;
221                 int                     semId;
222                 union semun semun;
223
224                 ProcGetNewSemKeyAndNum(&semKey, &semNum);
225
226                 semId = IpcSemaphoreCreate(semKey,
227                                                                    PROC_NSEMS_PER_SET,
228                                                                    IPCProtection,
229                                                                    IpcSemaphoreDefaultStartValue,
230                                                                    0,
231                                                                    &semstat);
232
233                 /*
234                  * we might be reusing a semaphore that belongs to a dead backend.
235                  * So be careful and reinitialize its value here.
236                  */
237                 semun.val = IpcSemaphoreDefaultStartValue;
238                 semctl(semId, semNum, SETVAL, semun);
239
240                 IpcSemaphoreLock(semId, semNum, IpcExclusiveLock);
241                 MyProc->sem.semId = semId;
242                 MyProc->sem.semNum = semNum;
243                 MyProc->sem.semKey = semKey;
244         }
245         else
246         {
247                 MyProc->sem.semId = -1;
248         }
249
250         /* ----------------------
251          * Release the lock.
252          * ----------------------
253          */
254         SpinRelease(ProcStructLock);
255
256         MyProc->pid = 0;
257         MyProc->xid = InvalidTransactionId;
258 #if 0
259         MyProc->pid = MyPid;
260 #endif
261
262         /* ----------------
263          * Start keeping spin lock stats from here on.  Any botch before
264          * this initialization is forever botched
265          * ----------------
266          */
267         MemSet(MyProc->sLocks, 0, MAX_SPINS * sizeof(*MyProc->sLocks));
268
269         /* -------------------------
270          * Install ourselves in the binding table.      The name to
271          * use is determined by the OS-assigned process id.  That
272          * allows the cleanup process to find us after any untimely
273          * exit.
274          * -------------------------
275          */
276         pid = getpid();
277         location = MAKE_OFFSET(MyProc);
278         if ((!ShmemPIDLookup(pid, &location)) || (location != MAKE_OFFSET(MyProc)))
279         {
280                 elog(FATAL, "InitProc: ShmemPID table broken");
281         }
282
283         MyProc->errType = NO_ERROR;
284         SHMQueueElemInit(&(MyProc->links));
285
286         on_exitpg(ProcKill, (caddr_t) pid);
287
288         ProcInitialized = TRUE;
289 }
290
291 /*
292  * ProcReleaseLocks() -- release all locks associated with this process
293  *
294  */
295 void
296 ProcReleaseLocks()
297 {
298         if (!MyProc)
299                 return;
300         LockReleaseAll(1, &MyProc->lockQueue);
301 }
302
303 /*
304  * ProcRemove -
305  *        used by the postmaster to clean up the global tables. This also frees
306  *        up the semaphore used for the lmgr of the process. (We have to do
307  *        this is the postmaster instead of doing a IpcSemaphoreKill on exiting
308  *        the process because the semaphore set is shared among backends and
309  *        we don't want to remove other's semaphores on exit.)
310  */
311 bool
312 ProcRemove(int pid)
313 {
314         SHMEM_OFFSET location;
315         PROC       *proc;
316
317         location = INVALID_OFFSET;
318
319         location = ShmemPIDDestroy(pid);
320         if (location == INVALID_OFFSET)
321                 return (FALSE);
322         proc = (PROC *) MAKE_PTR(location);
323
324         SpinAcquire(ProcStructLock);
325
326         ProcFreeSem(proc->sem.semKey, proc->sem.semNum);
327
328         proc->links.next = ProcGlobal->freeProcs;
329         ProcGlobal->freeProcs = MAKE_OFFSET(proc);
330
331         SpinRelease(ProcStructLock);
332
333         return (TRUE);
334 }
335
336 /*
337  * ProcKill() -- Destroy the per-proc data structure for
338  *              this process. Release any of its held spin locks.
339  */
340 static void
341 ProcKill(int exitStatus, int pid)
342 {
343         PROC       *proc;
344         SHMEM_OFFSET location;
345
346         /* --------------------
347          * If this is a FATAL exit the postmaster will have to kill all the
348          * existing backends and reinitialize shared memory.  So all we don't
349          * need to do anything here.
350          * --------------------
351          */
352         if (exitStatus != 0)
353                 return;
354
355         if (!pid)
356         {
357                 pid = getpid();
358         }
359
360         ShmemPIDLookup(pid, &location);
361         if (location == INVALID_OFFSET)
362                 return;
363
364         proc = (PROC *) MAKE_PTR(location);
365
366         if (proc != MyProc)
367         {
368                 Assert(pid != getpid());
369         }
370         else
371                 MyProc = NULL;
372
373         /* ---------------
374          * Assume one lock table.
375          * ---------------
376          */
377         ProcReleaseSpins(proc);
378         LockReleaseAll(1, &proc->lockQueue);
379
380 #ifdef USER_LOCKS
381         LockReleaseAll(0, &proc->lockQueue);
382 #endif
383
384         /* ----------------
385          * get off the wait queue
386          * ----------------
387          */
388         LockLockTable();
389         if (proc->links.next != INVALID_OFFSET)
390         {
391                 Assert(proc->waitLock->waitProcs.size > 0);
392                 SHMQueueDelete(&(proc->links));
393                 --proc->waitLock->waitProcs.size;
394         }
395         SHMQueueElemInit(&(proc->links));
396         UnlockLockTable();
397
398         return;
399 }
400
401 /*
402  * ProcQueue package: routines for putting processes to sleep
403  *              and  waking them up
404  */
405
406 /*
407  * ProcQueueAlloc -- alloc/attach to a shared memory process queue
408  *
409  * Returns: a pointer to the queue or NULL
410  * Side Effects: Initializes the queue if we allocated one
411  */
412 #ifdef NOT_USED
413 PROC_QUEUE *
414 ProcQueueAlloc(char *name)
415 {
416         bool            found;
417         PROC_QUEUE *queue = (PROC_QUEUE *)
418         ShmemInitStruct(name, (unsigned) sizeof(PROC_QUEUE), &found);
419
420         if (!queue)
421         {
422                 return (NULL);
423         }
424         if (!found)
425         {
426                 ProcQueueInit(queue);
427         }
428         return (queue);
429 }
430
431 #endif
432
433 /*
434  * ProcQueueInit -- initialize a shared memory process queue
435  */
436 void
437 ProcQueueInit(PROC_QUEUE *queue)
438 {
439         SHMQueueInit(&(queue->links));
440         queue->size = 0;
441 }
442
443
444
445 /*
446  * ProcSleep -- put a process to sleep
447  *
448  * P() on the semaphore should put us to sleep.  The process
449  * semaphore is cleared by default, so the first time we try
450  * to acquire it, we sleep.
451  *
452  * ASSUME: that no one will fiddle with the queue until after
453  *              we release the spin lock.
454  *
455  * NOTES: The process queue is now a priority queue for locking.
456  */
457 int
458 ProcSleep(PROC_QUEUE *queue,
459                   SPINLOCK spinlock,
460                   int token,
461                   int prio,
462                   LOCK *lock)
463 {
464         int                     i;
465         PROC       *proc;
466         struct itimerval timeval,
467                                 dummy;
468
469         proc = (PROC *) MAKE_PTR(queue->links.prev);
470         for (i = 0; i < queue->size; i++)
471         {
472                 if (proc->prio < prio)
473                         proc = (PROC *) MAKE_PTR(proc->links.prev);
474                 else
475                         break;
476         }
477
478         MyProc->prio = prio;
479         MyProc->token = token;
480         MyProc->waitLock = lock;
481
482         /* -------------------
483          * currently, we only need this for the ProcWakeup routines
484          * -------------------
485          */
486         TransactionIdStore((TransactionId) GetCurrentTransactionId(), &MyProc->xid);
487
488         /* -------------------
489          * assume that these two operations are atomic (because
490          * of the spinlock).
491          * -------------------
492          */
493         SHMQueueInsertTL(&(proc->links), &(MyProc->links));
494         queue->size++;
495
496         SpinRelease(spinlock);
497
498         /* --------------
499          * Postgres does not have any deadlock detection code and for this
500          * reason we must set a timer to wake up the process in the event of
501          * a deadlock.  For now the timer is set for 1 minute and we assume that
502          * any process which sleeps for this amount of time is deadlocked and will
503          * receive a SIGALRM signal.  The handler should release the processes
504          * semaphore and abort the current transaction.
505          *
506          * Need to zero out struct to set the interval and the micro seconds fields
507          * to 0.
508          * --------------
509          */
510         MemSet(&timeval, 0, sizeof(struct itimerval));
511         timeval.it_value.tv_sec = DEADLOCK_TIMEOUT;
512
513         if (setitimer(ITIMER_REAL, &timeval, &dummy))
514                 elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
515
516         /* --------------
517          * if someone wakes us between SpinRelease and IpcSemaphoreLock,
518          * IpcSemaphoreLock will not block.  The wakeup is "saved" by
519          * the semaphore implementation.
520          * --------------
521          */
522         IpcSemaphoreLock(MyProc->sem.semId, MyProc->sem.semNum, IpcExclusiveLock);
523
524         /* ---------------
525          * We were awoken before a timeout - now disable the timer
526          * ---------------
527          */
528         timeval.it_value.tv_sec = 0;
529
530
531         if (setitimer(ITIMER_REAL, &timeval, &dummy))
532                 elog(FATAL, "ProcSleep: Unable to diable timer for process wakeup");
533
534         /* ----------------
535          * We were assumed to be in a critical section when we went
536          * to sleep.
537          * ----------------
538          */
539         SpinAcquire(spinlock);
540
541         return (MyProc->errType);
542 }
543
544
545 /*
546  * ProcWakeup -- wake up a process by releasing its private semaphore.
547  *
548  *       remove the process from the wait queue and set its links invalid.
549  *       RETURN: the next process in the wait queue.
550  */
551 static PROC *
552 ProcWakeup(PROC *proc, int errType)
553 {
554         PROC       *retProc;
555
556         /* assume that spinlock has been acquired */
557
558         if (proc->links.prev == INVALID_OFFSET ||
559                 proc->links.next == INVALID_OFFSET)
560                 return ((PROC *) NULL);
561
562         retProc = (PROC *) MAKE_PTR(proc->links.prev);
563
564         /* you have to update waitLock->waitProcs.size yourself */
565         SHMQueueDelete(&(proc->links));
566         SHMQueueElemInit(&(proc->links));
567
568         proc->errType = errType;
569
570         IpcSemaphoreUnlock(proc->sem.semId, proc->sem.semNum, IpcExclusiveLock);
571
572         return retProc;
573 }
574
575
576 /*
577  * ProcGetId --
578  */
579 #ifdef NOT_USED
580 int
581 ProcGetId()
582 {
583         return (MyProc->procId);
584 }
585
586 #endif
587
588 /*
589  * ProcLockWakeup -- routine for waking up processes when a lock is
590  *              released.
591  */
592 int
593 ProcLockWakeup(PROC_QUEUE *queue, char *ltable, char *lock)
594 {
595         PROC       *proc;
596         int                     count;
597
598         if (!queue->size)
599                 return (STATUS_NOT_FOUND);
600
601         proc = (PROC *) MAKE_PTR(queue->links.prev);
602         count = 0;
603         while ((LockResolveConflicts((LOCKTAB *) ltable,
604                                                                  (LOCK *) lock,
605                                                                  proc->token,
606                                                                  proc->xid) == STATUS_OK))
607         {
608
609                 /*
610                  * there was a waiting process, grant it the lock before waking it
611                  * up.  This will prevent another process from seizing the lock
612                  * between the time we release the lock master (spinlock) and the
613                  * time that the awoken process begins executing again.
614                  */
615                 GrantLock((LOCK *) lock, proc->token);
616                 queue->size--;
617
618                 /*
619                  * ProcWakeup removes proc from the lock waiting process queue and
620                  * returns the next proc in chain.      If a writer just dropped its
621                  * lock and there are several waiting readers, wake them all up.
622                  */
623                 proc = ProcWakeup(proc, NO_ERROR);
624
625                 count++;
626                 if (!proc || queue->size == 0)
627                         break;
628         }
629
630         if (count)
631                 return (STATUS_OK);
632         else
633                 /* Something is still blocking us.      May have deadlocked. */
634                 return (STATUS_NOT_FOUND);
635 }
636
637 void
638 ProcAddLock(SHM_QUEUE *elem)
639 {
640         SHMQueueInsertTL(&MyProc->lockQueue, elem);
641 }
642
643 /* --------------------
644  * We only get to this routine if we got SIGALRM after DEADLOCK_TIMEOUT
645  * while waiting for a lock to be released by some other process.  After
646  * the one minute deadline we assume we have a deadlock and must abort
647  * this transaction.  We must also indicate that I'm no longer waiting
648  * on a lock so that other processes don't try to wake me up and screw
649  * up my semaphore.
650  * --------------------
651  */
652 static void
653 HandleDeadLock(int sig)
654 {
655         LOCK       *lock;
656         int                     size;
657
658         LockLockTable();
659
660         /* ---------------------
661          * Check to see if we've been awoken by anyone in the interim.
662          *
663          * If we have we can return and resume our transaction -- happy day.
664          * Before we are awoken the process releasing the lock grants it to
665          * us so we know that we don't have to wait anymore.
666          *
667          * Damn these names are LONG! -mer
668          * ---------------------
669          */
670         if (IpcSemaphoreGetCount(MyProc->sem.semId, MyProc->sem.semNum) ==
671                 IpcSemaphoreDefaultStartValue)
672         {
673                 UnlockLockTable();
674                 return;
675         }
676
677         /*
678          * you would think this would be unnecessary, but...
679          *
680          * this also means we've been removed already.  in some ports (e.g.,
681          * sparc and aix) the semop(2) implementation is such that we can
682          * actually end up in this handler after someone has removed us from
683          * the queue and bopped the semaphore *but the test above fails to
684          * detect the semaphore update* (presumably something weird having to
685          * do with the order in which the semaphore wakeup signal and SIGALRM
686          * get handled).
687          */
688         if (MyProc->links.prev == INVALID_OFFSET ||
689                 MyProc->links.next == INVALID_OFFSET)
690         {
691                 UnlockLockTable();
692                 return;
693         }
694
695         lock = MyProc->waitLock;
696         size = lock->waitProcs.size;/* so we can look at this in the core */
697
698 #ifdef DEADLOCK_DEBUG
699         DumpLocks();
700 #endif
701
702         /* ------------------------
703          * Get this process off the lock's wait queue
704          * ------------------------
705          */
706         Assert(lock->waitProcs.size > 0);
707         --lock->waitProcs.size;
708         SHMQueueDelete(&(MyProc->links));
709         SHMQueueElemInit(&(MyProc->links));
710
711         /* ------------------
712          * Unlock my semaphore so that the count is right for next time.
713          * I was awoken by a signal, not by someone unlocking my semaphore.
714          * ------------------
715          */
716         IpcSemaphoreUnlock(MyProc->sem.semId, MyProc->sem.semNum, IpcExclusiveLock);
717
718         /* -------------
719          * Set MyProc->errType to STATUS_ERROR so that we abort after
720          * returning from this handler.
721          * -------------
722          */
723         MyProc->errType = STATUS_ERROR;
724
725         /*
726          * if this doesn't follow the IpcSemaphoreUnlock then we get lock
727          * table corruption ("LockReplace: xid table corrupted") due to race
728          * conditions.  i don't claim to understand this...
729          */
730         UnlockLockTable();
731
732         elog(NOTICE, "Timeout -- possible deadlock");
733         return;
734 }
735
736 void
737 ProcReleaseSpins(PROC *proc)
738 {
739         int                     i;
740
741         if (!proc)
742                 proc = MyProc;
743
744         if (!proc)
745                 return;
746         for (i = 0; i < (int) MAX_SPINS; i++)
747         {
748                 if (proc->sLocks[i])
749                 {
750                         Assert(proc->sLocks[i] == 1);
751                         SpinRelease(i);
752                 }
753         }
754 }
755
756 /*****************************************************************************
757  *
758  *****************************************************************************/
759
760 /*
761  * ProcGetNewSemKeyAndNum -
762  *        scan the free semaphore bitmap and allocate a single semaphore from
763  *        a semaphore set. (If the semaphore set doesn't exist yet,
764  *        IpcSemaphoreCreate will create it. Otherwise, we use the existing
765  *        semaphore set.)
766  */
767 static void
768 ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum)
769 {
770         int                     i;
771         int32      *freeSemMap = ProcGlobal->freeSemMap;
772         unsigned int fullmask;
773
774         /*
775          * we hold ProcStructLock when entering this routine. We scan through
776          * the bitmap to look for a free semaphore.
777          */
778         fullmask = ~0 >> (32 - PROC_NSEMS_PER_SET);
779         for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++)
780         {
781                 int                     mask = 1;
782                 int                     j;
783
784                 if (freeSemMap[i] == fullmask)
785                         continue;                       /* none free for this set */
786
787                 for (j = 0; j < PROC_NSEMS_PER_SET; j++)
788                 {
789                         if ((freeSemMap[i] & mask) == 0)
790                         {
791
792                                 /*
793                                  * a free semaphore found. Mark it as allocated.
794                                  */
795                                 freeSemMap[i] |= mask;
796
797                                 *key = ProcGlobal->currKey + i;
798                                 *semNum = j;
799                                 return;
800                         }
801                         mask <<= 1;
802                 }
803         }
804
805         /* if we reach here, all the semaphores are in use. */
806         elog(WARN, "InitProc: cannot allocate a free semaphore");
807 }
808
809 /*
810  * ProcFreeSem -
811  *        free up our semaphore in the semaphore set. If we're the last one
812  *        in the set, also remove the semaphore set.
813  */
814 static void
815 ProcFreeSem(IpcSemaphoreKey semKey, int semNum)
816 {
817         int                     mask;
818         int                     i;
819         int32      *freeSemMap = ProcGlobal->freeSemMap;
820
821         i = semKey - ProcGlobal->currKey;
822         mask = ~(1 << semNum);
823         freeSemMap[i] &= mask;
824
825         if (freeSemMap[i] == 0)
826                 IpcSemaphoreKill(semKey);
827 }
828
829 /*
830  * ProcFreeAllSemaphores -
831  *        on exiting the postmaster, we free up all the semaphores allocated
832  *        to the lmgrs of the backends.
833  */
834 void
835 ProcFreeAllSemaphores()
836 {
837         int                     i;
838         int32      *freeSemMap = ProcGlobal->freeSemMap;
839
840         for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++)
841         {
842                 if (freeSemMap[i] != 0)
843                         IpcSemaphoreKill(ProcGlobal->currKey + i);
844         }
845 }