]> granicus.if.org Git - postgresql/blob - src/backend/storage/lmgr/proc.c
Compile and warning cleanup
[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.9 1996/11/08 05:58: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.9 1996/11/08 05:58:59 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"     /* substitute for <signal.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     signal(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 #if 0
256     MyProc->pid = MyPid;
257 #endif
258     
259     /* ----------------
260      * Start keeping spin lock stats from here on.  Any botch before
261      * this initialization is forever botched
262      * ----------------
263      */
264     memset(MyProc->sLocks, 0, MAX_SPINS*sizeof(*MyProc->sLocks));
265     
266     /* -------------------------
267      * Install ourselves in the binding table.  The name to
268      * use is determined by the OS-assigned process id.  That
269      * allows the cleanup process to find us after any untimely
270      * exit.
271      * -------------------------
272      */
273     pid = getpid();
274     location = MAKE_OFFSET(MyProc);
275     if ((! ShmemPIDLookup(pid,&location)) || (location != MAKE_OFFSET(MyProc)))
276         {
277             elog(FATAL,"InitProc: ShmemPID table broken");
278         }
279     
280     MyProc->errType = NO_ERROR;
281     SHMQueueElemInit(&(MyProc->links));
282     
283     on_exitpg(ProcKill, (caddr_t)pid);
284     
285     ProcInitialized = TRUE;
286 }
287
288 /*
289  * ProcReleaseLocks() -- release all locks associated with this process
290  *
291  */
292 void
293 ProcReleaseLocks()
294 {
295     if (!MyProc)
296         return;
297     LockReleaseAll(1,&MyProc->lockQueue);
298 }
299
300 /*
301  * ProcRemove -
302  *    used by the postmaster to clean up the global tables. This also frees
303  *    up the semaphore used for the lmgr of the process. (We have to do
304  *    this is the postmaster instead of doing a IpcSemaphoreKill on exiting
305  *    the process because the semaphore set is shared among backends and
306  *    we don't want to remove other's semaphores on exit.)
307  */
308 bool
309 ProcRemove(int pid)
310 {
311     SHMEM_OFFSET  location;
312     PROC *proc;
313     
314     location = INVALID_OFFSET;
315     
316     location = ShmemPIDDestroy(pid);
317     if (location == INVALID_OFFSET)
318         return(FALSE);
319     proc = (PROC *) MAKE_PTR(location);
320
321     SpinAcquire(ProcStructLock);
322     
323     ProcFreeSem(proc->sem.semKey, proc->sem.semNum);
324
325     proc->links.next =  ProcGlobal->freeProcs;
326     ProcGlobal->freeProcs = MAKE_OFFSET(proc);
327     
328     SpinRelease(ProcStructLock);
329
330     return(TRUE);
331 }
332
333 /*
334  * ProcKill() -- Destroy the per-proc data structure for
335  *      this process. Release any of its held spin locks.
336  */
337 static void
338 ProcKill(int exitStatus, int pid)
339 {
340     PROC                *proc;
341     SHMEM_OFFSET        location;
342     
343     /* -------------------- 
344      * If this is a FATAL exit the postmaster will have to kill all the
345      * existing backends and reinitialize shared memory.  So all we don't 
346      * need to do anything here.
347      * --------------------
348      */
349     if (exitStatus != 0)
350         return;
351     
352     if (! pid)
353         {
354             pid = getpid();
355         }
356     
357     ShmemPIDLookup(pid,&location);
358     if (location == INVALID_OFFSET)
359         return;
360     
361     proc = (PROC *) MAKE_PTR(location);
362     
363     if (proc != MyProc) {
364         Assert( pid != getpid() );
365     } else
366         MyProc = NULL;
367     
368     /* ---------------
369      * Assume one lock table.
370      * ---------------
371      */
372     ProcReleaseSpins(proc);
373     LockReleaseAll(1,&proc->lockQueue);
374     
375 #ifdef USER_LOCKS
376     LockReleaseAll(0,&proc->lockQueue);
377 #endif
378
379     /* ----------------
380      * get off the wait queue
381      * ----------------
382      */
383     LockLockTable();
384     if (proc->links.next != INVALID_OFFSET) {
385         Assert(proc->waitLock->waitProcs.size > 0);
386         SHMQueueDelete(&(proc->links));
387         --proc->waitLock->waitProcs.size;
388     }
389     SHMQueueElemInit(&(proc->links));
390     UnlockLockTable();
391     
392     return;
393 }
394
395 /*
396  * ProcQueue package: routines for putting processes to sleep
397  *      and  waking them up
398  */
399
400 /*
401  * ProcQueueAlloc -- alloc/attach to a shared memory process queue
402  *
403  * Returns: a pointer to the queue or NULL
404  * Side Effects: Initializes the queue if we allocated one
405  */
406 PROC_QUEUE *
407 ProcQueueAlloc(char *name)
408 {
409     bool        found;
410     PROC_QUEUE *queue = (PROC_QUEUE *)
411         ShmemInitStruct(name,(unsigned)sizeof(PROC_QUEUE),&found);
412     
413     if (! queue)
414         {
415             return(NULL);
416         }
417     if (! found)
418         {
419             ProcQueueInit(queue);
420         }
421     return(queue);
422 }
423
424 /*
425  * ProcQueueInit -- initialize a shared memory process queue
426  */
427 void
428 ProcQueueInit(PROC_QUEUE *queue)
429 {
430     SHMQueueInit(&(queue->links));
431     queue->size = 0;
432 }
433
434
435
436 /*
437  * ProcSleep -- put a process to sleep
438  *
439  * P() on the semaphore should put us to sleep.  The process
440  * semaphore is cleared by default, so the first time we try
441  * to acquire it, we sleep.
442  *
443  * ASSUME: that no one will fiddle with the queue until after
444  *      we release the spin lock.
445  *
446  * NOTES: The process queue is now a priority queue for locking.
447  */
448 int
449 ProcSleep(PROC_QUEUE *queue,
450           SPINLOCK spinlock,
451           int token,
452           int prio,
453           LOCK *lock)
454 {
455     int         i;
456     PROC        *proc;
457 #ifndef WIN32 /* figure this out later */
458     struct itimerval timeval, dummy;
459 #endif /* WIN32 */
460     
461     proc = (PROC *) MAKE_PTR(queue->links.prev);
462     for (i=0;i<queue->size;i++)
463         {
464             if (proc->prio < prio)
465                 proc = (PROC *) MAKE_PTR(proc->links.prev);
466             else
467                 break;
468         }
469     
470     MyProc->token = token;
471     MyProc->waitLock = lock;
472     
473     /* -------------------
474      * currently, we only need this for the ProcWakeup routines
475      * -------------------
476      */
477     TransactionIdStore((TransactionId) GetCurrentTransactionId(), &MyProc->xid);
478     
479     /* -------------------
480      * assume that these two operations are atomic (because
481      * of the spinlock).
482      * -------------------
483      */
484     SHMQueueInsertTL(&(proc->links),&(MyProc->links));
485     queue->size++;
486     
487     SpinRelease(spinlock);
488     
489     /* --------------
490      * Postgres does not have any deadlock detection code and for this 
491      * reason we must set a timer to wake up the process in the event of
492      * a deadlock.  For now the timer is set for 1 minute and we assume that
493      * any process which sleeps for this amount of time is deadlocked and will 
494      * receive a SIGALRM signal.  The handler should release the processes
495      * semaphore and abort the current transaction.
496      *
497      * Need to zero out struct to set the interval and the micro seconds fields
498      * to 0.
499      * --------------
500      */
501 #ifndef WIN32
502     memset(&timeval, 0, sizeof(struct itimerval));
503     timeval.it_value.tv_sec = DEADLOCK_TIMEOUT;
504     
505     if (setitimer(ITIMER_REAL, &timeval, &dummy))
506         elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
507 #endif /* WIN32 */
508     
509     /* --------------
510      * if someone wakes us between SpinRelease and IpcSemaphoreLock,
511      * IpcSemaphoreLock will not block.  The wakeup is "saved" by
512      * the semaphore implementation.
513      * --------------
514      */
515     IpcSemaphoreLock(MyProc->sem.semId, MyProc->sem.semNum, IpcExclusiveLock);
516     
517     /* ---------------
518      * We were awoken before a timeout - now disable the timer
519      * ---------------
520      */
521 #ifndef WIN32
522     timeval.it_value.tv_sec = 0;
523     
524     
525     if (setitimer(ITIMER_REAL, &timeval, &dummy))
526         elog(FATAL, "ProcSleep: Unable to diable timer for process wakeup");
527 #endif /* WIN32 */
528     
529     /* ----------------
530      * We were assumed to be in a critical section when we went
531      * to sleep.
532      * ----------------
533      */
534     SpinAcquire(spinlock);
535     
536     return(MyProc->errType);
537 }
538
539
540 /*
541  * ProcWakeup -- wake up a process by releasing its private semaphore.
542  *
543  *   remove the process from the wait queue and set its links invalid.
544  *   RETURN: the next process in the wait queue.
545  */
546 PROC *
547 ProcWakeup(PROC *proc, int errType)
548 {
549     PROC *retProc;
550     /* assume that spinlock has been acquired */
551     
552     if (proc->links.prev == INVALID_OFFSET ||
553         proc->links.next == INVALID_OFFSET)
554         return((PROC *) NULL);
555     
556     retProc = (PROC *) MAKE_PTR(proc->links.prev);
557     
558     /* you have to update waitLock->waitProcs.size yourself */
559     SHMQueueDelete(&(proc->links));
560     SHMQueueElemInit(&(proc->links));
561     
562     proc->errType = errType;
563     
564     IpcSemaphoreUnlock(proc->sem.semId, proc->sem.semNum, IpcExclusiveLock);
565     
566     return retProc;
567 }
568
569
570 /*
571  * ProcGetId --
572  */
573 int
574 ProcGetId()
575 {
576     return( MyProc->procId );
577 }
578
579 /*
580  * ProcLockWakeup -- routine for waking up processes when a lock is
581  *      released.
582  */
583 int
584 ProcLockWakeup(PROC_QUEUE *queue, char *ltable, char *lock)
585 {
586     PROC        *proc;
587     int count;
588     
589     if (! queue->size)
590         return(STATUS_NOT_FOUND);
591     
592     proc = (PROC *) MAKE_PTR(queue->links.prev);
593     count = 0;
594     while ((LockResolveConflicts ((LOCKTAB *) ltable,
595                                   (LOCK *) lock,
596                                   proc->token,
597                                   proc->xid) == STATUS_OK))
598         {
599             /* there was a waiting process, grant it the lock before waking it
600              * up.  This will prevent another process from seizing the lock
601              * between the time we release the lock master (spinlock) and
602              * the time that the awoken process begins executing again.
603              */
604             GrantLock((LOCK *) lock, proc->token);
605             queue->size--;
606             
607             /*
608              * ProcWakeup removes proc from the lock waiting process queue and
609              * returns the next proc in chain.  If a writer just dropped
610              * its lock and there are several waiting readers, wake them all up.
611              */
612             proc = ProcWakeup(proc, NO_ERROR);
613             
614             count++;
615             if (!proc || queue->size == 0)
616                 break;
617         }
618     
619     if (count)
620         return(STATUS_OK);
621     else
622         /* Something is still blocking us.  May have deadlocked. */
623         return(STATUS_NOT_FOUND);
624 }
625
626 void
627 ProcAddLock(SHM_QUEUE *elem)
628 {
629     SHMQueueInsertTL(&MyProc->lockQueue,elem);
630 }
631
632 /* --------------------
633  * We only get to this routine if we got SIGALRM after DEADLOCK_TIMEOUT
634  * while waiting for a lock to be released by some other process.  After
635  * the one minute deadline we assume we have a deadlock and must abort
636  * this transaction.  We must also indicate that I'm no longer waiting
637  * on a lock so that other processes don't try to wake me up and screw 
638  * up my semaphore.
639  * --------------------
640  */
641 void
642 HandleDeadLock(int sig)
643 {
644     LOCK *lock;
645     int size;
646     
647     LockLockTable();
648     
649     /* ---------------------
650      * Check to see if we've been awoken by anyone in the interim.
651      *
652      * If we have we can return and resume our transaction -- happy day.
653      * Before we are awoken the process releasing the lock grants it to
654      * us so we know that we don't have to wait anymore.
655      * 
656      * Damn these names are LONG! -mer
657      * ---------------------
658      */
659     if (IpcSemaphoreGetCount(MyProc->sem.semId, MyProc->sem.semNum) == 
660         IpcSemaphoreDefaultStartValue) {
661         UnlockLockTable();
662         return;
663     }
664     
665     /*
666      * you would think this would be unnecessary, but...
667      *
668      * this also means we've been removed already.  in some ports
669      * (e.g., sparc and aix) the semop(2) implementation is such that
670      * we can actually end up in this handler after someone has removed
671      * us from the queue and bopped the semaphore *but the test above
672      * fails to detect the semaphore update* (presumably something weird
673      * having to do with the order in which the semaphore wakeup signal
674      * and SIGALRM get handled).
675      */
676     if (MyProc->links.prev == INVALID_OFFSET ||
677         MyProc->links.next == INVALID_OFFSET) {
678         UnlockLockTable();
679         return;
680     }
681     
682     lock = MyProc->waitLock;
683     size = lock->waitProcs.size; /* so we can look at this in the core */
684     
685     /* ------------------------
686      * Get this process off the lock's wait queue
687      * ------------------------
688      */
689     Assert(lock->waitProcs.size > 0);
690     --lock->waitProcs.size;
691     SHMQueueDelete(&(MyProc->links));
692     SHMQueueElemInit(&(MyProc->links));
693     
694     /* ------------------
695      * Unlock my semaphore so that the count is right for next time.
696      * I was awoken by a signal, not by someone unlocking my semaphore.
697      * ------------------
698      */
699     IpcSemaphoreUnlock(MyProc->sem.semId, MyProc->sem.semNum, IpcExclusiveLock);
700     
701     /* -------------
702      * Set MyProc->errType to STATUS_ERROR so that we abort after
703      * returning from this handler.
704      * -------------
705      */
706     MyProc->errType = STATUS_ERROR;
707     
708     /*
709      * if this doesn't follow the IpcSemaphoreUnlock then we get lock
710      * table corruption ("LockReplace: xid table corrupted") due to
711      * race conditions.  i don't claim to understand this...
712      */
713     UnlockLockTable();
714     
715     elog(NOTICE, "Timeout -- possible deadlock");
716     return;
717 }
718
719 void
720 ProcReleaseSpins(PROC *proc)
721 {
722     int i;
723     
724     if (!proc)
725         proc = MyProc;
726     
727     if (!proc)
728         return;
729     for (i=0; i < (int)MAX_SPINS; i++)
730         {
731             if (proc->sLocks[i])
732                 {
733                     Assert(proc->sLocks[i] == 1);
734                     SpinRelease(i);
735                 }
736         }
737 }
738
739 /*****************************************************************************
740  * 
741  *****************************************************************************/
742
743 /*
744  * ProcGetNewSemKeyAndNum -
745  *    scan the free semaphore bitmap and allocate a single semaphore from
746  *    a semaphore set. (If the semaphore set doesn't exist yet,
747  *    IpcSemaphoreCreate will create it. Otherwise, we use the existing
748  *    semaphore set.)
749  */
750 static void
751 ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum)
752 {
753     int i;
754     int32 *freeSemMap = ProcGlobal->freeSemMap;
755     unsigned int fullmask;
756
757     /*
758      * we hold ProcStructLock when entering this routine. We scan through
759      * the bitmap to look for a free semaphore.
760      */
761     fullmask = ~0 >> (32 - PROC_NSEMS_PER_SET);
762     for(i=0; i < MAX_PROC_SEMS/PROC_NSEMS_PER_SET; i++) {
763         int mask = 1;
764         int j;
765
766         if (freeSemMap[i] == fullmask)
767             continue; /* none free for this set */
768
769         for(j = 0; j < PROC_NSEMS_PER_SET; j++) {
770             if ((freeSemMap[i] & mask) == 0) {
771                 /*
772                  * a free semaphore found. Mark it as allocated.
773                  */
774                 freeSemMap[i] |= mask;
775
776                 *key = ProcGlobal->currKey + i;
777                 *semNum = j;
778                 return;
779             }
780             mask <<= 1;
781         }
782     }
783
784     /* if we reach here, all the semaphores are in use. */
785     elog(WARN, "InitProc: cannot allocate a free semaphore");
786 }
787
788 /*
789  * ProcFreeSem -
790  *    free up our semaphore in the semaphore set. If we're the last one
791  *    in the set, also remove the semaphore set.
792  */
793 static void
794 ProcFreeSem(IpcSemaphoreKey semKey, int semNum)
795 {
796     int mask;
797     int i;
798     int32 *freeSemMap = ProcGlobal->freeSemMap;
799
800     i = semKey - ProcGlobal->currKey;
801     mask = ~(1 << semNum);
802     freeSemMap[i] &= mask;
803
804     if (freeSemMap[i]==0)
805         IpcSemaphoreKill(semKey);
806 }
807
808 /*
809  * ProcFreeAllSemaphores -
810  *    on exiting the postmaster, we free up all the semaphores allocated
811  *    to the lmgrs of the backends.
812  */
813 void
814 ProcFreeAllSemaphores()
815 {
816     int i;
817     int32 *freeSemMap = ProcGlobal->freeSemMap;
818
819     for(i=0; i < MAX_PROC_SEMS/PROC_NSEMS_PER_SET; i++) {
820         if (freeSemMap[i]!=0)
821             IpcSemaphoreKill(ProcGlobal->currKey + i);
822     }
823 }