Abstract 1 Introduction 2 Related Work 3 Model 4 Dynamic Concurrency 5 Dynamically Concurrent Universal Construction 6 Discussion References Appendix A ABC Graph – Proof of Linearizability and Wait-freedom Appendix B Universal Construction – Proof of Linearizability Appendix C Universal Construction – Proof of Wait-Freedom Appendix D Universal Construction – Proof of Dynamic Concurrency

Resolving Conflicts with Grace:
Dynamically Concurrent Universality

Petr Kuznetsov ORCID LTCI, Télécom Paris, Institut Polytechnique de Paris, France Nathan Josia Schrodt ORCID Télécom Paris, Institut Polytechnique de Paris, France
Technical University of Darmstadt, Germany
Abstract

Synchronization is the major obstacle to scalability in distributed computing. Concurrent operations on the shared data engage in synchronization when they encounter a conflict, i.e., their effects depend on the order in which they are applied. Ideally, one would like to detect conflicts in a dynamic manner, i.e., adjusting to the current system state. Indeed, it is very common that two concurrent operations conflict only in some rarely occurring states. In this paper, we define the notion of dynamic concurrency: an operation employs strong synchronization primitives only if it has to arbitrate with concurrent operations, given the current system state. We then present a dynamically concurrent universal construction.

Keywords and phrases:
Universal Construction, Consensus, Dynamic Concurrency
Copyright and License:
[Uncaptioned image] © Petr Kuznetsov and Nathan Josia Schrodt; licensed under Creative Commons License CC-BY 4.0
2012 ACM Subject Classification:
Theory of computation Distributed algorithms
Editors:
Andrei Arusoaie, Emanuel Onica, Michael Spear, and Sara Tucci-Piergiovanni

1 Introduction

When we design a distributed system that enables concurrent access to the shared data, we often have to face our main nemesis – synchronization. It is common that concurrent operations affect one another and, thus, cannot be executed in parallel; we say that the operations encounter a conflict. Potential conflicts are typically anticipated and resolved using consensus [10]. The processes agree on the order in which the operations are applied, as in the celebrated universal construction by Herlihy [15].

As consensus is perceived an expensive tool, a lot of efforts were invested in optimistic algorithms that avoid consensus-based synchronization in good scenarios, in particular when no contention is encountered, i.e., when an operation runs in isolation [23, 16, 4]. A step further in this direction is to avoid using consensus in the absence of conflicts, even when operations contend. Indeed, one can come up with a universal construction in which concurrent operations that do not conflict are executed in parallel without engaging in consensus to decide on their execution order [24, 6].

However, conflicts are treated here in a conservative way: two operations are considered conflicting if they do not commute in some state (but might commute in another). This can result in very inefficient implementations. Indeed, many data structures exhibit conflicts rarely, only in certain states. Consider, for example an asset-transfer system [13] that allows a set of users to execute transactions sending assets across their accounts. When two transactions transfer($100) and transfer($50) are concurrently applied on the same account holding $100, their effect depends on the order in which they are executed: the first one will be accepted, and the second one – rejected. On the other hand, if the account holds at least $150, the two transactions can be applied in any order with the same effect.

The notion of a dynamic conflict in asset-transfer systems is defined in a straightforward manner, and one can come up with a system that uses consensus only when necessary, i.e., concurrent transactions cannot all be served [32]. To the best of our knowledge, however, no attempt has been taken to define dynamic conflicts in a universal way, that would apply to any sequential object. We address this issue in the concurrent model, where processes communicate by applying operations on shared base objects, heading for a universal construction, a wait-free linearizable [17, 15] implementation of any object, given its sequential specification. We distinguish between basic read-write operations (offering consensus power 1 [15, 10, 26]) and strong (read-modify-write) synchronization primitives that enable nontrivial consensus solutions, such as CAS or LL/SC. Our goal is a dynamically concurrent universal construction that avoids using strong synchronization if concurrent operations commute in the current system state. The goal, quite easily grasped on an intuitive level, is however evasive when it comes to formal treatment. How is the “current state” defined in an execution of a concurrent algorithm?

Indeed, consider an operation in a concurrent history. The operation’s interval may be concurrent with different operations at different moments of its execution. How do we define the state of the implemented object and decide whether the operation commutes with concurrent operations in that state? Imagine an implementation in which every operation op can be thought of taking effect at a well-defined moment between its invocation and its response, a linearization point [17]. Then, each moment during the execution of op determines a system state, consisting of the effects of operations that have already taken place and the set of operations that have been invoked but not yet linearized. Even if the actual implementation does not allow us to determine an irreversible linearization point for each operation [14], we can derive a superset of all possible system states that might have occurred. Intuitively, an implementation is said to be dynamically concurrent if it uses strong synchronization for an operation only when, in some possible system state during its execution, it does not commute with some concurrent operations (see Figure 1 for an illustration).

The canonical approach to building a universal construction is to order operations using consensus and applying the sequential specification of the object to the ordering. A natural generalization of this approach, aimed at allowing concurrency, is to replace the linear model with a directed acyclic dependency graph of operations, in which all topological orderings yield equivalent linearizations. This way commuting operations are executed in parallel, which might considerably improve performance in workloads where dynamic conflicts are rare, often argued to be the most common case.

Our dynamically concurrent universal construction builds on this idea using a shared dependency graph together with a set of active operations. The current state is obtained by topologically ordering the current version of the graph, while the set of active operations is determined by identifying those that have been invoked but not yet added to the graph. If commutativity with the concurrent operations is observed, the current operation can be appended to the graph directly; otherwise, the algorithm resorts to ordering the operations via consensus. A particular difficulty here is to ensure that conflicting operations committed via different paths (direct commitment and consensus-based) cannot bypass each other, preserving correctness. We achieve this by a variant of commit-adopt [11] that guarantees that a directly committed operation is always placed in the causal past of every concurrent operation that encounters a conflict with it.

To sum up, in this paper we:

  1. (1)

    Define the notion of a dynamically concurrent object, the one that resorts to consensus only when a dynamic conflict arises.

  2. (2)

    Describe a dynamically concurrent universal construction.

The paper is organized as follows. In Section 2, we overview the related work and in Section 3, we recall our model assumptions. In Section 4, we formally define the notion of dynamic concurrency. In Section 5, we present our protocol and analyze its correctness. Section 6 concludes the paper with the discussion of ramifications of this work and open questions. Certain proofs are delegated to the appendix.

2 Related Work

Most concurrent data structures exhibiting strong consistency and availability guarantees, such as linearizability [17, 5] and wait-freedom [15], require using of consensus or strong synchronization primitives.

As consensus often becomes an implementation’s bottleneck, a lot of effort has been invested in optimistic solutions that avoid consensus in “good runs”, in particular, when no contention is observed [8, 20, 34]. In concurrent data structures, this approach instantiated in solo-fast implementations [4], where strong synchronization is only used when step contention is observed.

Schneider [29] introduced the notion of a state-machine replication, a concurrent implementation of an object given its sequential specification. Lamport [23] discovered the first fault-tolerant state-machine replication protocol and then generalized it to account for parallel execution of non-conflicting operations [24]. In the same vein, Zielinski proposed an elegant and fast generic broadcast protocol that ensures that conflicting broadcast messages are delivered in the same order (non-conflicting ones do not have to be ordered). Both proposals assume a static definition of conflicts, where certain pairs of commands are conservatively considered as conflicting if they do not commute in some state. The static understanding of conflicts has been also considered in the Byzantine fault-tolerant setting: in Byzantine generalized Paxos [28], Byblos [6] and RedBlue [25].

Aspnes and Herlihy [3] presented a wait-free construction for objects where every operations either commutes with or overwrites any other operation in any history. Like our approach, their method uses a precedence graph to capture dependencies between operations. Requiring that every operation either commutes or overwrites any other is a major restriction, since it limits the construction to objects with consensus number one. In contrast, our construction supports arbitrary objects and detects conflicts dynamically.

The notion of dynamic (state-dependent) conflicts has also been considered by Clements et al. [7] in their work on commutativity rule, an efficient generalization of disjoint-access-parallelism by Israeli and Rappoport [18] to semantics-aware systems. Kulkarni et al. [22, 21] discuss algorithms that achieve serializability of transactions and adjust to low-level dynamic conflicts incurred by underlying base-object operations: transactions that do not encounter low-level data conflict proceed in parallel, without unnecessary lock-based synchronization. In contrast, we focus here on high-level semantics-aware dynamic conflicts and wait-free implementations that do not engage in consensus in conflict-free scenarios.

To the best of our knowledge, the only attempt to account for dynamic conflicts in this context was taken in CryptoConcurrency [32], an asset transfer protocol that processes concurrent transactions on the same account in parallel, without resorting to consensus to order them, as long as they are not exhausting the current account balance. In this paper, we generalize this idea to universal construction that allows concurrent operations to proceed in parallel, as long as they are not in conflict in the current object state.

3 Model

We consider a system of n asynchronous processes. Each process can be arbitrarily fast or slow relative to the other processes and can crash fail, i.e., stop taking steps at any moment. The processes communicate through fundamental shared base objects like atomic read-write registers. Those base objects can be used to implement more complex, shared, sequential objects.

Objects.

A sequential object X is defined by a tuple (Q,q0,O,R,σ) where Q is a set of states, q0Q is the initial state, O is a set of operations, R is a set of outputs, and σ:O×QR×Q is a sequential specification with σ((o,q))=(r,q) if and only if operation o applied to X in state q returns r and changes its state to q. Since we consider only sequential objects, we will henceforth refer to them simply as objects.

Steps and Algorithms.

The behavior of a process when implementing a high-level object (as opposed to a base object) is described by a deterministic automaton that specifies how each process responds to invocations of that object. Upon receiving an invocation, the process advances by taking steps prescribed by its automaton. We refer to the descriptions of these automatons simply as implementations or algorithms. Each step consists of some terminating local computation together with a single operation on a shared base object. Infinite loops of purely local computation without any interaction with a base object are disallowed. In addition, an algorithm may invoke operations of other higher-level shared objects. Such invocations act as subautomata: control is delegated to the invoked object until its operation finishes, after which the original automaton continues. Conceptually, this recursive structure can be flattened, leaving only local computations and operations on base objects. After each step, a process updates its local state according to its transition rules and may return a response to the currently active operation of the high-level object being implemented. Crucially, a process is not allowed to start executing a new operation on this implemented object until the previous operation has terminated.

Runs and Operations.

A run is a (possibly infinite) sequence of events obtained by interleaving the steps of all processes, beginning from the initial global state. A run especially includes every taken step as well as invocations and response events of operations of every (high-level or base) object involved. For simplicity, we assume that each operation is unique, i.e., an operation is invoked at most once. This uniqueness can be ensured by assigning a caller-specific identifier and a sequence number to every operation. We denote the process executing an operation op as op.p, and refer to the invocation and response (event) of op as op.inv and op.res, respectively. Invocations and responses can be uniquely associated with operations.

Histories and Linearizability.

A history is a sequence H of invocations and responses without duplicates, where every response op.res is preceded by its unique matching invocation op.inv, and each invocation op.inv corresponds to at most one response. We define opH if and only if op.invH. A run induces a history for a given object by listing every invocation and response of operations on that object that occur in the run, in the order in which they appear. A history is sequential if every invocation can only be followed by a corresponding response. Thus, a sequential history S is a list of operations and vice versa. Whenever S is finite, the last operation in S might not have a response.

Operation op precedes op in H if op.res precedes op.inv in H. We write op<Hop. If neither op<Hop nor op<Hop but op,opH, op and op are concurrent in H. The relation <H is irreflexive, asymmetric and transitive and therefore forms a strict partial order. For a sequential history, <H is a total order. We refer to <H as the precedence relation of H. A sequential history S is legal if it satisfies the sequential specifications of the objects, i.e., for each object, starting from its initial state and sequentially applying the operations of S that involve this object yields exactly the responses recorded in S. An operation op is complete in H if H contains both the invocation and the response of op. A completion of H is a history H that only includes all complete operations of H and a subset of incomplete operations of H with corresponding responses. Furthermore, H has to preserve the precedence relation of H. Let us denote the history that consists of all invocations and responses of a process p in a history H as Hp.

Definition 1.

Histories H and H are equivalent if we have Hp=Hp for all processes p.

Definition 2.

Let H be a history. A sequential history S is a linearization of H if

  • S is equivalent to a completion of H,

  • S preserves the precedence relation of H and

  • S is legal.

If there exist such S for a history H, we call H linearizable.

An implementation (or algorithm) I is linearizable if, for every run R of I, the history induced by R is linearizable. Linearizability is a widely accepted correctness condition for concurrent objects. It requires that every operation on the object appears to take effect at a single point in time between its invocation and response, as though the object was accessed atomically.

Wait-freedom.

The gold standard for progress guarantees is wait-freedom. An implementation of an object is wait-free if, whenever a process that does not crash fail invokes an operation, that process is guaranteed to complete the operation in a finite number of its own steps, regardless of the actions or failures of other processes.

Snapshots and Consensus.

A fundamental object with a wait-free linearizable implementation using only atomic read-write registers is the snapshot object [1]. Each process has a dedicated component it can update with a write operation, and all processes can invoke snapshot() to obtain a consistent view of all components. Another key object is consensus, exporting a single operation propose(v). Each process can propose values v from a set V, and all invocations must return the same value (agreement), which must be one of the proposed values (validity). We say that a consensus object stores a value v, once an invocation of propose returned that value. Agreement ensures that no other propose operation on the same object can return a different value. Unlike the snapshot object, consensus cannot be implemented in a wait-free linearizable manner using only read-write registers [10]. Any base object that, together with read-write registers, enables solving wait-free consensus, such as CAS or LL/SC, is called a strong synchronization primitive.

4 Dynamic Concurrency

We now introduce the notion of a dynamically concurrent implementation. Before formalizing the notions of the system state and dynamic concurrency in Definitions 6 and 7, we define the equivalence and commutativity relations and then provide some intuition on dynamic concurrency.

4.1 Equivalences and Commutativity

In this section, we define equivalences between sequences of operations, as well as the notion of commutativity, crucial in formalizing the concept of dynamic concurrency in the next section and, accordingly, in determining whether certain operations require ordering in a dynamically concurrent universal construction. Let s and s be disjoint sequences of operations. We denote ss as the concatenation of s and s. A single operation is interpreted as a sequence of length one.

Definition 3.

Let s and s be orderings of a set of operations S. The orderings s and s are equivalent if according to the sequential specification the application of s to the initial state results in the same state as the application of s and every operation in S returns the same output in both orderings. We write ss.

If ss, and s and s share a common prefix p such that s=pr and s=pr for some r and r, we say that r and r are equivalent in p.

We immediately see that the relation of sequences of operations, defined in Definition 3, is a well-defined equivalence relation for a given set of operations, i.e., it is reflexive, symmetric and transitive.

Two states are said to be distinguishable if there exists a sequence of operations whose execution from these states yields different responses. If certain states cannot be distinguished in this way, the object’s states can be replaced by equivalence classes, where two states are equivalent if they are not distinguishable. Working with this quotient state space can increase the number of equivalent orderings and, potentially, the scenarios in which operations commute according to Definition 4.

Definition 4.

Let op be an operation and l and s sequences of operations with opl, ops and ls=. Operation op commutes in l with s if lsoplops.

Let S be a set of operations with opS and lS=. Operation op commutes in l with S if op commutes in l with every ordering of S.

Lemma 5.

Let l be a sequence of operations, s=s1s2 and s=s1s2 orderings of the same set of operations S, and op an operation with opl, opS and lS=.

If lsls and op commutes with s1, s1, s and s in l, then it follows that ls1ops2ls1ops2.

Proof.

The equivalence follows by transitivity of the equivalence relation.

ls1ops2lops1s2ls1s2opls1s2oplops1s2ls1ops2

A key result of Lemma 5 with s=s, used throughout this paper, is that if an operation commutes with every subset of a set, every placement of this operation in this set is equivalent.

4.2 Definitions

The idea behind a linearization, or linearizability in general, is that each operation can be viewed as taking effect atomically at a single moment between its invocation and its response. In many cases, this perspective is straightforward, as one can identify a step where an operation takes effect. In other cases, however, this perspective serves only as an abstraction, since no single concrete point in time can be pinpointed.

Our definition uses this abstraction to define possible configurations during the execution of an operation. Suppose that a linearizable history H is assigned a linearization point to each of its operations, i.e., the moment between its invocation and its response where the operation an be thought of taking effect. Then, the notion of a system state during an operation op follows naturally. Take a prefix of that enriched history that includes the invocation of op but not its linearization point. The system state consists of two elements.

  1. 1.

    The sequence of operations according to their linearization points.111Note that for a given sequential specification a sequence of operations defines the state of the object.

  2. 2.

    The set of all operations that have been invoked but not linearized.

Definition 6.

Let H be a history and op an operation. A system state during op in H is a pair (l,O), where l is a sequence and O a set of operations, such that:

  1. 1.

    There exists a prefix H of H with op.invH and op.resH.

  2. 2.

    There exists a linearization L of H such that l is a prefix of L and a linearization of H that does not include op.

  3. 3.

    The set O consists precisely of those operations op for which op.invH, opl, and opop.

Note that the linearization l in Definition 6 may also contain operations whose responses are not included in H, provided they precede op in L.

With the definition of system states in place, the notion of dynamic concurrency can be introduced. Intuitively, a dynamically concurrent implementation of an object uses a strong synchronization primitive while executing an operation op, only when, during its execution, it could have been in a system state where op does not commute with every subset of the concurrent operations in that current state.

Definition 7.

An implementation I of an object is dynamically concurrent if for every history H induced by a run of I and every operation op in H, the following holds:

Process op.p uses a strong synchronization primitive while executing op.

There exists a system state (l,O) during op in H such that there exists a subset OO with which op does not commute in l.

While the above definitions formalize the concepts of system state and dynamic concurrency, their intuition may not be immediately apparent. The following example demonstrates how these notions manifest in a concrete execution.

4.3 Example

To illustrate the concepts of system state and dynamic concurrency, consider the history H depicted in Figure 1. The history is induced by a run of 4 processes executing operations on a shared list object. This object exports the methods append(v), readLast(), readAll() and swap(i,j) for v{a,b,c,d} and 0ij. The method append(v) appends v at the end of the list and returns ok; readLast() returns the value at the last index of the list, for an empty list; readAll() returns the whole list; swap(i,j) swaps the values at indices i and j and returns ok if the list has at least j1 elements, otherwise it has no effect and returns .

Figure 1: An illustration of a history H with 4 processes using a shared list object. The response of p2.readAll() requires every linearization to order p1.append(b) before p3.append(a). Note that p3.swap(0,2) commutes with p2.readLast() in (p2.append(a),p1.append(b),p3.append(a)). Moreover, for every system state during p3.swap(0,2) in H, p3.swap(0,2) commutes with every subset of concurrent operations in the respective state of the object. Dynamic concurrency forbids p3 to use a strong synchronization primitive during the execution of p3.swap(0,2).

Let us now examine the system states during op:=p3.swap(0,2) in H. To determine these states, we consider all possible linearizations L of H. The response of p2.readAll() requires that in every linearization, p1.append(b) is ordered before p3.append(a). Hence, (p2.append(a),p1.append(b),p3.append(a)) forms a prefix of every linearization.

This constraint stems from the choice made in Definition 6 to require l to be a prefix of a linearization of the entire history H, rather than simply a linearization of a prefix H. The motivation for this choice is to obtain a more refined representation of the implementation’s internal state. In particular, the definition assumes that after certain operations have terminated (such as p1.append(b) and p3.append(a) in our example), the internal state of the implementation has either already determined their relative order, even though this ordering is not yet externally observable, or the implementation will never establish such an order at all (in the case of equivalent orderings).

Furthermore, in every linearization p2.readLast() precedes p2.readAll(), which in turn precedes p0.append(c) (if present) and p1.append(d). Excluding op for the moment, the possible linearizations are:

  1. 1.

    pre(p0.append(c),p1.append(d)),

  2. 2.

    pre(p1.append(d),p0.append(c)),

  3. 3.

    pre(p1.append(d)),

where pre:=(p2.append(a),p1.append(b),p3.append(a),p2.readLast(),p2.readAll()). All 14 linearizations that include op can be obtained by inserting it at any position after the first three operations. However, since we are only interested in those prefixes of linearizations that do not include op, we can simply omit it. According to Definition 6, we obtain the following system states during op in H.

  1. 1.

    l1=(p2.append(a),p1.append(b),p3.append(a)) with O1={p2.readLast()},

  2. 2.

    l2=(p2.append(a),p1.append(b),p3.append(a),p2.readLast()) with O2={},

  3. 3.

    l3=(p2.append(a),p1.append(b),p3.append(a),p2.readLast()) with O3={p1.append(d)},

  4. 4.

    l4=(p2.append(a),p1.append(b),p3.append(a),p2.readLast())
    with O4={p1.append(d),p0.append(c)},

  5. 5.

    l5=(p2.append(a),p1.append(b),p3.append(a),p2.readLast())
    with O5={p1.append(d),p0.append(c),p2.readAll()},

  6. 6.

    l6=pre with O6={p1.append(d),p0.append(c)},

  7. 7.

    l7=pre(p0.append(c)) with O7={p1.append(d)},

  8. 8.

    l8=pre(p1.append(d)) with O8={p0.append(c)},

  9. 9.

    l9=pre(p0.append(c),p1.append(d)) with O9={},

  10. 10.

    l10=pre(p1.append(d),p0.append(c)) with O10={}.

According to Definition 7, dynamic concurrency forbids p3 to use strong synchronization primitives during the execution of op, since in every system state (li,Oi) op commutes in li with every subset of Oi.222The condition for all i[1,10] is equivalent to requiring it only for i{1,5}. Note that this holds even though p0.append(c) and p1.append(d) do not commute in any state.

Similarly, p2 cannot use strong synchronization primitives during the execution of either p2.append(a) or p2.readLast().

As discussed above, the response of p2.readAll() enforces that p1.append(b) must be ordered before p3.append(a). Suppose, however, that p2.readAll() were absent from the history. In that case, p3.append(a) could instead be ordered before p1.append(b), since the response of p2.readLast() would remain correct provided that p3.swap(0,2) is ordered before p2.readLast(). In this scenario, p3.swap(0,2) no longer commutes with p2.readLast(), and consequently p3 would be permitted to use strong synchronization primitives while executing p3.swap(0,2).

5 Dynamically Concurrent Universal Construction

We now present our (wait-free and linearizable) dynamically concurrent universal construction. The universal construction takes the specification of a sequential object as a parameter and exports one method publish(op) that simulates the execution of an operation op and returns a response of op.

A typical universal construction invokes multiple instances of consensus to agree on the order in which invoked operations are linearized. The response of each operation is then computed from this order using the object’s sequential specification.

To exploit commutativity, we replace the linear ordering on operations with a shared dependency graph. Intuitively, in the graph, an operation has incoming edges from every operation that should precede it. Unrelated operations in the graph are then concurrent and commuting. A linearization is obtained by topologically ordering this dependency graph.

For this algorithm to be correct, we essentially have to ensure the following conditions. First of all, after the completion of publish(op) the operation op has to be added to the dependency graph. Secondly, this dependency graph has to be an acyclic graph to ensure that topological orderings exist. Another requirement is that the topological orderings, and therefore the dependency graph, should respect the real-time ordering of different invocations of publish, i.e., the precedence relation of the induced history. If publish(op) terminates before publish(op) is invoked, op should precede op in every topological ordering, i.e., there must be an edge from op to op in the dependency graph. Furthermore, all possible topological ordering of this dependency graph should be equivalent, as stated in Definition 3. Intuitively, unrelated graph vertices should commute and, thus, no process can distinguish their different orderings. Finally, we have to guarantee that this dependency graph is stable in the sense that no response of an operation, according to any topological ordering, changes when the graph evolves. This is necessary, since such a response might have already been returned by some process as the response of that operation.

We now introduce the Announce-Book-Commit Graph, a core component of our algorithm.

5.1 Announce-Book-Commit (ABC) Graph

Before introducing the main part of the algorithm, we describe the principal underlying data structure used to implement the dependency graph. The ABC Graph composes three data structures A, B and C into one snapshot object to allow an atomic read of all three structures.

  1. 1.

    A is a set of operations.

  2. 2.

    B is a set of tuples (op,b) where op is an operation and b is an integer.

  3. 3.

    C is a graph of operations, encoded as a set of tuples (op,M), where op is an operation and M is a set of operations, interpreted as the set of operations that have outgoing edges to op.

The ABC Graph exports three methods addA(op), addB(op,b) and addC(op,M) used to append elements to the corresponding sets in the following way. The set A is the set of all operations op with some preceding addA(op) operation. The set B is the set of all tuples such that (op,b)B if and only if there exists some preceding addB(op,b) and b is minimal among all preceding operations addB(op,). The graph C is the set of all tuples such that (op,M)C if and only if there exists some preceding operations addC(op,Mj) and M=jMj for all preceding operations addC(op,Mj). The method read() returns the current state of the three data structures. Note that, even though addB(op,) and addC(op,) may be called multiple times for the same operation op, the components B and C, as returned by read(), include at most one tuple per operation: the minimum value for B and the union of all sets for C.

We say that an operation is announced once it is added to A, booked once it is added to B, and committed once it is added to C. Correspondingly, we say that a process is announcing, booking, or committing an operation while executing addA, addB, or addC, respectively. In particular, when we say that an operation op is being committed for the first time, we mean that some process is currently executing addC(op,) that adds op into C for the first time. We denote by vertices(B) (resp., vertices(C)) the set of operations that appear in B (resp., C). Note that operations appear only as the first element of a tuple in B but in both parts of a tuple in C. Furthermore, we denote by edges(C) the set of edges of C, i.e., (op,op)edges(C) if and only if there exists (op,M)C with opM. The value associated with an operation op in B is called its B-value and is denoted by B[op]. If opvertices(B), we say that op has no B-value, and we define, by convention, B[op] to be .

Given the specification of an ABC Graph, we now discuss its interpretation and use within our universal construction. Each invocation of publish(op) announces its own operation op by adding it to the set A, which thus contains every operation invoked so far. For each operation op, the set B records information about the recency of the ABC Graph that publish(op) has observed (namely, the size of A). In our construction, each invocation publish(op) calls addA(op) and addB(op,) at most once, and only for its own parameter. Consequently, the aggregation mechanism for B exists solely for formal completeness and is not needed in practice. Graph C is interpreted as the dependency graph described above.

Algorithm 1 Announce-Book-Commit Graph.

Algorithm 1 provides a wait-free and linearizable implementation of the ABC Graph. The proof is deferred to Appendix A. In the implementation, processes store invocations of add operations in local registers (lines 8, 11 and 14) and write them to a shared snapshot object. The read operation aggregates all invocations of add to A, B and C as described above.

5.2 Dynamically Concurrent Universal Construction

In the following, we provide intuition for how the dynamically concurrent universal construction in Algorithm 2 operates: how it places an operation op correctly into the dependency graph, and why consensus is used only when necessary, satisfying dynamic concurrency (Definition 7). Formal proofs of linearizability, wait-freedom, and dynamic concurrency are deferred to the Appendices B, C, and D.

Algorithm 2 Dynamically Concurrent Universal Construction.

Once a process proposes an operation op (line 5), it first adds it to set A (line 6) to make it visible to concurrent and future operations. Then it takes a snapshot (line 7) of the current state (A, B and C). By construction, graph C contains the previously committed operations. The operations in Avertices(C) are therefore concurrent. Let us call this set CUR. Note that some operations in CUR may never be committed, as they may be issued by processes that have later crashed.

After reading the state for the first time, op is added to B (line 8) together with |A|, the cardinality of A. Intuitively, |A| indicates how many operations publish(op) has encountered at its first read, indicating the recency of its view of the system. This helps later to prioritize operations that may have missed conflicts. After announcing and booking the operation, the process reads the state again (line 9), which updates its local values for A, B and C (and implicitly CUR). The graph C is then topologically ordered (line 10). We denote this ordering by l. We assume inductively that all topological orderings of C are equivalent. If op is already committed (line 11), the result of the operation in l (according to the sequential specification) is returned (line 12); otherwise, the process checks whether op commutes with every subset of CUR{op} in l (line 14). Verifying commutativity with every subset of CUR{op} accounts for the possibility that some operations in CUR may never be committed due to crash failures. Moreover, Lemma 5 guarantees that, for every equivalent ordering of CUR{op} in l, every possible insertion point of op within those orderings is equivalent in l, as formalized in Definition 3. If commutativity is verified, publish(op) commits op (line 15) and returns its response in l(op) (line 16). Otherwise, ordering of concurrent operations is required and conflict resolution (lines 18-32), where consensus is used, is executed.

Conflict resolution proceeds in iterations. In each iteration, the process reads the state (line 22), selects an operation with the minimal B-value (among all non-committed operations) (line 26) and proposes it to the next consensus object (line 27). The state is read again (line 28) and unless the operation op^ that has been returned by the consensus object is already committed (line 29), it is committed by publish(op) in line 30.

Each operation op~ that is proposed to a consensus object, and therefore each operation that is committed in conflict resolution, has minimal B-value, according to a version of the ABC Graph that was read by some process executing conflict resolution. In the following, we are interested in the B-values according to this version of the ABC Graph. As can be easily verified, an operation is added to B at most once, and only by its corresponding invocation of publish. Consequently, the B-values of different versions of the ABC Graph differ only in the set of operations that have a (finite) B-value. Once an operation has a (finite) B-value, it is constant. Since we consider, by construction, a version of the ABC Graph where op~ has a minimal B-value (among all non-committed operations), there only exist operations with a higher B-value or operations that do not have a B value yet. Each operation that already has, or can obtain, a B-value of at most that of op~ has been encountered by publish(op~) in line 9.333Since the ABC Graph is linearizable, we can order all invocations of addA. Let opi be the i-th operation added to A. Since the ABC Graph is read again after executing addA(opi), opi will have a B-value of at least i, once it is booked. An operation that already has, or can obtain, a B-value of at most i, has to be among the first i operations added to A, and has therefore been encountered in line 9 by an operation with a B-value of i.

One can notice that publish(op~) might have not encountered (in line 9) concurrent operations with higher (possibly infinite) B-values. Therefore, publish(op~) might have encountered no conflict and already passed the check of commutativity (line 14). This is the reason why operations with minimal B-value are prioritized in conflict resolution. Operations with no B-value, i.e., operations that are not booked yet, will reread the state (line 9) before committing and therefore encounter every operation that already has a B-value. Thus, an operation with no B-value will either encounter a conflict too or find every conflict already resolved in C. Therefore, operations that are not in B, i.e. with no B-value, need not to be prioritized in the conflict resolution. In summary, an operation with minimal B-value (among concurrent operations) in some version of the ABC Graph has been or will be encountered in line 9 by publish(op) for every non-committed operation op. Such an operation can therefore be proposed to be committed in conflict resolution without risking conflicting operations to miss it.

Clearly, when an operation is proposed to CONSk, every previous CONSm with m<k stores an operation and those operations are committed. This does not imply that an operation stored in CONSk, once committed, will include all of those operations, stored in previous consensus objects, as dependencies. This is due to the fact, that operations can be concurrently committed in the conflict-free path, while being proposed to a consensus object. However, it is easy to see that if an operation op is committed in the conflict resolution path, every operation committed in the conflict resolution path using a later consensus object will include op in its dependencies.

The algorithm uses a snapshot object (line 2) that records the last completed iteration of every process, allowing processes to skip iterations that have already been completed by any process (lines 18, 19, 21 and 32). This does not affect correctness, as re-executing a completed iteration has no effect.

Sketch of Linearizability Proof.

Algorithm 2 ensures linearizability by checking commutativity among operations and resorting to consensus when conflicts arise. No two conflicting operations can proceed along the conflict-free path simultaneously, because at least one operation will detect the other. Operations that could have “missed” conflicts are prioritized, when committing operations in the conflict resolution path, to maintain a consistent view among all processes. While a conflict is being resolved, other conflicting operations also enter conflict resolution, whereas non-conflicting operations continue along the conflict-free path. The resulting dependency graph has topological orderings that are equivalent, and each such ordering serves as a linearization of the history.

Sketch of Wait-Freedom Proof.

Under the assumption that the ABC Graph is wait-free, any invocation of publish that encounters no conflict terminates trivially after a finite number of steps. For invocations that perform conflict resolution, the process skips iterations that have already been completed. Given at most cn concurrent processes, the corresponding operation is stored in a consensus object after at most c+1 iterations, unless it is concurrently committed via the conflict-free path. In all cases, publish completes after a finite number of steps.

Sketch of Dynamic Concurrency Proof.

When reading the ABC Graph (line 9), the tuple (linearize(C),Avertices(C){op}) essentially represents a system state. It can be shown that l:=linearize(C) is a linearization of the history induced by the current run, and that for every possible extension of the run the induced history has a linearization such that l is a prefix of that linearization. Furthermore, Avertices(C){op} consists of concurrent operations op such that oplinearize(C) and opop, as required by Definition 6. Note that Avertices(C){op} might be a subset of all concurrent operations, which only restricts the cases where consensus is invoked. Therefore, the check performed in line 14 directly corresponds to the dynamic concurrency condition: consensus is used only when the operation does not commute with some subset of the concurrent operations in that system state.

As mentioned above, formal proofs of linearizability, wait-freedom, and dynamic concurrency are provided in the Appendices B, C, and D.

6 Discussion

In this paper, we settle down the very possibility of implementing a dynamically concurrent universal construction that only resorts to consensus if the concurrent operations are in conflict, given the current object state.

The next question is what the cost of detecting and resolving dynamic conflicts is. In our construction, non-conflicting operations complete in two write-read rounds. However, both the communication overhead of these rounds and the local computation can become significant. The latter is exponential in the number of concurrent operations, as commutativity must be verified for every subset of those. In contrast, consensus itself may not be particularly costly, especially in a shared-memory setting, which suggests a trade-off between minimizing synchronization through dynamic conflict detection and the total computational and communication costs associated with it. It may well be that these costs outweigh the benefits gained from avoiding consensus in conflict-free scenarios. It is therefore appealing to undertake comparative performance analysis of universal-construction algorithms, with and without dynamic conflict resolution.

It is important to note that our construction makes no assumptions about the type of operations. It is common to avoid synchronization in read-only workloads, and the same optimization is possible here. Read-only operations do not need to be added to the dependency graph, since other processes do not need to be aware of them. They can compute their responses directly based on an up-to-date version of the dependency graph, proceeding efficiently while preserving correctness.

The current definition of a concurrent operation accounts for interval contention [4] – operations are considered concurrent if their intervals overlap. A more refined definition accounts for step contention which only accounts for concurrently active (taking steps) operations [16, 4]. It is tempting to design a universal construction that uses consensus only when a concurrently active conflicting operation is detected. We conjecture, however, that this might not be possible.444Note that every sequential objects admits a solo-fast implementation [4], in which operations complete without using strong synchronization primitives in the absence of step contention. However, using a solo-fast consensus object implementation in our algorithm would not achieve the desired effect. Even if no conflicting operation takes steps concurrently, some commuting operations could still introduce step contention on the solo-fast consensus implementation.

This work is close in spirit to the idea of optimal concurrency [12, 2], an earlier attempt to grasp the minimal use of synchronization in concurrent data structures. By naïvely running the sequential implementation of an object in the concurrent environment, we obtain, intuitively, the throughput baseline: no implementation is likely to perform better [9]. We can turn this (most probably incorrect) sequential implementation into a correct concurrent one by introducing synchronization primitives. One can then reason about the amount of concurrency the implementation exhibits using the set of correct (linearizable) concurrent schedules it accepts. An optimally concurrent implementation [12] rejects a schedule only if it results in an incorrect (i.e., not linearizable) execution. Examples of optimally concurrent implementations of concrete data structures are given in [12, 2]. In contrast, in this paper, we rely on the sequential specification of the object, rather then on a concrete sequential implementation of it. This makes our approach more general, even though we do not argue that the use of consensus is minimal in any sense. It is intriguing to explore the issue of “optimality” further, e.g., can one argue that no algorithm can use less strong synchronization?

Finally, dynamic conflict resolution can become an important tool in Byzantine fault-tolerant universal constructions [27, 33], in particular in DAG-based blockchains [19, 31, 30].

References

  • [1] Yehuda Afek, Hagit Attiya, Danny Dolev, Eli Gafni, Michael Merritt, and Nir Shavit. Atomic snapshots of shared memory. J. ACM, 40(4):873–890, September 1993. doi:10.1145/153724.153741.
  • [2] Vitaly Aksenov, Vincent Gramoli, Petr Kuznetsov, Di Shang, and Srivatsan Ravi. Optimal concurrency for list-based sets. In Victor Malyshkin, editor, PaCT, volume 12942 of Lecture Notes in Computer Science, pages 386–401. Springer, 2021. doi:10.1007/978-3-030-86359-3_29.
  • [3] J. Aspnes and M. Herlihy. Wait-free data structures in the asynchronous pram model. In Proceedings of the Second Annual ACM Symposium on Parallel Algorithms and Architectures, SPAA ’90, pages 340–349, New York, NY, USA, 1990. Association for Computing Machinery. doi:10.1145/97444.97701.
  • [4] Hagit Attiya, Rachid Guerraoui, Danny Hendler, and Petr Kuznetsov. The complexity of obstruction-free implementations. J. ACM, 56(4):24:1–24:33, 2009. doi:10.1145/1538902.1538908.
  • [5] Hagit Attiya and Jennifer L. Welch. Distributed computing - fundamentals, simulations, and advanced topics (2. ed.). Wiley series on parallel and distributed computing. Wiley, 2004.
  • [6] Rida Bazzi and Maurice Herlihy. Clairvoyant state machine replication. Information and Computation, 285:104701, 2022. doi:10.1016/J.IC.2021.104701.
  • [7] Austin T. Clements, M. Frans Kaashoek, Nickolai Zeldovich, Robert T. Morris, and Eddie Kohler. The scalable commutativity rule: designing scalable software for multicore processors. In Michael Kaminsky and Mike Dahlin, editors, SOSP, pages 1–17. ACM, 2013. doi:10.1145/2517349.2522712.
  • [8] James A. Cowling, Daniel S. Myers, Barbara Liskov, Rodrigo Rodrigues, and Liuba Shrira. HQ replication: A hybrid quorum protocol for byzantine fault tolerance. In Brian N. Bershad and Jeffrey C. Mogul, editors, OSDI, pages 177–190. USENIX Association, 2006. URL: http://www.usenix.org/events/osdi06/tech/cowling.html.
  • [9] Tudor David, Rachid Guerraoui, and Vasileios Trigonakis. Asynchronized concurrency: The secret to scaling concurrent search data structures. In ASPLOS, pages 631–644, 2015. doi:10.1145/2694344.2694359.
  • [10] Michael J. Fischer, Nancy A. Lynch, and Michael S. Paterson. Impossibility of distributed consensus with one faulty process. JACM, 32(2):374–382, April 1985. doi:10.1145/3149.214121.
  • [11] Eli Gafni. Round-by-round fault detectors (extended abstract): unifying synchrony and asynchrony. In PODC, pages 143–152, 1998.
  • [12] Vincent Gramoli, Petr Kuznetsov, and Srivatsan Ravi. In the search for optimal concurrency. In Jukka Suomela, editor, SIROCCO, volume 9988 of Lecture Notes in Computer Science, pages 143–158, 2016. doi:10.1007/978-3-319-48314-6_10.
  • [13] Rachid Guerraoui, Petr Kuznetsov, Matteo Monti, Matej Pavlovic, and Dragos-Adrian Seredinschi. The consensus number of a cryptocurrency. In PODC, 2019. URL: https://arxiv.org/abs/1906.05574.
  • [14] Maryam Helmi, Lisa Higham, and Philipp Woelfel. Strongly linearizable implementations: possibilities and impossibilities. In Darek Kowalski and Alessandro Panconesi, editors, PODC ’12, pages 385–394. ACM, 2012. doi:10.1145/2332432.2332508.
  • [15] Maurice Herlihy. Wait-free synchronization. ACM Trans. Program. Lang. Syst., 13(1):124–149, 1991. doi:10.1145/114005.102808.
  • [16] Maurice Herlihy, Victor Luchangco, and Mark Moir. Obstruction-free synchronization: Double-ended queues as an example. In 23rd International Conference on Distributed Computing Systems (ICDCS 2003), 19-22 May 2003, Providence, RI, USA, pages 522–529. IEEE Computer Society, 2003. doi:10.1109/ICDCS.2003.1203503.
  • [17] Maurice P. Herlihy and Jeannette M. Wing. Linearizability: A correctness condition for concurrent objects. ACM Trans. Program. Lang. Syst., 12(3):463–492, July 1990. doi:10.1145/78969.78972.
  • [18] Amos Israeli and Lihu Rappoport. Disjoint-access-parallel implementations of strong shared memory primitives. In Proceedings of the Thirteenth Annual ACM Symposium on Principles of Distributed Computing, PODC ’94, pages 151–160, 1994. doi:10.1145/197917.198079.
  • [19] Idit Keidar, Eleftherios Kokoris-Kogias, Oded Naor, and Alexander Spiegelman. All you need is DAG. In Avery Miller, Keren Censor-Hillel, and Janne H. Korhonen, editors, PODC ’21: ACM Symposium on Principles of Distributed Computing, Virtual Event, Italy, July 26-30, 2021, pages 165–175. ACM, 2021. doi:10.1145/3465084.3467905.
  • [20] Ramakrishna Kotla, Lorenzo Alvisi, Mike Dahlin, Allen Clement, and Edmund Wong. Zyzzyva: speculative byzantine fault tolerance. In Proceedings of twenty-first ACM SIGOPS symposium on Operating systems principles, pages 45–58, 2007.
  • [21] Milind Kulkarni, Donald Nguyen, Dimitrios Prountzos, Xin Sui, and Keshav Pingali. Exploiting the commutativity lattice. In Mary W. Hall and David A. Padua, editors, PLDI, pages 542–555. ACM, 2011. doi:10.1145/1993498.1993562.
  • [22] Milind Kulkarni, Dimitrios Prountzos, Donald Nguyen, and Keshav Pingali. Defining and implementing commutativity conditions for parallel execution. In Proceedings of the 14th ACM SIGPLAN Symposium on Principles and Practice of Parallel Programming. ACM, 2009.
  • [23] Leslie Lamport. The Part-Time parliament. ACM Transactions on Computer Systems, 16(2):133–169, May 1998. doi:10.1145/279227.279229.
  • [24] Leslie B Lamport. Generalized paxos, apr13 2010. US Patent 7,698,465. URL: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-2005-33.pdf.
  • [25] Cheng Li, Daniel Porto, Allen Clement, Johannes Gehrke, Nuno M. Preguiça, and Rodrigo Rodrigues. Making geo-replicated systems fast as possible, consistent when necessary. In Chandu Thekkath and Amin Vahdat, editors, 10th USENIX Symposium on Operating Systems Design and Implementation, OSDI 2012, Hollywood, CA, USA, October 8-10, 2012, pages 265–278. USENIX Association, 2012. URL: https://www.usenix.org/conference/osdi12/technical-sessions/presentation/li.
  • [26] M.C. Loui and H.H. Abu-Amara. Memory requirements for agreement among unreliable asynchronous processes. Advances in Computing Research, 4:163–183, 1987.
  • [27] Satoshi Nakamoto. Bitcoin: A peer-to-peer electronic cash system, 2008.
  • [28] Miguel Pires, Srivatsan Ravi, and Rodrigo Rodrigues. Generalized paxos made byzantine (and less complex). Algorithms, 11(9):141, 2018. doi:10.3390/A11090141.
  • [29] Fred B. Schneider. Implementing fault-tolerant services using the state machine approach: A tutorial. ACM Computing Surveys, 22(4):299–319, December 1990. doi:10.1145/98163.98167.
  • [30] Alexander Spiegelman, Balaji Arun, Rati Gelashvili, and Zekun Li. Shoal: Improving DAG-BFT latency and robustness. In Jeremy Clark and Elaine Shi, editors, Financial Cryptography and Data Security - 28th International Conference, FC 2024, Willemstad, Curaçao, March 4-8, 2024, Revised Selected Papers, Part I, volume 14744 of Lecture Notes in Computer Science, pages 92–109. Springer, 2024. doi:10.1007/978-3-031-78676-1_6.
  • [31] Alexander Spiegelman, Neil Giridharan, Alberto Sonnino, and Lefteris Kokoris-Kogias. Bullshark: DAG BFT protocols made practical. In Heng Yin, Angelos Stavrou, Cas Cremers, and Elaine Shi, editors, Proceedings of the 2022 ACM SIGSAC Conference on Computer and Communications Security, CCS 2022, Los Angeles, CA, USA, November 7-11, 2022, pages 2705–2718. ACM, 2022. doi:10.1145/3548606.3559361.
  • [32] Andrei Tonkikh, Pavel Ponomarev, Petr Kuznetsov, and Yvonne-Anne Pignolet. Cryptoconcurrency: (almost) consensusless asset transfer with shared accounts. In Weizhi Meng, Christian Damsgaard Jensen, Cas Cremers, and Engin Kirda, editors, CCS, pages 1556–1570. ACM, 2023. doi:10.1145/3576915.3616587.
  • [33] Gavin Wood. Ethereum: A secure decentralised generalised transaction ledger. Ethereum project yellow paper, 151(2014):1–32, 2014.
  • [34] Maofan Yin, Dahlia Malkhi, Michael K. Reiter, Guy Golan-Gueta, and Ittai Abraham. Hotstuff: BFT consensus with linearity and responsiveness. In Peter Robinson and Faith Ellen, editors, PODC, pages 347–356. ACM, 2019. doi:10.1145/3293611.3331591.

Appendix A ABC Graph – Proof of Linearizability and Wait-freedom

In this section, we prove in Lemma 8 that the implementation of the ABC Graph given in Algorithm 1 is both linearizable and wait-free, assuming that the underlying snapshot object S (line 6) itself is linearizable and wait-free.

Lemma 8.

Algorithm 1 is a wait-free linearizable implementation of an ABC Graph.

Proof.

Let R be a run of Algorithm 1, and let H be the history induced by R with respect to the ABC Graph. Furthermore, let HS denote the history induced by R with respect to the shared snapshot object S, i.e., H contains the invocations and responses of ABC Graph operations, while HS contains the invocations and responses of snapshot operations. Each ABC Graph operation accesses S exactly once (lines 9, 12, 15, 17). Consequently, there is a unique mapping from operations of S to the corresponding operations of the ABC Graph (lines 7, 10, 13, 16).

Let LS be a linearization of HS, and let L be the sequence of ABC Graph operations obtained by mapping each operation in LS to its originating ABC Graph operation. We show that L is a linearization of H.

For each op.resH there is a corresponding operation of S in LS and therefore opL. Similarly, for each operation on S in LS there is a corresponding op with op.invH. Assume that there are op,opH with op<Hop. It follows, that the corresponding operation on S of op precedes the one of op in HS and therefore in LS and consequently op<Lop. We conclude that L is a sequential history that is equivalent to a completion of H and preserves the precedence relation of H.

It remains to show that L is legal. Among the ABC Graph operations, only read() (line 16) produces a response value. This value is determined by the response value of S.snapshot() in line 17. Since S is linearizable, its response value contains exactly those write operations that precede it in LS. Consequently, the response value of read() contains precisely the addX operations (X{A,B,C}) that precede it in L. The aggregation of A, B, and C coincides with the specification given in Section 5.1. Therefore, Algorithm 1 is a linearizable implementation of an ABC Graph.

Algorithm 1 uses a single shared object: the snapshot object S (line 6). Each ABC Graph operation performs exactly one operation on S along with some local computation. As a result, the number of steps required by the ABC Graph implementations matches that of the underlying snapshot object. Therefore, assuming S is wait-free, the ABC Graph implementation is wait-free as well.

Appendix B Universal Construction – Proof of Linearizability

In the proof of correctness for Algorithm 2, we assume discrete global time, inaccessible to any individual process. Since Algorithm 1 is a linearizable implementation of the ABC Graph (Lemma 8), the execution of operations on this object admits a well-defined total order. We start from an initial time t=0. Each invocation of addA, addB or addC produces an atomic extension, which transforms the graph at time t into the graph at time t+1. We denote such an atomic extension by tt+1. The causing add operation is assigned the timestamp t+1. An extension is then defined as a sequence of one or more consecutive atomic extensions, representing the evolution of the graph across time. Since the read method is state-preserving, we assign it the same timestamp as its most recent predecessor (or t=0 if none exists). Consequently, if the same timestamp t is assigned to a read and an add operation, the read observes the new state, i.e., is scheduled after the add operation. We denote At, Bt and Ct as the values of A, B and C at time t.

As mentioned at the beginning of Section 5, linearizability of our construction is implied by the properties introduced there, which are consolidated and slightly extended in their formal statement in Lemma 9. The remainder of this section is dedicated to proving these properties and concluding in Lemma 26 that the universal construction in Algorithm 2 is linearizable.

Lemma 9.

Each run of Algorithm 2 fulfills the following properties. Let op and op be any two operations in that run.

  • For all times t0, Ct is acyclic.

  • Upon termination of publish(op), the operation op is committed.

  • If publish(op) precedes publish(op), then op precedes op in every topological ordering of Ct for every t after op has been committed.

  • Let t1,t20, and let s1 and s2 be topological orderings of Ct1 and Ct2, respectively. For every operation op such that ops1 and ops2, the sequential specification yields the same response for op in both s1 and s2.

  • For every time t20 and every operation opCt2, if op has a response, then there exists a time t1 with 0t1t2 and a topological ordering T of Ct1 that contains op, such that the response of op agrees with the sequential specification applied to T.

B.1 Fundamental Properties and Acyclicity

After expanding the notion of the inclusion relation to graphs in Definition 10, we establish some basic properties of Algorithm 2 through a series of Lemmata. Important insights are for example that the ABC Graph is only growing (Lemma 11), only growing by at most one node at a time (Lemma 13) and that the graph C is transitive (Lemma 14) and acyclic (Lemma 15) and consequently suitable for topological ordering. Lemma 17 shows that it is always possible to select a topological ordering of a version of C in which a prefix forms a topological ordering of an earlier version of C.

Definition 10.

Let G and H be graphs. We write GH if vertices(G)vertices(H)edges(G)edges(H).

Lemma 11.

The sets At and Bt, and graph Ct satisfy the following properties for every time t0:

  1. 1.

    Atvertices(Bt)vertices(Ct).

  2. 2.

    Let op be an operation. No other process than the one executing publish(op) announces or books op. It is announced at most once and booked at most once.

  3. 3.

    If tt, then AtAt, BtBt, and CtCt.

  4. 4.

    For any finite set of times t1tm, we have Ctm=jCtj.

Proof.

Items 1-3 follow trivially from the implementation. Item 4 follows directly from item 3.

Lemma 12.

The graph Ct is irreflexive for every time t0.

Proof.

The initial graph C0 is empty and trivially irreflexive. Let tt+1 be an atomic extension of the ABC Graph. If this is the result of an invocation of addA or addB, we have Ct+1=Ct and irreflexivity follows by induction. Assume that tt+1 is the result of an invocation of addC(op,M) and therefore adds incoming edges to at most one node. To prove irreflexivity, we have to show that opM. The method addC(op,M) is called in lines 15 and 30. In both cases, the condition opM has been explicitly checked previously.

Lemma 13.

Let Et:=tt+1 be an atomic extension of the ABC Graph and let this extension be the result of an invocation of addC(op,M). We have

  1. 1.

    M=vertices(Ct) for some time tt and

  2. 2.

    vertices(Ct+1)vertices(Ct){op}.

We define C(Et):=Ct for the Ct that was used to calculate M.

Proof.

The method addC(op,M) is called in lines 15 and 30. In both cases it is called with M=vertices(C) for some previously read graph C. With Lemma 11 (3) we have Mvertices(Ct) and therefore vertices(Ct+1)vertices(Ct){op}. As multiple graphs Ct might fulfill M=vertices(Ct), we define C(Et) as the Ct that was used in the actual execution of the algorithm to calculate M.

Lemma 13 states that, in any atomic extension, caused by an invocation of addC(op,M), in which incoming edges are added from the nodes in M to the node op, the set M must correspond to the nodes of a previous version of graph C. Consequently, at most one operation (namely, op) is added to graph C in such an extension, since every operation in M is already present in the graph. Furthermore, we can infer that for every time t0 and operation opCt, the exist times t1tk such that the set of nodes that have incoming edges to op in Ct is the union of all vertices(Cti) with 1ik, which equals vertices(Ctk) by Lemma 11 (4).

Lemma 14.

The graph Ct is transitive for every time t0.

Proof.

Proof by contradiction. Assume Ct is not transitive. There exists a,b,cvertices(Ct) with (a,b),(b,c)edges(Ct) but (a,c)edges(Ct). Let us denote the first time a was added to C as ta, and the first time b was added to C as tb (i.e., we have bvertices(Ct) for all t<tb and bvertices(Ct) for all ttb). As we have (b,c)edges(Ct), there must exist a time treadtb such that Ctread was used to add c (or incoming edges to c) to C. With (a,c)edges(Ct), we conclude tbtread<ta. As we have (a,b)edges(Ct) there must have been a read of C after (or at the same time as) ta that was used to add incoming edges to b. Therefore, this read is after tb and must have read b. As Ct is irreflexive by Lemma 12, this is a contradiction.

Lemma 15.

The graph Ct is acyclic for every time t0.

Proof.

The graph Ct is irreflexive by Lemma 12 and transitive by Lemma 14. Acyclicity follows directly.

Lemma 16.

Let 0t1t2, avertices(Ct1) and bvertices(Ct2)vertices(Ct1). Then for all t0, (b,a)Ct.

Proof.

The claim is trivial for t1=t2. Assume t1<t2. We proceed by contradiction. Suppose that there exists avertices(Ct1) and bvertices(Ct2)vertices(Ct1), such that, for some time t0, (b,a)Ct. Let us call ta the first time when a was committed and tb the first time when b was committed. Since avertices(Ct1), it follows that tat1 and since bvertices(Ct2)vertices(Ct1), it follows that t1<tb and therefore ta<tb. Since (b,a)Ct, it follows that a was committed with Ctread as dependency with tbtread. We conclude ta<tread, which contradicts acyclicity proven in Lemma 15.

Lemma 17.

Let 0t1t2. There exists a topological ordering r of Ct2 such that a prefix of r is a topological ordering of Ct1.

Proof.

By Lemma 15, there exists a topological ordering s of Ct2. By Lemma 16, there are no edges in Ct2 from nodes in vertices(Ct2)vertices(Ct1) to those in vertices(Ct1). Consequently, if we define r:=r1r2, where r1 is the subsequence of s with exactly the nodes in Ct1 and r2 is the subsequence of s with exactly the nodes in vertices(Ct2)vertices(Ct1), then r forms a topological ordering of Ct2. By Lemma 11 (3), r1 is a topological ordering of Ct1.

B.2 Commitment and Real-time Ordering

Having established fundamental properties of the ABC Graph when used by the Algorithm 2, most notably acyclicity and, consequently, the existence of topological orderings, in this section we proceed to prove in Lemma 18 that an operation is committed before its corresponding invocation of the publish method terminates, and in Lemma 19 that the algorithm satisfies real-time ordering.

Lemma 18.

Upon termination of publish(op), the operation op is committed.

Proof.

The method publish can return at lines 12, 16 and 24. If publish(op) returned at line 12, it had verified that op is committed in line 11. If publish(op) returned at line 16, it committed op in line 15. If publish(op) returned at line 24, it had verified that op is committed in line 23. Consequently, op is committed at return.

Lemma 19.

If publish(op) precedes publish(op), then op precedes op in every topological ordering of Ct for every t after op has been committed.

Proof.

Suppose publish(op) precedes publish(op). By Lemma 18, op is committed upon the termination of publish(op). By Lemma 13, there exists a graph Ct whose nodes have been used as incoming edges when committing op. Suppose that this graph Ct has been read after the termination of publish(op). We therefore have opCt, implying that the edge (op,op) is added to C by the time publish(op) terminates. The claim then follows directly.

It remains to show that Ct was read after the termination of publish(op). If op was committed by publish(op), Ct was read during the execution of publish(op) and therefore after the termination of publish(op). Otherwise, suppose that op was committed by another invocation p of publish and thus in conflict resolution. The graph Ct was read by p in line 28 after some invocation of publish had proposed op to some consensus object. This occurred after op had been booked by publish(op) (Lemma 11 (2)) and therefore after the termination of publish(op).

B.3 Equivalence of Topological Orderings

After proving the first three claims of Lemma 9, it remains to establish the final two. Before returning to these claims in Section B.4, we first prove Lemma 22 as an intermediate result.

Recall the definition of the graph C(Et) from Lemma 13 for an atomic extension Et that is the result of an invocation of addC(op,M). We have M=vertices(C(Et)). If op was added to C for the first time at Et, C(Et) consists precisely of those operations of Ct (equivalently, of Ct+1) that have an outgoing edge to op in Ct+1.

Definition 20.

Let Et:=tt+1 be an atomic extension of the ABC Graph and let this extension be the result of an invocation of addC(op,M). Let C(Et) be defined as in Lemma 13, such that M=vertices(C(Et)). Furthermore, let opCt.

We define P(Et)Ct as the subgraph that is disjoint from C(Et).

Given P(Et) as defined in Definition 20, for all opCt+1, exactly one of the following holds: opC(Et), opP(Et), or op=op. Note that we only define P(Et) in the case of opCt, i.e., op is committed for the first time. Intuitively, P(Et) captures the portion of the graph that was added after C(Et) but before op was added to Ct.555The extension(s) from C(Et) to Ct may include additional edges within C(Et), as well as edges from nodes in C(Et) to nodes in P(Et). This information is not represented when partitioning Ct in C(Et) and P(Et). Although edges from nodes in C(Et) to nodes in P(Et) may exist, the reverse is ruled out by transitivity (Lemma 14) and the definition of P(Et): if a node in P(Et) had an edge to a node in C(Et), it would (by transitivity) also have an edge to op and thus would belong to C(Et), contradicting disjointness.666Note that opCt by assumption and therefore (op,op)Ct+1 implies opC(Et) for every opCt+1.

Definition 21.

Let G be a graph whose nodes are operations, and let s be a sequence of operations. We write Gs for the subgraph of G induced by the set of nodes appearing in s.

Lemma 22.

For each fixed t0, all topological orderings of Ct are equivalent. Moreover, this equivalence holds not only for the complete sequences but also for any pair of prefixes consisting of the same set of operations.

Proof.

Let C0, , Ct, Ct+1, be the states of graph C as it evolves over time. As the induction hypothesis, assume that for all tt, any two topological orderings of Ct are equivalent – not only as full sequences, but also in the sense that any pair of prefixes consisting of the same set of operations are themselves equivalent.

As the hypothesis holds trivially for the empty graph C0, it remains to show that this property also holds for Ct+1. If the atomic extension Et:=tt+1 is the result of an invocation of addA or addB, we have Ct+1=Ct and the statement follows directly from the induction hypothesis. If only edges are added during Et, the statement also follows directly from the induction hypothesis, as every topological ordering of Ct+1 is also a valid topological ordering of Ct.

Now suppose that a new operation op is committed (added to C) for the first time in the atomic extension Et. Let us consider two prefixes of topological orderings of Ct+1 that consist of the same set of operations. If op is not among these operations, the equivalence follows from the induction hypothesis. In the following, we assume that op is included in both prefixes.

We define two subgraphs to analyze the structural implications of adding op to Ct. First, let C(Et), as defined in Lemma 13, denote the subgraph that has been read when op was added – that is, every node in C(Et) has an outgoing edge to op. Note that, since op was newly added, only nodes in C(Et) have edges to it in Ct+1. As every node in C(Et) has an outgoing edge to op, the nodes in C(Et) must appear before op in any topological ordering of Ct+1. Therefore, if a prefix of a topological ordering includes op, it must also include all nodes of C(Et). Hence, the presence of op in both prefixes implies that the nodes of C(Et) are also included in both prefixes. Second, let P(Et)Ct, as defined in Definition 20, denote the subgraph that is disjoint from C(Et). As opCt, P(Et) does not contain op.

Let o=o0opo1 be a prefix of a topological ordering of Ct+1. Since every node in C(Et) has an edge to op, it follows that vertices(C(Et))o0. By the induction hypothesis, we have o0co0~, where c is any topological ordering of C(Et), and o0~ is the subsequence of o0 containing only those operations not in C(Et).777A subsequence is obtained from a sequence by removing any number of elements from it without changing the order of the remaining elements. The subsequence does not have to appear continuously in the original sequence. Trivially, o0o1 is a topological ordering of Ct. Moreover, since there are no edges from nodes in P(Et) to nodes in C(Et), we can choose c, the topological ordering of C(Et), such that co0~o1 is also a topological ordering of Ct. This allows us to decompose the topological ordering o0o1 into two parts: c, a topological ordering of C(Et), and o0~o1, a topological ordering of P(Et)o. Using o0co0~, we conclude o=o0opo1co0~opo1. It remains to show that

co0~opo1co0~o1op,

which implies oo0o1op, i.e., that op can be delayed to the end of the sequence without affecting the resulting state or any response value. Once this is established, the induction hypothesis for Ct+1 follows from the induction hypothesis for Ct and the transitivity of the equivalence relation in the following way. Consider two prefixes o=o0opo1 and o=o0opo1 of topological orderings of Ct+1, and assume that both prefixes consist of the same set of operations. We conclude

o=o0opo1o0o1opo0o1opo0opo1=o.

The equivalence co0~opo1co0~o1op is shown by Lemma 23 below.

Lemma 23.

Suppose that an operation op is committed for the first time in the atomic extension Et:=tt+1, and the statement of Lemma 22 holds for every t with 0tt.888This is the inductive hypothesis on Ct in the proof of the lemma above.

Let o be a prefix of a topological ordering of Ct+1 that contains op and let c be a topological ordering of C(Et) that is also a prefix of some topological ordering of Ct.

For any fixed topological ordering w of P:=P(Et)o, all placements of op in w after c are equivalent, i.e., cw1opw2cw1opw2 for all w1w2=w1w2=w.

Proof.

We analyze the two cases, depending on whether op has been committed via the conflict-free or the conflict resolution path, separately. We refer to the statement of Lemma 22 as the Outer Induction Hypothesis.

Case 1: Conflict resolution path.

Let op be committed via the conflict resolution path. There exists an iteration k^ such that CONSk^ stores op. Let p be the invocation of publish that committed op. As p can reach iteration k^ only if all iterations k<k^ are completed and the outcomes of CONSk.propose() are added to C (lines 18 and 32), C(Et) read by p in line 28 includes all operations stored in CONSk with k<k^. Analogously, an operation committed in a subsequent iteration k>k^ will include op in its dependencies. Thus, as P(Et) is defined as the subgraph of Ct induced by vertices(Ct)vertices(C(Et)) and opCt, every opP(Et) has been committed via the conflict-free path.

Let p^ be an invocation of publish that proposed op to CONSk^ such that no proposal on that consensus object precedes the proposal of p^. Note that p^ could be different from p. In line 26, p^ selected an operation, i.e., op, from Bt^vertices(Ct^) with minimal B-value according to the ABC Graph at time t^, the time of p^’s read in line 22.

Consider an arbitrary invocation p=publish(op) such that opvertices(Ct^). If opBt^, then p will encounter opA in line 9, because opvertices(Bt^)At^. If opBt^, then B[op]B[op], since op has minimal B-value for time t^. Hence, p had or will encounter opA in line 9. We conclude, that every invocation p=publish(op), such that opvertices(Ct^), encounters opA in line 9.

Since t^, the time when p^ read in line 22, precedes p^’s proposal in line 27 and no proposal precedes p^’s proposal, the read of C(Et) in line 28 succeeds t^. By Lemma 11 (3), Ct^C(Et) follows. Let opP(Et) be an arbitrary operation. As established above, op was committed via the conflict-free path before op was committed. Since opCt^ implies opC(Et), which is a contradiction, we have opCt^. As we have shown above, op has found opA in line 9. Since op could not have found opC, we conclude that op has found opAvertices(C) in line 9 and verified commutativity in line 14.

Recall that o is defined as a prefix of a topological ordering of Ct+1 that contains op, and that c is defined as a topological ordering of C(Et) that is also a prefix of some topological ordering of Ct. Let w be a topological ordering of P:=P(Et)o. In the following we are showing that all placements of op in w after c are equivalent. We prove this by induction over P, starting with an empty set and inductively adding operations to P one by one. To define the induction order, we sort the operations according to the recency of the state that the corresponding invocation of publish read in line 9, from the oldest to the most recent. Note that we insert operations in their original place in w such that the relative ordering of w remains the same. We introduce the following notation. Let wj denote the sequences of operations that consists of the first j operations according to the sorting of P that we just defined. Our induction hypothesis is the following:

Inner Induction Hypothesis:

For every fixed prefix w^j of wj, all placements of op in w^j after c are equivalent.

Inner Induction Base Case:

The base case is trivial as w0, and therefore every prefix w^0, is empty.

Inner Induction Step:

Assume that the induction hypothesis holds true for 0j<|P|. Let w^j+1 be a prefix of wj+1. If w^j+1 is also a prefix of wj, the inner induction hypothesis for j applies and we are done. Let w^j+1=ujopvj such that ujvj is a prefix of wj and opP is the next operation, added in this inner induction step. We have to show, that all placements of op in w^j+1 after c are equivalent. This is done by showing that every placement of op is equivalent to placing it after w^j+1. The induction hypothesis for j+1 follows by transitivity.

By definition of w^j+1, we have cw^j+1op=cujopvjop. As cuj is a prefix of some topological ordering of the graph Ct we can use the Outer Induction Hypothesis.999By definition, c is a topological ordering of C(Et) and a prefix of some topological ordering of Ct. It is easy to see that cw is also a prefix of a topological ordering of Ct. When removing operations from w in the reverse order in which they were introduced during the induction, we always remove the operation that read the most recent state in line 9, which therefore cannot have an outgoing edge to any of the remaining operations. By induction, cwj is a prefix of a topological ordering of Ct; since uj is a prefix of w^j, which in turn is a prefix of wj, the same holds for cw^j and cuj. Let copuopj be a topological ordering of the same prefix of Ct, where cop are the operations that publish(op) read as C in line 9. The Outer Induction Hypothesis gives us cujcopuopj. Note that uopjopvjop is a subset of CUR (the operations in Avertices(C)) of publish(op) in line 9. Lemma 5 implies that all placements of op in this set are equivalent. It follows that

cw^j+1op =cujopvjop
copuopjopvjop
copuopjvjopop
cujvjopop.

We distinguish two cases, according to whether op should be placed in uj or vj.

Case 1.1.

To place op in uj=(uj)0(uj)1, where (uj)0(uj)1 is an arbitrary split of uj, we use the Inner Induction Hypothesis for j in the following way. Note that ujvj and therefore also uj are prefixes of wj.

cujvjopop cujopvjop
copuopjopvjop
copuopjopopvj
cujopopvj
c(uj)0op(uj)1opvj.

Case 1.2.

Suppose that op has to be placed in vj=(vj)0(vj)1, where (vj)0(vj)1 is an arbitrary split of vj.

cujvjopop cuj(vj)0op(vj)1op
copuopj(vj)0op(vj)1op
copuopjop(vj)0op(vj)1
cujop(vj)0op(vj)1.

We conclude that every placement of op in wj+1 after c is equivalent to placing op after cwj+1. Transitivity of the equivalence relation gives us the Inner Induction Hypothesis for j+1. The claim follows from the Inner Induction Hypothesis for j=|P|.

Case 2: Conflict-free path.

Assume now that op has been committed via the conflict-free path. Let w be a topological ordering of P. Recall that we have to show that every placement of op in w after c is equivalent. As op has been committed via the conflict-free path, commutativity with every subset of CURP has been checked. By Lemma 5, for any fixed ordering p of CURP, all placements of op in p after c are equivalent, i.e., cp0opp1cp0opp1 for p=p0p1=p0p1. We are therefore concerned about the operations that have been concurrently added and have not been encountered by op, i.e., PCUR. Consider opPCUR. By the definition of CUR, op has not been read by op in A in line 9. Therefore, op could not have been proposed to any consensus object in conflict resolution, as op has a higher B-value than op, and op has been booked prior to op. From this we infer that every opPCUR has been added via the conflict-free path. As op has not been found by op in A in line 9, op has read in line 7 a (proper) superset of what op has read in line 9 (Lemma 11 (3)). In particular, op has found opAvertices(C) in line 9 and verified commutativity in line 14.

To prove that all placements of op in w after c are equivalent, we follow an argument analogous to that employed in the conflict resolution path case above. In contrast to the conflict resolution path case, we do not need to begin with an empty set before incrementally adding operations. As shown above, we already got the equivalence of all placements of op in the orderings of subsets of CURP. The operations in PCUR are handled as in the conflict resolution path case. We sort them according to the recency of the state they read in line 9, from the oldest to the most recent. These operations are then inserted at their original positions in w, thereby preserving the relative ordering within w. We introduce the following notation. Let wj denote the sequences of operations that consists of the operations in CURP as well as the first j operations according to the sorting of PCUR that we just defined. The Inner Induction Hypothesis and step is identical to the conflict resolution path case. The base case is directly given by Lemma 5 as op commutes with every subset of CURP. The claim follows directly from the Inner Induction Hypothesis for j=|PCUR|.

B.4 Proof Completion

Lemmata 15, 18 and 19 correspond exactly to the first three items of Lemma 9. Having established Lemma 22 in the last section, we proceed to show the remaining two items of Lemma 9 in Lemmata 24 and 25, thereby completing the proof of Lemma 9. Finally, we show in Lemma 26 that Algorithm 2 is linearizable.

Lemma 24.

Let t1,t20, and let s1 and s2 be topological orderings of Ct1 and Ct2, respectively. For every operation op such that ops1 and ops2, the sequential specification yields the same return value for op in both s1 and s2.

Proof.

Assume, without loss of generality, 0t1t2. Let s1 and s2 be topological orderings of Ct1 and Ct2, respectively. By Lemma 17, there exists some topological ordering r=r1r2 of Ct2 such that r1 is a topological ordering of Ct1. Lemma 22 then implies rs2 and r1s1. Since ops1, we also have opr1. With r1s1, the response value for op is identical in both s1 and r1, and therefore also in r. The claim follows because rs2.

Lemma 25.

For every time t20 and every operation opCt2, if op has a response, then there exists a time t1 with 0t1t2 and a topological ordering T of Ct1 that contains op, such that the response of op agrees with the sequential specification applied to T.

Proof.

Since op has a response, the corresponding publish(op) terminated. If publish(op) returned at lines 12 or 24, the claim is immediate. Suppose that publish(op) returned at line 16. Let tcomtcom+1 be the atomic extension produced by add(op,M) in line 15 with M=vertices(Ctread) for some treadtcom (Lemma 13). Let l be the topological ordering of Ctread that publish(op) calculated in line 10.

Case 1: Suppose 𝒐𝒑𝑪𝒕𝒄𝒐𝒎.

By Lemma 17, there exists a topological ordering r=r1r2 of Ctcom such that r1 is a topological ordering of Ctread, and r2 consists of nodes from vertices(Ctcom)vertices(Ctread). Since opCtcom, there is no edge in Ctcom+1 from a node in vertices(Ctcom+1)vertices(Ctread) to op. Hence, T:=r1opr2 is a topological ordering of Ctcom+1. By Lemma 22, r1 is equivalent to l, and thus T is equivalent to lopr2. Consequently, the response of op (line 16) agrees with the sequential specification applied to T.

Case 2: Suppose that 𝒐𝒑𝑪𝒕𝒄𝒐𝒎.

Therefore, there was a preceding commitment of op. Let p be the process publish(op).p and p the process that committed op for the first time. For the sake of the argument, in the following, we will introduce alternatives to the original run, that we denote by ORUN. For a run R and a process q that can do a step, R.q denotes the run where q makes a step after R, and no other step is made. Since the ABC Graph is linearizable, we view each operation on it as one single step (together with local computations). Furthermore, let CR denote the graph C after the run R.

Let R be the prefix of ORUN right before p commits op. Since p has not found op after executing line 9, it is also about to commit op in the next step and return in line 16. Consequently, R.p, R.p.p and ORUN are indistinguishable for p at the time when p terminates publish(op). In particular, the response value of op is the same in all runs. An argument analogous to the one used in Case 1 shows that there is a topological ordering T:=r1opr2 of CR.p with r1l. Therefore, the response of op (line 16) agrees with the sequential specification applied to T.

Since R.p is a prefix of R.p.p, the graphs CR.p and CR.p.p belong to the same run R.p.p101010Let t^2 be the time at the end of run R.p.p. The graph CR.p appears in run R.p.p at time t^1. and therefore Lemma 24 is applicable: The sequential specification yields the same response value for op in every topological ordering of CR.p and CR.p.p. The same holds for CR.p and CR.p.p analogously. Furthermore, we observe that, as every two add operations on the ABC Graph commute, the graphs CR.p.p and CR.p.p are identical. We conclude that the sequential specification yields the same response for op in every topological ordering of CR.p and CR.p.

Since the response of op agrees with the sequential specification applied to a topological ordering of CR.p, namely T, it also agrees with every topological ordering of CR.p. Since R.p is effectively a prefix of the original run ORUN, CR.p is a version of graph C in ORUN. The claim follows directly.

Lemma 26.

Algorithm 2 is a linearizable universal construction. Moreover, for any run of the algorithm, every topological ordering of the final graph C is a linearization of the induced history.

Proof.

To prove the claim, we have to show that, for any given sequential specification and any finite run of Algorithm 2, a topological ordering of the final graph C is a linearization of the induced history.111111Note that the implementation of universal construction presented in Algorithm 2 exports the method publish(op), whereas the object specified by the sequential specification exports the method op directly. This difference does not affect correctness and can be disregarded when reasoning about equivalences of histories. Let H be the history of a run R of Algorithm 2. Let CR denote the graph C at the end of run R. Let S be a topological ordering of CR. Lemma 15 guarantees the existence of such a topological ordering. As a sequence of operations, S forms a sequential history. It remains to show that S serves as a linearization of H. By Definition 2, S must be equivalent121212Note the distinction between equivalence of histories (Definition 1) and equivalence of sequences of operations (Definition 3). to a completion of H, preserve the precedence relation of H and be legal.

By Lemmata 18 and 19, S is equivalent to a completion of H and preserves the precedence relation of H. It remains to show legality, i.e., that S satisfies the sequential specification. For each operation op in S, the response must agree with the sequential specification applied to S. For operations in S that do not have a response, we can assign one according to the sequential specification applied to S, which trivially satisfies legality.

Let op be an operation in S that has a response. By Lemma 25, there exists a topological ordering T of Ct for some time t0 that contains op, such that the response of op agrees with the sequential specification applied to T. By Lemma 24, the sequential specification yields the same response for op in both T and S, i.e., the response of op agrees with the sequential specification applied to S.

Since this holds for every operation in S, each operation returned the value prescribed by the sequential specification in S. Hence, S is legal. We conclude that S is a linearization of H, and therefore that Algorithm 2 is linearizable.

Appendix C Universal Construction – Proof of Wait-Freedom

Having established the linearizability of Algorithm 2 in the previous section, we now address its liveness guarantees, specifically proving in Lemma 27 that it satisfies wait-freedom, assuming that the underlying shared objects itself are wait-free. The underlying objects are a snapshot object K (line 2), an ABC Graph G (line 3), and a sequence of consensus objects CONSj for j1 (line 4). We showed in Section A, that Algorithm 1 is a wait-free linearizable implementation of an ABC Graph.

Lemma 27.

The universal construction given in Algorithm 2 is wait-free. Each invocation of publish terminates at most after c+2 iterations of conflict resolution, for the maximum level of concurrency cn.

Proof.

Let p be a process that executes publish(op).

Case 1.

Suppose p verifies commitment of op in line 11 or the commutativity in line 14. It is immediate that the number of steps it can take before terminating publish(op) is finite.

Case 2.

Suppose p fails to verify the commitment of op in line 11 and fails to verify the commutativity property in line 14. Let k0 denote the value of k0 read by p in line 18 and let cn be the maximum level of concurrency, i.e., the maximum number of processes that can propose operations concurrently. We prove by contradiction that publish(op) terminates at latest in iteration k0+c+2.

Assume, for the sake of contradiction, that publish(op) does not terminate by iteration k0+c+2. Since it did not return in iteration k0+1, op was not decided by any CONSk with kk0; otherwise, op would have been committed and publish(op) would have returned at line 24 in iteration k0+1. From iteration k0+1 onward, publish(op) executes the conflict resolution procedure; hence, each k with k0+1kk0+c+1, CONSk decides on an operation different from op. Otherwise, publish(op) would terminate in iteration k+1.

Let us consider the c different iterations with k0+2kk0+c+1 and the operations stored in CONSk. Since c is the concurrency bound, among those c operations there exists an operation op such that publish(op) was not active at the moment when p read k0. Either publish(op) terminated before p read k0 or publish(op) was invoked afterwards.

Case 2.1.

Suppose publish(op) was invoked after p read k0. The B-value of op will be higher than B[op]. Furthermore, every process that encounters op in line 22 will also encounter op with a lower B-value. Consequently, no process would propose op to a consensus object CONSk with k0+2kk0+c+1. This contradicts the validity of CONSk.

Case 2.2.

Suppose publish(op) terminated before p read k0. By Lemma 18, op is committed before p read k0 and consequently before any process executed line 22 in iteration k0+2; otherwise, p would have read at least k0+1 in line 18. Consequently, no process would propose opC to a subsequent consensus object, contradicting validity of consensus.

We conclude that the assumption that publish(op) does not terminate by iteration k0+c+2 leads to a contradiction. Therefore, Algorithm 2 is wait-free.

Appendix D Universal Construction – Proof of Dynamic Concurrency

After proving linearizability and wait-freedom of Algorithm 2, we show in Lemma 28 that the algorithm satisfies dynamic concurrency according to Definition 7.

Lemma 28.

The universal construction given in Algorithm 2 is dynamically concurrent.

Proof.

Let H be an arbitrary history induced by a run of Algorithm 2 and publish(op) an operation in H. Assume that process publish(op).p used a strong synchronization primitive while executing publish(op) and, thus, executed conflict resolution for op. Therefore, the conditions in lines 11 and 14 failed for publish(op).

Let t be the time when publish(op).p read the ABC Graph in line 9. It remains to show that there exists a set of operations O with O′′:=Atvertices(Ct){op}O such that (l,O) is a configuration during op in H for l:=linearize(Ct).

Let CH be the graph C at the end of the run that induced H. By Lemma 26, every topological ordering of CH is a linearization of H. By Lemma 16, no operation in vertices(CH)vertices(Ct) has an edge to an operation in vertices(Ct). Therefore, there exists a topological ordering l^=l^0l^1 of CH and thus a linearization of H such that opl^0 if and only if opCt for all opCH. Since l^0 is also a topological ordering of Ct, we have ll^0 by Lemma 22. Thus, L:=ll^1 is a linearization of H and l a prefix of L. Furthermore, since publish(op).p executed conflict resolution, it failed the condition opCt in line 11. We therefore have opl.

Let H be the maximal prefix of H such that op.resH implies opl for every operation op. We have to show op.invH and op.resH. Since H is maximal, it excludes op.inv only if there exists an operation opl with op.res<Hop.inv. Let op be an operation such that op.res precedes op.inv in H. By Lemma 18, op is committed upon termination of publish(op). Therefore, we have opCt and consequently opl. Thus, op.invH holds. Finally, op.resH is implied by opl.

Furthermore, we have to show that for all opl, it holds that op.invH. Suppose, for the sake of contradiction, that there exists opl with op.invH. Since H is maximal, op.invH implies that there exists op′′l with op′′.res<Hop.inv. By Lemma 19, op′′ precedes op in every topological ordering of Ct. This contradicts op′′l. We conclude that op.invH for all opl.

We now have to show that l is a linearization of H. By construction, l is a prefix of L, where L is a linearization of H. Consequently, l is a legal sequential history. Since H is a prefix of H, l preserves the precedence relation of H, as the precedence relation of H is a subset of that of H. It remains to show that l is equivalent to a completion of H. As established above, we have op.invH for all opl and, by construction, opl for all op.resH. Hence, we can construct a completion H^ of H such that opH^ if and only if opl. It remains to show that l is equivalent to H^ according to Definition 1. Suppose, for the sake of contradiction, that l and H^ are not equivalent. Hence, there exist some process p and operations op,op′′H^p such that op<H^op′′ but op′′<lop. This leads to a contradiction, since op<H^op′′ implies op<Hop′′, which in turn implies op<Hop′′, then op<Lop′′, and finally op<lop′′. Therefore, l is equivalent to H^, a completion of H, and consequently, l is a linearization of H.

It remains to show that O′′=Atvertices(Ct){op} is a subset of the set O of all operations op for which op.invH, opl, and opop. Let opO′′. By definition of O′′, we have opop and opvertices(Ct) and therefore opl. Suppose, for the sake of contradiction, that op.invH. By definition of H, there must exist an operation op′′l such that op′′.res precedes op.inv; otherwise, H would not be maximal. It follows from opO′′At that time t, when publish(op).p read the ABC Graph in line 9, succeeds op.inv and therefore op′′.res. Lemma 18 contradicts op′′Ct, i.e., op′′l. This contradiction proves O′′O. The claim follows from Definition 7.