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