
High-Level Small-Step Operational Semantics for Transactions Katherine F. Moore Dan Grossman University of Washington {kfm, djg}@cs.washington.edu Abstract transaction has exclusive access to the shared-memory heap. A high-level operational semantics for transactions can give That this basic approach is simple is a good thing; it corre- transactions a precise meaning in the context of a program- sponds exactly to why transactions simplify reasoning about ming language without exposing any details about how programs. We do not want language definitions in terms of transactions are implemented. Such semantics give program- transactional-memory implementations. On the other hand, mers a simple model and serve as a correctness criterion for we do not simplify to the point that an entire transaction ex- transactional-memory implementations. This paper presents ecutes as one computational step since doing so abstracts such a semantics for a simple λ-calculus with shared mem- away so much that it is not clear how to add interesting fea- ory, threads, and transactions. tures such an internal parallelism (multiple threads within We use our approach to investigate two important aspects a transaction) or weak atomicity (nontransctional memory of a transactional programming language, namely spawning accesses that conflict with a transaction). As discussed in new threads (in particular, its interaction with transactions) Section 5, this places our approach between prior work that and strong versus weak atomicity. For the former, we present was more suitable for proving implementations of transac- a sound type system that ensures different ways of spawning tions correct at the expense of simplicity [11, 22] and work threads are used correctly. For the latter, we use a different that took the transactions-in-one-step approach that does not type system to prove the widely-held belief that if each extend well to other features [8]. mutable memory location is used outside transactions or After presenting a basic language in the next section, inside transactions (but not both), then strong and weak we investigate two nontrivial extensions and prove appro- atomicity are indistinguishable. priate results. (Full proofs are in a companion technical re- port [14].) First, we consider three possibilities for what it means to spawn a thread during a transaction and formal- 1. Introduction ize all three in one language. A sound type-and-effect sys- tem classifies what code can run only in transactions, only A primary reason to add transactions to programming lan- outside transactions, or anywhere, thereby ensuring that no guages is that doing so makes it easier to reason about the be- form of spawn occurs where it cannot execute successfully. havior of shared-memory multithreaded programs. For such Second, we present a language that lets code outside transac- reasoning to be precise and well-founded, our languages tions access the heap while another thread executes a trans- need rigorous semantic definitions. Oversimplified, informal action. We prove that if all mutable memory is partitioned descriptions such as, “a transaction appears to happen all- such that a location is accessed only outside or only inside at-once to other threads” are insufficient for deciding what transactions, then this weaker semantics is indistinguishable programs mean, what programming idioms are correct, and from the stronger one. While this result is not surprising, it is what implementation strategies are meaning-preserving. Be- reassuring given the subtle pitfalls of nontransactional code cause operational semantics, in which languages are defined bypassing transactional mechanisms [3, 12, 9, 20, 6, 21]. by judgments that reduce programs to simpler programs, is Moreover, we believe the structure of the proof will shed in- an expressive, convenient, rigorous, and well-understood ap- sight into how to prove that more sophisticated (and less ob- proach, we advocate its use for defining transactions, resolv- viously correct) invariants than a simple partition of memory ing semantic subtleties, and proving properties of transac- are also correct. tional languages. This paper presents our first results from pursuing this agenda. Our goals are to formalize the simple “all-at-once” idea 2. Basic Formal Language underlying transactions and prove properties about how this This section presents the syntax and small-step operational concept interacts with other language features. Therefore, semantics for a λ-calculus with threads, shared memory, and our formal languages give a semantic definition to transac- transactions. It is primarily a starting point for the additions tions at a very high level, essentially enforcing that a running and type systems in the subsequent two sections, which 1 TRANSACT 2007 e ::= c | x | λx.e | e1 e2 | seq(e1, e2) A program state has the form a; H; T where a indicates | e1 := e2 | refe |!e if any thread is currently executing a transaction (a = • for | spawntl e | atomic e yes and a = ◦ for no), H is the mutable heap (a mapping | l | inatomic(e) from labels, also known as addresses, to values), and T is a v ::= c | λx.e | l collection of threads. Each thread is just an expression. We H ::= · | H, l 7→ v use T1 k T2 to combine two thread collections into a larger T ::= · | T k e one, and we assume k is commutative, associative, and has · a ::= ◦ | • (the empty collection) as an identity. We write e in place of · k e where convenient. Figure 1. Base Syntax At run-time we need two new expression forms, labels (l) and inatomic(e). The former represents a heap location. The latter represents a partially completed transaction with investigate internal parallelism and weak atomicity. Three remaining computation e. key design decisions characterize our approach: The program-state component a deserves additional dis- • The semantics is high-level. It relies on implicit nondeter- cussion. Our semantics allows at most one thread to exe- minism to find a successful execution sequence. There is cute a transaction at a time. In essence a is like a “global no notion of transactions conflicting or aborting. Rather, a lock” where • indicates the lock is held. This is not to sug- transaction is always isolated from other threads because gest our language is a desirable implementation, but it is the no thread may access shared memory if another thread high-level semantics that enforces atomicity and isolation. is in a transaction. This simple semantics provides a cor- We would like an efficient implementation to be correct if, rectness criterion for more realistic implementations and by definition, it is equivalent to our semantics. (Correctness a simple model for programmers. should also involve some notion of fairness, but we do not consider that in this work.) • The semantics is small-step. In particular, transactions take many computational steps to complete. While this 2.2 Operational Semantics decision is an unnecessary complication for the basic Our small-step operational semantics rewrites one pro- language, it is essential for considering the additional gram state a; H; T to another a0; H0; T 0. Source program thread interleavings that internal parallelism and weak e starts with ◦; ·; e and a terminal configuration has the form atomicity introduce. ◦; H; v1 k ... k vn, i.e., all threads are values (and no trans- • The semantics is strong. Nontransactional code cannot action is active). Although the source program contains only observe or influence partially completed transactions. a single e, the evaluation of e can spawn threads, which can This lets us prove in Section 4 that strong-isolation se- spawn more threads, etc. mantics is equivalent to weak-isolation semantics under The rule PROGRAM chooses a thread nondeterministi- certain conditions. One cannot do such a proof without cally and that thread takes a single step, which can affect defining both semantics. a and H as well as possibly create a new thread. So the judgment form for single-thread evaluation is a; H; e → Our language does not have an explicit abort/retry. It a0; H0; e0; T , where T is · if the step does not spawn a thread is easy to add as in prior work [8] by simply having no and some e00 if it does. evaluation rule for it: A transaction that aborts is simply one For conciseness, we use evaluation contexts (E) to iden- that can never be chosen by the nondeterministic semantics. tify where subexpressions are recursively evaluated and a However, it would complicate stating type-safety since we single rule (CONTEXT) for propagating changes from eval- would have to accommodate an abort making a thread stuck. uating the subexpression.1 As usual, the inductive definition 2.1 Syntax of E describes expressions with exactly one hole [·] and E[e] means the expression resulting from replacing the hole in Figure 1 presents the formal abstract syntax for our small E with e. So, for example, we can use CONTEXT to derive transactional language. Most expression forms are typical 0 0 0 a; H; ref(seq(e1, e2)) → a ; H ; ref(seq(e1, e2)); T if we for a λ-calculus with mutable references, including con- 0 0 0 can derive a; H; e1 → a ; H ; e1; T . stants (c), variables (x), functions (λx.e), function applica- Rules for reducing sequences, memory allocations, and e e seq(e , e ) tions ( 1 2), sequential composition ( 1 2 ), mem- function calls are entirely conventional. In APPLY, e[v/x] ory allocation (refe), assignment (e1:=e2), and derefer- means the capture-avoiding substitution of v for x in e. !e ence ( ). Many omitted constructs, such as records, would The rules for reading and writing labels (SET and GET) re- be straightforward additions. In addition, we have thread- quire a = ◦, meaning no other thread is currently executing creation (spawntl e), where the tl reminds us that threads must be created at top-level (not within a transaction), and 1 We do not treat the body of a transaction as an evaluation context precisely atomic-blocks (atomic e) for executing e as a transaction.
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages11 Page
-
File Size-