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