CPL 2016, week 13 Software transactional memory Oleg Batrashev Institute of Computer Science, Tartu, Estonia May 2, 2016
Overview Last weeks ◮ Clojure language core ◮ Immutable data structures ◮ Clojure simple state and design This week ◮ Software transactional memory (STM) Next weeks ◮ Agents in Clojure
Software transactional memory 68/83 Transactions - Outline Software transactional memory Transactions Software transactional memory
Software transactional memory 69/83 Transactions - ACID properties ◮ long used with databases ◮ result is either abort or commit ◮ actions with ◮ atomicity – all or none effects of the action become visible ◮ consistency – data stays in consistent state after a transaction ◮ isolation – during its work the action is “completely” unaffected by other threads ◮ durability – survives system failures ◮ Clojure STM transactions satisfy atomicity, consistency and isolation ◮ durability
Software transactional memory 70/83 Transactions - Isolation levels Not as obvious as may seem. Concurrency with ANSI isolation levels 1. serializable – ideal case, as if run sequentially 2. repeatable read – new data may interfere (where clause in SQL) ◮ phantom reads – second read returns more rows than first read 3. read committed – see the results of other transactions ◮ non-repeatable reads – read data changed by another commit 4. read uncommitted – see the changes in other transactions ◮ dirty reads – read data changed in another transaction
Software transactional memory 71/83 Transactions - Isolation levels (2) There is a criticism that ANSI levels ◮ too ambiguous ◮ does not cover all possible scenarios! See A Critique of ANSI SQL Isolation Levels (1995)
Software transactional memory 72/83 Software transactional memory - Outline Software transactional memory Transactions Software transactional memory
Software transactional memory 73/83 Software transactional memory - Classical problem ◮ transfer money from bankA to bankB (defn transfer [from to amount] (reset! from (- @from amount )) (reset! to (+ @to amount ))) ◮ Works without problems with single thread (def bankA (atom 20.0)) (def bankB (atom 10.0)) (transfer bankA bankB 4.0) (println @bankA) ; -> 16.0 (println @bankB) ; -> 14.0 ◮ deref/reset! is not atomic, so two threads may end up with incorrect result ◮ classical Java solution uses locks
Software transactional memory 74/83 Software transactional memory - Atomic is not enough ◮ make both operations atomic themselves (defn transfer2 [from to amount] (swap! from (fn [v] (- v amount ))) (swap! to (fn [v] (+ v amount )))) ◮ both swap! operations are atomic themselves ◮ however, if the second one fails (by any reason), the first one has already removed money from the first account ◮ it is required to restore the original state ◮ there are more complex examples with many reads/writes
Software transactional memory 75/83 Software transactional memory - Refs ◮ mutable entities that STM operates with ◮ must be accessed within a transaction ( dosync ) ◮ @/deref – read value of a ref ◮ inside transaction has repeatable read semantics ◮ in fact uses the value that was at the start of the transaction ◮ can do that using versions (explanation follows) ◮ ref-set – sets the value inside a transaction ◮ (alter ref update-fn & args...) – changes the value inside the transaction ◮ other transactions see it only after this commits ◮ commute – special case where mutation time is not critical ◮ re-executed at the end of a transaction if needed
Software transactional memory 76/83 Software transactional memory - STM Example ◮ banking example (def bankC (ref 20.0)) (def bankD (ref 10.0)) ◮ using deref and ref-set (defn transfer3 [from to amount] (dosync (ref -set from (- @from amount )) (ref -set to (+ @to amount )))) ◮ using alter (defn transfer4 [from to amount] (dosync (alter from (fn [v] (- v amount ))) (alter to (fn [v] (+ v amount )))))
Software transactional memory 77/83 Software transactional memory - STM Diagram
Software transactional memory 78/83 Software transactional memory - STM in Clojure multiversion concurrency control (MVCC) in Clojure (implementation may differ from these semantics) ◮ every transaction has start time ◮ every ref has version – time of last commit ◮ read fails if current transaction start time < ref time, transaction restarted unless ◮ copy has been made earlier on a write ◮ or there is appropriate version in the ref history queue (see next slide) ◮ creates in-transaction copy for every ref written in transaction ◮ copies are saved to the ref on commit, if their versions (times) has not been changed since the start of the transaction
Software transactional memory 79/83 Software transactional memory - Ref history queue ◮ every ref has history queue of previous ref values (ref -history -count rf) ◮ initially history is not maintained (count=0), even when written ◮ history queue count is increased if there is failed read (see previous slide) ◮ no actual value is yet stored at this point ◮ value is stored on any following commit ◮ idea: transaction need not be restarted if there is the version in the history that was at the start of the transaction ◮ ref-min-history, ref-max-history (see ref documentation)
Software transactional memory 80/83 Software transactional memory - Write-skew ◮ value used in a transaction that is never written, but may be modified at the end of the transaction ◮ book example: daylight parameter that defines how much damage is inflicted in the battle ◮ my example: max energy available <= sum of consumed energies ◮ if max energy is rewritten while transaction changes one of consumed energies ◮ transaction uses historical value of the max energy ◮ Clojure solution: ensure command – as (ref-set a @a) but more efficient ◮ does not cause other transactions to fail
Software transactional memory 81/83 Software transactional memory - Livelock problem ◮ one long transaction ◮ many short transactions that modify a value read or written by the long one ◮ read fails because history queue needs to grow very long before long transaction may succeed ◮ write fails because confilcted value always overwritten when long transaction finishes, so it has to restart Guideline: ◮ try to reduce number of refs with potential conflict ◮ try to keep transactions short
Software transactional memory 82/83 Software transactional memory - Summary ◮ Use two levels in your program: 1. immutable state of objects 2. stored in mutable refs that are modified in transactions ◮ for example 1. game state (field state, player states) is stored in immutable structures 2. game itself is stored in mutable ref that may be modified by several threads (e.g. socket listener threads)
Recommend
More recommend