]> granicus.if.org Git - postgresql/blob - src/backend/storage/lmgr/proc.c
Postgres95 1.01 Distribution - Virgin Sources
[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.1.1.1 1996/07/09 06:21:57 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.1.1.1 1996/07/09 06:21:57 scrappy Exp $
50  */
51 #include <sys/time.h>
52 #ifndef WIN32
53 #include <unistd.h>
54 #endif /* WIN32 */
55 #include <string.h>
56 #include <sys/types.h>
57 #include "libpq/pqsignal.h"     /* substitute for <signal.h> */
58
59 #if defined(PORTNAME_bsdi)
60 /* hacka, hacka, hacka (XXX) */
61 union semun {
62         int val; /* value for SETVAL */
63         struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
64         ushort *array; /* array for GETALL & SETALL */
65 };
66 #endif
67
68 #include "access/xact.h"
69 #include "utils/hsearch.h"
70 #include "utils/elog.h"
71
72 #include "storage/buf.h"        
73 #include "storage/lock.h"
74 #include "storage/shmem.h"
75 #include "storage/spin.h"
76 #include "storage/proc.h"
77
78 /*
79  * timeout (in seconds) for resolving possible deadlock
80  */
81 #ifndef DEADLOCK_TIMEOUT
82 #define DEADLOCK_TIMEOUT        60
83 #endif
84
85 /* --------------------
86  * Spin lock for manipulating the shared process data structure:
87  * ProcGlobal.... Adding an extra spin lock seemed like the smallest
88  * hack to get around reading and updating this structure in shared
89  * memory. -mer 17 July 1991
90  * --------------------
91  */
92 SPINLOCK ProcStructLock;
93
94 /*
95  * For cleanup routines.  Don't cleanup if the initialization
96  * has not happened.
97  */
98 static bool     ProcInitialized = FALSE;
99
100 static PROC_HDR *ProcGlobal = NULL;
101
102 PROC    *MyProc = NULL;
103
104 static void ProcKill(int exitStatus, int pid);
105 static void ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum);
106 static void ProcFreeSem(IpcSemaphoreKey semKey, int semNum);
107 #if defined(PORTNAME_linux)
108 extern int HandleDeadLock(int);
109 #else
110 extern int HandleDeadLock(void);
111 #endif
112 /*
113  * InitProcGlobal -
114  *    initializes the global process table. We put it here so that
115  *    the postmaster can do this initialization. (ProcFreeAllSem needs
116  *    to read this table on exiting the postmaster. If we have the first
117  *    backend do this, starting up and killing the postmaster without
118  *    starting any backends will be a problem.)
119  */
120 void
121 InitProcGlobal(IPCKey key)
122 {
123     bool found = false;
124
125     /* attach to the free list */
126     ProcGlobal = (PROC_HDR *)
127         ShmemInitStruct("Proc Header",(unsigned)sizeof(PROC_HDR),&found);
128
129     /* --------------------
130      * We're the first - initialize.
131      * --------------------
132      */
133     if (! found)
134         {
135             int i;
136
137             ProcGlobal->numProcs = 0;
138             ProcGlobal->freeProcs = INVALID_OFFSET;
139             ProcGlobal->currKey = IPCGetProcessSemaphoreInitKey(key);
140             for (i=0; i < MAX_PROC_SEMS/PROC_NSEMS_PER_SET; i++)
141                 ProcGlobal->freeSemMap[i] = 0;
142         }
143 }
144
145 /* ------------------------
146  * InitProc -- create a per-process data structure for this process
147  * used by the lock manager on semaphore queues.
148  * ------------------------
149  */
150 void
151 InitProcess(IPCKey key)
152 {
153     bool found = false;
154     int pid;
155     int semstat;
156     unsigned long location, myOffset;
157     
158     /* ------------------
159      * Routine called if deadlock timer goes off. See ProcSleep()
160      * ------------------
161      */
162 #ifndef WIN32
163     signal(SIGALRM, HandleDeadLock);
164 #endif /* WIN32 we'll have to figure out how to handle this later */
165
166     SpinAcquire(ProcStructLock);
167     
168     /* attach to the free list */
169     ProcGlobal = (PROC_HDR *)
170         ShmemInitStruct("Proc Header",(unsigned)sizeof(PROC_HDR),&found);
171     if (!found) {
172         /* this should not happen. InitProcGlobal() is called before this. */
173         elog(WARN, "InitProcess: Proc Header uninitialized");
174     }
175     
176     if (MyProc != NULL)
177         {
178             SpinRelease(ProcStructLock);
179             elog(WARN,"ProcInit: you already exist");
180             return;
181         }
182     
183     /* try to get a proc from the free list first */
184     
185     myOffset = ProcGlobal->freeProcs;
186     
187     if (myOffset != INVALID_OFFSET)
188         {
189             MyProc = (PROC *) MAKE_PTR(myOffset);
190             ProcGlobal->freeProcs = MyProc->links.next;
191         }
192     else
193         {
194             /* have to allocate one.  We can't use the normal binding
195              * table mechanism because the proc structure is stored
196              * by PID instead of by a global name (need to look it
197              * up by PID when we cleanup dead processes).
198              */
199             
200             MyProc = (PROC *) ShmemAlloc((unsigned)sizeof(PROC));
201             if (! MyProc)
202                 {
203                     SpinRelease(ProcStructLock);
204                     elog (FATAL,"cannot create new proc: out of memory");
205                 }
206             
207             /* this cannot be initialized until after the buffer pool */
208             SHMQueueInit(&(MyProc->lockQueue));
209             MyProc->procId = ProcGlobal->numProcs;
210             ProcGlobal->numProcs++;
211         }
212     
213     /*
214      * zero out the spin lock counts and set the sLocks field for
215      * ProcStructLock to 1 as we have acquired this spinlock above but 
216      * didn't record it since we didn't have MyProc until now.
217      */
218     memset(MyProc->sLocks, 0, sizeof(MyProc->sLocks));
219     MyProc->sLocks[ProcStructLock] = 1;
220
221
222     if (IsUnderPostmaster) {
223         IPCKey semKey;
224         int semNum;
225         int semId;
226         union semun semun;
227
228         ProcGetNewSemKeyAndNum(&semKey, &semNum);
229         
230         semId = IpcSemaphoreCreate(semKey,
231                                    PROC_NSEMS_PER_SET,
232                                    IPCProtection,
233                                    IpcSemaphoreDefaultStartValue,
234                                    0,
235                                    &semstat);
236         /*
237          * we might be reusing a semaphore that belongs to a dead
238          * backend. So be careful and reinitialize its value here.
239          */
240         semun.val = IpcSemaphoreDefaultStartValue;
241         semctl(semId, semNum, SETVAL, semun);
242
243         IpcSemaphoreLock(semId, semNum, IpcExclusiveLock);
244         MyProc->sem.semId = semId;
245         MyProc->sem.semNum = semNum;
246         MyProc->sem.semKey = semKey;
247     } else {
248         MyProc->sem.semId = -1;
249     }
250     
251     /* ----------------------
252      * Release the lock.
253      * ----------------------
254      */
255     SpinRelease(ProcStructLock);
256     
257     MyProc->pid = 0;
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         Assert( pid != getpid() );
368     } else
369         MyProc = NULL;
370     
371     /* ---------------
372      * Assume one lock table.
373      * ---------------
374      */
375     ProcReleaseSpins(proc);
376     LockReleaseAll(1,&proc->lockQueue);
377     
378     /* ----------------
379      * get off the wait queue
380      * ----------------
381      */
382     LockLockTable();
383     if (proc->links.next != INVALID_OFFSET) {
384         Assert(proc->waitLock->waitProcs.size > 0);
385         SHMQueueDelete(&(proc->links));
386         --proc->waitLock->waitProcs.size;
387     }
388     SHMQueueElemInit(&(proc->links));
389     UnlockLockTable();
390     
391     return;
392 }
393
394 /*
395  * ProcQueue package: routines for putting processes to sleep
396  *      and  waking them up
397  */
398
399 /*
400  * ProcQueueAlloc -- alloc/attach to a shared memory process queue
401  *
402  * Returns: a pointer to the queue or NULL
403  * Side Effects: Initializes the queue if we allocated one
404  */
405 PROC_QUEUE *
406 ProcQueueAlloc(char *name)
407 {
408     bool        found;
409     PROC_QUEUE *queue = (PROC_QUEUE *)
410         ShmemInitStruct(name,(unsigned)sizeof(PROC_QUEUE),&found);
411     
412     if (! queue)
413         {
414             return(NULL);
415         }
416     if (! found)
417         {
418             ProcQueueInit(queue);
419         }
420     return(queue);
421 }
422
423 /*
424  * ProcQueueInit -- initialize a shared memory process queue
425  */
426 void
427 ProcQueueInit(PROC_QUEUE *queue)
428 {
429     SHMQueueInit(&(queue->links));
430     queue->size = 0;
431 }
432
433
434
435 /*
436  * ProcSleep -- put a process to sleep
437  *
438  * P() on the semaphore should put us to sleep.  The process
439  * semaphore is cleared by default, so the first time we try
440  * to acquire it, we sleep.
441  *
442  * ASSUME: that no one will fiddle with the queue until after
443  *      we release the spin lock.
444  *
445  * NOTES: The process queue is now a priority queue for locking.
446  */
447 int
448 ProcSleep(PROC_QUEUE *queue,
449           SPINLOCK spinlock,
450           int token,
451           int prio,
452           LOCK *lock)
453 {
454     int         i;
455     PROC        *proc;
456 #ifndef WIN32 /* figure this out later */
457     struct itimerval timeval, dummy;
458 #endif /* WIN32 */
459     
460     proc = (PROC *) MAKE_PTR(queue->links.prev);
461     for (i=0;i<queue->size;i++)
462         {
463             if (proc->prio < prio)
464                 proc = (PROC *) MAKE_PTR(proc->links.prev);
465             else
466                 break;
467         }
468     
469     MyProc->token = token;
470     MyProc->waitLock = lock;
471     
472     /* -------------------
473      * currently, we only need this for the ProcWakeup routines
474      * -------------------
475      */
476     TransactionIdStore((TransactionId) GetCurrentTransactionId(), &MyProc->xid);
477     
478     /* -------------------
479      * assume that these two operations are atomic (because
480      * of the spinlock).
481      * -------------------
482      */
483     SHMQueueInsertTL(&(proc->links),&(MyProc->links));
484     queue->size++;
485     
486     SpinRelease(spinlock);
487     
488     /* --------------
489      * Postgres does not have any deadlock detection code and for this 
490      * reason we must set a timer to wake up the process in the event of
491      * a deadlock.  For now the timer is set for 1 minute and we assume that
492      * any process which sleeps for this amount of time is deadlocked and will 
493      * receive a SIGALRM signal.  The handler should release the processes
494      * semaphore and abort the current transaction.
495      *
496      * Need to zero out struct to set the interval and the micro seconds fields
497      * to 0.
498      * --------------
499      */
500 #ifndef WIN32
501     memset(&timeval, 0, sizeof(struct itimerval));
502     timeval.it_value.tv_sec = DEADLOCK_TIMEOUT;
503     
504     if (setitimer(ITIMER_REAL, &timeval, &dummy))
505         elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
506 #endif /* WIN32 */
507     
508     /* --------------
509      * if someone wakes us between SpinRelease and IpcSemaphoreLock,
510      * IpcSemaphoreLock will not block.  The wakeup is "saved" by
511      * the semaphore implementation.
512      * --------------
513      */
514     IpcSemaphoreLock(MyProc->sem.semId, MyProc->sem.semNum, IpcExclusiveLock);
515     
516     /* ---------------
517      * We were awoken before a timeout - now disable the timer
518      * ---------------
519      */
520 #ifndef WIN32
521     timeval.it_value.tv_sec = 0;
522     
523     
524     if (setitimer(ITIMER_REAL, &timeval, &dummy))
525         elog(FATAL, "ProcSleep: Unable to diable timer for process wakeup");
526 #endif /* WIN32 */
527     
528     /* ----------------
529      * We were assumed to be in a critical section when we went
530      * to sleep.
531      * ----------------
532      */
533     SpinAcquire(spinlock);
534     
535     return(MyProc->errType);
536 }
537
538
539 /*
540  * ProcWakeup -- wake up a process by releasing its private semaphore.
541  *
542  *   remove the process from the wait queue and set its links invalid.
543  *   RETURN: the next process in the wait queue.
544  */
545 PROC *
546 ProcWakeup(PROC *proc, int errType)
547 {
548     PROC *retProc;
549     /* assume that spinlock has been acquired */
550     
551     if (proc->links.prev == INVALID_OFFSET ||
552         proc->links.next == INVALID_OFFSET)
553         return((PROC *) NULL);
554     
555     retProc = (PROC *) MAKE_PTR(proc->links.prev);
556     
557     /* you have to update waitLock->waitProcs.size yourself */
558     SHMQueueDelete(&(proc->links));
559     SHMQueueElemInit(&(proc->links));
560     
561     proc->errType = errType;
562     
563     IpcSemaphoreUnlock(proc->sem.semId, proc->sem.semNum, IpcExclusiveLock);
564     
565     return retProc;
566 }
567
568
569 /*
570  * ProcGetId --
571  */
572 int
573 ProcGetId()
574 {
575     return( MyProc->procId );
576 }
577
578 /*
579  * ProcLockWakeup -- routine for waking up processes when a lock is
580  *      released.
581  */
582 int
583 ProcLockWakeup(PROC_QUEUE *queue, char *ltable, char *lock)
584 {
585     PROC        *proc;
586     int count;
587     
588     if (! queue->size)
589         return(STATUS_NOT_FOUND);
590     
591     proc = (PROC *) MAKE_PTR(queue->links.prev);
592     count = 0;
593     while ((LockResolveConflicts ((LOCKTAB *) ltable,
594                                   (LOCK *) lock,
595                                   proc->token,
596                                   proc->xid) == STATUS_OK))
597         {
598             /* there was a waiting process, grant it the lock before waking it
599              * up.  This will prevent another process from seizing the lock
600              * between the time we release the lock master (spinlock) and
601              * the time that the awoken process begins executing again.
602              */
603             GrantLock((LOCK *) lock, proc->token);
604             queue->size--;
605             
606             /*
607              * ProcWakeup removes proc from the lock waiting process queue and
608              * returns the next proc in chain.  If a writer just dropped
609              * its lock and there are several waiting readers, wake them all up.
610              */
611             proc = ProcWakeup(proc, NO_ERROR);
612             
613             count++;
614             if (!proc || queue->size == 0)
615                 break;
616         }
617     
618     if (count)
619         return(STATUS_OK);
620     else
621         /* Something is still blocking us.  May have deadlocked. */
622         return(STATUS_NOT_FOUND);
623 }
624
625 void
626 ProcAddLock(SHM_QUEUE *elem)
627 {
628     SHMQueueInsertTL(&MyProc->lockQueue,elem);
629 }
630
631 /* --------------------
632  * We only get to this routine if we got SIGALRM after DEADLOCK_TIMEOUT
633  * while waiting for a lock to be released by some other process.  After
634  * the one minute deadline we assume we have a deadlock and must abort
635  * this transaction.  We must also indicate that I'm no longer waiting
636  * on a lock so that other processes don't try to wake me up and screw 
637  * up my semaphore.
638  * --------------------
639  */
640 int
641 #if defined(PORTNAME_linux)
642 HandleDeadLock(int i)
643 #else
644 HandleDeadLock()
645 #endif
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 1;
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(1);
683     }
684     
685     lock = MyProc->waitLock;
686     size = lock->waitProcs.size; /* so we can look at this in the core */
687     
688     /* ------------------------
689      * Get this process off the lock's wait queue
690      * ------------------------
691      */
692     Assert(lock->waitProcs.size > 0);
693     --lock->waitProcs.size;
694     SHMQueueDelete(&(MyProc->links));
695     SHMQueueElemInit(&(MyProc->links));
696     
697     /* ------------------
698      * Unlock my semaphore so that the count is right for next time.
699      * I was awoken by a signal, not by someone unlocking my semaphore.
700      * ------------------
701      */
702     IpcSemaphoreUnlock(MyProc->sem.semId, MyProc->sem.semNum, IpcExclusiveLock);
703     
704     /* -------------
705      * Set MyProc->errType to STATUS_ERROR so that we abort after
706      * returning from this handler.
707      * -------------
708      */
709     MyProc->errType = STATUS_ERROR;
710     
711     /*
712      * if this doesn't follow the IpcSemaphoreUnlock then we get lock
713      * table corruption ("LockReplace: xid table corrupted") due to
714      * race conditions.  i don't claim to understand this...
715      */
716     UnlockLockTable();
717     
718     elog(NOTICE, "Timeout -- possible deadlock");
719     return 0;
720 }
721
722 void
723 ProcReleaseSpins(PROC *proc)
724 {
725     int i;
726     
727     if (!proc)
728         proc = MyProc;
729     
730     if (!proc)
731         return;
732     for (i=0; i < (int)MAX_SPINS; i++)
733         {
734             if (proc->sLocks[i])
735                 {
736                     Assert(proc->sLocks[i] == 1);
737                     SpinRelease(i);
738                 }
739         }
740 }
741
742 /*****************************************************************************
743  * 
744  *****************************************************************************/
745
746 /*
747  * ProcGetNewSemKeyAndNum -
748  *    scan the free semaphore bitmap and allocate a single semaphore from
749  *    a semaphore set. (If the semaphore set doesn't exist yet,
750  *    IpcSemaphoreCreate will create it. Otherwise, we use the existing
751  *    semaphore set.)
752  */
753 static void
754 ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum)
755 {
756     int i;
757     int32 *freeSemMap = ProcGlobal->freeSemMap;
758     unsigned int fullmask;
759
760     /*
761      * we hold ProcStructLock when entering this routine. We scan through
762      * the bitmap to look for a free semaphore.
763      */
764     fullmask = ~0 >> (32 - PROC_NSEMS_PER_SET);
765     for(i=0; i < MAX_PROC_SEMS/PROC_NSEMS_PER_SET; i++) {
766         int mask = 1;
767         int j;
768
769         if (freeSemMap[i] == fullmask)
770             continue; /* none free for this set */
771
772         for(j = 0; j < PROC_NSEMS_PER_SET; j++) {
773             if ((freeSemMap[i] & mask) == 0) {
774                 /*
775                  * a free semaphore found. Mark it as allocated.
776                  */
777                 freeSemMap[i] |= mask;
778
779                 *key = ProcGlobal->currKey + i;
780                 *semNum = j;
781                 return;
782             }
783             mask <<= 1;
784         }
785     }
786
787     /* if we reach here, all the semaphores are in use. */
788     elog(WARN, "InitProc: cannot allocate a free semaphore");
789 }
790
791 /*
792  * ProcFreeSem -
793  *    free up our semaphore in the semaphore set. If we're the last one
794  *    in the set, also remove the semaphore set.
795  */
796 static void
797 ProcFreeSem(IpcSemaphoreKey semKey, int semNum)
798 {
799     int mask;
800     int i;
801     int32 *freeSemMap = ProcGlobal->freeSemMap;
802
803     i = semKey - ProcGlobal->currKey;
804     mask = ~(1 << semNum);
805     freeSemMap[i] &= mask;
806
807     if (freeSemMap[i]==0)
808         IpcSemaphoreKill(semKey);
809 }
810
811 /*
812  * ProcFreeAllSemaphores -
813  *    on exiting the postmaster, we free up all the semaphores allocated
814  *    to the lmgrs of the backends.
815  */
816 void
817 ProcFreeAllSemaphores()
818 {
819     int i;
820     int32 *freeSemMap = ProcGlobal->freeSemMap;
821
822     for(i=0; i < MAX_PROC_SEMS/PROC_NSEMS_PER_SET; i++) {
823         if (freeSemMap[i]!=0)
824             IpcSemaphoreKill(ProcGlobal->currKey + i);
825     }
826 }