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