{-# LANGUAGE ScopedTypeVariables #-} module Serializable (testSerializable) where import Common import Control.Concurrent import Control.Exception as E import Data.IORef initCounter :: Connection -> IO () initCounter conn = do 0 <- execute_ conn "DROP TABLE IF EXISTS testSerializableCounter;\ \ CREATE TABLE testSerializableCounter (n INT)" 1 <- execute_ conn "INSERT INTO testSerializableCounter VALUES (0)" return () getCounter :: Connection -> IO Int getCounter conn = do [Only n] <- query_ conn "SELECT n FROM testSerializableCounter" return n putCounter :: Connection -> Int -> IO () putCounter conn n = do 1 <- execute conn "UPDATE testSerializableCounter SET n=?" (Only n) return () testSerializable :: TestEnv -> Test testSerializable TestEnv{..} = TestCase $ withConn $ \conn2 -> do initCounter conn attemptCounter <- newIORef (0 :: Int) readyToBother <- newEmptyMVar bothered <- newEmptyMVar finished <- newEmptyMVar _ <- forkIO $ do withTransactionSerializable conn2 $ do modifyIORef attemptCounter (+1) n <- getCounter conn2 True <- tryPutMVar readyToBother () readMVar bothered putCounter conn2 (n+1) putMVar finished () takeMVar readyToBother withTransactionSerializable conn $ do n <- getCounter conn putCounter conn (n+1) True <- tryPutMVar bothered () takeMVar finished ac <- readIORef attemptCounter assertEqual "attemptCounter" 2 ac ok <- E.catch (do withTransactionSerializable conn (fail "Whoops") return False) (\(_ :: IOException) -> return True) assertBool "Exceptions (besides serialization failure) should be\ \ propagated through withTransactionSerializable" ok -- Make sure transaction isn't dangling 1 <- execute_ conn "UPDATE testSerializableCounter SET n=12345" 0 <- execute_ conn "ROLLBACK" -- This prints "NOTICE: there is no transaction in progress" [Only (12345 :: Int)] <- query_ conn "SELECT n FROM testSerializableCounter" return ()