Algorithm Search⌘K

Proofs: Diffracting tree

These are the human proofs of Diffracting tree, at full length.

The diffracting tree is a quiescently consistent, lock-free counter full

This proof shows: correctness, quiescent-consistency, progress.

We use the pseudocode of the page, with width w=2kw = 2^k and w−1w - 1 balancers. Every read, write, compare-and-swap (CAS) and fetch-and-add on one word is atomic. Concurrent callers have distinct ids, and each thread runs at most one call at a time. A token is one call of GET-AND-INCREMENT.

Lemma 1 (the exchanger pairs symmetrically). If an exchange by token xx on slot ss returns y≠y \ne TIMEOUT, then yy is a token whose concurrent exchange on ss returned xx. An exchange that returns TIMEOUT is not returned by any other exchange.

Proof. Look at the writes to ss. From EMPTY, only a CAS at line L14 by some xx changes it, to (x,WAITING)(x, \text{WAITING}). From (x,WAITING)(x, \text{WAITING}), only two CASes can succeed: xx's own CAS at line L16, which expects its own id and sets EMPTY, or a CAS at line L18 by some yy, which sets (y,BUSY)(y, \text{BUSY}), and then yy returns xx. From (y,BUSY)(y, \text{BUSY}), no CAS of the code succeeds, since each expects EMPTY or WAITING, and the only plain writes are those of xx at lines L15 and L17. While xx waits after its successful line L14, it is the only thread that writes ss other than through line L18. So if yy's CAS succeeds, xx's CAS at line L16 fails, xx reads (y,BUSY)(y, \text{BUSY}) at line L15 or line L17, returns yy, and sets ss to EMPTY. If no such CAS by yy succeeds, xx's CAS at line L16 succeeds and xx returns TIMEOUT, and nobody returned xx. A token that returns at line L18 was paired with the waiting xx in the same way, so the pairing is a matching on tokens. □\square

Lemma 2 (one balancer). For a balancer bb, let o0o_0 and o1o_1 be the numbers of tokens that have returned side 0 and side 1 from BALANCE on bb. At any moment when no token is inside BALANCE on bb, o0−o1∈{0,1}o_0 - o_1 \in \{0, 1\}. If nn tokens have entered and left, o0=⌈n/2⌉o_0 = \lceil n/2 \rceil and o1=⌊n/2⌋o_1 = \lfloor n/2 \rfloor.

Proof. A token leaves either at line L9 or at line L11. By Lemma 1, the tokens that leave at line L9 come in pairs that met in a slot of bb. When no token is inside BALANCE on bb, both members of each pair have left, and since the ids differ, one of them took side 0 and the other side 1. So diffracted tokens add the same amount to o0o_0 and o1o_1. A token leaves at line L11 with the value tt that its successful CAS changed to 1−t1 - t. Successful CASes on b.toggleb.toggle are totally ordered, each flips the bit, and the bit starts at 0, so the values returned are 0,1,0,1,…0, 1, 0, 1, \ldots in this order. After mm successful CASes, side 0 got ⌈m/2⌉\lceil m/2 \rceil of them and side 1 got ⌊m/2⌋\lfloor m/2 \rfloor. Adding the pairs gives o0−o1=⌈m/2⌉−⌊m/2⌋∈{0,1}o_0 - o_1 = \lceil m/2 \rceil - \lfloor m/2 \rfloor \in \{0, 1\}, and with o0+o1=no_0 + o_1 = n the two counts are ⌈n/2⌉\lceil n/2 \rceil and ⌊n/2⌋\lfloor n/2 \rfloor. □\square

Lemma 3 (the step property of the tree). In a quiescent state in which nn tokens have entered the tree, wire ii has received ci=⌈(n−i)/w⌉c_i = \lceil (n - i)/w \rceil tokens, for 0≤i<w0 \le i < w.

Proof. In a quiescent state every token that entered a balancer has left it, so the tokens that enter a child are exactly the tokens that left its parent on that side. Induction on kk. For k=1k = 1 the tree is one balancer, and Lemma 2 gives c0=⌈n/2⌉c_0 = \lceil n/2 \rceil and c1=⌊n/2⌋=⌈(n−1)/2⌉c_1 = \lfloor n/2 \rfloor = \lceil (n - 1)/2 \rceil. For k>1k > 1, the root sends a=⌈n/2⌉a = \lceil n/2 \rceil tokens to the subtree of side 0 and b=⌊n/2⌋b = \lfloor n/2 \rfloor to the subtree of side 1. Both subtrees are diffracting trees of width w/2w/2, and the root has fixed bit 0 of the wire: wire jj of subtree dd is wire 2j+d2j + d of the tree. By induction, wire 2j2j gets ⌈(a−j)/(w/2)⌉\lceil (a - j)/(w/2) \rceil tokens. Since a−j=⌈(n−2j)/2⌉a - j = \lceil (n - 2j)/2 \rceil and ⌈⌈x/2⌉/(w/2)⌉=⌈x/w⌉\lceil \lceil x/2 \rceil / (w/2) \rceil = \lceil x/w \rceil for each integer xx, this is ⌈(n−2j)/w⌉\lceil (n - 2j)/w \rceil. In the same way b−j=⌈(n−1−2j)/2⌉b - j = \lceil (n - 1 - 2j)/2 \rceil, and wire 2j+12j + 1 gets ⌈(n−(2j+1))/w⌉\lceil (n - (2j + 1))/w \rceil. □\square

Lemma 4 (values in a quiescent state). In a quiescent state in which nn tokens have entered, the tokens have returned exactly the values 0,1,…,n−10, 1, \ldots, n - 1, each once.

Proof. Wire ii's counter was incremented by each of its cic_i tokens, by fetch-and-add, so they got k=0,…,ci−1k = 0, \ldots, c_i - 1 and returned i+wki + wk. A number v≥0v \ge 0 is i+wki + wk for exactly one pair, i=v mod wi = v \bmod w and k=⌊v/w⌋k = \lfloor v/w \rfloor, and k<⌈(n−i)/w⌉k < \lceil (n - i)/w \rceil holds exactly when i+wk<ni + wk < n. So the values returned are {0,…,n−1}\{0, \ldots, n - 1\}, each once. □\square

Theorem (quiescent consistency). Each history HH of the diffracting tree is quiescently consistent with respect to the sequential counter.

Proof. First complete HH. Extend the execution by letting only the pending calls take steps, in any fair order, and start no new call. By the theorem on progress below, and since only finitely many calls are left, they all return, and the extended history H′H' is HH with responses appended for its pending calls, a completion of HH that ends in a quiescent state. Let q1<q2<⋯<qrq_1 < q_2 < \cdots < q_r be the quiescent points of H′H', with qrq_r at the end, and let njn_j be the number of calls that returned before qjq_j. By Lemma 4 the calls that returned before qjq_j got exactly the values 0,…,nj−10, \ldots, n_j - 1, so the calls between qj−1q_{j-1} and qjq_j got exactly nj−1,…,nj−1n_{j-1}, \ldots, n_j - 1. Let SS list the calls of each segment in the order of their values, and the segments in order. SS is a legal sequential counter history with the same results as H′H', and if a quiescent point separates calls aa and bb, aa is in an earlier segment and precedes bb in SS. □\square

Theorem (lock-freedom). In every infinite execution in which some call is pending, infinitely many calls return.

Proof. Suppose that from some moment on no call returns. Each call passes kk balancers. In a balancer, the prism part (lines L7 to L9, with lines L12 to L19) ends within O(patience2)O(\text{patience}^2) own steps, and the fetch-and-add at line L5 is one step. So some thread must run the loop of line L10 for ever. Each turn of that loop that fails means that the toggle changed between its read and its CAS, which needs a successful CAS by another token, and each successful CAS ends that token's BALANCE on that balancer. No thread starts a new call before its current call returns, so at most pp calls are active, each with at most kk successful CASes. So after some moment no CAS at line L10 succeeds. Then no toggle changes, and the next CAS of a thread in the loop of line L10 succeeds. This contradicts the assumption. ■\blacksquare

Back to Diffracting tree