]> granicus.if.org Git - postgresql/blob - src/backend/storage/lmgr/proc.c
Revert removal of signed, volatile, and signal handler arg type tests.
[postgresql] / src / backend / storage / lmgr / proc.c
1 /*-------------------------------------------------------------------------
2  *
3  * proc.c
4  *        routines to manage per-process shared memory data structure
5  *
6  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.79 2000/08/29 09:36:44 petere Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 /*
16  *      Each postgres backend gets one of these.  We'll use it to
17  *      clean up after the process should the process suddenly die.
18  *
19  *
20  * Interface (a):
21  *              ProcSleep(), ProcWakeup(), ProcWakeupNext(),
22  *              ProcQueueAlloc() -- create a shm queue for sleeping processes
23  *              ProcQueueInit() -- create a queue without allocing memory
24  *
25  * Locking and waiting for buffers can cause the backend to be
26  * put to sleep.  Whoever releases the lock, etc. wakes the
27  * process up again (and gives it an error code so it knows
28  * whether it was awoken on an error condition).
29  *
30  * Interface (b):
31  *
32  * ProcReleaseLocks -- frees the locks associated with this process,
33  * ProcKill -- destroys the shared memory state (and locks)
34  *              associated with the process.
35  *
36  * 5/15/91 -- removed the buffer pool based lock chain in favor
37  *              of a shared memory lock chain.  The write-protection is
38  *              more expensive if the lock chain is in the buffer pool.
39  *              The only reason I kept the lock chain in the buffer pool
40  *              in the first place was to allow the lock table to grow larger
41  *              than available shared memory and that isn't going to work
42  *              without a lot of unimplemented support anyway.
43  *
44  * 4/7/95 -- instead of allocating a set of 1 semaphore per process, we
45  *              allocate a semaphore from a set of PROC_NSEMS_PER_SET semaphores
46  *              shared among backends (we keep a few sets of semaphores around).
47  *              This is so that we can support more backends. (system-wide semaphore
48  *              sets run out pretty fast.)                                -ay 4/95
49  *
50  * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.79 2000/08/29 09:36:44 petere Exp $
51  */
52 #include <sys/time.h>
53 #include <unistd.h>
54 #include <signal.h>
55 #include <sys/types.h>
56
57 #if defined(solaris_sparc) || defined(__CYGWIN__)
58 #include <sys/ipc.h>
59 #include <sys/sem.h>
60 #endif
61
62 #include "postgres.h"
63 #include "miscadmin.h"
64
65
66 /* In Ultrix and QNX, sem.h must be included after ipc.h */
67 #include <sys/sem.h>
68
69 #include "storage/proc.h"
70
71 void            HandleDeadLock(SIGNAL_ARGS);
72 static void ProcFreeAllSemaphores(void);
73 static bool GetOffWaitqueue(PROC *);
74
75 int DeadlockTimeout = 1000;
76
77 /* --------------------
78  * Spin lock for manipulating the shared process data structure:
79  * ProcGlobal.... Adding an extra spin lock seemed like the smallest
80  * hack to get around reading and updating this structure in shared
81  * memory. -mer 17 July 1991
82  * --------------------
83  */
84 SPINLOCK        ProcStructLock;
85
86 static PROC_HDR *ProcGlobal = NULL;
87
88 PROC       *MyProc = NULL;
89
90 static void ProcKill(int exitStatus, int pid);
91 static void ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum);
92 static void ProcFreeSem(IpcSemaphoreKey semKey, int semNum);
93
94 static char *DeadLockMessage = "Deadlock detected -- See the lock(l) manual page for a possible cause.";
95
96 /*
97  * InitProcGlobal -
98  *        initializes the global process table. We put it here so that
99  *        the postmaster can do this initialization. (ProcFreeAllSemaphores needs
100  *        to read this table on exiting the postmaster. If we have the first
101  *        backend do this, starting up and killing the postmaster without
102  *        starting any backends will be a problem.)
103  *
104  *        We also allocate all the per-process semaphores we will need to support
105  *        the requested number of backends.  We used to allocate semaphores
106  *        only when backends were actually started up, but that is bad because
107  *        it lets Postgres fail under load --- a lot of Unix systems are
108  *        (mis)configured with small limits on the number of semaphores, and
109  *        running out when trying to start another backend is a common failure.
110  *        So, now we grab enough semaphores to support the desired max number
111  *        of backends immediately at initialization --- if the sysadmin has set
112  *        MaxBackends higher than his kernel will support, he'll find out sooner
113  *        rather than later.
114  */
115 void
116 InitProcGlobal(IPCKey key, int maxBackends)
117 {
118         bool            found = false;
119
120         /* attach to the free list */
121         ProcGlobal = (PROC_HDR *)
122                 ShmemInitStruct("Proc Header", sizeof(PROC_HDR), &found);
123
124         /* --------------------
125          * We're the first - initialize.
126          * XXX if found should ever be true, it is a sign of impending doom ...
127          * ought to complain if so?
128          * --------------------
129          */
130         if (!found)
131         {
132                 int                     i;
133
134                 ProcGlobal->freeProcs = INVALID_OFFSET;
135                 ProcGlobal->currKey = IPCGetProcessSemaphoreInitKey(key);
136                 for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++)
137                         ProcGlobal->freeSemMap[i] = 0;
138
139                 /*
140                  * Arrange to delete semas on exit --- set this up now so that we
141                  * will clean up if pre-allocation fails...
142                  */
143                 on_shmem_exit(ProcFreeAllSemaphores, NULL);
144
145                 /*
146                  * Pre-create the semaphores for the first maxBackends processes,
147                  * unless we are running as a standalone backend.
148                  */
149                 if (key != PrivateIPCKey)
150                 {
151                         for (i = 0;
152                                  i < (maxBackends + PROC_NSEMS_PER_SET - 1) / PROC_NSEMS_PER_SET;
153                                  i++)
154                         {
155                                 IPCKey          semKey = ProcGlobal->currKey + i;
156                                 int                     semId;
157
158                                 semId = IpcSemaphoreCreate(semKey,
159                                                                                    PROC_NSEMS_PER_SET,
160                                                                                    IPCProtection,
161                                                                                    IpcSemaphoreDefaultStartValue,
162                                                                                    0);
163                                 if (semId < 0)
164                                         elog(FATAL, "InitProcGlobal: IpcSemaphoreCreate failed");
165                                 /* mark this sema set allocated */
166                                 ProcGlobal->freeSemMap[i] = (1 << PROC_NSEMS_PER_SET);
167                         }
168                 }
169         }
170 }
171
172 /* ------------------------
173  * InitProc -- create a per-process data structure for this process
174  * used by the lock manager on semaphore queues.
175  * ------------------------
176  */
177 void
178 InitProcess(IPCKey key)
179 {
180         bool            found = false;
181         unsigned long location,
182                                 myOffset;
183
184         SpinAcquire(ProcStructLock);
185
186         /* attach to the free list */
187         ProcGlobal = (PROC_HDR *)
188                 ShmemInitStruct("Proc Header", sizeof(PROC_HDR), &found);
189         if (!found)
190         {
191                 /* this should not happen. InitProcGlobal() is called before this. */
192                 elog(STOP, "InitProcess: Proc Header uninitialized");
193         }
194
195         if (MyProc != NULL)
196         {
197                 SpinRelease(ProcStructLock);
198                 elog(ERROR, "ProcInit: you already exist");
199                 return;
200         }
201
202         /* try to get a proc from the free list first */
203
204         myOffset = ProcGlobal->freeProcs;
205
206         if (myOffset != INVALID_OFFSET)
207         {
208                 MyProc = (PROC *) MAKE_PTR(myOffset);
209                 ProcGlobal->freeProcs = MyProc->links.next;
210         }
211         else
212         {
213
214                 /*
215                  * have to allocate one.  We can't use the normal shmem index
216                  * table mechanism because the proc structure is stored by PID
217                  * instead of by a global name (need to look it up by PID when we
218                  * cleanup dead processes).
219                  */
220
221                 MyProc = (PROC *) ShmemAlloc(sizeof(PROC));
222                 if (!MyProc)
223                 {
224                         SpinRelease(ProcStructLock);
225                         elog(FATAL, "cannot create new proc: out of memory");
226                 }
227
228                 /* this cannot be initialized until after the buffer pool */
229                 SHMQueueInit(&(MyProc->lockQueue));
230         }
231
232         /*
233          * zero out the spin lock counts and set the sLocks field for
234          * ProcStructLock to 1 as we have acquired this spinlock above but
235          * didn't record it since we didn't have MyProc until now.
236          */
237         MemSet(MyProc->sLocks, 0, sizeof(MyProc->sLocks));
238         MyProc->sLocks[ProcStructLock] = 1;
239
240
241         if (IsUnderPostmaster)
242         {
243                 IPCKey          semKey;
244                 int                     semNum;
245                 int                     semId;
246                 union semun semun;
247
248                 ProcGetNewSemKeyAndNum(&semKey, &semNum);
249
250                 /*
251                  * Note: because of the pre-allocation done in InitProcGlobal,
252                  * this call should always attach to an existing semaphore. It
253                  * will (try to) create a new group of semaphores only if the
254                  * postmaster tries to start more backends than it said it would.
255                  */
256                 semId = IpcSemaphoreCreate(semKey,
257                                                                    PROC_NSEMS_PER_SET,
258                                                                    IPCProtection,
259                                                                    IpcSemaphoreDefaultStartValue,
260                                                                    0);
261
262                 /*
263                  * we might be reusing a semaphore that belongs to a dead backend.
264                  * So be careful and reinitialize its value here.
265                  */
266                 semun.val = IpcSemaphoreDefaultStartValue;
267                 semctl(semId, semNum, SETVAL, semun);
268
269                 IpcSemaphoreLock(semId, semNum, IpcExclusiveLock);
270                 MyProc->sem.semId = semId;
271                 MyProc->sem.semNum = semNum;
272                 MyProc->sem.semKey = semKey;
273         }
274         else
275                 MyProc->sem.semId = -1;
276
277         /* ----------------------
278          * Release the lock.
279          * ----------------------
280          */
281         SpinRelease(ProcStructLock);
282
283         MyProc->pid = MyProcPid;
284         MyProc->databaseId = MyDatabaseId;
285         MyProc->xid = InvalidTransactionId;
286         MyProc->xmin = InvalidTransactionId;
287
288         /* ----------------
289          * Start keeping spin lock stats from here on.  Any botch before
290          * this initialization is forever botched
291          * ----------------
292          */
293         MemSet(MyProc->sLocks, 0, MAX_SPINS * sizeof(*MyProc->sLocks));
294
295         /* -------------------------
296          * Install ourselves in the shmem index table.  The name to
297          * use is determined by the OS-assigned process id.  That
298          * allows the cleanup process to find us after any untimely
299          * exit.
300          * -------------------------
301          */
302         location = MAKE_OFFSET(MyProc);
303         if ((!ShmemPIDLookup(MyProcPid, &location)) || (location != MAKE_OFFSET(MyProc)))
304                 elog(STOP, "InitProc: ShmemPID table broken");
305
306         MyProc->errType = NO_ERROR;
307         SHMQueueElemInit(&(MyProc->links));
308
309         on_shmem_exit(ProcKill, (caddr_t) MyProcPid);
310 }
311
312 /* -----------------------
313  * get off the wait queue
314  * -----------------------
315  */
316 static bool
317 GetOffWaitqueue(PROC *proc)
318 {
319         bool            getoffed = false;
320
321         LockLockTable();
322         if (proc->links.next != INVALID_OFFSET)
323         {
324                 int                     lockmode = proc->token;
325                 LOCK    *waitLock = proc->waitLock;
326
327                 Assert(waitLock);
328                 Assert(waitLock->waitProcs.size > 0);
329                 SHMQueueDelete(&(proc->links));
330                 --waitLock->waitProcs.size;
331                 Assert(waitLock->nHolding > 0);
332                 Assert(waitLock->nHolding > proc->waitLock->nActive);
333                 --waitLock->nHolding;
334                 Assert(waitLock->holders[lockmode] > 0);
335                 --waitLock->holders[lockmode];
336                 if (waitLock->activeHolders[lockmode] == waitLock->holders[lockmode])
337                         waitLock->waitMask &= ~(1 << lockmode);
338                 ProcLockWakeup(&(waitLock->waitProcs), LOCK_LOCKMETHOD(*waitLock), waitLock);
339                 getoffed = true;
340         }
341         SHMQueueElemInit(&(proc->links));
342         UnlockLockTable();
343
344         return getoffed;
345 }
346
347 /*
348  * ProcReleaseLocks() -- release all locks associated with this process
349  *
350  */
351 void
352 ProcReleaseLocks()
353 {
354         if (!MyProc)
355                 return;
356         LockReleaseAll(1, &MyProc->lockQueue);
357         GetOffWaitqueue(MyProc);
358 }
359
360 /*
361  * ProcRemove -
362  *        used by the postmaster to clean up the global tables. This also frees
363  *        up the semaphore used for the lmgr of the process. (We have to do
364  *        this is the postmaster instead of doing a IpcSemaphoreKill on exiting
365  *        the process because the semaphore set is shared among backends and
366  *        we don't want to remove other's semaphores on exit.)
367  */
368 bool
369 ProcRemove(int pid)
370 {
371         SHMEM_OFFSET location;
372         PROC       *proc;
373
374         location = INVALID_OFFSET;
375
376         location = ShmemPIDDestroy(pid);
377         if (location == INVALID_OFFSET)
378                 return FALSE;
379         proc = (PROC *) MAKE_PTR(location);
380
381         SpinAcquire(ProcStructLock);
382
383         ProcFreeSem(proc->sem.semKey, proc->sem.semNum);
384
385         proc->links.next = ProcGlobal->freeProcs;
386         ProcGlobal->freeProcs = MAKE_OFFSET(proc);
387
388         SpinRelease(ProcStructLock);
389
390         return TRUE;
391 }
392
393 /*
394  * ProcKill() -- Destroy the per-proc data structure for
395  *              this process. Release any of its held spin locks.
396  */
397 static void
398 ProcKill(int exitStatus, int pid)
399 {
400         PROC       *proc;
401         SHMEM_OFFSET location;
402
403         /* --------------------
404          * If this is a FATAL exit the postmaster will have to kill all the
405          * existing backends and reinitialize shared memory.  So all we don't
406          * need to do anything here.
407          * --------------------
408          */
409         if (exitStatus != 0)
410                 return;
411
412         ShmemPIDLookup(MyProcPid, &location);
413         if (location == INVALID_OFFSET)
414                 return;
415
416         proc = (PROC *) MAKE_PTR(location);
417
418         Assert(proc == MyProc || pid != MyProcPid);
419
420         MyProc = NULL;
421
422         /* ---------------
423          * Assume one lock table.
424          * ---------------
425          */
426         ProcReleaseSpins(proc);
427         LockReleaseAll(DEFAULT_LOCKMETHOD, &proc->lockQueue);
428
429 #ifdef USER_LOCKS
430
431         /*
432          * Assume we have a second lock table.
433          */
434         LockReleaseAll(USER_LOCKMETHOD, &proc->lockQueue);
435 #endif
436
437         /* ----------------
438          * get off the wait queue
439          * ----------------
440          */
441         GetOffWaitqueue(proc);
442
443         return;
444 }
445
446 /*
447  * ProcQueue package: routines for putting processes to sleep
448  *              and  waking them up
449  */
450
451 /*
452  * ProcQueueAlloc -- alloc/attach to a shared memory process queue
453  *
454  * Returns: a pointer to the queue or NULL
455  * Side Effects: Initializes the queue if we allocated one
456  */
457 #ifdef NOT_USED
458 PROC_QUEUE *
459 ProcQueueAlloc(char *name)
460 {
461         bool            found;
462         PROC_QUEUE *queue = (PROC_QUEUE *)
463                 ShmemInitStruct(name, sizeof(PROC_QUEUE), &found);
464
465         if (!queue)
466                 return NULL;
467         if (!found)
468                 ProcQueueInit(queue);
469         return queue;
470 }
471
472 #endif
473
474 /*
475  * ProcQueueInit -- initialize a shared memory process queue
476  */
477 void
478 ProcQueueInit(PROC_QUEUE *queue)
479 {
480         SHMQueueInit(&(queue->links));
481         queue->size = 0;
482 }
483
484
485 /*
486  *      Handling cancel request while waiting for lock
487  *
488  */
489 static bool lockWaiting = false;
490 void
491 SetWaitingForLock(bool waiting)
492 {
493         if (waiting == lockWaiting)
494                 return;
495         lockWaiting = waiting;
496         if (lockWaiting)
497         {
498                 /* The lock was already released ? */
499                 if (MyProc->links.next == INVALID_OFFSET)
500                 {
501                         lockWaiting = false;
502                         return;
503                 }
504                 if (QueryCancel)                /* cancel request pending */
505                 {
506                         if (GetOffWaitqueue(MyProc))
507                         {
508                                 lockWaiting = false;
509                                 elog(ERROR, "Query cancel requested while waiting lock");
510                         }
511                 }
512         }
513 }
514 void
515 LockWaitCancel(void)
516 {
517         struct itimerval timeval,
518                                 dummy;
519
520         if (!lockWaiting)
521                 return;
522         lockWaiting = false;
523         /* Deadlock timer off */
524         MemSet(&timeval, 0, sizeof(struct itimerval));
525         setitimer(ITIMER_REAL, &timeval, &dummy);
526         if (GetOffWaitqueue(MyProc))
527                 elog(ERROR, "Query cancel requested while waiting lock");
528 }
529
530 /*
531  * ProcSleep -- put a process to sleep
532  *
533  * P() on the semaphore should put us to sleep.  The process
534  * semaphore is cleared by default, so the first time we try
535  * to acquire it, we sleep.
536  *
537  * ASSUME: that no one will fiddle with the queue until after
538  *              we release the spin lock.
539  *
540  * NOTES: The process queue is now a priority queue for locking.
541  */
542 int
543 ProcSleep(PROC_QUEUE *waitQueue,/* lock->waitProcs */
544                   LOCKMETHODCTL *lockctl,
545                   int token,                    /* lockmode */
546                   LOCK *lock)
547 {
548         int                     i;
549         SPINLOCK        spinlock = lockctl->masterLock;
550         PROC       *proc;
551         int                     myMask = (1 << token);
552         int                     waitMask = lock->waitMask;
553         int                     aheadHolders[MAX_LOCKMODES];
554         bool            selfConflict = (lockctl->conflictTab[token] & myMask),
555                                 prevSame = false;
556         bool            deadlock_checked = false;
557         struct itimerval timeval,
558                                 dummy;
559
560         MyProc->token = token;
561         MyProc->waitLock = lock;
562
563         proc = (PROC *) MAKE_PTR(waitQueue->links.prev);
564
565         /* if we don't conflict with any waiter - be first in queue */
566         if (!(lockctl->conflictTab[token] & waitMask))
567                 goto ins;
568
569         for (i = 1; i < MAX_LOCKMODES; i++)
570                 aheadHolders[i] = lock->activeHolders[i];
571         (aheadHolders[token])++;
572
573         for (i = 0; i < waitQueue->size; i++)
574         {
575                 /* am I waiting for him ? */
576                 if (lockctl->conflictTab[token] & proc->holdLock)
577                 {
578                         /* is he waiting for me ? */
579                         if (lockctl->conflictTab[proc->token] & MyProc->holdLock)
580                         {
581                                 MyProc->errType = STATUS_ERROR;
582                                 elog(NOTICE, DeadLockMessage);
583                                 goto rt;
584                         }
585                         /* being waiting for him - go past */
586                 }
587                 /* if he waits for me */
588                 else if (lockctl->conflictTab[proc->token] & MyProc->holdLock)
589                         break;
590                 /* if conflicting locks requested */
591                 else if (lockctl->conflictTab[proc->token] & myMask)
592                 {
593
594                         /*
595                          * If I request non self-conflicting lock and there are others
596                          * requesting the same lock just before me - stay here.
597                          */
598                         if (!selfConflict && prevSame)
599                                 break;
600                 }
601
602                 /*
603                  * Last attempt to don't move any more: if we don't conflict with
604                  * rest waiters in queue.
605                  */
606                 else if (!(lockctl->conflictTab[token] & waitMask))
607                         break;
608
609                 prevSame = (proc->token == token);
610                 (aheadHolders[proc->token])++;
611                 if (aheadHolders[proc->token] == lock->holders[proc->token])
612                         waitMask &= ~(1 << proc->token);
613                 proc = (PROC *) MAKE_PTR(proc->links.prev);
614         }
615
616 ins:;
617         /* -------------------
618          * assume that these two operations are atomic (because
619          * of the spinlock).
620          * -------------------
621          */
622         SHMQueueInsertTL(&(proc->links), &(MyProc->links));
623         waitQueue->size++;
624
625         lock->waitMask |= myMask;
626         SpinRelease(spinlock);
627
628         /* --------------
629          * We set this so we can wake up periodically and check for a deadlock.
630          * If a deadlock is detected, the handler releases the processes
631          * semaphore and aborts the current transaction.
632          *
633          * Need to zero out struct to set the interval and the micro seconds fields
634          * to 0.
635          * --------------
636          */
637         MemSet(&timeval, 0, sizeof(struct itimerval));
638         timeval.it_value.tv_sec = DeadlockTimeout / 1000;
639         timeval.it_value.tv_usec = (DeadlockTimeout % 1000) * 1000;
640
641         SetWaitingForLock(true);
642         do
643         {
644                 MyProc->errType = NO_ERROR;             /* reset flag after deadlock check */
645
646                 if (!deadlock_checked)
647                         if (setitimer(ITIMER_REAL, &timeval, &dummy))
648                                 elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
649                 deadlock_checked = true;
650
651                 /* --------------
652                  * if someone wakes us between SpinRelease and IpcSemaphoreLock,
653                  * IpcSemaphoreLock will not block.  The wakeup is "saved" by
654                  * the semaphore implementation.
655                  * --------------
656                  */
657                 IpcSemaphoreLock(MyProc->sem.semId, MyProc->sem.semNum,
658                                                  IpcExclusiveLock);
659         } while (MyProc->errType == STATUS_NOT_FOUND);          /* sleep after deadlock
660                                                                                                                  * check */
661         lockWaiting = false;
662
663         /* ---------------
664          * We were awoken before a timeout - now disable the timer
665          * ---------------
666          */
667         timeval.it_value.tv_sec = 0;
668         timeval.it_value.tv_usec = 0;
669         if (setitimer(ITIMER_REAL, &timeval, &dummy))
670                 elog(FATAL, "ProcSleep: Unable to diable timer for process wakeup");
671
672         /* ----------------
673          * We were assumed to be in a critical section when we went
674          * to sleep.
675          * ----------------
676          */
677         SpinAcquire(spinlock);
678
679 rt:;
680
681 #ifdef LOCK_DEBUG
682         /* Just to get meaningful debug messages from DumpLocks() */
683         MyProc->waitLock = (LOCK *) NULL;
684 #endif
685
686         return MyProc->errType;
687 }
688
689
690 /*
691  * ProcWakeup -- wake up a process by releasing its private semaphore.
692  *
693  *       remove the process from the wait queue and set its links invalid.
694  *       RETURN: the next process in the wait queue.
695  */
696 PROC *
697 ProcWakeup(PROC *proc, int errType)
698 {
699         PROC       *retProc;
700
701         /* assume that spinlock has been acquired */
702
703         if (proc->links.prev == INVALID_OFFSET ||
704                 proc->links.next == INVALID_OFFSET)
705                 return (PROC *) NULL;
706
707         retProc = (PROC *) MAKE_PTR(proc->links.prev);
708
709         /* you have to update waitLock->waitProcs.size yourself */
710         SHMQueueDelete(&(proc->links));
711         SHMQueueElemInit(&(proc->links));
712
713         proc->errType = errType;
714
715         IpcSemaphoreUnlock(proc->sem.semId, proc->sem.semNum, IpcExclusiveLock);
716
717         return retProc;
718 }
719
720 /*
721  * ProcLockWakeup -- routine for waking up processes when a lock is
722  *              released.
723  */
724 int
725 ProcLockWakeup(PROC_QUEUE *queue, LOCKMETHOD lockmethod, LOCK *lock)
726 {
727         PROC       *proc;
728         int                     count = 0;
729         int                     last_locktype = 0;
730         int                     queue_size = queue->size;
731
732         Assert(queue->size >= 0);
733
734         if (!queue->size)
735                 return STATUS_NOT_FOUND;
736
737         proc = (PROC *) MAKE_PTR(queue->links.prev);
738         while ((queue_size--) && (proc))
739         {
740
741                 /*
742                  * This proc will conflict as the previous one did, don't even
743                  * try.
744                  */
745                 if (proc->token == last_locktype)
746                         continue;
747
748                 /*
749                  * Does this proc conflict with locks held by others ?
750                  */
751                 if (LockResolveConflicts(lockmethod,
752                                                                  lock,
753                                                                  proc->token,
754                                                                  proc->xid,
755                                                                  (XIDLookupEnt *) NULL) != STATUS_OK)
756                 {
757                         if (count != 0)
758                                 break;
759                         last_locktype = proc->token;
760                         continue;
761                 }
762
763                 /*
764                  * there was a waiting process, grant it the lock before waking it
765                  * up.  This will prevent another process from seizing the lock
766                  * between the time we release the lock master (spinlock) and the
767                  * time that the awoken process begins executing again.
768                  */
769                 GrantLock(lock, proc->token);
770
771                 /*
772                  * ProcWakeup removes proc from the lock waiting process queue and
773                  * returns the next proc in chain.
774                  */
775
776                 count++;
777                 queue->size--;
778                 proc = ProcWakeup(proc, NO_ERROR);
779         }
780
781         Assert(queue->size >= 0);
782
783         if (count)
784                 return STATUS_OK;
785         else
786         {
787                 /* Something is still blocking us.      May have deadlocked. */
788 #ifdef LOCK_DEBUG
789                 if (lock->tag.lockmethod == USER_LOCKMETHOD ? Trace_userlocks : Trace_locks)
790                 {
791                         elog(DEBUG, "ProcLockWakeup: lock(%lx) can't wake up any process", MAKE_OFFSET(lock));
792                         if (Debug_deadlocks)
793                         DumpAllLocks();
794                 }
795 #endif
796                 return STATUS_NOT_FOUND;
797         }
798 }
799
800 void
801 ProcAddLock(SHM_QUEUE *elem)
802 {
803         SHMQueueInsertTL(&MyProc->lockQueue, elem);
804 }
805
806 /* --------------------
807  * We only get to this routine if we got SIGALRM after DeadlockTimeout
808  * while waiting for a lock to be released by some other process.  If we have
809  * a real deadlock, we must also indicate that I'm no longer waiting
810  * on a lock so that other processes don't try to wake me up and screw
811  * up my semaphore.
812  * --------------------
813  */
814 void
815 HandleDeadLock(SIGNAL_ARGS)
816 {
817         LOCK       *mywaitlock;
818
819         LockLockTable();
820
821         /* ---------------------
822          * Check to see if we've been awoken by anyone in the interim.
823          *
824          * If we have we can return and resume our transaction -- happy day.
825          * Before we are awoken the process releasing the lock grants it to
826          * us so we know that we don't have to wait anymore.
827          *
828          * Damn these names are LONG! -mer
829          * ---------------------
830          */
831         if (IpcSemaphoreGetCount(MyProc->sem.semId, MyProc->sem.semNum) ==
832                 IpcSemaphoreDefaultStartValue)
833         {
834                 UnlockLockTable();
835                 return;
836         }
837
838         /*
839          * you would think this would be unnecessary, but...
840          *
841          * this also means we've been removed already.  in some ports (e.g.,
842          * sparc and aix) the semop(2) implementation is such that we can
843          * actually end up in this handler after someone has removed us from
844          * the queue and bopped the semaphore *but the test above fails to
845          * detect the semaphore update* (presumably something weird having to
846          * do with the order in which the semaphore wakeup signal and SIGALRM
847          * get handled).
848          */
849         if (MyProc->links.prev == INVALID_OFFSET ||
850                 MyProc->links.next == INVALID_OFFSET)
851         {
852                 UnlockLockTable();
853                 return;
854         }
855
856 #ifdef LOCK_DEBUG
857     if (Debug_deadlocks)
858         DumpAllLocks();
859 #endif
860
861         MyProc->errType = STATUS_NOT_FOUND;
862         if (!DeadLockCheck(MyProc, MyProc->waitLock))
863         {
864                 UnlockLockTable();
865                 return;
866         }
867
868         mywaitlock = MyProc->waitLock;
869
870         /* ------------------------
871          * Get this process off the lock's wait queue
872          * ------------------------
873          */
874         Assert(mywaitlock->waitProcs.size > 0);
875         lockWaiting = false;
876         --mywaitlock->waitProcs.size;
877         SHMQueueDelete(&(MyProc->links));
878         SHMQueueElemInit(&(MyProc->links));
879
880         /* ------------------
881          * Unlock my semaphore so that the count is right for next time.
882          * I was awoken by a signal, not by someone unlocking my semaphore.
883          * ------------------
884          */
885         IpcSemaphoreUnlock(MyProc->sem.semId, MyProc->sem.semNum,
886                                            IpcExclusiveLock);
887
888         /* -------------
889          * Set MyProc->errType to STATUS_ERROR so that we abort after
890          * returning from this handler.
891          * -------------
892          */
893         MyProc->errType = STATUS_ERROR;
894
895         /*
896          * if this doesn't follow the IpcSemaphoreUnlock then we get lock
897          * table corruption ("LockReplace: xid table corrupted") due to race
898          * conditions.  i don't claim to understand this...
899          */
900         UnlockLockTable();
901
902         elog(NOTICE, DeadLockMessage);
903         return;
904 }
905
906 void
907 ProcReleaseSpins(PROC *proc)
908 {
909         int                     i;
910
911         if (!proc)
912                 proc = MyProc;
913
914         if (!proc)
915                 return;
916         for (i = 0; i < (int) MAX_SPINS; i++)
917         {
918                 if (proc->sLocks[i])
919                 {
920                         Assert(proc->sLocks[i] == 1);
921                         SpinRelease(i);
922                 }
923         }
924         AbortBufferIO();
925 }
926
927 /*****************************************************************************
928  *
929  *****************************************************************************/
930
931 /*
932  * ProcGetNewSemKeyAndNum -
933  *        scan the free semaphore bitmap and allocate a single semaphore from
934  *        a semaphore set. (If the semaphore set doesn't exist yet,
935  *        IpcSemaphoreCreate will create it. Otherwise, we use the existing
936  *        semaphore set.)
937  */
938 static void
939 ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum)
940 {
941         int                     i;
942         int32      *freeSemMap = ProcGlobal->freeSemMap;
943         int32           fullmask = (1 << (PROC_NSEMS_PER_SET + 1)) - 1;
944
945         /*
946          * we hold ProcStructLock when entering this routine. We scan through
947          * the bitmap to look for a free semaphore.
948          */
949
950         for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++)
951         {
952                 int                     mask = 1;
953                 int                     j;
954
955                 if (freeSemMap[i] == fullmask)
956                         continue;                       /* this set is fully allocated */
957
958                 for (j = 0; j < PROC_NSEMS_PER_SET; j++)
959                 {
960                         if ((freeSemMap[i] & mask) == 0)
961                         {
962
963                                 /*
964                                  * a free semaphore found. Mark it as allocated. Also set
965                                  * the bit indicating whole set is allocated.
966                                  */
967                                 freeSemMap[i] |= mask + (1 << PROC_NSEMS_PER_SET);
968
969                                 *key = ProcGlobal->currKey + i;
970                                 *semNum = j;
971                                 return;
972                         }
973                         mask <<= 1;
974                 }
975         }
976
977         /* if we reach here, all the semaphores are in use. */
978         elog(ERROR, "InitProc: cannot allocate a free semaphore");
979 }
980
981 /*
982  * ProcFreeSem -
983  *        free up our semaphore in the semaphore set.
984  */
985 static void
986 ProcFreeSem(IpcSemaphoreKey semKey, int semNum)
987 {
988         int                     mask;
989         int                     i;
990         int32      *freeSemMap = ProcGlobal->freeSemMap;
991
992         i = semKey - ProcGlobal->currKey;
993         mask = ~(1 << semNum);
994         freeSemMap[i] &= mask;
995
996         /*
997          * Formerly we'd release a semaphore set if it was now completely
998          * unused, but now we keep the semaphores to ensure we won't run out
999          * when starting new backends --- cf. InitProcGlobal.  Note that the
1000          * PROC_NSEMS_PER_SET+1'st bit of the freeSemMap entry remains set to
1001          * indicate it is still allocated; ProcFreeAllSemaphores() needs that.
1002          */
1003 }
1004
1005 /*
1006  * ProcFreeAllSemaphores -
1007  *        called at shmem_exit time, ie when exiting the postmaster or
1008  *        destroying shared state for a failed set of backends.
1009  *        Free up all the semaphores allocated to the lmgrs of the backends.
1010  */
1011 static void
1012 ProcFreeAllSemaphores()
1013 {
1014         int                     i;
1015         int32      *freeSemMap = ProcGlobal->freeSemMap;
1016
1017         for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++)
1018         {
1019                 if (freeSemMap[i] != 0)
1020                         IpcSemaphoreKill(ProcGlobal->currKey + i);
1021         }
1022 }