A Concurrency Monad (2) Running a Concurrent Computation (1) Running a Computation: Introduce a Monad Representing “Interleavable Type Output = [Char] Computations”

A Concurrency Monad (2) Running a Concurrent Computation (1) Running a Computation: Introduce a Monad Representing “Interleavable Type Output = [Char] Computations”

A Concurrency Monad (2) Running a Concurrent Computation (1) Running a computation: Introduce a monad representing “interleavable type Output = [Char] computations”. At this stage, this amounts to little MGS 2012: FUN Lecture 5 type ThreadQueue = [Thread] Concurrency more than a convenient way to construct threads by sequential composition. type State = (Output, ThreadQueue) Henrik Nilsson How can Threads be constructed sequentially? runCM :: CM a -> Output University of Nottingham, UK The only way is to parameterize thread prefixes runCM m = runHlp ("", []) (thread m) on the rest of the Thread. This leads directly to where continuations. runHlp s t = case dispatch s t of Left (s', t) -> runHlp s' t Right o -> o MGS 2012: FUN Lecture 5 – p.1/40 MGS 2012: FUN Lecture 5 – p.4/40 MGS 2012: FUN Lecture 5 – p.7/40 This Lecture A Concurrency Monad (3) Running a Concurrent Computation (2) newtype CM a = CM ((a -> Thread) -> Thread) • A concurrency monad (adapted from Dispatch on the operation of the currently Claessen (1999)) fromCM :: CM a -> ((a -> Thread) -> Thread) running Thread. Then call the scheduler. • Traditional, lock-based concurrent fromCM (CM x) = x dispatch :: State -> Thread programming in Haskell -> Either (State, Thread) Output • Review of issues with lock-based concurrent thread :: CM a -> Thread dispatch (o, rq) (Print c t) = programming thread m = fromCM m (const End) schedule (o ++ [c], rq ++ [t]) dispatch (o, rq) (Fork t1 t2) = • Software Transactional Memory (STM monad) instance Monad CM where schedule (o, rq ++ [t1, t2]) • Why pure functional programming and STM is return x = CM (\k -> k x) dispatch (o, rq) End = a great fit m >>= f = CM $ \k -> schedule (o, rq) fromCM m (\x -> fromCM (f x) k) MGS 2012: FUN Lecture 5 – p.2/40 MGS 2012: FUN Lecture 5 – p.5/40 MGS 2012: FUN Lecture 5 – p.8/40 A Concurrency Monad (1) A Concurrency Monad (4) Running a Concurrent Computation (3) Demonstration that the notion of concurrent Atomic operations: Selects next Thread to run, if any. computation can be captured by a monad, and cPrint :: Char -> CM () schedule :: State -> Either (State, Thread) interesting example of a monad. cPrint c = CM (\k -> Print c (k ())) Output A Thread represents a process: a stream of schedule (o, []) = Right o primitive atomic operations: cFork :: CM a -> CM () schedule (o, t:ts) = Left ((o, ts), t) data Thread = Print Char Thread cFork m = CM (\k -> Fork (thread m) (k ())) | Fork Thread Thread | End cEnd :: CM a cEnd = CM (\_ -> End) Note that a Thread represents the entire rest of a computation. MGS 2012: FUN Lecture 5 – p.3/40 MGS 2012: FUN Lecture 5 – p.6/40 MGS 2012: FUN Lecture 5 – p.9/40 Example: Concurrent Processes Any Use? Example: Basic Synchronization (1) Traditional lock-based synchronization: MVars p1 :: CM () p2 :: CM () p3 :: CM () • A number of libraries and embedded used as semaphores. p1 = do p2 = do p3 = do langauges use similar ideas, e.g. module Main where cPrint 'a' cPrint '1' cFork p1 - Fudgets cPrint 'b' cPrint '2' cPrint 'A' - Yampa import Control.Concurrent ... ... cFork p2 - FRP in general cPrint 'j' cPrint '0' cPrint 'B' • Studying semantics of concurrent programs. countFromTo :: Int -> Int -> IO () countFromTo m n main = print (runCM p3) • Aid for testing, debugging, and reasoning | m > n = return () about concurrent programs. Result: aAbc1Bd2e3f4g5h6i7j890 | otherwise = do Note: As it stands, the output is only made putStrLn (show m) available after all threads have terminated.) countFromTo (m+1) n MGS 2012: FUN Lecture 5 – p.10/40 MGS 2012: FUN Lecture 5 – p.13/40 MGS 2012: FUN Lecture 5 – p.16/40 Incremental Output Concurrent Programming in Haskell Example: Basic Synchronization (2) main = do Incremental output: Primitives for concurrent programming provided start <- newEmptyMVar runCM :: CM a -> Output as operations of the IO monad (or “sin bin” :-). done <- newEmptyMVar runCM m = dispatch [] (thread m) They are in the module Control.Concurrent. forkIO $ do Excerpts: dispatch :: ThreadQueue -> Thread -> Output takeMVar start dispatch rq (Print c t) = c : schedule (rq ++ [t]) forkIO :: IO () -> IO ThreadId countFromTo 1 10 dispatch rq (Fork t1 t2) = schedule (rq ++ [t1, t2]) killThread :: ThreadId -> IO () putMVar done () dispatch rq End = schedule rq threadDelay :: Int -> IO () putStrLn "Go!" newMVar :: a -> IO (MVar a) putMVar start () schedule :: ThreadQueue -> Output newEmptyMVar :: IO (MVar a) takeMVar done schedule [] = [] putMVar :: MVar a -> a -> IO () (countFromTo 11 20) schedule (t:ts) = dispatch ts t takeMVar :: MVar a -> IO a putStrLn "Done!" MGS 2012: FUN Lecture 5 – p.11/40 MGS 2012: FUN Lecture 5 – p.14/40 MGS 2012: FUN Lecture 5 – p.17/40 Example: Concurrent processes 2 MVars Example: Unbounded Buffer (1) module Main where p1 :: CM () p2 :: CM () p3 :: CM () • The fundamental synchronisation mechanism p1 = do p2 = do p3 = do is the MVar (“em-var”). import Control.Monad (when) cPrint 'a' cPrint '1' cFork p1 • An MVar is a “one-item box” that may be import Control.Concurrent cPrint 'b' undefined cPrint 'A' empty or full. ... ... cFork p2 newtype Buffer a = • takeMVar putMVar cPrint 'j' cPrint '0' cPrint 'B' Reading ( ) and writing ( ) Buffer (MVar (Either [a] (Int, MVar a))) are atomic operations: main = print (runCM p3) - Writing to an empty MVar makes it full. newBuffer :: IO (Buffer a) - Writing to a full MVar blocks. newBuffer = do Result: aAbc1Bd*** Exception: Prelude.undefined - Reading from an empty MVar blocks. b <- newMVar (Left []) - Reading from a full MVar makes it empty. return (Buffer b) MGS 2012: FUN Lecture 5 – p.12/40 MGS 2012: FUN Lecture 5 – p.15/40 MGS 2012: FUN Lecture 5 – p.18/40 Example: Unbounded Buffer (2) Example: Unbounded Buffer (5) Compositionality? (2) readBuffer :: Buffer a -> IO a readBuffer (Buffer b) = do The buffer can now be used as a channel of What about this? bc <- takeMVar b communication between a set of “writers” and a case bc of mutex <- newMVar () set of “readers”. E.g. Left (x : xs) -> do ... putMVar b (Left xs) main = do takeMVar mutex return x b <- newBuffer x1 <- readBuffer b Left [] -> do forkIO (writer b) x2 <- readBuffer b w <- newEmptyMVar forkIO (writer b) putMVar b (Right (1,w)) putMVar mutex () forkIO (reader b) takeMVar w Right (n,w) -> do forkIO (reader b) putMVar b (Right (n + 1, w)) ... takeMVar w MGS 2012: FUN Lecture 5 – p.19/40 MGS 2012: FUN Lecture 5 – p.22/40 MGS 2012: FUN Lecture 5 – p.25/40 Example: Unbounded Buffer (3) Example: Unbounded Buffer (6) Compositionality? (3) Suppose we would like to read from one of two Why isn’t Buffer simply defined as reader :: Buffer Int -> IO () reader n b = rLoop buffers. newtype Buffer a = Buffer [a] where That is, composing alternatives. ? rLoop = do Hmmm. How do we even begin? Hint: What would happen if e.g. an attempt is x <- readBuffer b made to read from an empty buffer? when (x > 0) $ do • No way to attempt reading a buffer without putStrLn (n ++ ": " ++ show x) risking blocking. rLoop • We have to change or enrich the buffer implementation. E.g. add a tryReadBuffer operation, and then repeatedly poll the two buffers in a tight loop. Not so good! MGS 2012: FUN Lecture 5 – p.20/40 MGS 2012: FUN Lecture 5 – p.23/40 MGS 2012: FUN Lecture 5 – p.26/40 Example: Unbounded Buffer (4) Compositionality? (1) Locks Are Pessimistic writeBuffer :: Buffer a -> a -> IO () writeBuffer (Buffer b) x = do Suppose we would like to read two consecutive • In practice, it is often the case that conflicts bc <- takeMVar b elements from a buffer b? that would lead to actual harm are rare. case bc of That is, sequential composition. • Lock-based synchronisation thus tends to Left xs -> limit concurrency unnecessarily, potentially putMVar b (Left (xs ++ [x])) Would the following work? harming performance in particular on parallel Right (n,w) -> do x1 <- readBuffer b hardware (such as multi-core processors). putMVar w x x2 <- readBuffer b if n > 1 then putMVar b (Right (n - 1, w)) else putMVar b (Left []) MGS 2012: FUN Lecture 5 – p.21/40 MGS 2012: FUN Lecture 5 – p.24/40 MGS 2012: FUN Lecture 5 – p.27/40 Software Transactional Memory (1) STM and Pure Declarative Languages Example: Buffer Revisited (2) • • STM perfect match for purely declarative Software Transactional Memory (STM) is a readBuffer :: Buffer a -> STM a new promising approach to facilitate writing languages: readBuffer (Buffer b) = do correct and performant concurrent code. - reading and writing of shared mutable xs <- readTVar b case xs of • Inspired by the notion of database transactions. variables explicit and relatively rare; - most computations are pure and need not [] -> retry • (x : xs') -> do Operations on shared mutable variables be logged. grouped into transactions. writeTVar b xs' • return x • Disciplined use of effects through monads a Transactions optimistically executed huge payoff: easy to ensure that only effects concurrently. that can be undone can go inside a transaction. writeBuffer :: Buffer a -> a -> STM () • Each transaction succeeds or fails in its entirety, writeBuffer (Buffer b) x = do (Imagine the havoc arbitrary I/O actions could cause if xs <- readTVar b depending on if there actually was a problem. part of transaction: How to undo? What if retried?) writeTVar b (xs ++ [x]) MGS 2012: FUN Lecture 5 – p.28/40 MGS 2012: FUN Lecture 5 – p.31/40 MGS 2012: FUN Lecture 5 – p.34/40 Software Transactional Memory (2) The STM monad Example: Buffer Revisited (3) • Transactions thus atomic w.r.t. other The software transactional memory abstraction The main program and code for readers and transactions. provided by a monad STM. Distinct from IO! writers can remain unchanged, except that STM • Failed transactions are automatically retried Defined in Control.Concurrent.STM.

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    5 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us