<<

A Fast

Leslie Lamport November 14, 1985 revised October 31, 1986 This report appeared in the ACM Transactions on Computer Systems, Volume 5, Number 1, February 1987, Pages 1–11.

c Digital Equipment Corporation 1988

This work may not be copied or reproduced in whole or in part for any com- mercial purpose. Permission to copy in whole or in part without payment of fee is granted for nonprofit educational and research purposes provided that all such whole or partial copies include the following: a notice that such copying is by permission ofthe Systems Research Center ofDigital Equipment Corporation in Palo Alto, ; an acknowledgment of the authors and individual contributors to the work; and all applicable portions ofthe copyright notice. Copying, reproducing, or republishing for any other purpose shall require a license with payment offeeto the Systems Research Center. All rights reserved. Author’s Abstract A new solution to the mutual exclusion problem is presented that, in the absence ofcontention, requires only seven memory accesses. It assumes atomic reads and atomic writes to shared registers.

Capsule Review To build a useful computing system from a collection of processors that com- municate by sharing memory, but lack any atomic operation more complex than a memory read or write, it is necessary to implement mutual exclusion using only these operations. Solutions to this problem have been known for twenty years, but they are linear in the number of processors. Lamport presents a new algorithm which takes constant time (five writes and two reads) in the absence ofcontention, which is the normal case. To achieve this performance it sacrifices fairness, which is probably unimportant in practical applications. The paper gives an informal argument that the algorithm’s performance in the absence of contention is optimal, and a fairly formal proof of safety and freedom from deadlock, using a slightly modified Owicki-Gries method. The proofs are extremely clear, and use very little notation.

Butler Lampson iv Contents

1 Introduction 1

2 The 2

3 Correctness Proofs 6 3.1MutualExclusion...... 7 3.2DeadlockFreedom...... 11

References 15 vi 1 Introduction

The mutual exclusion problem—guaranteeing mutually exclusive access to a critical section among a number ofcompeting processes—is well known, and many solutions have been published. The original version ofthe prob- lem, as presented by Dijkstra [2], assumed a shared memory with atomic read and write operations. Since the early 1970s, solutions to this version have been oflittle practical interest. Ifthe concurrent processes are being time-shared on a single processor, then mutual exclusion is easily achieved by inhibiting hardware interrupts at crucial times. On the other hand, multiprocessor computers have been built with atomic test-and-set instruc- tions that permitted much simpler mutual exclusion algorithms. Since about 1974, researchers have concentrated on finding algorithms that use a more restricted form of shared memory or that use message passing instead of shared memory. Oflate, the original version ofthe problem has not been widely studied. Recently, there has arisen interest in building shared-memory multipro- cessor computers by connecting standard processors and memories, with as little modification to the hardware as possible. Because ordinary sequen- tial processors and memories do not have atomic test-and-set operations, it is worth investigating whether shared-memory mutual exclusion algorithms are a practical alternative. Experience gained since shared-memory mutual exclusion algorithms were first studied seems to indicate that the early solutions were judged by criteria that are not relevant in practice. A great deal ofeffort went into developing algorithms that do not allow a process to wait longer than it “should” while other processes are entering and leaving the critical sec- tion [1, 3, 6]. However, the current beliefamong operating system designers is that contention for a critical section is rare in a well-designed system; most ofthe time, a process will be able to enter without having to wait [5]. Even an algorithm that allows an individual process to wait forever (be “starved”) by other processes entering the critical section is considered acceptable, since such starvation is unlikely to occur. This beliefshould perhaps be classified as folklore, since there does not appear to be enough experience with multi- processor operating systems to assert it with great confidence. Nevertheless, in this paper it is accepted as fact, and solutions are judged by how fast they are in the absence ofcontention. Ofcourse, a solution must not take much too long or lead to deadlock when there is contention. With modern high-speed processors, an operation that accesses shared

1 memory takes much more time than one that can be performed locally. Hence, the number ofreads and writes to shared memory is a good measure ofan algorithm’s execution time. All the published N-process solutions that I know ofrequire a process to execute O(N) operations to shared memory in the absence ofcontention. This paper presents a solution that does only five writes and two reads ofshared memory in this case. An even faster solution is also given, but it requires an upper bound on how long a process can remain in its critical section. An informal argument is given to suggest that these algorithms are optimal.

2 The Algorithms

Each process is assumed to have a unique identifier, which for convenience is taken to be a positive integer. Atomic reads and writes are permitted to single words ofmemory, which are assumed to be long enough to hold a process number. The critical section and all code outside the mutual exclusion protocol are assumed not to modify any variables used by the algorithms. Perhaps the simplest possible algorithm is one suggested by Michael Fischer, in which process number i executes the following algorithm, where x is a word ofshared memory, angle brackets enclose atomic operations, and await b is an abbreviation for while ¬b do skip:

repeat await  x =0;  x := i ;  delay  until  x = i ; critical section; x := 0

The delay operation causes the process to wait sufficiently long so that, if another process j had read the value of x in its await statement before process i executed its x := i statement, then j will have completed the following x := j statement. It is traditional to make no assumption about process speeds because, when processes time-share a processor, a process can be delayed for quite a long time between successive operations. However, assumptions about execution times may be permissible in a true multipro- cessor ifthe algorithm can be executed by a low-level operating system routine with hardware interrupts disabled. Indeed, an algorithm with busy

2 waiting should never be used ifcontending processes can share a processor, since a waiting process i could be tying up a processor needed to run the other process that i is waiting for. The algorithm above appears to require a total ofonly five memory access times in the absence ofcontention, since the delay must wait foronly a single memory access to occur. However, the delay must be for the worst case access time. Since there could be N − 1 processes contending for access to the memory, the worst case time must be at least O(N) times the best case (most probable) time needed to perform a memory access.1 Moreover, in computer systems that use a static priority for access to memory, there may not even be an upper bound to the time taken by a memory access. Therefore, an algorithm that has such a delay in the absence of contention is not acceptable. Before constructing a better algorithm, let us consider the minimum se- quence ofmemory accesses needed to guarantee mutual exclusion starting from the initial state of the system. The goal is an algorithm that requires a fixed number ofmemory accesses, independent of N, in the absence of contention. The argument is quite informal, some assertions having such flimsy justification that they might better be called assumptions, and the conclusion could easily be wrong. But even ifit should be wrong, the ar- gument can guide the search for a more efficient algorithm, since such an algorithm must violate some assertion in the proof. Delays long enough to ensure that other processes have done something seem to require O(N) time because ofpossible memory contention, so we may assume that no delay operations are executed. Therefore, only mem- ory accesses need be considered. Let Si denote the sequence ofwrites and reads executed by process i in entering its critical section when there is no contention—that is, the sequence executed when every read returns either the initial value or a value written by an earlier operation in Si. There is no point having a process write a variable that is not read by another process. Any access by Si to a memory word not accessed by Sj can play no part in preventing both i and j from entering the critical section at the same time. Therefore, in a solution using the minimal num- ber ofmemory references, all the Si should access the same set ofmemory words. (Remember that Si consists ofthe accesses performed in the absence

1Memory contention is not necessarily caused by processes contending for the critical section; it could result from processes accessing other words stored in the same memory module as x. Memory contention may be much more probable than contention for the critical section.

3 ofcontention.) Since the number ofmemory words accessed is fixed, inde- pendent of N,byincreasingN we can guarantee that there are arbitrarily many processes i for which Si consists ofthe identical sequence ofwrites and reads—that is, identical except for the actual values that are written, which may depend upon i. Therefore, by restricting our attention to those pro- cesses, we may assume with no loss ofgenerality that every process accesses the same memory words in the same order. ThereisnopointmakingthefirstoperationinSi a read, since all pro- cesses could execute the read and find the initial value before any process executes its next step. So, the first operation in Si should be a write of some variable x. It obviously makes no sense for the second operation in Si to be another write to x. There is also no reason to make it a write to another variable y, since the two writes could be replaced by a single write to a longer word. (In this lower bound argument, no limit on word length need be assumed.) Therefore, the second operation in Si should be a read. This operation should not be a read of x because the second operation of each process could be executed immediately after its first operation, with no intervening operations from other processes, in which case every process reads exactly what it had just written and obtains no new information. Therefore, each process must perform a write to x followed by a read of another variable y. There is no reason to read a variable that is not written or write a variable that is not read, so Si must also contain a read of x and awriteofy. The last operation in Si, which is the last operation performed before entering the critical section in the absence ofcontention, should not be a write because that write could not help the process decide whether or not to enter the critical section. Therefore, the best possible algorithm is one in which Si consists ofthe sequence write x, read y, write y, read x—a sequence that is abbreviated as w-x, r-y, w-y, r-x. Let us assume that Si is ofthis form. Thus each process first writes x, then reads y. Ifit finds that y has its initial value, then it writes y and reads x. Ifit finds that x has the value it wrote in its first operation, then it enters the critical section. After executing its critical section, a process must execute at least one write operation to indicate that the critical section is vacant, so processes entering later realize there is no contention. The process cannot do this with a write of x, since every process writes x as the first access to shared memory when performing the protocol. Therefore, a process must write y, resetting y to its initial value, after exiting the critical section. Thus, the minimum sequence ofmemory accesses in the absence ofcon-

4 start:  x := i ; if  y =0  then goto start fi;  y := i ; if  x = i  then delay; if  y = i  then goto start fifi; critical section;  y := 0 

Figure 1: Algorithm 1—process i’s program. tention that a mutual exclusion algorithm must perform is: w-x, r-y, w-y, r-x, critical section, w-y. This is the sequence ofmemory accesses performed by Algorithm 1 in Figure 1, where y is initially zero, the initial value of x is irrelevant, and the program for process number i is shown. It is described in this form, with goto statements, to put the operations performed in the absence ofconflict at the leftmargin. The delay in the second then clause must be long enough so that, if another process j read y equal to zero in the first if statement before i set y equal to i,thenj will either enter the second then clause or else execute the critical section and reset y to zero before i finishes executing the delay statement. (This delay is allowed because it is executed only if there is contention.) It is shown in Section 3 that this algorithm guarantees mutual exclusion and is deadlock free. However, an individual process may be starved. Algorithm 1 requires an upper bound not only on the time required to perform an individual operation such as a memory reference, but also on the time needed to execute the critical section. While such an upper bound may exist and be reasonably small in some applications, this is not usually the case. In most situations, an algorithm that does not require this upper bound is needed. Let us consider how many memory accesses such an algorithm must perform in the absence of contention. Remember that the minimal protocol to enter the critical section had to be ofthe form w-x, r-y, w-y, r-x. Consider the following sequence of operations performed by processes 1, 2, and 3 in executing this protocol, where subscripts denote the process performing an operation:

w2-x, w1-x, r1-y, r2-y, w1-y, w2-y, r1-x, w3-x, r2-x At this point, process 1 can enter its critical section. However, the values that process 1 wrote in x and y have been overwritten without having been

5 start:  b[i]:=true ;  x := i ; if  y =0  then  b[i]:=false ; await  y =0; goto start fi;  y := i ; if  x = i  then  b[i]:=false ; for j := 1 to N do await ¬b[j]  od; if  y = i  then await  y =0; goto start fifi; critical section;  y := 0 ;  b[i]:=false 

Figure 2: Algorithm 2—process i’s program. seen by any other process. The state is the same as it would have been had process 1 not executed any ofits operations. Process 2 has discovered that there is contention, but has no way ofknowing that process 1 is in its critical section. Since no assumption about how long a process can stay in its critical section is allowed, process 1 must set another variable to indicate that it is in its critical section, and must reset that variable to indicate that it has left the critical section. Thus, an optimal algorithm must involve two more memory accesses (in the case ofno contention) than Algorithm 1. Such an algorithm is given in Figure 2, where b[i] is a Boolean variable initially set to false. Like Algorithm 1, this algorithm guarantees mutual exclusion and is deadlock free, but allows starvation of individual processes. In private correspondence, Gary Peterson has described a modified ver- sion ofAlgorithm 2 that is starvation free. However, it requires one addi- tional memory reference in the absence of contention.

3 Correctness Proofs

There are two properties ofthe algorithms to be proved: mutual exclusion and deadlock freedom, the latter meaning that, if a process is trying to enter its critical section, then some process (perhaps a different one) eventually is in its critical section.

6 α:  x := i ; β: if  y =0  then goto α fi; γ:  y := i ; δ  {Pi } δ: if  x = i  then achieve Pi ;  {Pi } : if  y = i  then goto α fifi; cs {Pi } [ζ: critical section]; cs {Pi } η:  y := 0 

Figure 3: A generic algorithm—process i’s program.

The proofs for both algorithms are based upon the “generic” algorithm ofFigure 3, where the program forprocess i is shown. This program dif- fers from Algorithm 1 in the following ways: (i) labels have been added, (ii) assertions, enclosed in curly braces, have been attached, (iii) the critical section is enclosed in square brackets, whose meaning is explained below, and (iv) the delay has been replaced by an achieve statement. The achieve statement represents some unspecified code to guarantee that, ifand when  it is finished executing, the assertion Pi is true. More precisely, it represents a sequence ofatomic operations that, iffinite, includes one operation that   makes Pi true and no later operations that make Pi false. It is clear that this generic algorithm represents Algorithm 1 ifthe achieve statement is implemented by the delay. For the purpose ofproving mutual exclusion, it also adequately represents Algorithm 2 ifthe achieve statement is implemented by the for loop in the second then clause. This is because, to enter its critical section, a process executes the same sequence of reads and writes of x and y in the generic algorithm as in Algorithm 2. The await y = 0 statements and the reads and writes ofthe b[i] in Algorithm 2 can be viewed as delays in the execution ofthe generic algorithm. Adding delays to a program, even infinite delays, cannot invalidate a safety prop- erty such as mutual exclusion. Hence, the mutual exclusion property ofthe generic algorithm will imply the same property for Algorithm 2. The ade- quacy ofthe generic algorithm forproving deadlock freedom ofAlgorithm 2 is discussed below.

3.1 Mutual Exclusion Mutual exclusion is a safety property, and safety properties are usually proved by assertional reasoning—for example, with the Owicki–Gries meth- od [8]. However, since Algorithm 1 is based upon timing considerations,

7 it cannot be proved correct with ordinary assertional methods, so a hybrid proofis given. The assertions in Figure 3 are for a proof with the Owicki–Gries method, as described by us in [7] and Owicki and Gries in [8]. As explained below, a slight generalization ofthe usual Owicki–Gries method is used. Each assertion is attached to a control point, except that the square brackets cs surrounding the critical section indicate that the assertion Pi is attached to every control point within the critical section. Let Ai denote the assertion that is true ifand only ifprocess i is at a control point whose attached assertion is true, where the trivial assertion true is attached to all control points with no explicit assertion. One proves that i Ai is always true by proving that it is true ofthe initial state and that, for every i: i Sequential Correctness Executing any atomic action ofprocess in a state with j Aj true leaves Ai true. This is essentially a Floyd-style proof[4] ofprocess i, except that one can assume, for all j = i,that Aj is true before executing an action of i. (The assumption that Aj is true provides a more powerful proof method than the standard Owicki– Gries method, in the sense that simpler assertions may be used.) Interference Freedom For each j = i, executing any atomic action of process j in a state in which Ai and Aj aretrueleavesAi true. This proves that executing an action ofprocess j cannot falsify an assertion attached to process i.

The assertions are chosen so that the truth of Ai ∧Aj implies that processes i and j are not both in their critical sections. That is, the intersection ofthe assertions attached to points in the critical sections of i and j equals false. Assertions explicitly mention process control points, as in [7], instead of encoding them with dummy variables as Owicki and Gries did in [8]. The assertion at(λi) is true ifand only ifcontrol in process i is just before the statement labeled λ. The assertion in(csi) is true ifand only ifcontrol in process i is at the beginning ofthe critical section, within it, or right after it (and at the beginning ofstatement η). The assertions in Figure 3 are defined as follows: δ Pi : x = i ⊃ y =0  Pi : y = i ⊃∀j : ¬(at(γj) ∨ at(δj) ∨ in(csj)) cs Pi : y =0 ∧∀j = i:[¬in(csj)] ∧ [(at(γj ) ∨ at(δj )) ⊃ x = j] cs cs  Note that Pi ∧ Pj ≡ false,soprovingthat i Ai is always true establishes the desired mutual exclusion property.

8 Since no assertions are attached to the entry point ofthe algorithm, or to the rest ofa process’s program, i Ai is true initially. The proofof sequential correctness for process i requires the following verifications: δ • Executing γ leaves Pi true. This is obvious, since γ sets y equal to i, and i =0. • If the test in statement δ finds x = i,causingi to enter the critical cs δ section, then Pi is true. The assumed truth of Pi before the test implies that y>0. It is obvious that, for any j = i,(at(γj )∨at(δj)) ⊃ x = j is true, since x = i implies that x = j. The truth of ¬in(csj) is proved as follows. We may assume that Aj is true before i executes the test. Since at(δi)istrue,Aj implies that if in(csj) is true, then cs Pj is true, so x = i. Hence, if in(csj) is true before executing the test, then the test must find x = i and not enter the critical section. (The assumption that Aj is true is crucial; a more complicated program annotation is needed for a standard Owicki–Gries style proof.)   • Upon termination of the achieve Pi statement, Pi is true. This is the assumed semantics ofthe achieve statement. • If the test in statement  finds y = i,causingi to enter the critical cs section, then Pi is true. Since i = 0, the first conjunct (y =0)of cs Pi is obviously true ifexecuting  causes i to enter its critical section.  The assumed truth of Pi before executing  implies that, if y = i, then for all j = i: ¬(at(γj) ∨ at(δj ) ∨ in(csj)) is true. This in turn cs implies the truth ofthe second conjunct of Pi before the execution of , which implies the truth ofthat conjunct after the execution of , since executing the test does not affect control in any other process. cs • Executing any step of the critical section leaves Pi true. This follows from the implicit assumption that a process does not modify x or y while in the critical section, and the fact that executing one process does not affect control in another process. The second part ofthe Owicki–Gries method proof,showing noninter- ference, requires proving that no action by another process j can falsify any ofthe assertions attached to process i. Note that the implication A ⊃ B canbefalsifiedonlybymakingA true or B false.

δ δ Pi : Process i is the only one that sets x to i, so process j can falsify Pi only by setting y to zero. It does this only by executing statement η.

9 cs However, the assertion Pj , which is assumed to be true when j exe- cutes η, states that, ifprocess i is at control point δ,thenx = i,in δ which case setting y to zero does not falsify Pi .

 Pi : Only process i sets y to i,soj canfalsifythisassertiononlybyreaching control point γ or δ or by entering its critical section when y = i. However, it cannot reach δ without being at γ, it can reach γ only by executing the test at β and finding y = 0, and, ifit is not at δ,it can enter its critical section only by executing the test at  and finding y = j,noneofwhicharepossiblewheny = i.

cs cs Pi :SincePi asserts that no other process is at control point η,noother process can make y = 0 become false. To show that no other process j can make in(csj) become true, observe that it can do so only in two ways: (i) by executing the test at statement δ with x = j, or (ii) by cs executing  and finding y = j. The first is impossible because Pi asserts that if j is at δ then x = j, and the second is impossible  because Pj , which is assumed to be true at that point, asserts that if y = j then in(csi) is false, contrary to the hypothesis.

Finally, we must show that process j cannot falsify (at(γj) ∨ at(δj)) ⊃ x = j. It could do this only by reaching control point γ,whichitcan do only by executing the test in statement β and finding y equal to cs zero. However, this is impossible because Pi asserts that y =0.

This completes the proofofthe mutual exclusion property for the generic algorithm ofFigure 3. To prove that Algorithms 1 and 2 satisfythis prop- erty, it is necessary to prove that the program for process i correctly imple-  ments the achieve Pi statement. In these proofs, control points in the two algorithms will be labeled by the same names as the corresponding control points in the generic algorithm. Thus,  is the control point just before the if test in the second then clause. Let γ–η denote the set ofcontrol points consisting of γ, δ,allcontrol points in the critical section, and η. For Algorithm 1, we must show that, ifat the end ofthe delay y = i, then no other process j has control in γ–η. Since no other process can set y to i,ify equals i upon completion ofthe delay, then it must have equaled i at the beginning ofthe delay. Ifprocess j has not yet entered γ–η by the time i began executing the delay statement, then it cannot enter before the end of the delay statement, because the only way j can enter γ–η is by executing β when y =0or when y = j,bothof

10 which are impossible with y = i. By assumption, the delay is chosen to be long enough so that any process in γ–η at the beginning ofthe delay will have exited before the end ofthe delay. Hence, at the end ofthe delay, no  processisinγ–η,soPi is true. This completes the proofofmutual exclusion forAlgorithm 1. Note  how behavioral reasoning was used to prove that Pi holds after the delay. An assertional proofofthis property would be quite difficult, requiring the introduction ofan explicit clock and complicated axioms about the duration ofoperations. It is not difficult to convert the proofforthe generic algorithm into a completely assertional proof for Algorithm 2, and this will be left as an exercise for the reader who wants a completely rigorous proof. A less formal behavioral proofis given here. Once again, we must prove that, if y = i when control reaches , then no other process j is in γ–η. As in Algorithm 1, if y equals i when process i reaches , then it must have equaled i throughout the execution ofthe for statement. Hence, ifprocess j is outside γ–η some time during the execution of i’s for statement, then it is not in γ–η when i reaches . However, b[j] is true when process j is in γ–η.Toreach, process i must find b[j] false when executing the for loop, so j was not in γ–η at that time and is thus not in it when i reaches . This completes the proofofmutual exclusion forAlgorithm 2.

3.2 Deadlock Freedom Deadlock freedom means that, if a process tries to enter the critical section, then it or some other process must eventually be in the critical section. This is a liveness property, which can be proved formally using temporal logic— for example, with the method of Owicki and Lamport [9]. However, only an informal sketch of the proof will be given. The reader who is well versed in temporal logic will be able to flesh out the informal proof into a formal one in the style ofOwicki and Lamport. Once again, correctness is proved first for the generic algorithm of Fig- ure 3. Let in(δi) be true ifand only ifcontrol in process i is at the beginning ofor within the statement δ, but not within the then clause of .Deadlock freedom rests upon the following safety property:

S. y = i =0 ⊃ (in(δi) ∨ in(csi))

It is a simple matter to show that this assertion is true initially and is left true by every program action, so it is always true.

11 For convenience, the proofwill be expressed in terms ofsome simple temporal assertions—assertions that are true or false at a certain time during the execution. For any temporal assertions P and Q, the assertion ✷P (read henceforth P )istrueatsomeinstantifandonlyifP is true then and at all later times; and P ❀ Q (read P leads to Q)istrueatsomeinstantifP is false then, or Q is true then or at some future time. A precise semantics of the temporal operators ✷ and ❀ can be found in [9]. Deadlock freedom is expressed by the formula at(αi) ❀ ∃j : in(csj), which is proved by assuming that at(αi)and✷(∀j : ¬in(csj)) are true and obtaining a contradiction. (This is a proofby contradiction, based upon the temporal logic tautology ¬(P ❀ Q) ≡ (P ∧ ✷¬Q).) The proofis done by demonstrating a sequence of ❀ relations (A1 ❀ A2, A2 ❀ A3, etc.) leading to false, which is the required contradiction. Note that when one ofthese relations is ofthe form P ❀ Q ∧ ✷R, we can assume that ✷R is true in all the subsequent proofs. (Once ✷R becomes true, it remains true forever.) Also note that P ⊃ Q implies P ❀ Q. The proofrequires the following assumption about the achieve state- ment:

T. Ifprocess i executes the achieve statement with ✷(y = i ∧∀j : ¬in(csj)) true, then that statement will terminate.

The sequence of ❀ relations is given below. at(αi) ❀ y = 0 Process i either finds y = 0 in statement β or else sets y to i in the following statement. y =0 ⊃ ✷y =0 Oncey is nonzero, it can be set to zero only by some process executing statement η. However, this cannot happen since we are assuming ✷∀j : ¬in(csj).

(✷y =0) ❀ ∃j : ✷y = j Once y becomes and remains forever nonzero, no process can reach statement γ that has not already done so. Even- tually, all the processes that are at γ will execute γ, after which the value of y remains the same.

(✷y = j) ❀ at(j)BytheinvariantS,y = j implies in(csj) ∨ in(δj). Since we have assumed ✷¬in(csj), this implies that control in process j is within δ and, ifit is at the beginning of δ, must find x = j.By Assumption T, this implies that control in process j must eventually reach .

12 (✷y = j ∧ at(j)) ❀ false Process j must eventually execute the test in state- ment , find y = j, and enter the critical section, contradicting the assumption ✷¬in(csj).

This completes the proof of deadlock freedom for the generic algorithm. Since Assumption T is obviously true for Algorithm 1, this proves dead- lock freedom for Algorithm 1. For Algorithm 2, observe that the proof for the generic algorithm remains valid even if the two goto’s can be de- layed indefinitely. Thus, the proofholds for Algorithm 2 even though a process can remain forever in an await y =0 statement. To prove the deadlock freedom of Algorithm 2, it suffices to prove Assumption T, that ✷(y = i ∧∀j : ¬in(csj)) implies that process i’s for loop eventually ter- minates. This is easy to see, since b[j] must eventually become false and remain forever false for every process j. A more formal proof, in the style of Owicki and Lamport [9], is left as an exercise for the reader.

Acknowledgements

I wish to thank Jeremy Dion and Michael Powell for bringing the problem to my attention. Michael Powell independently discovered the basic write x, read y, write y, read x mutual exclusion protocol used in the algorithms. I also wish to thank Michael Fischer for his comments on the problem and on the manuscript.

13 14 References

[1] N. G. deBruijn. Additional comments on a problem in concurrent pro- gramming control. Communications of the ACM, 8(9):137–138, March 1967.

[2] E. W. Dijkstra. Solution ofa problem in concurrent programming con- trol. Communications of the ACM, 8(9):569, September 1965.

[3] Murray A. Eisenberg and Michael R. McGuire. Further comments on Dijkstra’s concurrent programming control problem. Communications of the ACM, 15(11):999, November 1972.

[4] R. W. Floyd. Assigning meanings to programs. In Proceedings of the Symposium on Applied Math., Vol. 19, pages 19–32, American Mathe- matical Society, 1967.

[5] Anita K. Jones and Peter Schwarz. Experience using multiprocessor systems—A status report. ACM Computing Surveys, 12(2):121–165, June 1980.

[6] D. E. Knuth. Additional commments on a problem in concurrent pro- gram control. Communications of the ACM, 9(5):321, May 1966.

[7] Leslie Lamport. Proving the correctness ofmultiprocess programs. IEEE Transactions on Software Engineering, SE-3(2):125–143, March 1977.

[8] Susan Owicki and David Gries. An axiomatic prooftechnique forparallel programs. Acta Informatica, 6(4):319–340, 1976.

[9] Susan Owicki and Leslie Lamport. Proving liveness properties ofcon- current programs. ACM Transactions on Programming Languages and Systems, 4(3):455–495, July 1982.

15