📄 proc.c
字号:
/* * ProcKill() -- Destroy the per-proc data structure for * this process. Release any of its held LW locks. */static voidProcKill(void){ /* use volatile pointer to prevent code rearrangement */ volatile PROC_HDR *procglobal = ProcGlobal; Assert(MyProc != NULL); /* Release any LW locks I am holding */ LWLockReleaseAll(); /* * Make real sure we release any buffer locks and pins we might be * holding, too. It is pretty ugly to do this here and not in a * shutdown callback registered by the bufmgr ... but we must do this * *after* LWLockReleaseAll and *before* zapping MyProc. */ AbortBufferIO(); UnlockBuffers(); AtEOXact_Buffers(false); /* Get off any wait queue I might be on */ LockWaitCancel(); /* Remove from the standard lock table */ LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc, true, InvalidTransactionId);#ifdef USER_LOCKS /* Remove from the user lock table */ LockReleaseAll(USER_LOCKMETHOD, MyProc, true, InvalidTransactionId);#endif SpinLockAcquire(ProcStructLock); /* Return PGPROC structure (and semaphore) to freelist */ MyProc->links.next = procglobal->freeProcs; procglobal->freeProcs = MAKE_OFFSET(MyProc); /* PGPROC struct isn't mine anymore */ MyProc = NULL; SpinLockRelease(ProcStructLock);}/* * DummyProcKill() -- Cut-down version of ProcKill for dummy (checkpoint) * processes. The PGPROC and sema are not released, only marked * as not-in-use. */static voidDummyProcKill(void){ Assert(MyProc != NULL && MyProc == DummyProc); /* Release any LW locks I am holding */ LWLockReleaseAll(); /* Release buffer locks and pins, too */ AbortBufferIO(); UnlockBuffers(); AtEOXact_Buffers(false); /* I can't be on regular lock queues, so needn't check */ /* Mark DummyProc no longer in use */ MyProc->pid = 0; /* PGPROC struct isn't mine anymore */ MyProc = NULL;}/* * 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 */#ifdef NOT_USEDPROC_QUEUE *ProcQueueAlloc(char *name){ bool found; PROC_QUEUE *queue = (PROC_QUEUE *) ShmemInitStruct(name, sizeof(PROC_QUEUE), &found); if (!queue) return NULL; if (!found) ProcQueueInit(queue); return queue;}#endif/* * ProcQueueInit -- initialize a shared memory process queue */voidProcQueueInit(PROC_QUEUE *queue){ SHMQueueInit(&(queue->links)); queue->size = 0;}/* * ProcSleep -- put a process to sleep * * Caller must have set MyProc->heldLocks to reflect locks already held * on the lockable object by this process (under all XIDs). * * Locktable's masterLock must be held at entry, and will be held * at exit. * * Result: STATUS_OK if we acquired the lock, STATUS_ERROR if not (deadlock). * * ASSUME: that no one will fiddle with the queue until after * we release the masterLock. * * NOTES: The process queue is now a priority queue for locking. * * P() on the semaphore should put us to sleep. The process * semaphore is normally zero, so when we try to acquire it, we sleep. */intProcSleep(LOCKMETHODTABLE *lockMethodTable, LOCKMODE lockmode, LOCK *lock, PROCLOCK *proclock){ LWLockId masterLock = lockMethodTable->masterLock; PROC_QUEUE *waitQueue = &(lock->waitProcs); int myHeldLocks = MyProc->heldLocks; bool early_deadlock = false; PGPROC *proc; int i; /* * Determine where to add myself in the wait queue. * * Normally I should go at the end of the queue. However, if I already * hold locks that conflict with the request of any previous waiter, * put myself in the queue just in front of the first such waiter. * This is not a necessary step, since deadlock detection would move * me to before that waiter anyway; but it's relatively cheap to * detect such a conflict immediately, and avoid delaying till * deadlock timeout. * * Special case: if I find I should go in front of some waiter, check to * see if I conflict with already-held locks or the requests before * that waiter. If not, then just grant myself the requested lock * immediately. This is the same as the test for immediate grant in * LockAcquire, except we are only considering the part of the wait * queue before my insertion point. */ if (myHeldLocks != 0) { int aheadRequests = 0; proc = (PGPROC *) MAKE_PTR(waitQueue->links.next); for (i = 0; i < waitQueue->size; i++) { /* Must he wait for me? */ if (lockMethodTable->conflictTab[proc->waitLockMode] & myHeldLocks) { /* Must I wait for him ? */ if (lockMethodTable->conflictTab[lockmode] & proc->heldLocks) { /* * Yes, so we have a deadlock. Easiest way to clean * up correctly is to call RemoveFromWaitQueue(), but * we can't do that until we are *on* the wait queue. * So, set a flag to check below, and break out of * loop. Also, record deadlock info for later * message. */ RememberSimpleDeadLock(MyProc, lockmode, lock, proc); early_deadlock = true; break; } /* I must go before this waiter. Check special case. */ if ((lockMethodTable->conflictTab[lockmode] & aheadRequests) == 0 && LockCheckConflicts(lockMethodTable, lockmode, lock, proclock, MyProc, NULL) == STATUS_OK) { /* Skip the wait and just grant myself the lock. */ GrantLock(lock, proclock, lockmode); return STATUS_OK; } /* Break out of loop to put myself before him */ break; } /* Nope, so advance to next waiter */ aheadRequests |= (1 << proc->waitLockMode); proc = (PGPROC *) MAKE_PTR(proc->links.next); } /* * If we fall out of loop normally, proc points to waitQueue head, * so we will insert at tail of queue as desired. */ } else { /* I hold no locks, so I can't push in front of anyone. */ proc = (PGPROC *) &(waitQueue->links); } /* * Insert self into queue, ahead of the given proc (or at tail of * queue). */ SHMQueueInsertBefore(&(proc->links), &(MyProc->links)); waitQueue->size++; lock->waitMask |= (1 << lockmode); /* Set up wait information in PGPROC object, too */ MyProc->waitLock = lock; MyProc->waitHolder = proclock; MyProc->waitLockMode = lockmode; MyProc->errType = STATUS_OK; /* initialize result for success */ /* * If we detected deadlock, give up without waiting. This must agree * with CheckDeadLock's recovery code, except that we shouldn't * release the semaphore since we haven't tried to lock it yet. */ if (early_deadlock) { RemoveFromWaitQueue(MyProc); MyProc->errType = STATUS_ERROR; return STATUS_ERROR; } /* mark that we are waiting for a lock */ waitingForLock = true; /* * Release the locktable's masterLock. * * NOTE: this may also cause us to exit critical-section state, possibly * allowing a cancel/die interrupt to be accepted. This is OK because * we have recorded the fact that we are waiting for a lock, and so * LockWaitCancel will clean up if cancel/die happens. */ LWLockRelease(masterLock); /* * Set timer so we can wake up after awhile and check for a deadlock. * If a deadlock is detected, the handler releases the process's * semaphore and sets MyProc->errType = STATUS_ERROR, allowing us to * know that we must report failure rather than success. * * By delaying the check until we've waited for a bit, we can avoid * running the rather expensive deadlock-check code in most cases. */ if (!enable_sig_alarm(DeadlockTimeout, false)) elog(FATAL, "could not set timer for process wakeup"); /* * If someone wakes us between LWLockRelease and PGSemaphoreLock, * PGSemaphoreLock will not block. The wakeup is "saved" by the * semaphore implementation. Note also that if CheckDeadLock is * invoked but does not detect a deadlock, PGSemaphoreLock() will * continue to wait. There used to be a loop here, but it was useless * code... * * We pass interruptOK = true, which eliminates a window in which * cancel/die interrupts would be held off undesirably. This is a * promise that we don't mind losing control to a cancel/die interrupt * here. We don't, because we have no state-change work to do after * being granted the lock (the grantor did it all). */ PGSemaphoreLock(&MyProc->sem, true); /* * Disable the timer, if it's still running */ if (!disable_sig_alarm(false)) elog(FATAL, "could not disable timer for process wakeup"); /* * Now there is nothing for LockWaitCancel to do. */ waitingForLock = false; /* * Re-acquire the locktable's masterLock. */ LWLockAcquire(masterLock, LW_EXCLUSIVE); /* * We don't have to do anything else, because the awaker did all the * necessary update of the lock table and MyProc. */ return MyProc->errType;}/* * ProcWakeup -- wake up a process by releasing its private semaphore. * * Also remove the process from the wait queue and set its links invalid. * RETURN: the next process in the wait queue. * * XXX: presently, this code is only used for the "success" case, and only * works correctly for that case. To clean up in failure case, would need * to twiddle the lock's request counts too --- see RemoveFromWaitQueue. */PGPROC *ProcWakeup(PGPROC *proc, int errType){ PGPROC *retProc; /* assume that masterLock has been acquired */ /* Proc should be sleeping ... */ if (proc->links.prev == INVALID_OFFSET || proc->links.next == INVALID_OFFSET) return (PGPROC *) NULL; /* Save next process before we zap the list link */ retProc = (PGPROC *) MAKE_PTR(proc->links.next); /* Remove process from wait queue */ SHMQueueDelete(&(proc->links)); (proc->waitLock->waitProcs.size)--; /* Clean up process' state and pass it the ok/fail signal */ proc->waitLock = NULL; proc->waitHolder = NULL; proc->errType = errType; /* And awaken it */ PGSemaphoreUnlock(&proc->sem); return retProc;}/* * ProcLockWakeup -- routine for waking up processes when a lock is * released (or a prior waiter is aborted). Scan all waiters * for lock, waken any that are no longer blocked. */voidProcLockWakeup(LOCKMETHODTABLE *lockMethodTable, LOCK *lock){ PROC_QUEUE *waitQueue = &(lock->waitProcs); int queue_size = waitQueue->size; PGPROC *proc; int aheadRequests = 0; Assert(queue_size >= 0); if (queue_size == 0) return; proc = (PGPROC *) MAKE_PTR(waitQueue->links.next); while (queue_size-- > 0) { LOCKMODE lockmode = proc->waitLockMode; /* * Waken if (a) doesn't conflict with requests of earlier waiters, * and (b) doesn't conflict with already-held locks. */ if ((lockMethodTable->conflictTab[lockmode] & aheadRequests) == 0 && LockCheckConflicts(lockMethodTable, lockmode, lock, proc->waitHolder, proc, NULL) == STATUS_OK) { /* OK to waken */ GrantLock(lock, proc->waitHolder, lockmode);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -