head 1.14; access; symbols; locks; strict; comment @ * @; 1.14 date 92.06.17.04.58.19; author mer; state Exp; branches; next 1.13; 1.13 date 92.05.28.21.14.53; author mer; state Exp; branches; next 1.12; 1.12 date 92.04.13.19.03.52; author mer; state Exp; branches; next 1.11; 1.11 date 92.04.01.22.45.23; author mer; state Exp; branches; next 1.10; 1.10 date 92.02.25.21.39.14; author mer; state Exp; branches; next 1.9; 1.9 date 91.11.14.19.40.04; author kemnitz; state Exp; branches; next 1.8; 1.8 date 91.08.29.23.52.03; author mer; state Exp; branches; next 1.7; 1.7 date 91.08.26.18.03.00; author mer; state Exp; branches; next 1.6; 1.6 date 91.08.14.12.28.46; author mer; state Exp; branches; next 1.5; 1.5 date 91.08.12.15.16.41; author mer; state Exp; branches; next 1.4; 1.4 date 91.07.29.07.17.30; author mer; state Exp; branches; next 1.3; 1.3 date 91.07.23.19.55.22; author mer; state Exp; branches; next 1.2; 1.2 date 91.07.22.15.21.43; author mer; state Exp; branches; next 1.1; 1.1 date 91.07.22.14.50.28; author mer; state Exp; branches; next ; desc @new file for new lock manager @ 1.14 log @fix a bug resulting from xid size change @ text @/* * proc.c -- routines to manage per-process shared memory data * structure * * Each postgres backend gets one of these. We'll use it to * clean up after the process should the process suddenly die. * * * Interface (a): * ProcSleep(), ProcWakeup(), ProcWakeupNext(), * ProcQueueAlloc() -- create a shm queue for sleeping processes * ProcQueueInit() -- create a queue without allocing memory * * Locking and waiting for buffers can cause the backend to be * put to sleep. Whoever releases the lock, etc. wakes the * process up again (and gives it an error code so it knows * whether it was awoken on an error condition). * * Interface (b): * * ProcReleaseLocks -- frees the locks associated with this process, * ProcKill -- destroys the shared memory state (and locks) * associated with the process. * * 5/15/91 -- removed the buffer pool based lock chain in favor * of a shared memory lock chain. The write-protection is * more expensive if the lock chain is in the buffer pool. * The only reason I kept the lock chain in the buffer pool * in the first place was to allow the lock table to grow larger * than available shared memory and that isn't going to work * without a lot of unimplemented support anyway. * * $Header: /u/mer/pg/src/storage/lmgr/RCS/proc.c,v 1.13 1992/05/28 21:14:53 mer Exp mer $ */ #include #include #include "utils/hsearch.h" #include "utils/log.h" #include "storage/buf.h" #include "storage/lock.h" #include "storage/shmem.h" #include "storage/spin.h" #include "storage/proc.h" #include "storage/procq.h" extern int MyPid; /* for parallel backends w/same xid */ /* -------------------- * Spin lock for manipulating the shared process data structure: * ProcGlobal.... Adding an extra spin lock seemed like the smallest * hack to get around reading and updating this structure in shared * memory. -mer 17 July 1991 * -------------------- */ SPINLOCK ProcStructLock; /* * For cleanup routines. Don't cleanup if the initialization * has not happened. */ Boolean ProcInitialized = FALSE; PROC_HDR *ProcGlobal = NULL; PROC *MyProc = NULL; /* ------------------------ * InitProc -- create a per-process data structure for this process * used by the lock manager on semaphore queues. * ------------------------ */ void InitProcess(key) IPCKey key; { bool found = false; int pid; int semstat; unsigned int location, myOffset; /* ------------------ * Routine called if deadlock timer goes off. See ProcSleep() * ------------------ */ signal(SIGALRM, HandleDeadLock); SpinAcquire(ProcStructLock); /* attach to the free list */ ProcGlobal = (PROC_HDR *) ShmemInitStruct("Proc Header",(unsigned)sizeof(PROC_HDR),&found); /* -------------------- * We're the first - initialize. * -------------------- */ if (! found) { ProcGlobal->numProcs = 0; ProcGlobal->freeProcs = INVALID_OFFSET; ProcGlobal->currKey = IPCGetProcessSemaphoreInitKey(key); } if (MyProc != NULL) { SpinRelease(ProcStructLock); elog(WARN,"ProcInit: you already exist"); return; } /* try to get a proc from the free list first */ myOffset = ProcGlobal->freeProcs; if (myOffset != INVALID_OFFSET) { MyProc = (PROC *) MAKE_PTR(myOffset); ProcGlobal->freeProcs = MyProc->links.next; } else { /* have to allocate one. We can't use the normal binding * table mechanism because the proc structure is stored * by PID instead of by a global name (need to look it * up by PID when we cleanup dead processes). */ MyProc = (PROC *) ShmemAlloc((unsigned)sizeof(PROC)); if (! MyProc) { SpinRelease(ProcStructLock); elog (FATAL,"cannot create new proc: out of memory"); } /* this cannot be initialized until after the buffer pool */ SHMQueueInit(&(MyProc->lockQueue)); MyProc->procId = ProcGlobal->numProcs; ProcGlobal->numProcs++; } /* * zero out the spin lock counts and set the sLocks field for ProcStructLock * to * 1 as we have acquired this spinlock above but didn't record it since * we didn't have MyProc until now. */ bzero(MyProc->sLocks, sizeof(MyProc->sLocks)); MyProc->sLocks[ProcStructLock] = 1; MyProc->sem.semId = IpcSemaphoreCreate(ProcGlobal->currKey, 1, IPCProtection, IpcSemaphoreDefaultStartValue, &semstat); IpcSemaphoreLock(MyProc->sem.semId, 0, IpcExclusiveLock); /* ------------- * Bump key val for next process * ------------- */ MyProc->sem.semKey = ProcGlobal->currKey; ProcGlobal->currKey++; /* ---------------------- * Release the lock. * ---------------------- */ SpinRelease(ProcStructLock); MyProc->sem.semNum = 0; MyProc->pid = MyPid; /* ---------------- * Start keeping spin lock stats from here on. Any botch before * this initialization is forever botched * ---------------- */ bzero(MyProc->sLocks, MAX_SPINS*sizeof(*MyProc->sLocks)); /* ------------------------- * Install ourselves in the binding table. The name to * use is determined by the OS-assigned process id. That * allows the cleanup process to find us after any untimely * exit. * ------------------------- */ pid = getpid(); location = MAKE_OFFSET(MyProc); if ((! ShmemPIDLookup(pid,&location)) || (location != MAKE_OFFSET(MyProc))) { elog(FATAL,"InitProc: ShmemPID table broken"); } MyProc->errType = NO_ERROR; SHMQueueElemInit(&(MyProc->links)); on_exitpg(ProcKill, pid); ProcInitialized = TRUE; } /* * ProcReleaseLocks() -- release all locks associated with this process * */ void ProcReleaseLocks() { if (!MyProc) return; LockReleaseAll(1,&MyProc->lockQueue); } bool ProcSemaphoreKill(pid) int pid; { SHMEM_OFFSET location,ShmemPIDDestroy(); PROC *proc; location = INVALID_OFFSET; ShmemPIDLookup(pid,&location); if (location == INVALID_OFFSET) return(FALSE); proc = (PROC *) MAKE_PTR(location); IpcSemaphoreKill(proc->sem.semKey); return(TRUE); } /* * ProcKill() -- Destroy the per-proc data structure for * this process. Release any of its held spin locks. */ bool ProcKill(exitStatus, pid) int exitStatus; int pid; { PROC *proc; SHMEM_OFFSET location,ShmemPIDDestroy(); /* -------------------- * If this is a FATAL exit the postmaster will have to kill all the existing * backends and reinitialize shared memory. So all we don't need to do * anything here. * -------------------- */ if (exitStatus != 0) return(TRUE); if (! pid) { pid = getpid(); } location = ShmemPIDDestroy(pid); if (location == INVALID_OFFSET) return(FALSE); proc = (PROC *) MAKE_PTR(location); if (proc != MyProc) Assert( pid != getpid() ); else MyProc = NULL; /* --------------- * Assume one lock table. * --------------- */ ProcReleaseSpins(proc); LockReleaseAll(1,&proc->lockQueue); /* ---------------- * get off the wait queue * ---------------- */ if (proc->links.next != INVALID_OFFSET) SHMQueueDelete(&(proc->links)); SHMQueueElemInit(&(proc->links)); SpinAcquire(ProcStructLock); /* ---------------------- * If ProcGlobal hasn't been initialized (i.e. in the postmaster) * attach to it. * ---------------------- */ if (!ProcGlobal) { bool found = false; ProcGlobal = (PROC_HDR *) ShmemInitStruct("Proc Header",(unsigned)sizeof(PROC_HDR),&found); if (!found) elog(NOTICE, "ProcKill: Couldn't find ProcGlobal in the binding table"); ProcGlobal = (PROC_HDR *)NULL; return(FALSE); } proc->links.next = ProcGlobal->freeProcs; ProcGlobal->freeProcs = MAKE_OFFSET(proc); SpinRelease(ProcStructLock); return(TRUE); } /* * ProcQueue package: routines for putting processes to sleep * and waking them up */ /* * ProcQueueAlloc -- alloc/attach to a shared memory process queue * * Returns: a pointer to the queue or NULL * Side Effects: Initializes the queue if we allocated one */ PROC_QUEUE * ProcQueueAlloc(name) char *name; { Boolean found; PROC_QUEUE *queue = (PROC_QUEUE *) ShmemInitStruct(name,(unsigned)sizeof(PROC_QUEUE),&found); if (! queue) { return(NULL); } if (! found) { ProcQueueInit(queue); } return(queue); } /* * ProcQueueInit -- initialize a shared memory process queue */ void ProcQueueInit(queue) PROC_QUEUE *queue; { SHMQueueInit(&(queue->links)); queue->size = 0; } /* * ProcSleep -- put a process to sleep * * P() on the semaphore should put us to sleep. The process * semaphore is cleared by default, so the first time we try * to acquire it, we sleep. * * ASSUME: that no one will fiddle with the queue until after * we release the spin lock. * * NOTES: The process queue is now a priority queue for locking. */ int ProcSleep(queue, spinlock, token, prio, lock) PROC_QUEUE *queue; SPINLOCK spinlock; int token; int prio; LOCK * lock; { int i; PROC *proc; struct itimerval timeval, dummy; proc = (PROC *) MAKE_PTR(queue->links.prev); for (i=0;isize;i++) { if (proc->prio < prio) proc = (PROC *) MAKE_PTR(proc->links.prev); else break; } MyProc->token = token; MyProc->waitLock = lock; /* ------------------- * currently, we only need this for the ProcWakeup routines * ------------------- */ TransactionIdStore((TransactionId) GetCurrentTransactionId(), &MyProc->xid); /* ------------------- * assume that these two operations are atomic (because * of the spinlock). * ------------------- */ SHMQueueInsertTL(&(proc->links),&(MyProc->links)); queue->size++; SpinRelease(spinlock); /* -------------- * Postgres does not have any deadlock detection code and for this * reason we must set a timer to wake up the process in the event of * a deadlock. For now the timer is set for 1 minute and we assume that * any process which sleeps for this amount of time is deadlocked and will * receive a SIGALRM signal. The handler should release the processes * semaphore and abort the current transaction. * * Need to zero out struct to set the interval and the micro seconds fields * to 0. * -------------- */ bzero(&timeval, sizeof(struct itimerval)); timeval.it_value.tv_sec = 60; if (setitimer(ITIMER_REAL, &timeval, &dummy)) elog(FATAL, "ProcSleep: Unable to set timer for process wakeup"); /* -------------- * if someone wakes us between SpinRelease and IpcSemaphoreLock, * IpcSemaphoreLock will not block. The wakeup is "saved" by * the semaphore implementation. * -------------- */ IpcSemaphoreLock(MyProc->sem.semId, MyProc->sem.semNum, IpcExclusiveLock); /* --------------- * We were awoken before a timeout - now disable the timer * --------------- */ timeval.it_value.tv_sec = 0; if (setitimer(ITIMER_REAL, &timeval, &dummy)) elog(FATAL, "ProcSleep: Unable to diable timer for process wakeup"); /* ---------------- * We were assumed to be in a critical section when we went * to sleep. * ---------------- */ SpinAcquire(spinlock); return(MyProc->errType); } /* * ProcWakeup -- wake up a process by releasing its private semaphore. * * remove the process from the wait queue and set its links invalid. * RETURN: the next process in the wait queue. */ PROC * ProcWakeup(proc, errType) PROC *proc; int errType; { PROC *retProc; /* assume that spinlock has been acquired */ retProc = (PROC *) MAKE_PTR(proc->links.prev); SHMQueueDelete(&(proc->links)); SHMQueueElemInit(&(proc->links)); proc->errType = errType; IpcSemaphoreUnlock(proc->sem.semId, proc->sem.semNum, IpcExclusiveLock); return retProc; } /* * ProcGetId -- */ int ProcGetId() { return( MyProc->procId ); } /* * ProcLockWakeup -- routine for waking up processes when a lock is * released. */ int ProcLockWakeup( queue, ltable, lock) PROC_QUEUE *queue; char * ltable; char * lock; { PROC *proc; int count; if (! queue->size) return(STATUS_NOT_FOUND); proc = (PROC *) MAKE_PTR(queue->links.prev); count = 0; while ((LockResolveConflicts ((LOCKTAB *) ltable, (LOCK *) lock, proc->token, proc->xid, proc->pid) == STATUS_OK)) { /* there was a waiting process, grant it the lock before waking it * up. This will prevent another process from seizing the lock * between the time we release the lock master (spinlock) and * the time that the awoken process begins executing again. */ GrantLock((LOCK *) lock, proc->token); queue->size--; /* * ProcWakeup removes proc from the lock waiting process queue and * returns the next proc in chain. If a writer just dropped * its lock and there are several waiting readers, wake them all up. */ proc = ProcWakeup(proc, NO_ERROR); count++; if (queue->size == 0) break; } if (count) return(STATUS_OK); else /* Something is still blocking us. May have deadlocked. */ return(STATUS_NOT_FOUND); } void ProcAddLock(elem) SHM_QUEUE *elem; { SHMQueueInsertTL(&MyProc->lockQueue,elem); } /* -------------------- * We only get to this routine I sleep for a minute * waiting for a lock to be released by some other process. After * the one minute deadline we assume we have a deadlock and must abort * this transaction. We must also indicate that I'm no longer waiting * on a lock so that other processes don't try to wake me up and screw * up my semaphore. * -------------------- */ int HandleDeadLock() { LOCK *lock; LOCKT lockt; LockLockTable(); /* --------------------- * Check to see if we've been awoken by anyone in the interim. * * If we have we can return and resume our transaction -- happy day. * Before we are awoken the process releasing the lock grants it to * us so we know that we don't have to wait anymore. * * Damn these names are LONG! -mer * --------------------- */ if (IpcSemaphoreGetCount(MyProc->sem.semId, MyProc->sem.semNum) == IpcSemaphoreDefaultStartValue) { UnlockLockTable(); return 1; } lock = MyProc->waitLock; lockt = (LOCKT) MyProc->token; /* ---------------------- * Decrement the holders fields since we can wait no longer. * the name hodlers is misleading, it represents the sum of the number * of active holders (those who have acquired the lock), and the number * of waiting processes trying to acquire the lock. * ---------------------- */ LockDecrWaitHolders(lock, lockt); /* ------------------------ * Get this process off the lock's wait queue * ------------------------ */ SHMQueueDelete(&MyProc->links); SHMQueueElemInit(&(MyProc->links)); lock->waitProcs.size--; UnlockLockTable(); /* ------------------ * Unlock my semaphore, so that the count is right for next time. * I was awoken by a signal not by someone unlocking my semaphore. * ------------------ */ IpcSemaphoreUnlock(MyProc->sem.semId, MyProc->sem.semNum, IpcExclusiveLock); elog(NOTICE, "Timeout -- possible deadlock"); /* ------------- * Set MyProc->errType to STATUS_ERROR so that we abort after * returning from this handler. * ------------- */ MyProc->errType = STATUS_ERROR; } void ProcReleaseSpins(proc) PROC *proc; { int i; if (!proc) proc = MyProc; if (!proc) return; for (i=0; i < (int)MAX_SPINS; i++) { if (proc->sLocks[i]) { Assert(proc->sLocks[i] == 1); SpinRelease(i); } } } @ 1.13 log @transaction ids are now longs, needed some casts (size change might cause problems for lmgr routines and the hash tags @ text @d33 1 a33 1 * $Header: /private/mer/pg.latest/src/storage/lmgr/RCS/proc.c,v 1.12 1992/04/13 19:03:52 mer Exp mer $ d278 1 a278 1 * toss the wait queue d283 1 d467 1 a469 1 SHMQueueElemInit(&(proc->links)); d507 1 a507 1 &proc->xid, d594 1 @ 1.12 log @make shm queue links invalid after removing fromt the lock wait queue @ text @d33 1 a33 1 * $Header: /a/postgres/ctree/newconf/RCS/proc.c,v 1.11 1992/04/01 22:45:23 mer Exp $ d391 1 a391 2 TransactionIdStore((TransactionId) GetCurrentTransactionId(), (char *) &MyProc->xid); @ 1.11 log @sometimes try to release locks before Myproc is initted (e.g. elog(FATAL)) @ text @d33 1 a33 1 * $Header: /users/mer/pg/src/storage/lmgr/RCS/proc.c,v 1.10 1992/02/25 21:39:14 mer Exp mer $ d436 1 d453 3 d457 1 a457 1 void d462 1 d465 1 d469 1 d472 2 d517 4 a520 3 ProcWakeup(proc, NO_ERROR); /* get next proc in chain. If a writer just dropped its lock * and there are several waiting readers, wake them all up. d522 1 a522 1 proc = (PROC *) MAKE_PTR(proc->links.prev); @ 1.10 log @bug fix via purify @ text @d33 1 a33 1 * $Header: /users/mer/pg/src/storage/lmgr/RCS/proc.c,v 1.9 1991/11/14 19:40:04 kemnitz Exp mer $ d210 2 @ 1.9 log @protos checkin. @ text @d33 1 a33 1 * $Header: RCS/proc.c,v 1.8 91/08/29 23:52:03 mer Exp Locker: kemnitz $ a138 1 d142 8 @ 1.8 log @purge old lmgr code @ text @d33 1 a33 1 * $Header: /users/mer/postgres/src/storage/lmgr/RCS/proc.c,v 1.7 1991/08/26 18:03:00 mer Exp $ a234 1 void ProcReleaseSpins(); d382 2 a383 1 TransactionIdStore(GetCurrentTransactionId(), &MyProc->xid); d475 2 a476 2 Address ltable; Address lock; d486 2 a487 2 while ((LockResolveConflicts (ltable, lock, d497 1 a497 1 GrantLock(lock, proc->token); @ 1.7 log @changes to support parallel backends @ text @d33 1 a33 1 * $Header: RCS/proc.c,v 1.6 91/08/14 12:28:46 mer Exp Locker: mer $ d38 7 a46 6 #include "storage/shmem.h" #include "storage/spin.h" #include "storage/lock.h" #include "utils/hsearch.h" #include "storage/buf.h" #include "utils/log.h" d74 1 a81 2 int HandleDeadLock(); int ProcKill(); d200 1 d206 1 d228 1 d333 1 d355 1 d444 1 d462 1 d472 1 d517 1 @ 1.6 log @multi user fixes @ text @d33 1 a33 1 * $Header: RCS/proc.c,v 1.5 91/08/12 15:16:41 mer Exp $ d47 2 d165 1 a165 1 MyProc->backendId = MyBackendId; d482 1 a482 1 proc->backendId) == STATUS_OK)) @ 1.5 log @multi-user fixes and functionality additions @ text @d33 1 a33 1 * $Header: RCS/proc.c,v 1.4 91/07/29 07:17:30 mer Exp $ d153 1 a162 1 MyProc->sem.semKey = key; d203 17 d222 1 a222 1 * this process. d224 2 a225 1 ProcKill(pid) d232 9 d256 1 a256 1 d258 1 a258 1 * assume one lock table a267 1 IpcSemaphoreKill((proc->sem.semKey)); d401 1 a401 1 timeval.it_value.tv_sec = 15; @ 1.4 log @check for and handle deadlocks. @ text @d33 1 a33 1 * $Header: RCS/proc.c,v 1.3 91/07/23 19:55:22 mer Exp Locker: mer $ d79 1 d87 2 d94 1 a94 2 * We're the first - initialize. Postmaster will always be 1st * so there's no worry about locking here. d106 1 a110 13 /* ----------------- * Critical section ... want to read and update ProcGlobal * * Acquiring the spin lock here bothers me as we go off and do a bunch * of stuff including allocating shared memory which fiddles with another * spin lock. This just seemed like an invitation for deadlock, so in * the else clause of this if I release and re-acquire the lock to try * to avoid these problems, at the expense of some extra overhead. * mer 18 July 1991 * ----------------- */ SpinAcquire(ProcStructLock); a118 7 MyProc->sem.semId = IpcSemaphoreCreate(ProcGlobal->currKey, 1, IPCProtection, IpcSemaphoreDefaultStartValue, &semstat); IpcSemaphoreLock(MyProc->sem.semId, 0, IpcExclusiveLock); a121 6 /* ---------------- * release the lock before going and getting shared memory * ---------------- */ SpinRelease(ProcStructLock); d131 1 a137 6 /* ------------------ * Reacquire the lock before modifying ProcGlobal * ------------------ */ SpinAcquire(ProcStructLock); d140 1 a140 6 MyProc->sem.semId = IpcSemaphoreCreate(ProcGlobal->currKey, 1, IPCProtection, IpcSemaphoreDefaultStartValue, &semstat); IpcSemaphoreLock(MyProc->sem.semId, 0, IpcExclusiveLock); d142 6 a147 5 /* ------------- * Bump key val for next process * ------------- */ ProcGlobal->currKey++; d149 5 a153 1 } d156 1 a156 1 * Release the lock once and for all d163 1 d165 4 a168 4 /* ------------------ * Bump the current key up by one. The next process then creates * a semaphore with a unique key. * ------------------ d170 1 a170 1 ProcGlobal->currKey++; d180 2 a181 2 location = myOffset; if ((! ShmemPIDLookup(pid,&location)) || (location != myOffset)) d189 2 d212 1 a213 3 if (! ProcInitialized) return; d234 2 a235 1 LockReleaseAll(1,&MyProc->lockQueue); d244 19 d266 1 d375 1 a375 1 timeval.it_value.tv_sec = 60; d388 9 d450 5 a454 2 while ((LockResolveConflicts ( ltable, lock, proc->token, &proc->xid ) == STATUS_OK)) d556 21 @ 1.3 log @move out structs for shmem size computation in lock.c @ text @d33 1 a33 1 * $Header: RCS/proc.c,v 1.2 91/07/22 15:21:43 mer Exp Locker: mer $ d35 2 d38 2 a41 1 #include "storage/proc.h" d78 1 d80 6 d329 1 a329 1 ProcSleep(queue,spinlock,token,prio) d334 1 d338 1 d350 1 d369 18 d479 71 @ 1.2 log @get rid of sem.h include @ text @d33 1 a33 1 * $Header: RCS/proc.c,v 1.1 91/07/22 14:50:28 mer Exp Locker: mer $ a57 41 /* * Each backend has: */ typedef struct proc { /* proc->links MUST BE THE FIRST ELEMENT OF STRUCT (see ProcWakeup()) */ SHM_QUEUE links; /* proc can be waiting for one event(lock) */ SEMA sem; /* ONE semaphore to sleep on */ int errType; /* error code tells why we woke up */ int procId; /* unique number for this structure * NOT unique per backend, these things * are reused after the backend dies. */ int critSects; /* If critSects > 0, we are in sensitive * routines that cannot be recovered when * the process fails. */ int prio; /* priority for sleep queue */ TransactionIdData xid; /* transaction currently being executed * by this proc */ int token; /* info for proc wakeup routines */ SHM_QUEUE lockQueue; /* locks associated with current transaction */ } PROC; /* * Global variables for this module. * This is only used for garbage collection. */ typedef struct procglobal { SHMEM_OFFSET freeProcs; int numProcs; IPCKey currKey; } PROC_HDR; @ 1.1 log @Initial revision @ text @d33 1 a33 1 * $Header$ a36 1 #include "storage/sem.h" @