Online Book Reader

Home Category

Beautiful Code [269]

By Root 5196 0
ensures that no other thread can see a state in which money has been deposited in to but not yet withdrawn from from.

Isolation

During a call atomically act, the action act is completely unaffected by other threads. It is as if act takes a snapshot of the state of the world when it begins running, and then executes against that snapshot.

Here is a simple execution model for atomically. Suppose there is a single, global lock. Then atomically act grabs the lock, performs the action act, and releases the lock. This implementation brutally ensures that no two atomic blocks can be executed simultaneously, and thereby ensures atomicity.

There are two problems with this model. First, it does not ensure isolation at all: while one thread is accessing an IORef inside an atomic block (holding the Global Lock), there is nothing to stop another thread from writing the same IORef directly (i.e., outside atomically, without holding the Global Lock), thereby destroying the isolation guarantee. Second, performance is dreadful because every atomic block is serialized even if no actual interference is possible.

I will discuss the second problem shortly, in the section "Implementing Transactional Memory." Meanwhile, the first objection is easily addressed with the type system. We give atomically the following type:

atomically :: STM a -> IO a

The argument of atomically is an action of type STM a. An STM action is like an IO action, in that it can have side effects, but the range of side effects for STM actions is much smaller. The main thing you can do in an STM action is to read or write a transactional variable, of type (TVar a), much as we could read or write IORefs in an IO action:[##]

[##] The nomenclature is inconsistent here: it would be more consistent to use either TVar and IOVar,or TRef and IORef. But it would be disruptive to change at this stage; for better or worse, we have TVar and IORef.

readTVar :: TVar a -> STM a

writeTVar :: TVar a -> a -> STM ( )

STM actions can be composed together with the same do notation as IO actions—the do notation is overloaded to work on both types, as is return.[***] Here, for example, is the code for withdraw:

[***] This overloading of do notation and return is not an ad hoc trick to support IO and STM. Rather, IO and STM are both examples of a common pattern, called a monad (described in P. L. Wadler, "The essence of functional programming," 20th ACM Symposium on Principles of Programming Languages [POPL '92], Albuquerque, pp. 1–14, ACM, January 1992), and the overloading is achieved by expressing that common pattern using Haskell's very general type-class mechanism (described in P. L. Wadler and S. Blott, "How to make ad-hoc polymorphism less ad hoc," Proc 16th ACM Symposium on Principles of Programming Languages, Austin, Texas, ACM, January 1989; and Simon Peyton Jones, Mark Jones, and Erik Meijer, "Type classes: an exploration of the design space," J. Launch-bury, editor, Haskell workshop, Amsterdam, 1997).

type Account = TVar Int

withdraw :: Account -> Int -> STM ( )

withdraw acc amount

= do { bal <- readTVar acc

; writeTVar acc (bal - amount) }

We represent an Account by a transactional variable containing an Int for the account balance. Then withdraw is an STM action that decrements the balance in the account by amount.

To complete the definition of transfer, we can define deposit in terms of withdraw:

deposit :: Account -> Int -> STM ( )

deposit acc amount = withdraw acc (- amount)

Notice that transfer ultimately performs four primitive read/write actions: a read and then write on account to, followed by a read and then write on account from. These four actions execute atomically, and that meets the specification given at the start of the section "A Simple Example: Bank Accounts."

The type system neatly prevents us from reading or writing a TVar outside of a transaction. For example, suppose we tried this:

bad :: Account -> IO ( )

bad acc = do { hPutStr stdout "Withdrawing..."

; withdraw acc 10 }

This program is rejected

Return Main Page Previous Page Next Page

®Online Book Reader