b3cc concurrency 05 software transactional memory 1
play

B3CC: Concurrency 05: Software Transactional Memory (1) Trevor L. - PowerPoint PPT Presentation

B3CC: Concurrency 05: Software Transactional Memory (1) Trevor L. McDonell Utrecht University, B2 2020-2021 Announcement From next week (Nov 30) we will o ff er the online werkcollege: - Monday 13:15 - 15:00 - Thursday 9:00 - 10:45 As


  1. B3CC: Concurrency 05: Software Transactional Memory (1) Trevor L. McDonell Utrecht University, B2 2020-2021

  2. Announcement • From next week (Nov 30) we will o ff er the online werkcollege: - Monday 13:15 - 15:00 - Thursday 9:00 - 10:45 • As well as the on-campus werkcollege at the usual time: - Tuesday 15:15 - 17:00 2

  3. Critical sections 3

  4. Example: bank accounts • Model bank accounts and operations like withdrawing, depositing, and transferring money between accounts - It should not be possible to observe a state where, during a transfer, money has been withdrawn from one account but yet to be deposited into the target account Thread A Thread B $150 $200 Account 1 Account 2 Account 3 $500 $300 $200 4

  5. Example: bank accounts Thread A Thread B $150 $200 Account 1 Account 2 Account 3 $500 $300 $200 • Thread A: • Thread B: 5

  6. Example: bank accounts Thread A Thread B $150 $200 Account 1 Account 2 Account 3 $500 $300 $200 • Thread A: • Thread B: - Read balance of account 2 6

  7. Example: bank accounts Thread A Thread B $150 $200 Account 1 Account 2 Account 3 $500 $300 $200 • Thread A: • Thread B: - Read balance of account 2 - Check for sufficient funds 7

  8. Example: bank accounts Thread A Thread B $150 $200 Account 1 Account 2 Account 3 $500 $300 $200 • Thread A: • Thread B: - Read balance of account 2 - Check for sufficient funds - Read balance of account 2 8

  9. Example: bank accounts Thread A Thread B $150 $200 Account 1 Account 2 Account 3 $500 $300 $200 • Thread A: • Thread B: - Read balance of account 2 - Check for sufficient funds - Read balance of account 2 - Check for sufficient funds 9

  10. Example: bank accounts Thread A Thread B $150 $200 Account 1 Account 2 Account 3 $500 $100 $400 • Thread A: • Thread B: - Read balance of account 2 - Check for sufficient funds - Read balance of account 2 - Check for sufficient funds - Update balance of account 2 10

  11. Example: bank accounts Thread A Thread B $150 $200 Account 1 Account 2 Account 3 $650 $150 $400 • Thread A: • Thread B: - Read balance of account 2 - Check for sufficient funds - Read balance of account 2 - Check for sufficient funds - Update balance of account 2 - Update balance of account 2 11

  12. Attempt #1 • The basic idea: type Account = IORef Int deposit ::; Int ->. Account ->. IO () deposit amount acc = do balance <.- readIORef acc writeIORef acc (balance + amount) withdraw ::; Int ->. Account ->. IO () withdraw amount acc = deposit (-amount) acc 12

  13. Attempt #2 • Use locks so that updates are atomic: type Account = MVar Int deposit ::; Int ->. Account ->. IO () deposit amount acc = modifyMVar_ acc $ \balance ->. return (balance + amount)) transfer ::; Int ->. Account ->. Account ->. IO () 
 transfer amount from to = do 
 withdraw amount from 
 inconsistent state! deposit amount to 13

  14. Attempt #3 • We need to implement transfer di ff erently type Account = MVar Int transfer ::; Int ->. Account ->. Account ->. IO () transfer amount from to = modifyMVar_ from $ \fromBalance ->. do modifyMVar_ to $ \toBalance ->. return (toBalance + amount) return (fromBalance - amount) P 0 : P 1 : transfer 100 acc1 acc2 transfer 200 acc2 acc1 14

  15. Attempt #4 • Take locks in an a fixed (but arbitrary) order; release in the opposite order type Account = MVar Int transfer ::; Int ->. Account ->. Account ->. IO () transfer amount from to = if from < to then modifyMVar_ from $ \fromBalance ->. modifyMVar_ to $ \toBalance ->. ..../ else modifyMVar_ to $ \toBalance ->. modifyMVar_ from $ \fromBalance ->. ..../ 15

  16. Extending the example • What happens if we want to… - Block (wait) until the from account has sufficient funds? - Withdraw from a second account if the first does not have sufficient funds? • I hold locks #3 and #5 • And now need to acquire lock #2, or #4 or… 16

  17. Locks are bad • Problems with locks include: - Taking too few locks - Taking too many locks • Inhibits concurrency (at best) or causes deadlock (at worst) - Taking the wrong locks - Taking locks in the wrong order - Error recovery - Lost wake-ups or erroneous retries 17

  18. Locks are bad • The killer: - Locks don’t support modular programming - We needed to create a new function transfer , rather than using the (correctly working) functions withdraw and deposit 18

  19. Atomic blocks 19

  20. An alternative • The idea: - Garbage collectors allow us to program without malloc and free; 
 Can we do the same for locks? What would that look like? - Modular concurrency! - Locks are pessimistic; let’s be optimistic instead 20

  21. Software transactional memory • A technique for implementing atomic blocks - Atomicity: effects become visible to other threads all at once - Isolation: cannot see the effects of other threads - Use a different type to wrap operations whose effects can be undone if necessary (more on this later) import Control.Concurrent.STM data STM a --. abstract instance Monad STM --. among other things atomically ::; STM a ->. IO a 21

  22. Software transactional memory • Sharing state - Instead of IORef , we use TVar as a transactional variable - Basic interface: import Control.Concurrent.STM.TVar newTVar ::; a ->. STM (TVar a) readTVar ::; TVar a ->. STM a writeTVar ::; TVar a ->. a ->. STM () 22

  23. Software transactional memory • Sharing state - Instead of MVar we have an equivalent TMVar - A variable is either full or empty: threads wait for the appropriate state - Basic interface: import Control.Concurrent.STM.TMVar newTMVar ::; a ->. STM (TMVar a) newEmptyTMVar ::; STM (TMVar) readTMVar ::; TMVar a ->. STM a writeTMVar ::; TMVar a ->. a ->. STM () 23

  24. Revisiting accounts • STM actions are composed together in the same way as IO actions type Account = TVar Int deposit ::; Int ->. Account ->. STM () deposit amount account = do balance <.- readTVar account writeTVar (balance + amount) account withdraw ::; Int ->. Account ->. STM () withdraw amount = deposit (-amount) 24

  25. Revisiting accounts • STM actions are executed as a single, isolated atomic block transfer ::; Int ->. Account ->. Account ->. IO () transfer amount from to = atomically $ do withdraw amount from deposit amount to 25

  26. STM • Types are used to isolate transactional actions from arbitrary IO actions - To get from STM to IO we have to execute the entire action atomically - Can’t mix monads! bad ::; Int ->. Account ->. STM () bad amount account = do putStrLn “withdrawing!” --. ::; IO () withdraw amount account --. ::; STM () good ::; Int ->. Account ->. IO () good amount account = do putStrLn “withdrawing!” --. ::; IO () atomically $ withdraw amount account --. ::; IO () 26

  27. Implementing transactional memory • How to implement atomically - Single global lock? - Instead: optimistic execution, without taking any locks • At the start of the atomic block begin a thread local transaction log - Each writeTVar records the address and the new value to the log - Each readTVar searches the log and • Takes the value of an earlier writeTVar; or • Reads the TVar and records the value into the log 27

  28. Implementing transactional memory • At the end of the atomic block the transaction log must be validated - Checks each readTVar in the log matches the current value - If successful all writeTVar recorded in the log are committed to the real TVars - The validate and commit steps must be truly atomic 28

  29. Implementing transactional memory • What if validation fails? - The operation executed with an inconsistent view of memory - Re-execute the transaction with a new transaction log • Since none of the writes are committed to memory, this is safe to do • It is critical that the atomic block contains no actions other than reads and writes to TVars atomically $ do x <.- readTVar xv y <.- readTVar yv if x > y then brexit --. ::; IO () side effects! else return () 29

  30. Summary (so far) • STM gives us: - Atomic transactions for shared memory - Encapsulation of concurrent code - Help avoid common locking problems • But… - Just like garbage collection, is no silver bullet - Can not solve all problems: e.g. starvation & contention 30

  31. tot ziens Photo by JC Gellidon

Recommend


More recommend