⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 readme

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻
📖 第 1 页 / 共 2 页
字号:
process already holds locks on this same lockable object that conflictwith the request of any pending waiter, then the process will beinserted in the wait queue just ahead of the first such waiter.  (If wedid not make this check, the deadlock detection code would adjust thequeue order to resolve the conflict, but it's relatively cheap to makethe check in ProcSleep and avoid a deadlock timeout delay in this case.) Note special case when inserting before the end of the queue: if theprocess's request does not conflict with any existing lock nor anywaiting request before its insertion point, then go ahead and grant thelock without waiting.When a lock is released, the lock release routine (ProcLockWakeup) scansthe lock object's wait queue.  Each waiter is awoken if (a) its requestdoes not conflict with already-granted locks, and (b) its request doesnot conflict with the requests of prior un-wakable waiters.  Rule (b)ensures that conflicting requests are granted in order of arrival. Thereare cases where a later waiter must be allowed to go in front ofconflicting earlier waiters to avoid deadlock, but it is notProcLockWakeup's responsibility to recognize these cases; instead, thedeadlock detection code will re-order the wait queue when necessary.To perform deadlock checking, we use the standard method of viewing thevarious processes as nodes in a directed graph (the waits-for graph orWFG).  There is a graph edge leading from process A to process B if Awaits for B, ie, A is waiting for some lock and B holds a conflictinglock.  There is a deadlock condition if and only if the WFG contains acycle.  We detect cycles by searching outward along waits-for edges tosee if we return to our starting point.  There are three possibleoutcomes:1. All outgoing paths terminate at a running process (which has nooutgoing edge).2. A deadlock is detected by looping back to the start point.  Weresolve such a deadlock by canceling the start point's lock request andreporting an error in that transaction, which normally leads totransaction abort and release of that transaction's held locks.  Notethat it's sufficient to cancel one request to remove the cycle; we don'tneed to kill all the transactions involved.3. Some path(s) loop back to a node other than the start point.  Thisindicates a deadlock, but one that does not involve our startingprocess. We ignore this condition on the grounds that resolving such adeadlock is the responsibility of the processes involved --- killing ourstart- point process would not resolve the deadlock.  So, cases 1 and 3both report "no deadlock".Postgres' situation is a little more complex than the standard discussionof deadlock detection, for two reasons:1. A process can be waiting for more than one other process, since theremight be multiple PROCLOCKs of (non-conflicting) lock types that allconflict with the waiter's request.  This creates no real difficultyhowever; we simply need to be prepared to trace more than one outgoingedge.2. If a process A is behind a process B in some lock's wait queue, andtheir requested locks conflict, then we must say that A waits for B, sinceProcLockWakeup will never awaken A before B.  This creates additionaledges in the WFG.  We call these "soft" edges, as opposed to the "hard"edges induced by locks already held.  Note that if B already holds anylocks conflicting with A's request, then their relationship is a hard edgenot a soft edge.A "soft" block, or wait-priority block, has the same potential forinducing deadlock as a hard block.  However, we may be able to resolvea soft block without aborting the transactions involved: we can insteadrearrange the order of the wait queue.  This rearrangement reverses thedirection of the soft edge between two processes with conflicting requestswhose queue order is reversed.  If we can find a rearrangement thateliminates a cycle without creating new ones, then we can avoid an abort.Checking for such possible rearrangements is the trickiest part of thealgorithm.The workhorse of the deadlock detector is a routine FindLockCycle() whichis given a starting point process (which must be a waiting process).It recursively scans outward across waits-for edges as discussed above.If it finds no cycle involving the start point, it returns "false".(As discussed above, we can ignore cycles not involving the start point.)When such a cycle is found, FindLockCycle() returns "true", and as itunwinds it also builds a list of any "soft" edges involved in the cycle.If the resulting list is empty then there is a hard deadlock and theconfiguration cannot succeed.  However, if the list is not empty, thenreversing any one of the listed edges through wait-queue rearrangementwill eliminate that cycle.  Since such a reversal might create cycleselsewhere, we may need to try every possibility.  Therefore, we need tobe able to invoke FindLockCycle() on hypothetical configurations (waitorders) as well as the current real order.The easiest way to handle this seems to be to have a lookaside table thatshows the proposed new queue order for each wait queue that we areconsidering rearranging.  This table is checked by FindLockCycle, and itbelieves the proposed queue order rather than the real order for each lockthat has an entry in the lookaside table.We build a proposed new queue order by doing a "topological sort" of theexisting entries.  Each soft edge that we are currently consideringreversing creates a property of the partial order that the topological sorthas to enforce.  We must use a sort method that preserves the inputordering as much as possible, so as not to gratuitously break arrivalorder for processes not involved in a deadlock.  (This is not true of thetsort method shown in Knuth, for example, but it's easily done by a simpledoubly-nested-loop method that emits the first legal candidate at eachstep.  Fortunately, we don't need a highly efficient sort algorithm, sincethe number of partial order constraints is not likely to be large.)  Notethat failure of the topological sort tells us we have conflicting orderingconstraints, and therefore that the last-added soft edge reversalconflicts with a prior edge reversal.  We need to detect this case toavoid an infinite loop in the case where no possible rearrangement willwork: otherwise, we might try a reversal, find that it still leads toa cycle, then try to un-reverse the reversal while trying to get rid ofthat cycle, etc etc.  Topological sort failure tells us the un-reversalis not a legitimate move in this context.So, the basic step in our rearrangement method is to take a list ofsoft edges in a cycle (as returned by FindLockCycle()) and successivelytry the reversal of each one as a topological-sort constraint added towhatever constraints we are already considering.  We recursively searchthrough all such sets of constraints to see if any one eliminates allthe deadlock cycles at once.  Although this might seem impossiblyinefficient, it shouldn't be a big problem in practice, because therewill normally be very few, and not very large, deadlock cycles --- ifany at all.  So the combinatorial inefficiency isn't going to hurt us.Besides, it's better to spend some time to guarantee that we've checkedall possible escape routes than to abort a transaction when we didn'treally have to.Each edge reversal constraint can be viewed as requesting that the waitingprocess A be moved to before the blocking process B in the wait queue theyare both in.  This action will reverse the desired soft edge, as well asany other soft edges between A and other processes it is advanced over.No other edges will be affected (note this is actually a constraint on ourtopological sort method to not re-order the queue more than necessary.)Therefore, we can be sure we have not created any new deadlock cycles ifneither FindLockCycle(A) nor FindLockCycle(B) discovers any cycle.  Giventhe above-defined behavior of FindLockCycle, each of these searches isnecessary as well as sufficient, since FindLockCycle starting at theoriginal start point will not complain about cycles that include A or Bbut not the original start point.In short then, a proposed rearrangement of the wait queue(s) is determinedby one or more broken soft edges A->B, fully specified by the output oftopological sorts of each wait queue involved, and then tested by invokingFindLockCycle() starting at the original start point as well as each ofthe mentioned processes (A's and B's).  If none of the tests detect acycle, then we have a valid configuration and can implement it byreordering the wait queues per the sort outputs (and then applyingProcLockWakeup on each reordered queue, in case a waiter has become wakable).If any test detects a soft cycle, we can try to resolve it by adding eachsoft link in that cycle, in turn, to the proposed rearrangement list.This is repeated recursively until we either find a workable rearrangementor determine that none exists.  In the latter case, the outer levelresolves the deadlock by aborting the original start-point transaction.The particular order in which rearrangements are tried depends on theorder FindLockCycle() happens to scan in, so if there are multipleworkable rearrangements of the wait queues, then it is unspecified whichone will be chosen.  What's more important is that we guarantee to tryevery queue rearrangement that could lead to success.  (For example,if we have A before B before C and the needed order constraints areC before A and B before C, we would first discover that A before Cdoesn't work and try the rearrangement C before A before B.  This wouldeventually lead to the discovery of the additional constraint B before C.)Got that?Miscellaneous notes:1. It is easily proven that no deadlock will be missed due to ourasynchronous invocation of deadlock checking.  A deadlock cycle in the WFGis formed when the last edge in the cycle is added; therefore the lastprocess in the cycle to wait (the one from which that edge is outgoing) iscertain to detect and resolve the cycle when it later runs CheckDeadLock.This holds even if that edge addition created multiple cycles; the processmay indeed abort without ever noticing those additional cycles, but wedon't particularly care.  The only other possible creation of deadlocks isduring deadlock resolution's rearrangement of wait queues, and we alreadysaw that that algorithm will prove that it creates no new deadlocks beforeit attempts to actually execute any rearrangement.2. It is not certain that a deadlock will be resolved by aborting thelast-to-wait process.  If earlier waiters in the cycle have not yet runCheckDeadLock, then the first one to do so will be the victim.3. No live (wakable) process can be missed by ProcLockWakeup, since itexamines every member of the wait queue (this was not true in the 7.0implementation, BTW).  Therefore, if ProcLockWakeup is always invokedafter a lock is released or a wait queue is rearranged, there can be nofailure to wake a wakable process.  One should also note thatLockWaitCancel (abort a waiter due to outside factors) must runProcLockWakeup, in case the canceled waiter was soft-blocking otherwaiters.4. We can minimize excess rearrangement-trial work by being careful toscan the wait queue from the front when looking for soft edges.  Forexample, if we have queue order A,B,C and C has deadlock conflicts withboth A and B, we want to generate the "C before A" constraint first,rather than wasting time with "C before B", which won't move C farenough up.  So we look for soft edges outgoing from C starting at thefront of the wait queue.5. The working data structures needed by the deadlock detection code canbe limited to numbers of entries computed from MaxBackends.  Therefore,we can allocate the worst-case space needed during backend startup. Thisseems a safer approach than trying to allocate workspace on the fly; wedon't want to risk having the deadlock detector run out of memory, elsewe really have no guarantees at all that deadlock will be detected.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -