43 research outputs found
Concurrent Data Structures Linked in Time
Arguments about correctness of a concurrent data structure are typically
carried out by using the notion of linearizability and specifying the
linearization points of the data structure's procedures. Such arguments are
often cumbersome as the linearization points' position in time can be dynamic
(depend on the interference, run-time values and events from the past, or even
future), non-local (appear in procedures other than the one considered), and
whose position in the execution trace may only be determined after the
considered procedure has already terminated.
In this paper we propose a new method, based on a separation-style logic, for
reasoning about concurrent objects with such linearization points. We embrace
the dynamic nature of linearization points, and encode it as part of the data
structure's auxiliary state, so that it can be dynamically modified in place by
auxiliary code, as needed when some appropriate run-time event occurs. We name
the idea linking-in-time, because it reduces temporal reasoning to spatial
reasoning. For example, modifying a temporal position of a linearization point
can be modeled similarly to a pointer update in separation logic. Furthermore,
the auxiliary state provides a convenient way to concisely express the
properties essential for reasoning about clients of such concurrent objects. We
illustrate the method by verifying (mechanically in Coq) an intricate optimal
snapshot algorithm due to Jayanti, as well as some clients
Shape predicates allow unbounded verification of linearizability using canonical abstraction
Canonical abstraction is a static analysis technique that represents states as 3-valued logical structures, and is able to construct finite representations of systems with infinite statespaces for verification. The granularity of the abstraction can be altered by the definition of instrumentation predicates, which derive their meaning from other predicates. We introduce shape predicates for preserving certain structures of the state during abstraction. We show that shape predicates allow linearizability to be verified for concurrent data structures using canonical abstraction alone, and use the approach to verify a stack and two queue algorithms. This contrasts with previous efforts to verify linearizability with canonical abstraction, which have had to employ other techniques as well
Specifying and Verifying Concurrent Algorithms with Histories and Subjectivity
We present a lightweight approach to Hoare-style specifications for
fine-grained concurrency, based on a notion of time-stamped histories that
abstractly capture atomic changes in the program state. Our key observation is
that histories form a partial commutative monoid, a structure fundamental for
representation of concurrent resources. This insight provides us with a
unifying mechanism that allows us to treat histories just like heaps in
separation logic. For example, both are subject to the same assertion logic and
inference rules (e.g., the frame rule). Moreover, the notion of ownership
transfer, which usually applies to heaps, has an equivalent in histories. It
can be used to formally represent helping---an important design pattern for
concurrent algorithms whereby one thread can execute code on behalf of another.
Specifications in terms of histories naturally abstract granularity, in the
sense that sophisticated fine-grained algorithms can be given the same
specifications as their simplified coarse-grained counterparts, making them
equally convenient for client-side reasoning. We illustrate our approach on a
number of examples and validate all of them in Coq.Comment: 17 page
Embedding Hindsight Reasoning in Separation Logic
Proving linearizability of concurrent data structures remains a key challenge
for verification. We present temporal interpolation as a new proof principle to
conduct such proofs using hindsight arguments within concurrent separation
logic. Temporal reasoning offers an easy-to-use alternative to prophecy
variables and has the advantage of structuring proofs into easy-to-discharge
hypotheses. To hindsight theory, our work brings the formal rigor and proof
machinery of concurrent program logics. We substantiate the usefulness of our
development by verifying the linearizability of the Logical Ordering (LO-)tree
and RDCSS. Both of these involve complex proof arguments due to
future-dependent linearization points. The LO-tree additionally features
complex structure overlays. Our proof of the LO-tree is the first formal proof
of this data structure. Interestingly, our formalization revealed an unknown
bug and an existing informal proof as erroneous
Order out of Chaos: Proving Linearizability Using Local Views
Proving the linearizability of highly concurrent data structures, such as those using optimistic concurrency control, is a challenging task. The main difficulty is in reasoning about the view of the memory obtained by the threads, because as they execute, threads observe different fragments of memory from different points in time. Until today, every linearizability proof has tackled this challenge from scratch.
We present a unifying proof argument for the correctness of unsynchronized traversals, and apply it to prove the linearizability of several highly concurrent search data structures, including an optimistic self-balancing binary search tree, the Lazy List and a lock-free skip list. Our framework harnesses sequential reasoning about the view of a thread, considering the thread as if it traverses the data structure without interference from other operations. Our key contribution is showing that properties of reachability along search paths can be deduced for concurrent traversals from such interference-free traversals, when certain intuitive conditions are met. Basing the correctness of traversals on such local view arguments greatly simplifies linearizability proofs. At the heart of our result lies a notion of order on the memory, corresponding to the order in which locations in memory are read by the threads, which guarantees a certain notion of consistency between the view of the thread and the actual memory.
To apply our framework, the user proves that the data structure satisfies two conditions: (1) acyclicity of the order on memory, even when it is considered across intermediate memory states, and (2) preservation of search paths to locations modified by interfering writes. Establishing the conditions, as well as the full linearizability proof utilizing our proof argument, reduces to simple concurrent reasoning. The result is a clear and comprehensible correctness proof, and elucidates common patterns underlying several existing data structures
Simplifying proofs of linearisability using layers of abstraction
Linearisability has become the standard correctness criterion for concurrent
data structures, ensuring that every history of invocations and responses of
concurrent operations has a matching sequential history. Existing proofs of
linearisability require one to identify so-called linearisation points within
the operations under consideration, which are atomic statements whose execution
causes the effect of an operation to be felt. However, identification of
linearisation points is a non-trivial task, requiring a high degree of
expertise. For sophisticated algorithms such as Heller et al's lazy set, it
even is possible for an operation to be linearised by the concurrent execution
of a statement outside the operation being verified. This paper proposes an
alternative method for verifying linearisability that does not require
identification of linearisation points. Instead, using an interval-based logic,
we show that every behaviour of each concrete operation over any interval is a
possible behaviour of a corresponding abstraction that executes with
coarse-grained atomicity. This approach is applied to Heller et al's lazy set
to show that verification of linearisability is possible without having to
consider linearisation points within the program code