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