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