📄 proc.c
字号:
* 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. */intProcSleep(PROC_QUEUE *waitQueue,/* lock->waitProcs */ LOCKMETHODCTL *lockctl, int token, /* lockmode */ LOCK *lock){ int i; SPINLOCK spinlock = lockctl->masterLock; PROC *proc; int myMask = (1 << token); int waitMask = lock->waitMask; int aheadHolders[MAX_LOCKMODES]; bool selfConflict = (lockctl->conflictTab[token] & myMask), prevSame = false; bool deadlock_checked = false; struct itimerval timeval, dummy; MyProc->token = token; MyProc->waitLock = lock; proc = (PROC *) MAKE_PTR(waitQueue->links.prev); /* if we don't conflict with any waiter - be first in queue */ if (!(lockctl->conflictTab[token] & waitMask)) goto ins; for (i = 1; i < MAX_LOCKMODES; i++) aheadHolders[i] = lock->activeHolders[i]; (aheadHolders[token])++; for (i = 0; i < waitQueue->size; i++) { /* am I waiting for him ? */ if (lockctl->conflictTab[token] & proc->holdLock) { /* is he waiting for me ? */ if (lockctl->conflictTab[proc->token] & MyProc->holdLock) { MyProc->errType = STATUS_ERROR; elog(NOTICE, DeadLockMessage); goto rt; } /* being waiting for him - go past */ } /* if he waits for me */ else if (lockctl->conflictTab[proc->token] & MyProc->holdLock) break; /* if conflicting locks requested */ else if (lockctl->conflictTab[proc->token] & myMask) { /* * If I request non self-conflicting lock and there are others * requesting the same lock just before me - stay here. */ if (!selfConflict && prevSame) break; } /* * Last attempt to don't move any more: if we don't conflict with * rest waiters in queue. */ else if (!(lockctl->conflictTab[token] & waitMask)) break; prevSame = (proc->token == token); (aheadHolders[proc->token])++; if (aheadHolders[proc->token] == lock->holders[proc->token]) waitMask &= ~(1 << proc->token); proc = (PROC *) MAKE_PTR(proc->links.prev); }ins:; /* ------------------- * assume that these two operations are atomic (because * of the spinlock). * ------------------- */ SHMQueueInsertTL(&(proc->links), &(MyProc->links)); waitQueue->size++; lock->waitMask |= myMask; SpinRelease(spinlock); /* -------------- * We set this so we can wake up periodically and check for a deadlock. * If a deadlock is detected, the handler releases the processes * semaphore and aborts the current transaction. * * Need to zero out struct to set the interval and the micro seconds fields * to 0. * -------------- */ MemSet(&timeval, 0, sizeof(struct itimerval)); timeval.it_value.tv_sec = \ (DeadlockCheckTimer ? DeadlockCheckTimer : DEADLOCK_CHECK_TIMER); do { MyProc->errType = NO_ERROR; /* reset flag after deadlock check */ if (!deadlock_checked) if (setitimer(ITIMER_REAL, &timeval, &dummy)) elog(FATAL, "ProcSleep: Unable to set timer for process wakeup"); deadlock_checked = true; /* -------------- * 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); } while (MyProc->errType == STATUS_NOT_FOUND); /* sleep after deadlock * check */ /* --------------- * 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);rt:;#ifdef LOCK_MGR_DEBUG /* Just to get meaningful debug messages from DumpLocks() */ MyProc->waitLock = (LOCK *) NULL;#endif 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 *proc, int errType){ PROC *retProc; /* assume that spinlock has been acquired */ if (proc->links.prev == INVALID_OFFSET || proc->links.next == INVALID_OFFSET) return (PROC *) NULL; retProc = (PROC *) MAKE_PTR(proc->links.prev); /* you have to update waitLock->waitProcs.size yourself */ SHMQueueDelete(&(proc->links)); SHMQueueElemInit(&(proc->links)); proc->errType = errType; IpcSemaphoreUnlock(proc->sem.semId, proc->sem.semNum, IpcExclusiveLock); return retProc;}/* * ProcLockWakeup -- routine for waking up processes when a lock is * released. */intProcLockWakeup(PROC_QUEUE *queue, LOCKMETHOD lockmethod, LOCK *lock){ PROC *proc; int count = 0; int trace_flag; int last_locktype = 0; int queue_size = queue->size; Assert(queue->size >= 0); if (!queue->size) return STATUS_NOT_FOUND; proc = (PROC *) MAKE_PTR(queue->links.prev); while ((queue_size--) && (proc)) { /* * This proc will conflict as the previous one did, don't even * try. */ if (proc->token == last_locktype) continue; /* * Does this proc conflict with locks held by others ? */ if (LockResolveConflicts(lockmethod, lock, proc->token, proc->xid, (XIDLookupEnt *) NULL) != STATUS_OK) { if (count != 0) break; last_locktype = proc->token; continue; } /* * 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, proc->token); /* * ProcWakeup removes proc from the lock waiting process queue and * returns the next proc in chain. */ count++; queue->size--; proc = ProcWakeup(proc, NO_ERROR); } Assert(queue->size >= 0); if (count) return STATUS_OK; else { /* Something is still blocking us. May have deadlocked. */ trace_flag = (lock->tag.lockmethod == USER_LOCKMETHOD) ? \ TRACE_USERLOCKS : TRACE_LOCKS; TPRINTF(trace_flag, "ProcLockWakeup: lock(%x) can't wake up any process", MAKE_OFFSET(lock));#ifdef DEADLOCK_DEBUG if (pg_options[trace_flag] >= 2) DumpAllLocks();#endif return STATUS_NOT_FOUND; }}voidProcAddLock(SHM_QUEUE *elem){ SHMQueueInsertTL(&MyProc->lockQueue, elem);}/* -------------------- * We only get to this routine if we got SIGALRM after DEADLOCK_CHECK_TIMER * while waiting for a lock to be released by some other process. If we have * a real deadlock, 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. * -------------------- */static voidHandleDeadLock(int sig){ LOCK *mywaitlock; 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; } /* * you would think this would be unnecessary, but... * * this also means we've been removed already. in some ports (e.g., * sparc and aix) the semop(2) implementation is such that we can * actually end up in this handler after someone has removed us from * the queue and bopped the semaphore *but the test above fails to * detect the semaphore update* (presumably something weird having to * do with the order in which the semaphore wakeup signal and SIGALRM * get handled). */ if (MyProc->links.prev == INVALID_OFFSET || MyProc->links.next == INVALID_OFFSET) { UnlockLockTable(); return; }#ifdef DEADLOCK_DEBUG DumpAllLocks();#endif MyProc->errType = STATUS_NOT_FOUND; if (!DeadLockCheck(MyProc, MyProc->waitLock)) { UnlockLockTable(); return; } mywaitlock = MyProc->waitLock; /* ------------------------ * Get this process off the lock's wait queue * ------------------------ */ Assert(mywaitlock->waitProcs.size > 0); --mywaitlock->waitProcs.size; SHMQueueDelete(&(MyProc->links)); SHMQueueElemInit(&(MyProc->links)); /* ------------------ * 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); /* ------------- * Set MyProc->errType to STATUS_ERROR so that we abort after * returning from this handler. * ------------- */ MyProc->errType = STATUS_ERROR; /* * if this doesn't follow the IpcSemaphoreUnlock then we get lock * table corruption ("LockReplace: xid table corrupted") due to race * conditions. i don't claim to understand this... */ UnlockLockTable(); elog(NOTICE, DeadLockMessage); return;}voidProcReleaseSpins(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); } }}/***************************************************************************** * *****************************************************************************//* * ProcGetNewSemKeyAndNum - * scan the free semaphore bitmap and allocate a single semaphore from * a semaphore set. (If the semaphore set doesn't exist yet, * IpcSemaphoreCreate will create it. Otherwise, we use the existing * semaphore set.) */static voidProcGetNewSemKeyAndNum(IPCKey *key, int *semNum){ int i; int32 *freeSemMap = ProcGlobal->freeSemMap; int32 fullmask = (1 << (PROC_NSEMS_PER_SET + 1)) - 1; /* * we hold ProcStructLock when entering this routine. We scan through * the bitmap to look for a free semaphore. */ for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++) { int mask = 1; int j; if (freeSemMap[i] == fullmask) continue; /* this set is fully allocated */ for (j = 0; j < PROC_NSEMS_PER_SET; j++) { if ((freeSemMap[i] & mask) == 0) { /* * a free semaphore found. Mark it as allocated. Also set * the bit indicating whole set is allocated. */ freeSemMap[i] |= mask + (1 << PROC_NSEMS_PER_SET); *key = ProcGlobal->currKey + i; *semNum = j; return; } mask <<= 1; } } /* if we reach here, all the semaphores are in use. */ elog(ERROR, "InitProc: cannot allocate a free semaphore");}/* * ProcFreeSem - * free up our semaphore in the semaphore set. */static voidProcFreeSem(IpcSemaphoreKey semKey, int semNum){ int mask; int i; int32 *freeSemMap = ProcGlobal->freeSemMap; i = semKey - ProcGlobal->currKey; mask = ~(1 << semNum); freeSemMap[i] &= mask; /* * Formerly we'd release a semaphore set if it was now completely * unused, but now we keep the semaphores to ensure we won't run out * when starting new backends --- cf. InitProcGlobal. Note that the * PROC_NSEMS_PER_SET+1'st bit of the freeSemMap entry remains set to * indicate it is still allocated; ProcFreeAllSemaphores() needs that. */}/* * ProcFreeAllSemaphores - * called at shmem_exit time, ie when exiting the postmaster or * destroying shared state for a failed set of backends. * Free up all the semaphores allocated to the lmgrs of the backends. */static voidProcFreeAllSemaphores(){ int i; int32 *freeSemMap = ProcGlobal->freeSemMap; for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++) { if (freeSemMap[i] != 0) IpcSemaphoreKill(ProcGlobal->currKey + i); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -