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