Lectures onType Theory
Chapter 87
Chapter 87Optional

Compositional Linearizability and Modular Concurrent Objects

Prerequisites. Direct starred prerequisites: Chapter 86. No later core chapter depends on this route.

Two exchanger calls overlap. Thread 0 offers 2 and returns 4; thread 1 offers 4 and returns 2. No ordering of two independent deterministic atomic exchanges explains both results: the first ordered operation would have to return before the second offer was available. A paired specification can perform the two calls and the two returns as one concurrency class. The problem is to expose that behavior as a black box without losing composition.

Finite traces make the obstruction calculable

Fix a finite number T:N of threads and put Name(T):={i:Ni<T}. An effect signature E assigns to each result type A a type E(A) of operations returning values of type A. A thread event is i:call(m)ori:ret(m,v),i:Name(T), m:E(A), v:A.

Definition 87.1 — Sequentially consistent traces

An active map a assigns to each thread either no operation or one package A,m:E(A). The judgment SeqCons(a,p) is generated from the end of a finite trace by

SeqCons(a,[])
CL-Seq-nil
a(i)=nonea(i)=someA,mji. a(j)=a(j)SeqCons(a,p)
SeqCons(a,i:call(m)::p)
CL-Seq-call
a(i)=someA,ma(i)=noneji. a(j)=a(j)SeqCons(a,p)
SeqCons(a,i:ret(m,v)::p)
CL-Seq-ret

A complete history begins under the everywhere-empty active map and ends under the everywhere-empty active map. A pending history may instead be completed by appending matching returns or by deleting pending calls. Every comparison in this chapter applies the same completion choice to its two sides.

The trace 0:call(inc);0:ret(inc,1);1:call(get);1:ret(get,1) satisfies CL-Seq-call, CL-Seq-ret, CL-Seq-call, and CL-Seq-ret in that order. A return by thread 1 before its call fails the first premise of CL-Seq-ret.

Definition 87.2 — The frozen LTS specification

A specification is an element V:SpecT(E). Its data are a state type SV, an initial state sV, and a labelled relation sStepV(e)s(e:Name(T)×Event(E)). It also contains a proof that every trace from sV satisfies SeqCons under the empty active map. Write Tr(V) for its finite traces from sV.

The sequential-consistency proof is data in the card because later linking must not manufacture a return for a call that never occurred. An arbitrary labelled graph is therefore not a SpecT(E).

Exercise 87.1

★☆☆ Let the one-thread counter have states n:N: an inc call keeps n fixed, and its matching return emits n+1 and moves to n+1. Starting at zero, list every visible length-two prefix whose first event is 0:call(inc). Reject the prefix that returns 2, and name the state-transition premise that rejects it.

Programs, modules, and linking

The LHL artifact defines its own program tree. It does not import the interaction-tree library of chapter 86.

Definition 87.3 — The artifact-local program and module interface

For an underlay signature E, programs are given by ProgE(A)::=Return(a)Tau(p)Vis(m,k), where a:A, m:E(B), and k:BProgE(A). An implementation M:Impl(E,F) maps each m:F(A) to a program M(m):ProgE(A). The displayed grammar denotes the greatest fixed point of the operator F(X):=A+X+B:TypeE(B)×(BX). Consequently its constructors are observations, not a finite syntax induction principle. For example, guarded corecursion defines spin:ProgE(A) by spin=Tau(spin).

The bridge from definition 86.1 is the constructor-preserving map ReturnRet,TauTau,VisVis. It preserves finite traces by induction on the observer budget. It does not identify the two libraries or transfer a Rocq theorem between them.

For a relation S on programs, the artifact-local one-layer relation EuttF(S) matches equal returns, matches equal visible operations with S-related continuations, and has the three silent clauses

S(p,q)
EuttF(S,Tau(p),Tau(q))
EuttF(S,p,q)
EuttF(S,Tau(p),q)
EuttF(S,p,q)
EuttF(S,p,Tau(q))

Write pPq for the greatest fixed point of this monotone operator. It is termination sensitive: the two asymmetric clauses remove a finite silent prefix inside one inductive layer, but do not equate spin with a return. For modules M,N:Impl(E,F), the displayed notation MPN below denotes its pointwise lifting: M(m)PN(m) for every result type A and operation m:F(A).

The artifact makes substitution productive by defining it mutually with a continuation operation. For k:BProgF(A) and p:ProgE(B), the complete observation equations are substM(Return(a)):=Return(a),substM(Tau(p)):=Tau(substM(p)),substM(Vis(m,k)):=Tau(bindSubstM(k,M(m))),bindSubstM(k,Return(b)):=Tau(substM(k(b))),bindSubstM(k,Tau(p)):=Tau(bindSubstM(k,p)),bindSubstM(k,Vis(u,h)):=Vis(u,λx.bindSubstM(k,h(x))). Every recursive call is guarded by Tau or lies in a visible continuation. The administrative silent steps are why the module laws below hold at P rather than by constructor equality. These operations are local to the artifact and do not use interp from chapter 86.

Running M:Impl(E,F) above V:SpecT(E) produces the linked specification VM:SpecT(F). An overlay call starts M(m) for the selected thread. A visible operation of M(m) becomes an underlay call and return in V. A program Tau changes only the thread state. The next four rules exhibit the thread-local cases.

q(i)=Idle
qi:call(m)q[iCont(m,M(m))]
CL-Overlay-call
q(i)=Cont(m,Return(v))
qi:ret(m,v)q[iIdle]
CL-Overlay-ret
q(i)=Cont(m,Vis(u,k))sStepV(i:call(u))s
(q,s)CL(q[iUCall(m,u,k)],s)
CL-Underlay-call
q(i)=UCall(m,u,k)sStepV(i:ret(u,v))s
(q,s)CL(q[iCont(m,k(v))],s)
CL-Underlay-ret

The silent case sends Cont(m,Tau(p)) to Cont(m,p) without changing s. Thus no scheduler object is part of the signature: one transition chooses one thread name and applies its matching rule.

Definition 87.4 — Vertical and horizontal composition

Let M:Impl(E,F) and N:Impl(F,G). Vertical module composition substitutes M into the upper program N(g): (M;N)(g):=substM(N(g)),(M;N)(g):ProgE(A)(g:G(A)). The identity module is idM(m):=Vis(m,Return).

For tagged signatures E1+E2 and specifications Vi:SpecT(Ei) over the same finite thread set, the horizontal tensor V1V2:SpecT(E1+E2) has state ActiveT(E1+E2)×SV1×SV2. On a left call by i, the active entry for i must be empty, becomes the left-tagged operation, and the first component specification takes the corresponding call step; the right state is unchanged. A left return requires that same left-tagged active operation and clears the entry. The two right-tagged clauses exchange the component roles. All active entries for ji are preserved. This shared active map rules out a call in one component followed by a return through the other, even though a thread may use either component at different times. The tensor M1M2 dispatches on operation tags and maps every event generated by the chosen module through the same tag.

Lemma 87.5 — Module and linking algebra

Let the data have types V:SpecT(E),M:Impl(E,F),N:Impl(F,G),L:Impl(G,H). The local weak equivalence satisfies idM;MPM,M;idMPM,(M;N);LPM;(N;L),(VM)NV(M;N),(V1V2)(M1M2)=(V1M1)(V2M2) where the tensor equation additionally quantifies over Vi:SpecT(Ei) and Mi:Impl(Ei,Fi); the signature sums are tagged. The relation is linked-LTS isomorphism up to renaming the nested thread and product states. There is deliberately no law VidM=V: the left side is the identity saturation K(V) and exposes linearization brackets absent from V.

Proof of Lemma 87.5 — Module and linking algebra

Proof. For the module unit and associativity laws, coinduct on the local program. Return and silent forms are immediate. At a visible form, substitution expands one local bind; its return, silent, and visible cases establish the two unit equations and reassociate the nested substitutions. This produces P because only a finite administrative silent prefix can differ.

For linking compatibility, relate the nested linked state containing the outer continuation to the single linked state containing its substituted program. The overlay rules match directly. An underlay call/return pair in the nested state corresponds to the visible substitution clause in the single state. This establishes the displayed without erasing an overlay bracket.

For tensor, induct on a finite execution. A left-tagged event changes only the first component on both sides; a right-tagged event changes only the second. The shared active-map premise preserves the selected tagged operation until its matching return, and every unselected entry is unchanged. These facts exhaust the transition relation and preserve global SeqCons. ◻

Identity saturation determines linearizability

Definition 87.6 — Refinement and identity saturation

Write Ref(V,V) when every completed finite trace of V is a completed finite trace of V, under the same pending-call convention. Define K(V):=VidM. K(V) exposes the bracket between an overlay call and its corresponding underlay action. Its overlay trace set can be strictly larger than that of V because different threads may overlap these brackets. We say V is compositionally linearizable with respect to V, written Lin(V,V), if and only if Ref(V,K(V)).

The direction of Ref is fixed by the trace inclusion: the concrete V occurs first. Reversing the arguments would allow a concrete object to add behaviors not admitted by its specification.

Theorem 87.7 — Observational refinement and locality

Let VE,VE:SpecT(E),M:Impl(E,G). If Lin(VE,VE), then Ref(VEM,VEM). For tagged signatures over the same finite thread set, with Vi,Vi:SpecT(Ei), Lin(V1V2,V1V2)Lin(V1,V1)Lin(V2,V2).

Proof of Theorem 87.7 — Observational refinement and locality

Proof. For observational refinement, take a trace of VEM. Projecting away the program states yields a trace of VE. The hypothesis places that trace in K(VE)=VEidM. Reinsert the same M transitions. Linking compatibility gives (VEidM)MVE(idM;M)VEM, where the last step is the module unit law. Thus the reinserted execution is an execution of VEM; no equation K(VE)=VE is used.

For locality from right to left, project a tensor trace to its left and right tags. The two hypotheses linearize the projections independently. Their shuffle preserves per-thread order because the shared active map permits at most one tagged operation of a thread at a time. The tensor clause of lemma 87.5 reconstructs a trace of K(V1V2).

For the reverse implication, take a trace of V1 and tensor it with the empty trace of V2. Tensor linearizability and left projection give Lin(V1,V1). The right result follows by exchanging the two product projections; this exchange preserves the trace, completion, and thread-order hypotheses. ◻

Theorem 87.8 — Horizontal and vertical composition

For i{1,2}, let VEi:SpecTi(Ei), Mi:Impl(Ei,Fi), and VFi:SpecTi(Fi), where T1=T2=T and both signature sums are tagged. Write Vi:=VEi and Vi:=VFi. If Lin(V1M1,V1) and Lin(V2M2,V2), then Lin((V1V2)(M1M2),V1V2). For the vertical statement, let VE:SpecT(E),ME:Impl(E,F),VF:SpecT(F),MF:Impl(F,G),VG:SpecT(G). If Lin(VEME,VF) and Lin(VFMF,VG), then Lin(VE(ME;MF),VG).

Proof of Theorem 87.8 — Horizontal and vertical composition

Proof. The tensor equation in lemma 87.5, followed by the two directions of theorem 87.7, gives the horizontal claim. For the vertical claim, apply observational refinement to the first hypothesis with client MF. Compose the resulting trace inclusion with the second hypothesis, then reassociate linking by lemma 87.5. The right-hand specification is K(VG), as required by the definition of the displayed linearizability judgment. ◻

Atomic, set, and interval boundaries

For an atomic specification, each call is immediately followed by its return. Then an execution of K(V) chooses one point between the overlay call and return at which the atomic transition occurs. Hence Lin(,V) specializes to Herlihy–Wing linearizability, with the completion convention of definition 87.1.

The exchanger trace 0:call(exch(2));1:call(exch(4));0:ret(exch(2),4);1:ret(exch(4),2) has no atomic linearization. If thread 0 is first, its return 4 depends on thread 1’s later offer. If thread 1 is first, its return 2 depends on thread 0’s later offer. A paired set step sees both offers and returns the crossed pair.

Exercise 87.3

★★☆ Enumerate the two total orders of the exchanger operations. For each order, write the first return required by a deterministic atomic specification and show why the required offered value is unavailable at that point.

Theorem 87.9 — Restricted set specialization

Fix a finite history H and a particular partition π=(C1,,Cn) into nonempty concurrency classes. Suppose each class contains at most one operation per thread, the class order respects real-time and per-thread order, and both the set-sequential and linked-LTS readings use the same pending-call completion. Let Vset be the ordinary event-labelled LTS whose traces are exactly the invocation-then-response sequentializations admitted by those classes. Then (H,π) is accepted by the set-sequential specification if and only if the event trace of H lies in K(Vset) through a linked execution whose underlay trace is one of the sequentializations determined by π.

Proof of Theorem 87.9 — Restricted set specialization

Proof. From left to right, choose the invocation and response orders supplied by the set-sequential witness for each Cq={oq,1,,oq,rq}. In K(Vset), emit the overlay calls in their history order, take the individual event-labelled underlay call/return steps in the chosen class sequentialization, and then emit the corresponding overlay returns in their history order. Nonemptiness and the at-most-one-operation condition keep every thread state single-valued. Real-time and per-thread order show that concatenating these segments preserves SeqCons.

From right to left, use the stipulated underlay witness belonging to π. Its successive class sequentializations are traces of Vset; erase the identity-module brackets and read each segment back as Cq. The thread-state invariant and the hypotheses on π give single-valued thread states, real-time order, and per-thread order. Apply the shared completion convention to pending final calls. This reconstructs an accepted set-sequential execution for the same fixed partition, rather than silently replacing π by an existentially chosen grouping. ◻

Deleting the at-most-one-operation condition invalidates the reverse construction: one thread state cannot contain two active operations. Allowing an empty class invalidates the forward construction because it contributes no event-labelled underlay segment.

Exercise 87.4

★★★ Apply theorem 87.9 to the two-operation exchanger class. Construct both directions explicitly. Then put two operations of thread 0 in the same class and identify the exact thread-state invariant that fails.

Interval-sequential specifications are different. An operation may influence several alternating invocation and response classes. A one-shot write-snapshot can therefore return a set reflecting an interval of overlap rather than one atomic or set transition. This chapter uses that example only under its one-shot and blocking assumptions; the restricted set theorem does not imply the interval result.

Suggested first pass.

None of these problems is a prerequisite. Begin with exercise 87.1 and exercise 87.3; for the practical sequence, complete stages 1–3 before the two composition checks.

Exercise 87.5

★★★ Reconstruct both directions of locality for two one-bit registers over the same two-thread set and tagged operation signatures. Give the two trace projections, show that each preserves completion and per-thread order, and interleave the two linearized projections into a tensor trace. Use the shared active map to exclude overlapping left and right operations by one thread.

Exercise 87.6

★★☆ Give a one-shot write-snapshot history in which one operation overlaps two others. Show that no atomic point yields its returned set, then write the three interval classes that do. Do not apply theorem 87.9: explain which single-class premise is absent.

Exercise 87.7

★★★ Practical project.compositional-lts-checker Implement in Kappa a parser for the supplied finite LTS fixtures and a breadth-first trace-inclusion checker. Maintain the invariant that the queue is ordered first by depth and then by thread, tag, and value. Stage 1 checks the atomic counter through depth 6; stage 2 reports the first four-event atomic-exchanger counterexample; stage 3 accepts the paired-set exchanger through depth 4; stage 4 implements horizontal composition; stage 5 checks two counters through depth 6. The exact summary lines are atomic-counter: PASS depth=6 (bounded), the four-event exchanger failure, exchanger-vs-paired-set: PASS depth=4 (bounded), and two-counter-horizontal: PASS depth=6 (bounded). Reversing the thread tie order must change the reported first counterexample and fail the canonical-trace test.

Sources. The identity-saturation definition and Theorems 4–6 are frozen to the LTS presentation of Hatti, Oliveira Vale, Wang, Feng, and Shao. The more general game-semantic account of Oliveira Vale, Shao, and Chen supplies provenance and the Karoubi construction, but no game-semantic theorem is silently transferred to the displayed LTS. Herlihy and Wing own the atomic history definition; Castañeda, Rajsbaum, and Raynal own the interval-sequential comparison. The restricted set theorem above is book-owned and has exactly the hypotheses stated in theorem 87.9. The theorem owners and comparison sources are [HOVW^+26, OVSC24, HW90, CRR15].

Search the book

Type to search the local edition.