Proving linearizability using forward simulations
Ahmed Bouajjani, Michael Emmi, Constantin Enea, Suha Orhun Mutluergil

TL;DR
This paper demonstrates that many complex concurrent data structure implementations can be proven linearizable using only forward simulation, simplifying correctness proofs and enabling automation.
Contribution
It shows that forward simulation alone suffices for proving linearizability of complex implementations, challenging the belief that backward reasoning is necessary.
Findings
Many complex implementations can be proved correct with forward simulation.
Forward simulation proofs are simpler and more natural.
These proofs are suitable for automation.
Abstract
Linearizability is the standard correctness criterion concurrent data structures such as stacks and queues. It allows to establish observational refinement between a concurrent implementation and an atomic reference implementation.Proving linearizability requires identifying linearization points for each method invocation along all possible computations, leading to valid sequential executions, or alternatively, establishing forward and backward simulations. In both cases, carrying out proofs is hard and complex in general. In particular, backward reasoning is difficult in the context of programs with data structures, and strategies for identifying statically linearization points cannot be defined for all existing implementations. In this paper, we show that, contrary to common belief, many such complex implementations, including, e.g., the Herlihy&Wing Queue and the Time-Stamped Stack,…
Peer Reviews
No public reviews on file for this paper yet. If you reviewed it on a platform where reviews are public (OpenReview, ICLR, NeurIPS, ICML), you can paste yours below so the community can read it here.
Videos
No videos yet. Explain this paper in a talk, walkthrough, or lecture? Add one.
Taxonomy
TopicsDistributed systems and fault tolerance · Software System Performance and Reliability · Formal Methods in Verification
\mathlig
,.,,…, \mathlig,..,… \mathlig..,…, \mathlig–¿⟶ \mathlig¡=¿⇔ \mathlig==¿⟹ \mathlig—-¿↦ \mathlig¡-← \mathlig-¿→ \mathlig ¿⇀ \mathlig..… \mathlig/—∧ \mathlig—/∨ \mathlig=¿⇒ \mathlig¡=≤ \mathlig==≡ \mathlig =≈ \mathlig!=≠ \mathlig—-⊢ \mathlig—=⊧ \mathlig ∼
\newatcommanda \newatcommandb \newatcommandc \newatcommandC \newatcommandd \newatcommandD \newatcommande \newatcommandE \newatcommandf \newatcommandF \newatcommandg \newatcommandG \newatcommandh \newatcommandj \newatcommandk \newatcommandl \newatcommandll \newatcommandL \newatcommandm \newatcommandn \newatcommando \newatcommandoo \newatcommandO \newatcommandp \newatcommandP \newatcommandq \newatcommandQ \newatcommandr \newatcommands \newatcommandss \newatcommandS \newatcommandt \newatcommandu \newatcommandU \newatcommandv \newatcommandw \newatcommandW \newatcommandx \newatcommandy \newatcommandY \newatcommandz \newatcommand0 ∅ \newatcommand ∖
\reservestyle\mathfn
\reservestyle\metalangkeyword
\reservestyle\semanticdomain
\reservestyle\proglangkeyword \mathfnmin,max,log,dom,range \semanticdomainBools[B], Nats[N], Lab[A], Ints[Z], Reads[R], Writes[W], Events[E], Confs[C]
\semanticdomainMethods[M], Vals[V], Labels[L], Ops[O], Acts[A],
\semanticdomainVars, Ids[IDs], Frames, Stmts, Tasks, Configs, MsgQueues, Locks,
\proglangkeywordvar, const, bool, int, true, false, while[while ], do[ do ], if[if ], then[ then ], else[ else ], assume[assume ], return[return ], proc, call[call ], async, goto newThread, newLock, acquire, release, sendMsg[sendMsg ]
\metalangkeywordtrue,false \metalangkeywordret,op \mathfnpast,future,width \metalangkeywordpush,pop \metalangkeywordread,write,eventId,id,main
11institutetext: IRIF, University Paris Diderot & CNRS, 11email: {abou,cenea}@irif.fr 22institutetext: Nokia Bell Labs 22email: [email protected] 33institutetext: Koc University33email: [email protected]
Proving linearizability using forward simulations
Ahmed Bouajjani 11
Michael Emmi 22
Constantin Enea 11
Suha Orhun Mutluergil 33
Abstract
Linearizability is the standard correctness criterion concurrent data structures such as stacks and queues. It allows to establish observational refinement between a concurrent implementation and an atomic reference implementation. Proving linearizability requires identifying linearization points for each method invocation along all possible computations, leading to valid sequential executions, or alternatively, establishing forward and backward simulations. In both cases, carrying out proofs is hard and complex in general. In particular, backward reasoning is difficult in the context of programs with data structures, and strategies for identifying statically linearization points cannot be defined for all existing implementations. In this paper, we show that, contrary to common belief, many such complex implementations, including, e.g., the Herlihy&Wing Queue and the Time-Stamped Stack, can be proved correct using only forward simulation arguments. This leads to simple and natural correctness proofs for these implementations that are amenable to automation.
1 Introduction
Programming efficient concurrent implementations of atomic collections, e.g., stacks and queues, is error prone. To minimize synchronization overhead between concurrent method invocations, implementors avoid blocking operations like lock acquisition, allowing methods to execute concurrently. However, concurrency risks unintended inter-operation interference, and risks conformance to atomic reference implementations. Conformance is formally captured by (observational) refinement, which assures that all behaviors of programs using these efficient implementations would also be possible were the atomic reference implementations used instead.
Observational refinement can be formalized as a trace inclusion problem, and the latter can itself be reduced to an invariant checking problem, but this requires in general introducing history and prophecy variables [1]. Alternatively, verifying refinement requires in general establishing a forward simulation and a backward simulation [20]. While simulations are natural concepts, backward reasoning, corresponding to the use of prophecy variables, is in general hard and complex for programs manipulating data structures. Therefore, a crucial issue is to understand the limits of forward reasoning in establishing refinement. More precisely, an important question is to determine for which concurrent abstract data structures, and for which classes of implementations, it is possible to carry out a refinement proof using only forward simulations.
To get rid of backward simulations (or prophecy variables) while preserving completeness w.r.t. refinement, it is necessary to have reference implementations that are deterministic. Interestingly, determinism allows also to simplify the forward simulation checking problem. Indeed, in this case, this problem can be reduced to an invariant checking problem. Basically, the simulation relation can be seen as an invariant of the system composed of the two compared programs. Therefore, existing methods and tools for invariant checking can be leveraged in this context.
But, in order to determine precisely what is meant by determinism, an important point is to fix the alphabet of observable events along computations. Typically, to reason about refinement between two library implementations, the only observable events are the calls and returns corresponding to the method invocations along computations. This means that only the external interface of the library is considered to compare behaviors, and nothing else from the implementations is exposed. Unfortunately, it can be shown that in this case, it is impossible to have deterministic atomic reference implementations for common data structures such as stacks and queues (see, e.g., [24]). Then, an important question is what is the necessary amount of information that should be exposed by the implementations to overcome this problem ?
One approach addressing this question is based on linearizability [17] and its correspondence with refinement [11, 7]. Linearizability of a computation (of some implementation) means that each of the method invocations can be seen as happening at some point, called linearization point, occurring somewhere between the call and return events of that invocation. The obtained sequence of linearization points along the computation should define a sequence of operations that is possible in the atomic reference implementation. Proving the existence of such sequences of linearization points, for all the computations of a concurrent library, is a complex problem [3, 5, 13]. However, proving linearizability becomes less complex when linearization points are fixed for each method, i.e., associated with the execution of a designated statement in its source code [5]. In this case, we can consider that libraries expose in addition to calls and returns, events signaling linearization points. By extending this way the alphabet of observable events, it becomes straightforward to define deterministic atomic reference implementations. Therefore, proving linearizability can be carried out using forward simulations when linearization points are fixed, e.g., [28, 4, 27, 2]. Unfortunately, this approach is not applicable to efficient implementations such as the LCRQ queue [21] (based on the principle of the Herlihy&Wing queue [17]), and the Time-Stamped Stack [9]. The proofs of linearizability of these implementations are highly nontrivial, very involved, and hard to read, understand and automatize. Therefore, the crucial question we address is what is precisely the kind of information that is necessary to expose in order to obtain deterministic atomic reference implementations for such data structures, allowing to derive simple and natural linearizability proofs for such complex implementations, based on forward simulations, that are amenable to automation ?
We observe that the main difficulty in reasoning about these implementations is that, linearization points of enqueue/push operations occurring along some given computation, depend in general on the linearization points of dequeue/pop operations that occur arbitrarily far in the future. Therefore, since linearization points for enqueue/push operations cannot be determined in advance, the information that could be fixed and exposed can concern only the dequeue/pop operations.
One first idea is to consider that linearization points are fixed for dequeue/pop methods and only for these methods. We show that under the assumption that implementations expose linearizations points for these methods, it is possible to define deterministic atomic reference implementations for both queues and stacks. We show that this is indeed useful by providing a simple proof of the Herlihy&Wing queue (based on establishing a forward simulation) that can be carried out as an invariant checking proof.
However, in the case of Time-Stamped Stack, fixing linearization points of pop operations is actually too restrictive. Nevertheless, we show that our approach can be generalized to handle this case. The key idea is to reason about what we call commit points, and that correspond roughly speaking to the last point a method accesses to the shared data structure during its execution. We prove that by exposing commit points (instead of linearization points) for pop methods, we can still provide deterministic reference implementations. We show that using this approach leads to a quite simple proof of the Time-Stamped Stack, based on forward simulations.
2 Preliminaries
We formalize several abstraction relations between libraries using a simple yet universal model of computation, namely labeled transition systems (LTS). This model captures shared-memory programs with an arbitrary number of threads, abstracting away the details of any particular programming system irrelevant to our development.
A labeled transition system (LTS) over the possibly-infinite alphabet is a possibly-infinite set of states with initial state , and a transition relation . The th symbol of a sequence is denoted , and the empty sequence is denoted by . An execution of is an alternating sequence of states and transition labels (called also actions) for some such that for each such that . We write as shorthand for the subsequence of , for any (in particular ). The projection of a sequence is the maximum subsequence of over alphabet . This notation is extended to sets of sequences as usual. A trace of is the projection of an execution of . The set of executions, resp., traces, of an LTS is denoted by , resp., . An LTS is deterministic if for any state and any sequence , there is at most one state such that . More generally, for an alphabet , an LTS is -deterministic if for any state s and any sequence , there is at most one state such that and is a subsequence of .
2.1 Libraries
Programs interact with libraries by calling named library methods, which receive parameter values and yield return values upon completion. We fix arbitrary sets and of method names and parameter/return values. We fix an arbitrary set of operation identifiers, and for given sets and of methods and values, we fix the sets
[TABLE]
of call actions and return actions; each call action combines a method and value with an operation identifier . Operation identifiers are used to pair call and return actions. We may omit the second field from a call/return action for methods that have no inputs or return values. For notational convenience, we take for the rest of the paper.
A library is an LTS over alphabet such that . We assume that the traces of a library satisfy standard well-formedness properties, e.g., return actions correspond to previous call actions, which for lack of space are delegated to Appendix 0.A. An operation is called completed in a trace when occurs in , for some and . Otherwise, it is called pending.
The projection of a library trace over is called a history. The set of histories of a library is denoted by . Since libraries only dictate methods’ executions between their respective calls and returns, for any history they admit, they must also admit histories with weaker inter-operation ordering, in which calls may happen earlier, and/or returns later. A history is weaker than a history , written , iff there exists a history obtained from by appending return actions, and deleting call actions, s.t.: is a permutation of that preserves the order between return and call actions, i.e., if a given return action occurs before a given call action in , then the same holds in .
A library is called atomic when there exists a set of sequential histories such that contains every weakening of a history in . Atomic libraries are often considered as specifications for concurrent objects. Libraries can be made atomic by guarding their methods bodies with global lock acquisitions.
A library is called a queue implementation when ( is the method that enqueues a value and is the method removing a value) and where EMPTY is the value returned by when the queue is empty. Similarly, a library is called a stack implementation when and . For queue and stack implementations, we assume that the same value is never added twice, i.e., for every trace of such a library and every two call actions and where we have that . As shown in several works [2, 6], this assumption is without loss of generality for libraries that are data independent, i.e., their behaviors are not influenced by the values added to the collection, which is always the case in practice. On a technical note, this assumption is used to define (-)deterministic abstract implementations of stacks and queues in Section 4 and Section 5.
2.2 Refinement and Linearizability
Conformance of a library to a specification given as an “abstract” library is formally captured by (observational) refinement. Informally, we say refines iff every computation of every program using would also be possible were used instead. We assume that a program can interact with the library only through call and return actions, and thus refinement can be defined as history set inclusion. Refinement is equivalent to the linearizability criterion [17] when is an atomic library [11, 7].
Definition 1
A library refines another library iff .
Linearizability [17] requires that every history of a concurrent library can be “linearized” to a sequential history admitted by a library used as a specification. Formally, a sequential history with only complete operations is called a linearization of a history when . A history is linearizable w.r.t. a library iff there exists a linearization of such that . A library is linearizable w.r.t. , written , iff each history is linearizable w.r.t. .
Theorem 2.1 ([11, 7])
* iff refines , if is atomic.*
In the rest of the paper, we discuss methods for proving refinement (and thus, linearizability) focusing mainly on queue and stack implementations.
3 Refinement Proofs
Library refinement is the instance of a more general notion of refinement between LTSs which for some alphabet of observable actions is defined as the inclusion of sets of traces projected on . Library refinement corresponds to the case . Typically, -refinement between two LTSs and is proved using simulation relations which roughly, require that can mimic every step of using a (possibly empty) sequence of steps. Mainly, there are two kinds of simulation relations, forward or backward, depending on whether the preservation of steps is proved starting from a similar state forward or backward. It has been shown that -refinement is equivalent to the existence of backward simulations, modulo the addition of history variables that record events in the implementation, and to the existence of forward simulations provided that the right-hand side LTS is -deterministic [1, 20]. We focus on proofs based on forward simulations because they are easier to automatize.
In general, forward simulations are not a complete proof method for library refinement because libraries are not -deterministic (the same sequence of call/return actions can lead to different states depending on the interleaving of the internal actions). However, there are classes of atomic libraries, e.g., libraries with “fixed linearization points” (defined later in this section), for which it is possible to identify a larger alphabet of observable actions (including call/return actions), and implementations that are -deterministic. For queues and stacks, Section 4 and Section 5 define other such classes of implementations that cover all the implementations that we are aware of.
Let and be two libraries over and , resp., such that . Also, let be a set of actions s.t. .
Definition 2
The library -refines iff .
Notice that -refinement implies refinement for any as in Definition 2.
We define a notion of forward simulation that can be used to prove -refinement (a dual notion of backward simulation is defined in Appendix 0.B). For a relation , is the set of elements related by to elements of , i.e., .
Definition 3
A relation is called a -forward simulation from to iff and:
- •
If , for some , and , then there exists such that , , for some , and , for each .
- •
If , for some and , then there exists such that and .
A -forward simulation requires that every step of corresponds to a sequence of steps of . To imply -refinement, every step of labeled by an observable action should be simulated by a sequence of steps of where exactly one transition is labeled by and all the other transitions are labeled by non-observable actions.
The following shows the soundness and the completeness of -forward simulations (when is -deterministic). It is an instantiation of previous results [1, 20].
Theorem 3.1
* -refines when there is a -forward simulation from to . Moreover, if -refines and is -deterministic, then there is a -forward simulation from to .*
The linearization of a concurrent history can be also defined in terms of linearization points. Informally, a linearization point of an operation in an execution is a point in time where the operation is conceptually effectuated; given the linearization points of each operation, the linearization of a concurrent history is the sequential history which takes operations in order of their linearization points. For some libraries, the linearization points correspond to a fixed set of actions. For instance, in the case of atomic libraries where method bodies are guarded with a global-lock acquisition, the linearization point of every method invocation corresponds to the execution of the body. When the linearization points are fixed, we assume that the library is an LTS over an alphabet that includes actions with , and . The action represents the linearization point of the operation returning value . Let denote the set of such actions. The projection of a library trace over is called an extended history. A trace or extended history is called -complete when every completed operation has a linearization point, i.e., each return action is preceded by an action . A library over alphabet is called with fixed linearization points iff and every trace is -complete.
Proving the correctness of an implementation of a concurrent object such as a queue or a stack with fixed linearization points reduces to proving that is a -refinement of an abstract implementation of the same object where method bodies are guarded with a global-lock acquisition. Since the abstract implementation is usually -deterministic, by Theorem 3.1, proving -refinement is equivalent to finding a -forward simulation from to .
Section 4 and Section 5 extend this result to queue and stack implementations where the linearization point of the methods adding values to the collection is not fixed.
4 Queues With Fixed Dequeue Linearization Points
The typical abstract implementation of a concurrent queue, denoted as , maintains a sequence of values, the enqueue adds a value atomically to the beginning of the sequence, and the dequeue removes a value from the end of the sequence (if any, otherwise it returns EMPTY). Both methods have a fixed linearization point when the update of the sequence happens. For some queue implementations, e.g., the Herlihy&Wing Queue [17] ( for short), there exists no forward simulation to although they are a refinement of . The main reason is that the enqueue methods don’t have a fixed linearization point. In this section, we propose a new abstract implementation for queues, denoted as , which roughly maintains a partially-ordered set of values instead of a sequence. We show that there exists a forward simulation from any correct queue implementation where only the dequeue methods have fixed linearization points (the enqueue methods are unconstrained) to . This covers all the queue implementations that we are aware of, in particular , Baskets Queue [18], LCRQ [21], or Time-Stamped Queue [9] (where the enqueues don’t have fixed linearization points). We also describe a forward simulation from to .
4.1 Enqueue Methods With Non-Fixed Linearization Points
We describe where the linearization points of the enqueue methods are not fixed. The shared state consists of an array items storing the values in the queue and a counter back storing the index of the first unused position in items. Initially, all the positions in the array are null and back is 0. An enqueue method starts by reserving a position in items (i stores the index of this position and back is incremented so the same position can’t be used by other enqueues) and then, stores the input value x at this position. The dequeue method traverses the array items starting from the beginning and atomically swaps null with the encountered value. If the value is not null, then the dequeue returns that value. If it reaches the end of the array, then it restarts.
The linearization points of the enqueues are not fixed, they depend on dequeues executing in the future. Consider the following trace with two concurrent enqueues ( represents the value of i in operation ): , , , , . Assuming that the linearization point corresponds to the assignment of i, the history of this trace should be linearized to , , , . However, a dequeue executing until completion after this trace will return (only position is filled in the array items) which is not consistent with this linearization. On the other hand, assuming that enqueues should be linearized at the assignment of items[i] and extending the trace with and a completed dequeue that in this case returns , leads to the incorrect linearization: , , , , , .
The dequeue method has a fixed linearization point which corresponds to an execution of swap returning a non-null value. This action alone contributes to the effect of that value being removed from the queue. Every concurrent history can be linearized to a sequential history where dequeues occur in the order of their linearization points in the concurrent history. This claim is formally proved in Section 4.3.
Since the linearization points of the enqueues are not fixed, there exists no forward simulation from to . In the following, we describe the abstract implementation for which such a forward simulation does exist.
4.2 Abstract Queue Implementation
Informally, records the happens-before order between enqueue operations for which the added value has not been removed by a dequeue operation. The linearization point of a dequeue operation with return value is enabled only if the happens-before stored in the current state contains a minimal enqueue that adds the value . The effect of the linearization point is that the minimal enqueue is removed from the current state and the return value is recorded in the library state. When the return value is EMPTY, the linearization point of a dequeue is enabled only if the current state stores only pending enqueues (the dequeue overlaps with all the enqueue operations stored in the current state and it can be linearized before all of them). The return of a dequeue is enabled only if the returned value matches the one fixed at the linearization point.
Figure 2 pictures two executions of for two extended histories (that include dequeue linearization points). The state of after each action is pictured as a graph below the action. The nodes of this graph represent enqueue operations and the edges happens-before constraints. Each node is labeled by a value (the input of the enqueue) and a flag PEND or COMP showing whether the operation is pending or completed. For instance, in the case of the first history, the dequeue linearization point is enabled because the current happens-before contains a minimal enqueue operation with input . Note that a linearization point is also enabled at this state.
Formally, the states of are tuples where is a set of operation identifiers, is a strict partial order, labels every identifier with a value and a pending/completed flag (the flag is used to track the happens-before order), records the return value of a dequeue fixed at its linearization point ( denotes a partial function), and records the control point of every enqueue () or dequeue operation (). All the components are in the initial state, and the transition relation is defined in Fig. 3. The alphabet of contains call/return actions and dequeue linearization points, denoted by . is the set of all actions .
Concerning enqueue operations, the rule call-enq orders the invoked operation after all the completed enqueues in the current state, and the rules ret-enq1/ret-enq2 flip the corresponding flag from PEND to COMP provided that the operation is still present in the current state. For dequeue operations, call-deq only increments the control point and ret-deq checks whether the return value is the same as the one fixed at the linearization point. The linearization point rule lin-deq1 corresponds to the case of a non-empty queue, showing that is enabled only if has been added by an enqueue which is minimal in the current happens-before. When enabled, it removes the enqueue adding from the state. The linearization point rule lin-deq2 corresponds to the case of dequeue operations linearized with an EMPTY return value.
The following result states that the library has exactly the same set of histories as the standard abstract library (see Appendix 0.C for a proof).
Theorem 4.1
* is a refinement of and vice-versa.*
A trace of a queue implementation is called -complete when every completed dequeue has a linearization point, i.e., each return action is preceded by an action . A queue implementation over alphabet , such that , is called with fixed dequeue linearization points when every trace is -complete.
The following result shows that -forward simulations are a sound and complete proof method for showing the correctness of a queue implementation with fixed dequeue linearization points (up to the correctness of the linearization points). It is obtained from Theorem 4.1 and Theorem 3.1 using the fact that the alphabet of is exactly and is deterministic.
Corollary 1
A queue implementation with fixed dequeue linearization points is a -refinement of iff there exists a -forward simulation from to .
4.3 A Correctness Proof For Herlihy&Wing Queue
We describe a forward simulation from to . A state is related by to an state that consists of all the enqueue operations for which the input is still present in the array items and all the pending enqueue operations that have at most reserved an array position, ordered by a relation satisfying the following:
- (a)
pending enqueues are maximal, i.e., for every two enqueues and such that is pending, we have that ,
- (b)
is consistent with the order in which positions of items have been reserved, i.e., for every two enqueues and such that , we have that ,
- (c)
an enqueue which has reserved a position can’t be ordered before another enqueue that has reserved a position when the position has been “observed” by a non-linearized dequeue that may “observe” in the current array traversal, i.e., for every two enqueues and , and a dequeue , such that
[TABLE]
we have that . The predicate holds when the dequeue is at a control point after a swap returning null and before the increment of i.
An enqueue is labeled by where is the input value if it’s pending and by , otherwise. Also, for every dequeue operation such that , we have that .
We show that is indeed a -forward simulation. Let and be states of and , respectively, such that . We omit discussing the trivial case of transitions labeled by call and return actions which are simulated by similar transitions of (for the return a dequeue operation , we use the equality between the local variable in and the component in ).
We show that each internal step of an enqueue or dequeue, except the execution of swap returning a non-null value in dequeue (which represents its linearization point), is simulated by an empty sequence of transitions, i.e., for every state obtained through one of these steps, if , then for each state . Essentially, this consists in proving the following property, called monotonicity: the set of possible orders associated by to doesn’t exclude any order associated to .
Concerning enqueues, let be the state obtained from when a pending enqueue reserves an array position. This enqueue must be maximal in both and any state related to (since it’s pending). Moreover, there is no dequeue that can “observe” this position before restarting the array traversal. Therefore, item (c) in the definition of doesn’t constrain the order between and some other enqueue neither in nor in . Since this transition doesn’t affect the constraints on the order between enqueues different from (their local variables remain unchanged), monotonicity holds. This property is trivially satisfied by the second step of enqueue which doesn’t affect i.
To prove monotonicity in the case of dequeue internal steps different from its linearization point, it is important to track the non-trivial instantiations of item (c) in the definition of over the two states and , i.e., the triples for which (1) holds. Instantiations that are enabled only in may in principle lead to a violation of monotonicity (since they restrict the orders associated to ). For the two steps that begin an array traversal, i.e., reading the index of the last used position and setting i to [math], there exist no such new instantiations in because the value of i is either not set or [math]. The same is true for the increment of i in a dequeue since the predicate holds in state . The execution of swap returning null in a dequeue enables new instantiations in , thus adding potentially new constraints . We show that these instantiations are however vacuous because must be pending in and thus maximal in every order associated by to . Let and be two enqueues such that together with the dequeue they satisfy the property (1) in but not in . We write for the value of the variable i of operation in state . We have that and . The latter implies that the enqueue didn’t executed the second statement (since the position it reserved is still null) and it is pending in . The step that checks that the value returned by swap is null doesn’t modify the variables in property (1) and also, it doesn’t change the valuation of the predicate .
Finally, we show that the linearization point of a dequeue of , i.e., an execution of swap returning a non-null value , from state and leading to a state is simulated by a transition labeled by of from state . By the definition of , there is a unique enqueue which filled the position updated by , i.e., and . We show that is minimal in the order of which implies that is enabled in . Thus, instantiating item (c) in the definition of with and we get that every enqueue that reserved a position smaller than the one of can’t be ordered before in the order . Also, applying item (b) with we get the same for every enqueue that reserved a bigger position. An enqueue that didn’t reserved a position is by definition maximal in and therefore, not a predecessor of . Then, the state obtained from through a transition is related to because (1) the value added by is not anymore present in items which implies that doesn’t occur in any state related to , and (2) the value of is set to which implies that is set to in every state related to .
5 Stacks With Fixed Pop Commit Points
While the abstract queue in Section 4 can be adapted to stacks (the linearization point with is enabled when is added by a push which is maximal in the happens-before order stored in the state), it can’t simulate (through forward simulations) existing stack implementations like the Time-Stamped Stack [9] (, for short) where the linearization points of the pop operations are not fixed. Exploiting particular properties of the stack semantics, we refine the ideas used in and define a new abstract implementation for stacks, denoted as , which is able to simulate such implementations. Forward simulations to are complete for proving the correctness of stack implementations provided that the point in time where the return value of a pop operation is determined, called commit point, corresponds to a fixed action.
5.1 Pop Methods With Fixed Commit Points
We explain the meaning of the commit points on a simplified version of the Time-Stamped Stack [9] (, for short) given in Figure 4. This implementation maintains an array of singly-linked lists, one for each thread, where list nodes contain a data value (field data), a timestamp (field ts), the next pointer (field next), and a boolean flag indicating whether the node represents a value removed from the stack (field taken). Initially, each list contains a sentinel dummy node pointing to itself with timestamp and the flag taken set to false.
Pushing a value to the stack proceeds in several steps: adding a node with maximal timestamp in the list associated to the thread executing the push (given by the special variable myTID), asking for a new timestamp (given by the shared variable TS), and updating the timestamp of the added node. Popping a value from the stack consists in traversing all the lists, finding the first element which doesn’t represent a removed value (i.e., taken is false) in each list, and selecting the element with the maximal timestamp. A compare-and-swap (CAS) is used to set the taken flag of this element to true. The procedure restarts if the CAS fails.
The push operations don’t have a fixed linearization point because adding a node to a list and updating its timestamp are not executed in a single atomic step. The nodes can be added in an order which is not consistent with the order between the timestamps assigned later in the execution. Also, the value added by a push that just added an element to a list can be popped before the value added by a completed push (since it has a maximal timestamp). The same holds for pop operations: The only reasonable choice for a linearization point is a successful CAS (that results in updating the field taken). Fig. 5 pictures an execution showing that this action doesn’t correspond to a linearization point, i.e., an execution for which the pop operations in every correct linearization are not ordered according to the order between successful CASs. In every correct linearization of that execution, the pop operation removing is ordered before the one removing although they perform a successful CAS in the opposite order.
An interesting property of the successful CASs in pop operations is that they fix the return value, i.e., the return value is youngest->data where youngest is the node updated by the CAS. We call such actions commit points. More generally, commit points are actions that access shared variables, from which every control-flow path leads to the return control point and contains no more accesses to the shared memory (i.e., after a commit point, the return value is computed using only local variables).
When the commit points of pop operations are fixed to particular implementation actions (e.g., a successful CAS) we assume that the library is an LTS over an alphabet that contains actions with and (denoting the commit point of the pop with identifier and returning ). Let be the set of such actions.
5.2 Abstract stack implementation
We define an abstract stack over alphabet that essentially, similarly to , maintains the happens-before order of the pushes whose value has not been yet removed. Pops are treated differently since the commit points are not necessarily linearization points, intuitively, a pop can be linearized before its commit. Each pop operation starts by taking a snapshot of the greatest completed push operations in the happens-before order, and continuously tracks the push operations which are overlapping with it. The commit point with is enabled only if was added by one of the push operations in the initial snapshot, or by a push happening earlier when all the values from the initial snapshot have been removed, or by one of the push operations that overlaps with pop . The commit point is enabled only if all the values added by push operations ending before started have been removed. The effect of the commit points is explained below through examples.
Figure 6 pictures two executions of for two extended histories (that include pop commit points). For readability, we give the state of only after several execution prefixes delimited at the right by a dotted line. We focus on pop operations – the effect of push calls and returns is similar to enqueue calls and returns in . Let us first consider the history on the top part. The first state we give is reached after the call of pop with identifier . This shows the effect of a pop invocation: the greatest completed pushes according to the current happens-before (here, the push with identifier ) are marked as (from “before” operation 3), and the pending pushes are marked as (from “overlapping” with operation 3). As a side remark, any other push operation that starts after pop would be also marked as . The commit point (pictured with a red circle) is enabled because was added by a push marked as . The effect of the commit point is that push is removed from the state (the execution on the bottom shows a more complicated case). For the second pop, the commit point is enabled because was added by a push marked as . The execution on the bottom shows an example where the marking for some pop is updated at commit points. The pushes and are marked as and when the pops and start. Then, is enabled since was added by which is marked as . Besides removing , the commit point produces a state where a pop committing later, e.g., pop , can remove which was added by a predecessor of in the happens-before ( could become the top of the stack when is removed). This history is valid because can be linearized after and . Thus, push 2, a predecessor of the push which is removed, is marked as . Push which is also a predecessor of the removed push is not marked as because it happens before another push, i.e., push 3, which is already marked as (the value added by push 3 should be removed before the value added by push 1 could become the top of the stack).
Formally, the states of are tuples where is a strict partial order over the set of operation identifiers, labels every identifier in with a value and a pending/completed flag, records the return value of a pending pop fixed at its commit point, records the control point of every push () or pop operation (), records the greatest completed push operations before a pop started or happening earlier provided that the values of all the push happening later have been removed, and records push operations overlapping with a pop. All the components are in the initial state, and the transition relation is defined in Fig. 7.
The transition rules which don’t correspond to commit point actions are similar to those for . The rule com-pop1 for is enabled only if there exists a push which added value and which belongs to or . When enabled, the push is removed from the set (and the order ) and for every other pop such that belongs to , is replaced in by its predecessors which are followed exclusively by pushes overlapping with (these predecessors become maximal closed pushes once is removed). Also, is set to . The rule com-pop1 for is enabled only if is empty (i.e., all the values added by pushes ending before , if any, have been removed). Then, is set to .
Let be the standard abstract implementation of a stack (where elements are stored in a sequence; push, resp., pop operations add, resp., remove, an element from the beginning of the sequence in one atomic step). For , the alphabet of is . The following result states that the library has exactly the same set of histories as (see Appendix 0.D for a proof).
Theorem 5.1
* is a refinement of and vice-versa.*
A trace of a stack implementation is called -complete when every completed pop has a commit point, i.e., each return is preceded by an action . A stack implementation over , such that , is called with fixed pop commit points when every trace is -complete.
As a consequence of Theorem 3.1, -forward simulations are a sound and complete proof method for showing the correctness of a stack implementation with fixed pop commit points (up to the correctness of the commit points).
Corollary 2
A stack with fixed pop commit points is a -refinement of iff there is a -forward simulation from to .
Linearization points can also be seen as commit points and thus the following holds.
Corollary 3
A stack implementation with fixed pop linearization points where transition labels are substituted with is a -refinement of iff there is a -forward simulation from to .
5.3 A Correctness Proof For Time-Stamped Stack
We describe a forward simulation from to . Except for the constraints on the components and of a state, it is similar to the simulation from to . Thus, the states associated by to a state satisfy the following. The set consists of all the identifiers of pushes in which didn’t added yet a node to pools or for which the input is still present in pools (i.e., the node created by the push has taken set to false). A push is labeled by where is the input value if it’s pending and by , otherwise.
To describe the order relation we consider the following notations: , resp., , denotes the timestamp of the node created by the push in state (the ts field of this node), resp., the id of the thread executing . By an abuse of terminology, we call the timestamp of in state . Also, when intuitively, a traversal of pools would encounter the node created by before the one created by . More precisely, when , or and the node created by is reachable from the one created by in the list pointed to by . The order relation satisfies the following: (1) pending pushes are maximal, (2) is consistent with the order between node timestamps, i.e., implies , and (3) includes the order between pushes executed in the same thread, i.e., and implies .
The components and satisfy the following constraints (their domain is the set of identifiers of pending pops):
- •
a pop with that reached a node with timestamp (its variable n points to this node) overlaps with every push that created a node with a timestamp bigger than and which occurs in pools before the node reached by , i.e., , , , , and implies , for each
- •
a pop with overlaps with every push that created a node which occurs in pools before the node reached by , i.e., , , , and implies , for each
- •
if the variable youngest of a pop points to a node which is not taken, then this node was created by a push in or the node currently reached by is followed in pools by another node which was created by a push in , i.e., , , and implies or that there exists such that , , and either or and TODO is traversing the last list in the array pools, for each
There are some more constraints on and that can be seen as invariants of , i.e., and don’t contain predecessors of pushes from (for each , and implies ). They can be found in Appendix 0.E.
Finally, for every pop operation such that , we have that .
The proof that is indeed a forward simulation from to follows the same lines as the one given for the Herlihy&Wing Queue. It can be found in Appendix 0.E.
6 Related Work
Many techniques for linearizability verification, e.g., [28, 4, 27, 2], are based on forward simulation arguments, and typically only work for libraries where the linearization point of every invocation of a method is fixed to a particular statement in the code of . The works in [25, 8, 10, 29] deal with external linearization points where the action of an operation can be the linearization point of a concurrently executing operation . We say that the linearization point of is external. This situation arises in read-only methods like the contains method of an optimistic set [22], libraries based on the elimination back-off scheme, e.g., [14], or flat combining [15, 12]. In these implementations, an operation can do an update on the shared state that becomes the linearization point of a concurrent read-only method (e.g., a contains returning true may be linearized when an add method adds a new value to the shared state) or an operation may update the data structure on behalf of other concurrently executing operations (whose updates are published in the shared state). In all these cases, every linearization point can still be associated syntactically to a statement in the code of a method and doesn’t depend on operations executed in the future (unlike and ). However, identifying the set of operations for which such a statement is a linearization point can only be done by looking at the whole program state (the local states of all the active operations). This poses a problem in the context of compositional reasoning (where auxiliary variables are required), but still admits a forward simulation argument. For manual proofs, such implementations with external linearization points can still be defined as LTSs that produce -complete traces and thus still fall in the class of implementations for which forward simulations are enough for proving refinement. These proof methods are not complete and they are not able to deal with implementations like or .
There also exist linearizability proof techniques based on backward simulations or alternatively, prophecy variables, e.g., [26, 24, 19]. These works can deal with implementations where the linearization points are not fixed, but the proofs are conceptually more complex and less amenable to automation.
The works in [16, 6] propose reductions of linearizability to assertion checking where the idea is to define finite-state automata that recognize violations of concurrent queues and stacks. These automata are simple enough in the case of queues and there is a proof of based on this reduction [16]. However, in the case of stacks, the automata become much more complicated and we are not aware of a proof for an implementation such as which is based on this reduction.
Appendix 0.A Libraries
Programs interact with libraries by calling named library methods, which receive parameter values and yield return values upon completion. We fix arbitrary sets and of method names and parameter/return values.
We fix an arbitrary set of operation identifiers, and for given sets and of methods and values, we fix the sets
[TABLE]
of call actions and return actions; each call action combines a method and value with an operation identifier . Operation identifiers are used to pair call and return actions. We assume every set of words is closed under isomorphic renaming of operation identifiers. We denote the operation identifier of a call/return action by . Call and return actions and are matching, written , when . We may omit the second field from a call/return action for methods that have no inputs (e.g., the pop method of a stack) or return values (e.g., the push method of a stack). A word over alphabet , such that , is well formed when:
- •
Each return is preceded by a matching call:
implies for some .
- •
Each operation identifier is used in at most one call/return:
and implies .
We say that the well-formed word is sequential when
- •
Operations do not overlap:
and implies for some .
Well-formed words represent traces of a library. We assume every set of well-formed words is closed under isomorphic renaming of operation identifiers. For notational convenience, we take for the rest of the paper. When the value of a certain field in a call/return action is not important we use the placeholder , e.g., instead of when the input can take any value.
An operation is called completed in a well-formed trace when occurs in , for some and . Otherwise, it is called pending.
Libraries dictate the execution of methods between their call and return points. Accordingly, a library cannot prevent a method from being called, though it can decide not to return. Furthermore, any library action performed in the interval between call and return points can also be performed should the call have been made earlier, and/or the return made later. A library thus allows any sequence of invocations to its methods made by some program.
Definition 4
A library is an LTS over alphabet such that and each trace is well formed, and
- •
Call actions cannot be disabled:
implies if is well formed.
- •
Call actions cannot disable other actions:
implies .
- •
Return actions cannot enable other actions:
implies .
Note that even a library that implements atomic methods, e.g., by guarding method bodies with a global-lock acquisition, admits executions in which method calls and returns overlap. For simplicity, Definition 4 assumes that every thread performs a single operation. The extension to multiple operations per thread is straightforward, e.g. the closure rules must assume that the actions and belong to different threads
Appendix 0.B Normal Forward/Backward Simulations
We define a class of forward/backward simulations, called normal simulations, that are used in the proofs in Appendix 0.C and Appendix 0.D.
Definition 5
Let and be two libraries over alphabets and , respectively, such that , and a set of actions such that . A relation is called a normal -forward simulation from to iff the following holds:
- (i)
- (ii-a)
If , for some , and , then there exists such that , , and , for each .
- (ii-b)
If , for some , and , then there exists such that , , and , for each .
- (ii-c)
If , for some , and , then there exists such that .
- (ii-d)
If , for some and , then there exists such that and .
With normal -forward simulations, a step of labeled by a call, resp., return, action is simulated by a sequence of steps of that start, resp., end, with the same action, and a step of labeled by another observable action should be matched by a step of labeled by the same action. The rest of the transitions in are matched to a possibly empty sequence of transitions of with arbitrary labels.
A dual notion of forward simulation is the backward simulation:
Definition 6
Let and be two libraries over a common alphabet , and a set of actions such that . A relation is called a normal -backward simulation from to iff the following holds:
- (i)
- (ii-a)
If , for some , and , then there exists such that , , and , for each .
- (ii-b)
If , for some , and , then there exists such that , , and , for each .
- (ii-c)
If , for some , and , then there exists such that
- (ii-d)
If for some and , then there exists such that and .
Appendix 0.C Proof of Theorem 4.1
We show that and refine each other. We start by giving a formal definition of the standard reference implementation . Thus, the states of are tuples where is a sequence of values, records the input value of an enqueue, records the return value of a dequeue fixed at its linearization point ( denotes a partial function), and records the control point of every enqueue () or dequeue operation (). All the components are in the initial state, and the transition relation is defined in Fig. 8. The alphabet of contains call/return actions and enqueue/dequeue linearization points.
To prove that is a refinement of we define a normal -backward simulation (i.e, a backward simulation as in Definition 6) from to . The reverse is shown using a normal -forward simulation (i.e, a forward simulation as in Definition 5).
Lemma 1
* is a refinement of .*
Proof
We define a normal -backward simulation from to as follows. Given an state and an state we have that iff the following hold:
- •
the sequence is a linearization of a partial order where contains values labeling elements of and all the values corresponding to completed enqueues, i.e., ordered according to the happens-before order between the enqueues that added them, i.e., iff there exists such that , , and .
- •
the return values fixed at dequeue linearization points are the same, i.e., for every , ,
- •
every dequeue is at the same control point in both and , i.e., for every and , iff ,
- •
every pending enqueue has the same input value in both and , i.e., for every , ,
- •
a pending enqueue from has been linearized whenever its value is contained in , i.e., for every , if and ,
- •
a pending enqueue from hasn’t been linearized whenever its value is not in , i.e., for every , iff and ,
- •
a pending enqueue which is not in has been linearized, i.e., for every , if and ,
- •
an enqueue is completed in whenever it is completed in , i.e., for every , iff ,
For the conditions described above, if we fix the set and , then the state related to becomes unique. We use this fact in the proof. In some places, we only give , and without explicitly defining or show that there exists with the given that is related to by just finding a such that is a linearization of where is induced from .
or and not describing explicitly.
In the following, we show that indeed is a normal -backward simulation from to .
.
- call-enq
Let and . Either or not.
First consider the former case. We know that and is maximal in . Hence where contains linearization of pending elements in . Then, pick . We can find such a with . Let be the partial order that is used while constructing from and . We can find for relating to such that does not contain the values of pending elements that formed suffix of and coming from linearization of .
One can also see that where such that and are the pending elements that are linearized to form . Note that obeys the definition of normal backward simulation definition.
For the second case, pick such that . We can find a with related to by using the same partial order that is used while relating to . holds because .
- call-deq
Let and . Pick such that it is equal to in every field except that . Then, and .
- lin-deq1
Let , and . We pick such that . We first show that . Let be the partial order that is linearized to obtain and be the element such that . We know that is minimal in due to the premise of the rule lin-deq1. Hence, we can obtain such that and is a linearization of it.
In addition, . The action is enabled in state since is the minimum element of . Note that the transition relating to obeys the definition of normal forward simulation.
- lin-deq2
Let and . We pick for relating to such that . Such a is a valid choice since all the elements are pending. Then, is the only linearization of . Hence, action is enabled in and holds.
- ret-enq1
Let , and . Assume be the partial order of which linearization is . Pick . Then, holds since and . Construct such that is obtained by linearizing the partial order . Then, holds and it is a valid action with respect to normal backward-simulation relation definition.
- ret-enq2
Let , and . Since and , we can pick where is the strict partial order such that is its linearization. Construct such that is obtained by linearizing the partial order . Then, holds and it is a valid action with respect to normal backward-simulation relation definition.
- ret-deq
Let and . Assume is the partial order of which linearization is . Construct such that and is the partial order is obtained from. since and . Then, holds. We have since . Hence the is enabled in . Moreover, is a valid transition with respect to the normal backward simulation relation definition.
Lemma 2
* is a refinement of .*
Proof
We define a normal -forward simulation from to as follows. Given state and an state we have that iff the following hold:
- •
the sequence is a linearization of a partial order where contains values labeling elements of and all the values corresponding to completed enqueues, i.e., ordered according to the happens-before order between the enqueues that added them, i.e., iff there exists such that , , and .
- •
every dequeue is at the same control point in both and , i.e., for every and , iff ,
- •
every enqueue is pending in whenever it is pending in , i.e., for every , iff ,
- •
every enqueue is completed in whenever it is completed in , i.e., for every , iff ,
- •
every pending enqueue which is not linearized or whose value is present in is a member of , i.e., for every ,
[TABLE]
- •
every completed enqueue whose value is present in is a member of , i.e., for every ,
[TABLE]
- •
pending enqueues are maximal in , i.e., for every and , if ,
- •
the return values fixed at dequeue linearization points are the same, i.e., for every , .
In the following, we show that indeed is a normal -forward simulation from to .
- call-enq
Let and . Then, is an enabled action in since premise of call-enq holds in and . Obtain such that . Note that is unique since is deterministic with respect to .
Next, we show that . Let be the partial order used while relating to . Same partial order can be used while relating to since , and . The only change we have in control point fields after the actions is that and which satisfies the conditions on . Moreover is a maximal pending node in as required by the conditions. Consequently, .
- call-deq
Let and . Then, is an enabled action in since premise of call-deq holds in and . Obtain such that . Note that is unique since is deterministic with respect to .
Next, we show that . Since , and , we can pick same partial order in and show that is a linearization of it. The only change in control points after the transitions is that which does not violate any condition in . Consequently, .
- lin-enq
Let and . Then, pick such that . Note that is a valid transition with respect to the normal forward simulation relation definition. We show that . If is the partial order in of which one linearization is , we pick . can be linearized to since is a maximal pending node and can be linearized at the end. Moreover, the only change in control point which does not violate the conditions.
- lin-deq1
Let , and . Then, is an enabled action in . There must exist such that and is minimal in (since is linearized as the minimum element in according to premise of lin-deq1 of ). Obtain such that . Note that is unique since is deterministic with respect to .
Next, we show that . Let be the partial order used while relating to such that is a linearization of this partial order. Since we have shown that is minimal in that partial order, is a linearization of where . Note that holds. The only change in control points is that which does not violate the conditions for relating to . Note that the fifth condition of still holds for while relating to . After transitions and the last condition on is preserved.
- lin-deq2
Let and . Then, is an enabled action in . If , then use for linearization cannot be cannot be a linearization of . Obtain such that . Note that is unique since is deterministic with respect to .
Next, we show that . Let be the partial order used while relating to such that is a linearization of this partial order. We can use the same for relating to because field is the same for both , ; and , , fields are same for both and . The only change in control points is that which does not violate the conditions for relating to . After transitions and the last condition on is preserved.
- ret-enq
Let and . Then, there are two cases assuming data independence: (i) and (ii) or not.
First, consider the former case. Then, ret-enq1 rule of is applicable. Its precondition holds since fifth condition of holds while relating to . Apply this rule () to obtain . Note that is unique since is deterministic with respect to and it is a valid action according to the normal forward-simulation relation definition.
Next, we show that . Since , we know that where is the partial order satisfying first condition of while relating to , and takes part in the linearization i.e., . We can use the same partial order for relating to such that it satisfies the first condition of . The only change in control points is that which does not violate the conditions for relating to . Note that the sixth condition of also continue to hold for for the post-states.
Second, consider the latter case: , but . Since , by the fifth and sixth conditions. Hence, the pre-condition of ret-enq2 is satisfied by . Apply this rule () to obtain . Note that is unique since is deterministic with respect to and it is a valid action according to the normal forward-simulation relation definition.
Next, we show that . For satisfying the first condition, one can use the same partial order that is used for relating pre-states since fields of , and , , fields of and are the same. The only change in control points is that which does not violate the conditions for relating to .
- ret-deq
Let and . Then, is an enabled action in due to premise ret-deq of and the last condition on (since ). Obtain such that . Note that is unique since is deterministic with respect to .
We see that . Pre-states are equal to the post-states with the only exception in the control points such that . All the conditions except the third one continues to hold in the post states since they hold in the pre-states. The third rule regarding the control points of dequeues also continue to the hold since changes in the control point of does not violate it.
Appendix 0.D Proof of Theorem 5.1
We show that and refine each other. The standard reference implementation is defined exactly as the one for queues, , except that pop linearization points extract values from the beginning of the sequence stored in the state.
Thus, the states of are tuples where is a sequence of values, records the input value of a push, records the return value of a pop fixed at its linearization point ( denotes a partial function), and records the control point of every push () or pop operation (). All the components are in the initial state, and the transition relation is defined in Fig. 9. The alphabet of contains call/return actions and push/pop linearization points.
To prove that is a refinement of we define a normal -backward simulation (i.e, a backward simulation as in Definition 6) from to . The reverse is shown using a normal -forward simulation (i.e, a forward simulation as in Definition 5).
Lemma 3
* is a refinement of .*
Proof
We define a normal -backward simulation from to as follows. Given an state and an state we have that iff the following hold:
- •
if a pop has committed or respectively, it has returned in , then it had been linearized or respectively, it has returned in , i.e., for every , if then ,
- •
a push is completed in whenever the same is true in , i.e., for every , iff ,
- •
a push is pending in iff either it is a non-linearized pending push in or its linearization point has been executed, i.e., for every , iff and doesn’t occur in , or ,
- •
if a pop didn’t commit in then it is pending in and it may have been linearized, i.e., for every , if then ,
- •
there exists a partial injective function which associates uncommitted pops to pushes in such that:
- –
for every , iff
- –
the sequence is the mirror of a linearization of a partial order where contains values labeling elements of except for those in the range of , and all the values corresponding to completed pushes which are not in the range of , i.e., ordered according to the happens-before order between the pushes that added them, i.e., iff there exists such that , , and
- –
every pop in the domain of has been linearized, i.e., for every , implies ,
- –
every pop which is not in the domain of hasn’t been linearized, i.e., for every , implies ,
- –
every push in the range of has been linearized, i.e., for every , implies ,
- –
a pending enqueue from has been linearized when its value is contained in , i.e., for every , if and , then .
- •
the return values fixed at pop commit points are the same, i.e., for every , if is defined, then ,
- •
every pending push has the same input value in both and , i.e., for every , ,
In the following, we show that indeed is a normal -backward simulation from to :
- •
Let be a transition in and . We consider two cases depending on whether the value occurs on a position in the sequence of or not. If it occurs, let be a state where essentially, the component is the prefix of the sequence of that contains the first positions (except for some set of pushes that will be defined hereafter, all operations are at the same control point). Let be the following trace:
[TABLE]
where is the value on position in the sequence of and is the length of this sequence (we assume that positions are indexed starting from [math]). Let . For every with , we must have that in . We take in for every and undefined for . We have that is a valid sequence of transitions of and (the latter can be proved by taking the same function used in establishing that ). Now, assume that the value is not in the sequence of . We consider an state where the component is the same as the one in . There are two sub-cases depending on whether there exists a pending pop such that when establishing that . If it exists, the operations are at the same control point in both and except for the push for which is undefined in , and the the pop for which we take in . We have that
[TABLE]
in . If there exists no such pop , it can be easily seen that there exists such that and .
- •
Let be a transition in and . We consider two cases depending on whether in the function used to relate to we have that . In other words, either the newly invoked pop operation did not linearize yet () or it linearizes and removes an element inserted by a linearized push (). The second case also splits into two sub-cases: The value removed by pop is inserted by a push that is still pending or the push has returned. We will look at all three cases separately. The easiest one is the first case. There exists some where essentially the component is the same as the one in , such that and .
For the first sub-case of the second case, we take an state where (we use to denote the component in ). It must happen that . The operations are at the same control point in both and , except for in which case is undefined. We have that and . The latter holds because essentially, is a maximal node in (since it is pending).
For the second sub-case, we define an state where the sequence is the minimal prefix of that includes the value added by . Let be the index of this value in and with the identifiers of the pushes that added the values following in . Let be the following trace:
[TABLE]
where is the value on position in the sequence and is the length of this sequence. We have that is a valid sequence of transitions of and . The latter relies on the fact that is a greatest completed push in and all pushes with are pending in .
- •
Let be a transition in and . When this transition results in removing a greatest completed push or a pending push in , then there exists an state such that is a valid sequence of transitions and , for some and defined as in the second case of . When it removes a completed push which is followed by other completed pushes (in the happens-before in ), then we pick . We have that and (for the latter we must choose a function such that where is the push removed by the transition.
- •
Let be a transition in and . We consider two cases depending on whether the happens-before in contains push . If it contains push , there are two sub-cases: (1) if its input is present in then there exists an state such that is a valid sequence of transitions and , and (2) otherwise, we take a state where essentially, for which we have that and . If the happens-before in doesn’t contain push , then there exists an state such that .
- •
The case of pop returns is trivial. Such transitions are simulated by transitions of .
Lemma 4
* is a refinement of .*
Proof
We define a normal -forward simulation from to as follows. Given an state and an state we have that iff the following hold:
every pop is at the same control point in both and , i.e., for every and , iff , 2. 2.
a push has been invoked in whenever it has been invoked in , i.e., for every , iff , 3. 3.
a push which is linearized in has been invoked in , i.e., for every , if then , 4. 4.
a push is completed in iff the same holds in , i.e., for every , iff , 5. 5.
the pair in satisfies the following:
- •
for every , if , , and occurs in , then and ,
- •
for every , if , , and occurs in , then and , 6. 6.
every pending push in is overlapping with every non-linearized pop, i.e., for every , if then . 7. 7.
every completed push is either overlapping or was the greatest completed push before a non-linearized pop started, i.e., for every , if , then , 8. 8.
for every push that overlaps with a pop or was maximal in when started, its successors are overlapping with , i.e., and implies for each 9. 9.
predecessors of pushes in for a given pop are neither overlapping with nor in , i.e., and implies for each 10. 10.
pending pushes are maximal in , for every and , if , 11. 11.
the sequence is the mirror of a linearization of a partial order where contains values labeling elements of and all the values corresponding to completed pushes, i.e., ordered according to the happens-before order between the pushes that added them, i.e., iff there exists such that , , and . 12. 12.
the return values fixed at pop linearization/commit points are the same, i.e., for every , , 13. 13.
every pending push has the same input value in both and , i.e., for every , ,
In the following, we show that indeed is a normal -backward simulation from to :
- •
Let be a transition in and . We have that where (recall that is deterministic). Since the push is non-linearized in , the component of both and are the same and in . Then, the component in states related by to is allowed to exclude values added by pushes in which are labeled as pending. The effect of in implies that overlaps with all pending pops.
- •
Let be a transition in and . We have that where . The only difference between and is that the components and in contain the greatest completed pushes in and the pending pushes in , respectively (these components were undefined in ). The relation doesn’t exclude this particular choice for and when applied to and .
- •
Let be a transition in and . We have that , i.e., the transition is simulated by an empty sequence of transitions, because essentially the component of still corresponds to a linearization of the pushes in according to item 11 in the definition of . The sequence in contains the value added by the push at the end, but this is allowed by since is labeled as pending in .
- •
Let be a transition in and . We have that where . The transition labeled by is enabled in because was the first value in the sequence of . Indeed, this implies that was added by a push which is maximal in the happens-before stored in . This clearly implies that . In addition, the sequence in does correspond to a linearization of the pushes in (which don’t contain anymore) because in had this property with respect to and in is obtained by deleting the first value in the sequence of .
- •
Let be a transition in and . We have that where . There are two cases depending on whether the value added by is still present in the sequence of . If it is not, then the push doesn’t occur in the happens-before from , and the only effect of these two transitions is changing the control point of . Therefore, clearly holds. When this value is still present, the effect of in is changing the flag of push from to . Since the order between pushes doesn’t change, we have that .
- •
Let be a transition in and . We have that where . This case is obvious, the only change between and being the control point of .
Appendix 0.E Proving the correctness of
The LTS corresponding to the description of given in Fig. 4 is defined as usual. The control points and transition labels we use in the following proof are pictured in Fig. 10. To simplify the proof, we take the initializations of some local variables together as atomic.
States of the TS-Stack contains the global variables and local variables as fields. Global variables are just elements of their domains and local variables are maps from operation identifiers to their domains. We say for referencing the value of local variable of operation in state . There is only one special local variable called . Its value is unique to each pending operation in a state i.e., concurrent operations cannot have the same value. TS-Stack states also contains sets which are operation identifier sets of push and pops respectively, and the control point function which is a map from operation identifiers to the control points set that are presented in the flow diagram Figure 10. Transition relation of the TS-STack is presented in Figure 11 (push rules) and Figure 12 (pop rules). Next, we show that the linearizability of TS Stack.
Lemma 1
* is a -refinement of .*
Proof
We show that the relation defined in Section 5.3 is a -forward simulation from to . For readability, we recall the definition of .
Let us make some clarifications before defining the relation. In order not to confuse nodes in TS Stack and nodes in , we call nodes of as vertices from now on. We also define ordering relation (called traverse order) among the operations in a state of . It basically reflects the traverse order of pop operations. For two push operations is state we say that iff either or and is reachable from using next pointers. is obtained from in the usual way.
The relation contains iff the following are satisfied:
- Nodes
iff is a push operation in () such that either it has not inserted its node to the pool yet ( and ) or its node is not taken by a pop (, and ).
- Pend/Comp
A vertex is pending () iff satisfies the previous condition, and it is not completed in ( and ). Similarly, this vertex is completed () iff satisfies the previous condition, and it is completed in (). Pending vertices are maximal with respect to i.e., if is a pending vertex, then for all .
- TSOrder
If a node has a smaller timestamp than the other node in , the operations that inserted them cannot be ordered reversely in . More formally, let s.t. . Then, .
- TidOrder
Order among the nodes inserted by the same threads in must be preserved among the operations that inserted them in . Let s.t. and . Then, .
- Frontiers
Every maximally closed or pending vertex can be removed by a pending pop. More formally, let such that . Then, for all pops , . In the other case, let such that and for all other such that , we know . Then, for all pop operations , or .
- MaximalOV
If a push is a candidate to be removed by a pop , then every other push invoked after is a candidate to be removed by since is concurrent with . More formally, let such that and there exists a pop such that or . Then, .
- MinimalBE
If a push has finished before the pop is invoked and yet is a candidate to be removed by , other pushes completed before can not be candidates to be removed by at that state. More formally, let such that and there exists a pop such that . Then, neither nor .
- ReverseFrontiers
If all immediate followers of a push are concurrent with pop , then is either concurrent or maximally closed with respect to . More formally, let and for all such that , , where is a pop operation. Then, .
- FixReturn
If a pop is after its commit point action in , then the value of this operation in is fixed to . More formally, Let be the pop operation such that and . Then, .
- TraverseBefore
If a pop operation is currently visiting node , it has non-null node as the and there is a non-null not taken node coming before in the traverse order with a greater timestamp than , then the operation that inserts must be concurrent with . More formally, assume and . Let such that , , and . Then, .
- TraverseBeforeNull
If a pop operation is currently visiting node , and its field is null, then every other node coming before in the traverse order must be concurrent with . More formally , let and assume there exists an operation such that , and . Then, .
- TraverseAfter
If a pop operation is currently visiting node that is not null and its youngest element is not null and still not taken in state , then either is a candidate to be removed by in or there exists a later node than such that is a candidate in and it has a bigger timestamp than n. More formally, assume that there exists such that , and . Then, either or there exists s.t. and and either or .
Next, we will show that is really a -forward simulation relation. Except the trivial base case, we case-split on the transition rules. We first assume and . Then, we find corresponding transition obeying the -forward simulation relation conditions and obtain such that and .
We observe that if , then the corresponding rule in is . Otherwise, .
Let the following describe : where is the precondition (guard) that needs to be satisfied for enabling and describe the if (equivalently ).
For the cases , we first need to show is enabled in state i.e., satisfies . If this can not be directly obtained from the information that satisfies and using one or two obvious conditions on (since ), we show the derivation in the proof. Then, is obtained in a unique way since is deterministic on its alphabet . The, only other thing to show is . We show this by proving that does not violate any of the conditions of the described above. Suppose conditions on are of the form where the is a vector of operation identifiers and defined on states and must hold if the guard defined on and holds. We say that a vector is a new instantiation of the condtion if does not satisfy the while relating pre-states, but it satisfies while relating post-states.
We only explain why the new instantiations due to the difference between and or the difference between and do not violate the conditions. We skip the instances that we assumed while relating to .
For the cases in which , we have and the only thing to show is . Again, we only explain why the new instantiations due to the difference between and do not violate the conditions.
In the following, we show that is a -forward simulation relation.
- init
- call-push
The same derivation rule of is applied to to obtain . The premise of the rule is satisfied by trivially in the sense explained before. The new vertex is added to the such that is maximal, pending and every completed vertex is ordered before in . Moreover, is overlapping with every pending pop. To see that we observe the following: Nodes condition is preserved because . Since the newly added vertex is maximal and pending in , Pend/Comp condition is preserved. Frontiers and MaximalOV conditions are not violated since is added to set for every pending pop operation .
- push1
We have and show .Nodes and Pend/Comp conditions are still satisfied since remains to be a pending vertex. TSOrder is still preserved. Timestamp of is maximal and every other nodes of push operations with maximal timestamp in are pending vertices in . Hence there can be no ordering between those pushes and in that can violate TSOrder. Moreover, is maximal in which means that it cannot be ordered before another push of which node has a lower timestamp. TidOrder is also satisfied. Since is ordered after every completed push in and every other push by the same thread is completed, ordering required by the TidOrder is present.
- push2
We have and show . Nodes and Pend/Comp conditions are still satisfied since remains to be a pending vertex. One can also see that the TraverseBefore condition is preserved. Let the pop visiting node and . Since and are both pending in and , (by the Frontiers condition). Hence, TraverseBefore is preserved.
- push3
We have and show . We consider two cases: is true or it is false. For the former case, . The only new instantiation we check is does not violate Nodes condition while relating to .
For the latter case, we have . Nodes and Pend/Comp conditions are still satisfied since remains to be a pending vertex after changing to .
- push4
We have and show .
We consider two cases: is true or it is false. For the former case, Nodes condition is still satisfied since remains to be not a vertex.
For the latter case Nodes and Pend/Comp conditions are still satisfied since remains to be a pending vertex. TSOrder condition is still not violated since if , then is a completed vertex in and . By the premise of the rule (which can be shown to hold for every operation at control point ) and consequently . Since every other push by the thread of is completed, TidOrder still continues to hold for the same reasons. TraverseAfter condition is also preserved. Let be the push and be the pop such that , , and or . Assume after the action. Then, must be a pending push both in and by the premise of the derivation rule and must be true by Frontiers condition and . Hence, the TraverseAfter condition is preserved.
- ret-push
We consider two cases, is false or true. For the former case, we obtain by applying ret-push1 rule of . Nodes and Pend/Comp conditions are still satisfied since becomes a completed vertex in . Frontiers condition still holds since although become a maximally closed vertex in , we have for all pending nodes (due to Frontiers condition, and was a pending operation in state , ).
For the latter case, we obtain by applying ret-push2 rule of . Nodes condition is still satisfied since .
- call-pop
The same derivation rule of is applied to to obtain . Frontiers condition holds for relating to since for every pending vertex and for all completed vertex . due to action applied on . MaximalOV condition holds for since pending vertices are maximal in and for any maximally closed vertex in , if is ordered before other vertex , then is a pending operation by definition of being maximally closed and due to the changes by inv-pop action on . MinimalBE condition holds while relating to for the pop because only maximally closed vertices are in and if a push is ordered before a maximally closed push in , neither (since is not maximally closed) nor (since cannot be pending). ReverseFrontiers condition holds while relating to for the pop because, if for all immediate successors of in , then are pending vertices (due to call-pop action of ), is a maximally closed vertex and (due to call-pop action of ).
- pop1
We have and .
- pop2
We have and show . TraverseBefore condition while relating to still holds for . Assume is a non-null node. Then, for all nodes in such that we have in because (since is added to the pool after by the same thread) and in (since either or ). TraverseAfter does not have any new instatiations since the guard mentions the nodes after while relating to whereas it mentions nodes after or including which contains the all nodes in the former case.
- pop3
We have and .
- pop4
We have and .
- pop5
We have and show . TraverseBefore condition while relating to still holds for since and TraverseBefore holds while relating to .
TraverseAfter condition also continues to hold for . There are two possible cases: or not.
First, consider the former case. Since TraverseBeforeNull is satisfied while relating to , for every operation such that and we have . Consider all such such that . If there exists such a such that , then since ReverseFrontiers condition holds relating to . Otherwise, either is maximal in or all the vertices ordered after in we have . Then, either or one of these vertices must be maximal in and must be in since Frontiers condition holds (one of them is maximal in ) while relating to .
Second, assume there exists push operations such that and . Since TraverseBefore is satisfied while relating to , if there exists a push such that is not taken and , then . Then, for all such that is not taken and , then since . If there exists such a such that , then since ReverseFrontiers condition holds relating to . Otherwise, either is maximal in or all the vertices ordered after in we have . Then, either or one of these vertices must be maximal in and must be in since Frontiers condition holds (one of them is maximal in ) while relating to .
- pop6
We have and show . TraverseAfter continues to hold while relating to for . Let such that , and . Note that . Then, since and ( cannot be MAX_INT since would be pending and otherwise). Hence, there exists another push such that and .
- pop7
We have and .
- pop8
We have and .
- com-pop
is obtained by applying com-pop1 rule of . We first show that precondition of com-pop1 rule of si satisfied by . If removes a node such that there exists a push such that in , then since it is non-null and not taken. Moreover, since TraverseAfter is preserved while relating to and all the nodes that come after in terms of traverse order in have lower timestamp values than and .
Next, we show that . We case split on the conditions of considering new instantiations.
Nodes condition is still preserved after removes the node pushed by operation in since anymore by due to action.
Frontiers condition is still preserved if removes the vertex and makes another maximally closed in . Since all the other nodes ordered after (except possibly ) in are pending, (due to Frontiers condition while relating to ) for some pending pop . Then, by action.
For the MinimalBE condition, we do not have a new instance. If becomes true although , we cannot have such that and since does not add to if its successor is not pending with respect to .
ReverseFrontiers condition is still preserved. If removes the vertex and there exists an immediate predecessor of such that all of immediate successors of are in , then due to the action .
TraverseAfter condition is still preserved after removes the node of push . Let be another pop operation such that for some push and be the only node such that and comes after in the traverse order of and . Hence, there is no such that comes after in the traverse order and except (i). In other direction, if for all such that comes before in the traverse order and , then since TraverseBefore condition holds while relating to . Then, for all such that comes before in the traverse order of and implies since if (ii). Then, for all such that if , then except due to (i) and (ii). If , then since ReverseFrontiers hold while relating to and after applying the action . Otherwise, if , then after applying .
FixReturn condition continues to hold. If removes the node pushed by in , then removes the vertex (assuming data independece) and . Then, after applying commit actions at both sides.
- pop9
We have and .
- ret-pop
is obtained by applying ret-pop rule of and .
The reference list from the paper itself. Each links out to its DOI / PubMed record.
- 1Abadi and Lamport [1991] M. Abadi and L. Lamport. The existence of refinement mappings. Theor. Comput. Sci. , 82(2):253–284, 1991. doi: 10.1016/0304-3975(91)90224-P. URL http://dx.doi.org/10.1016/0304-3975(91)90224-P . · doi ↗
- 2Abdulla et al. [2013] P. A. Abdulla, F. Haziza, L. Holík, B. Jonsson, and A. Rezine. An integrated specification and verification technique for highly concurrent data structures. In TACAS , pages 324–338, 2013.
- 3Alur et al. [2000] R. Alur, K. L. Mc Millan, and D. Peled. Model-checking of correctness conditions for concurrent objects. Inf. Comput. , 160(1-2):167–188, 2000.
- 4Amit et al. [2007] D. Amit, N. Rinetzky, T. W. Reps, M. Sagiv, and E. Yahav. Comparison under abstraction for verifying linearizability. In CAV ’07 , volume 4590 of LNCS , pages 477–490, 2007.
- 5Bouajjani et al. [2013] A. Bouajjani, M. Emmi, C. Enea, and J. Hamza. Verifying concurrent programs against sequential specifications. In ESOP ’13 , volume 7792 of LNCS , pages 290–309. Springer, 2013.
- 6Bouajjani et al. [2015 a] A. Bouajjani, M. Emmi, C. Enea, and J. Hamza. On reducing linearizability to state reachability. In M. M. Halldórsson, K. Iwama, N. Kobayashi, and B. Speckmann, editors, Automata, Languages, and Programming - 42nd International Colloquium, ICALP 2015, Kyoto, Japan, July 6-10, 2015, Proceedings, Part II , volume 9135 of Lecture Notes in Computer Science , pages 95–107. Springer, 2015 a. ISBN 978-3-662-47665-9. doi: 10.1007/978-3-662-47666-6. URL h · doi ↗
- 7Bouajjani et al. [2015 b] A. Bouajjani, M. Emmi, C. Enea, and J. Hamza. Tractable refinement checking for concurrent objects. In Rajamani and Walker [ 23 ] , pages 651–662. ISBN 978-1-4503-3300-9. doi: 10.1145/2676726.2677002. URL http://doi.acm.org/10.1145/2676726.2677002 .
- 8Derrick et al. [2011] J. Derrick, G. Schellhorn, and H. Wehrheim. Verifying Linearisability with Potential Linearisation Points , pages 323–337. Springer Berlin Heidelberg, Berlin, Heidelberg, 2011. ISBN 978-3-642-21437-0.
