import IO
import Data.IORef
import Control.Concurrent
import Control.Concurrent.STM
import Control.Exception

{- Slide 19 -}
main1 = do {putStrLn (reverse "yes");
            putStrLn "no" }

{- Slide 20 -} 
main2 = do { r <-newIORef 0;
            incR r;
            s <- readIORef r;
            print s }

incR :: IORef Int -> IO ()
incR r = do { v <- readIORef r;
              writeIORef r (v+1)}


{- Slide 21 -}
main3 = do { r <- newIORef 0;
             forkIO (incR r);        -- A race condition.  Depending on timing, final value of r can be 1 or 2.
             incR r;
--             threadDelay 0;
             s <- readIORef r;
             print s }

{- The code Slide 22 doesn't type check 
main4 = do { 
   r <- newIORef 0;
   forkIO (atomically (incR r));
   atomically (incR r);
   s <- readIORef r;
   print s }
-}


{- Slide 23 -}
incT :: TVar Int -> STM ()
incT r = do { v <- readTVar r;
              writeTVar r (v+1) } 

main5   = do { r <- atomically (newTVar 0);
               forkIO (atomically (incT r));
               atomically (incT r);   
               threadDelay 5;
               v <- atomically (readTVar r);
               print v }

{- Slide 25 -}
incT2 :: TVar Int -> STM ()
incT2 r = do { incT r; incT r }
 
main6 :: IO ()
main6 = do { r <- atomically (newTVar 0);
             forkIO (atomically (incT2 r));
             atomically (incT2 r);   
             threadDelay 5;
             v <- atomically (readTVar r);
             print v 
           }

{- Slide 28 -}
withdraw :: TVar Int -> Int -> STM ()
withdraw acc n = do {
   bal <- readTVar acc;
   if bal < n 
      then retry
      else writeTVar acc (bal-n) }

deposit :: TVar Int -> Int -> STM ()
deposit acc n = do {
  bal <- readTVar acc;
  writeTVar acc (bal + n) }

{- Slide 29 -}
withdraw2 a1 a2 =  atomically (do { withdraw a1 3;
                                    withdraw a2 7 })

{- Slide 31 -}
withdraw3 a1 a2 b = 
  atomically (do { 
    withdraw a1 3	
     `orElse` 
    withdraw a2 3; 
    deposit b 3 })

  
{- Slide 32 -}
transfer :: TVar Int -> TVar Int -> TVar Int -> STM ()
transfer a1 a2 b = do	
  { withdraw a1 3
     `orElse`
    withdraw a2 3;
    deposit b 3 }

transfer2 a1 a2 a3 a4 b = atomically
        (transfer a1 a2 b
		`orElse`
	 transfer a3 a4 b)

{- Slide 36 -}
newAccount :: STM (TVar Int)
newAccount = 
  do { v <- newTVar 0; 
       always (do { cts <- readTVar v;
                    return (cts >= 0) });
       return v }
