comp3151/9151 foundations of concurrency lecture 8cs3151/17s2/lec/pdf/lecture08la.pdf · critical...
TRANSCRIPT
Readers and Writers Haskell Operational Semantics
COMP3151/9151 Foundations ofConcurrency Lecture 8
Liam O’Connor
CSE, UNSW (and data61)
8 Sept 2017
1
Readers and Writers Haskell Operational Semantics
Shared DataConsider the Readers and Writers problem from Lecture 6:
Problem
We have a large data structure (i.e. a structure that cannot beupdated in one atomic step) that is shared between some numberof writers who are updating the data structure and some numberof readers who are attempting to retrieve a coherent copy of thedata structure.
Desiderata:
We want atomicity, in that each update happens in one go,and updates-in-progress or partial updates are not observable.
We want consistency, in that any reader that starts after anupdate finishes will see that update.
We want to minimise waiting.
2
Readers and Writers Haskell Operational Semantics
A Crappy Solution
Treat both reads and updates as critical sections — use any oldcritical section solution (mutexes, semaphores, etc.) tosequentialise all reads and writes to the data structure.
Observation
Updates are atomic and reads are consistent — but reads can’thappen concurrently, which leads to unnecessary contention.
3
Readers and Writers Haskell Operational Semantics
A Better Solution
Use a monitor with two condition variables.
See Algorithm 6.12 of the slides or Ben-Ari’s Algorithm 7.4.
Observation
We have atomicity and consistency, and now multiple reads canexecute concurrently. Still, we don’t allow updates to executeconcurrently with reads, to prevent partial updates from beingobserved by a reader.
4
Readers and Writers Haskell Operational Semantics
Reading and Writing
Complication
Now suppose we don’t want readers to wait (much) while an updateis performed. Instead, we’d rather they get an older version of thedata structure.
Trick: Rather than update the data structure in place, a writercreates their own local copy of the data structure, and then merelyupdates the (shared) pointer to the data structure to point to theircopy.Liam: Draw on the board
Atomicity The only shared write is now just to one pointer.
Consistency Reads that start before the pointer update get theolder version, but reads that start after get the latest.
5
Readers and Writers Haskell Operational Semantics
Persistent Data StructuresCopying is O(n) in the worst case, but we can do better for manytree-like types of data structure.
Example (Binary Search Tree)
64
37
20
3 22
40
102
Pointer
64
37
40
42
6
Readers and Writers Haskell Operational Semantics
Persistent Data StructuresCopying is O(n) in the worst case, but we can do better for manytree-like types of data structure.
Example (Binary Search Tree)
64
37
20
3 22
40
102
Pointer
64
37
40
42
7
Readers and Writers Haskell Operational Semantics
Persistent Data StructuresCopying is O(n) in the worst case, but we can do better for manytree-like types of data structure.
Example (Binary Search Tree)
64
37
20
3 22
40
102
Pointer
64
37
40
42
8
Readers and Writers Haskell Operational Semantics
Persistent Data StructuresCopying is O(n) in the worst case, but we can do better for manytree-like types of data structure.
Example (Binary Search Tree)
64
37
20
3 22
40
102
Pointer
64
37
40
42
102
20
3 22
9
Readers and Writers Haskell Operational Semantics
Purely Functional Data Structures
Persistent data structures that exclusively make use of copyingover mutation are called purely functional data structures. Theyare so called because operations on them are best expressed in theform of mathematical functions that, given an input structure,return a new output structure:
insert v Leaf = Branch v Leaf Leafinsert v (Branch x l r) = if v ≤ x then
Branch x (insert v l) relseBranch x l (insert v r)
Purely functional programming languages like Haskell are designedto facilitate programming in this way.
10
Readers and Writers Haskell Operational Semantics
Haskell
Haskell is a programming language where all programs are writtenin the form of immutable definitions. There is no intrinsic notionof time or mutable state. Syntax is mostly derived from simplemathematical notation.
Example (Factorial)
factorial :: Int→ Intfactorial 0 = 1factorial n = n ∗ factorial (n − 1)
Liam: Show more examples, use GHCi a bit
11
Readers and Writers Haskell Operational Semantics
Computing with Functions
Unfortunately, pure functions don’t capture everything we wantfrom computations. Processes have to interact with the user,global state, other processes, and the operating system.We model real processes in Haskell using the IO type.
IO τ =A (possibly effectful) process that, when executed,produces a result of type τ
Note the semantics of evaluation and execution are different things.
12
Readers and Writers Haskell Operational Semantics
Building up IO
pure :: ∀a. a→ IO a(�=) :: ∀a b. IO a→ (a→ IO b)→ IO b
getChar :: IO CharputChar :: Char→ IO ()
Example (Echo)
echo :: IO ()echo = getChar �= (λx . putChar x �= λy . echo)
(here λx . y is notation for an anonymous function that returns y given argument x)
13
Readers and Writers Haskell Operational Semantics
Monadic IO
Monad Laws
Define a sequential composition operatorIO◦ as follows:
(IO◦) :: ∀a b c. (a→ IO b)→ (b → IO c)→ (a→ IO c)
(fIO◦ g) x = f x �= g
We have the following laws to aid reasoning:
Identity fIO◦ pure = f = pure
IO◦ fAssociativity f
IO◦ (gIO◦ h) = (f
IO◦ g)IO◦ h
14
Readers and Writers Haskell Operational Semantics
Do Notation
Haskell defines some convenience notation to make writingprocesses easier.
do x ← P;Q = P �= λx . do Qdo P;Q = P �= λ . do Q
do P = P
Example (Echo, revisited)
echo :: IO ()echo = do
x ← getCharputChar xecho
15
Readers and Writers Haskell Operational Semantics
Adding Concurrency
We can have multiple threads easily enough:
forkIO :: IO () → IO ()
Example (Dueling Printers)
let loop c = do putChar c ; loop cin do forkIO (loop ‘a’); loop ‘z’
But what sort of synchronisation primitives are available?
16
Readers and Writers Haskell Operational Semantics
MVars
The MVar is the simplest synchronisation primitive in Haskell. Itcan be thought of as a shared box which holds at most one value.
Threads must take the value out of a full box to read it, and mustput a value into an empty box to update it.
MVar Functions
newMVar :: ∀a. a→ IO (MVar a) Create a new MVartakeMVar :: ∀a. MVar a→ IO a Read/remove the valueputMVar :: ∀a. MVar a→ a→ IO () Update/insert a value
Taking from an empty MVar or putting into a full one results inblocking.An MVar can be thought of as channel containing at most onevalue.
17
Readers and Writers Haskell Operational Semantics
Readers and WritersWe can treat MVars as shared variables with some definitions:
writeMVar m v = do takeMVar m; putMVar m vreadMVar m = do v ← takeMVar m; putMVar m v ; pure v
problem :: DB → IO ()problem initial = do
db ← newMVar initialwl ← newMVar ()let reader = readMVar db �= · · ·let writer = do
takeMVar wld ← readMVar dblet d ′ = update devaluate d ′
writeMVar db d ′
putMVar wl ()
18
Readers and Writers Haskell Operational Semantics
Fairness
Each MVar has an attached FIFO queue, so GHC Haskell canensure the following fairness property:
No thread can be blocked indefinitely on an MVar unlessanother thread holds that MVar indefinitely.
In LTL, this is something like (where Lm is the queue for MVar m):
(p ∈ Lm ∧�♦putMVar(m))⇒ ♦(p /∈ Lm)
It seems to me that a stronger guarantee can be made, but this is what is stated in the literature.
19
Readers and Writers Haskell Operational Semantics
Evaluation Semantics
The semantics of Haskell’s evaluation are interesting but notparticularly relevant for us. We will assume that it happens quietlywithout a fuss:
β-equivalence (λx . M[x ]) N ≡β M[N]α-equivalence λx . M[x ] ≡α λy . M[y ]η-equivalence λx . M x ≡η M
Let our ambient congruence relation ≡ be ≡αβη enriched with thefollowing extra equations, justified by the monad laws:
pure N �= M ≡ M N(X �= Y )�= Z ≡ X �= (λx . Y x �= Z )X ≡ X �= pure
20
Readers and Writers Haskell Operational Semantics
Processes
This means that a Haskell expression of type IO τ for will boildown to either pure x where x is a value of type τ ; or a�= Mwhere a is some primitive IO action (forkIO p, readMVar v , etc.)and M is some function producing another IO τ . This is the headnormal form for IO expressions.
Definition
Define a language of processes P, which contains all (head-normal)expressions of type IO ().
We want to define the semantics of the execution of theseprocesses. Let’s use operational semantics:
(7→) ⊆ P × P
21
Readers and Writers Haskell Operational Semantics
Semantics for forkIO
To model forkIO, we need to model the parallel execution ofmultiple processes in our process language. We shall add a parallelcomposition operator to the language of processes:
P,Q ::= a�= M| pure ()| P ‖ Q| · · ·
And the following ambient congruence equations:
P ‖ Q ≡ Q ‖ PP ‖ (Q ‖ R) ≡ (P ‖ Q) ‖ R
22
Readers and Writers Haskell Operational Semantics
Semantics for forkIO
If we have multiple processes active, pick one of themnon-deterministically to move:
P 7→ P ′
P ‖ Q 7→ P ′ ‖ Q
The forkIO operation introduces a new process:
(forkIO P �= M) 7→ P ‖ (pure ()�= M)
23
Readers and Writers Haskell Operational Semantics
Semantics for MVars
MVars are modelled as a special type of process, identified by aunique name. Values of MVar type merely contain the name ofthe process, so that putMVar and friends know where to look.
P,Q ::= a�= M| pure ()| P ‖ Q| 〈〉n | 〈v〉n| · · ·
〈〉n ‖ (putMVar n v �= M) 7→ 〈v〉n ‖ (pure ()�= M)
〈v〉n ‖ (takeMVar n�= M) 7→ 〈〉n ‖ (pure v �= M)
24
Readers and Writers Haskell Operational Semantics
Semantics for newMVar
We might think that newMVar should have semantics like this:
(newMVar v �= M) 7→ 〈v〉n ‖ (pure n�= M)(n fresh)
But this approach has a number of problems:
The name n is now globally-scoped, without an explicit binderto introduce it.
It doesn’t accurately model the lifetime of the MVar, whichshould be garbage-collected once all threads that can access itfinish.
It makes MVars global objects, so our semantics aren’t veryabstract. We would like local communication to be local inour model.
25
Readers and Writers Haskell Operational Semantics
Restriction Operator
We introduce a restriction operator ν to our language of processes:
P,Q ::= a�= M| pure ()| P ‖ Q| 〈〉n | 〈v〉n| (ν n) P
Writing (ν n) P says that the MVar name n is only available inprocess P. Mentioning n outside P is not well-formed. We needthe following additional congruence equations:
(ν n) (ν m) P ≡ (ν m) (ν n) P(ν n)(P ‖ Q) ≡ P ‖ (ν n) Q (if n /∈ P)
26
Readers and Writers Haskell Operational Semantics
Better Semantics for newMVar
The rule for newMVar is much the same as before, but now weexplicitly restrict the MVar to M.
(newMVar v �= M) 7→ (ν n)(〈v〉n ‖ (pure n�= M))(n fresh)
We can always execute under a restriction:
P 7→ P ′
(ν n) P 7→ (ν n) P ′
Question
What happens when you put an MVar inside another MVar?
27
Readers and Writers Haskell Operational Semantics
Garbage Collection
If an MVar is no longer used, we just replace it with the do-nothingprocess:
(ν n) 〈〉n 7→ pure ()(ν n) 〈v〉n 7→ pure ()
Extra processes that have outlived their usefulness disappear:
pure () ‖ P 7→ P
28
Readers and Writers Haskell Operational Semantics
Process Algebra
Our language P is called a process algebra, and our method ofascribing semantics to P is called structural operational semantics.Neither are a focus of this course, but they are points whichintersect heavily with other courses at UNSW.
Process algebras and calculi of various kinds are covered ingreat detail in COMP6752 with Rob van Glabbeek, who is anexpert in this field.
Operational Semantics are a major part of the courseCOMP[39]161, which I develop and teach along with GabiKeller, who is the lecturer in charge.
If there’s time!
We can talk about CCS.
29
Readers and Writers Haskell Operational Semantics
Bibliography
Simon Marlow
Parallel and Concurrent Programming in Haskell
O’Reilly, 2013
http://chimera.labs.oreilly.com/books/1230000000929
Simon Peyton Jones, Andrew Gordon and Sigbjorn Finne
Concurrent Haskell
POPL ’96
Association for Computer Machinery
http://microsoft.com/en-us/research/wp-content/uploads/1996/01/concurrent-haskell.pdf
Simon Marlow (Editor)
Haskell 2010 Language Report
https://www.haskell.org/onlinereport/haskell2010/
30