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