linear-locks-0.1.0.0: test/Test/LinearLocks/LockSetSpec.hs
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE QualifiedDo #-}
{-# LANGUAGE NoFieldSelectors #-}
module Test.LinearLocks.LockSetSpec where
import Control.Functor.Linear qualified as L
import Control.Monad.IO.Class.Linear qualified as L
import Data.Vector.Unboxed qualified as VU
import LinearLocks
import LinearLocks.Internal.LockSet qualified as Internal
import LinearLocks.Internal.Mutex qualified as Internal
import LinearLocks.Mutex qualified as Mutex
import LinearLocks.Mutex.Strict qualified as StrictMutex
import LinearLocks.RWLock qualified as RWLock
import Prelude.Linear (Ur (..))
import Test.Hspec.Expectations.Pretty (shouldNotBe, shouldThrow)
import "tasty-hunit-compat" Test.Tasty.HUnit
-- | Doctests
--
-- >>> :{
-- >>> unit_locks_in_a_set_must_have_the_same_level :: IO ()
-- >>> unit_locks_in_a_set_must_have_the_same_level = do
-- >>> m1 <- Mutex.new 2 "hello"
-- >>> m2 <- Mutex.new 3 "world"
-- >>> set <- newLockSet (m1, m2)
-- >>> pure ()
-- >>> :}
-- ...
-- ... • Couldn't match type ‘2’ with ‘3’
-- ... arising from a use of ‘newLockSet’
-- ...
unit_read_lock_set :: IO ()
unit_read_lock_set = do
m1 <- Mutex.new 0 "m1"
m2 <- Mutex.new 0 "m2"
m3 <- Mutex.new 0 "m3"
set <- newLockSet (m1, m2, m3)
lockScope \key -> L.do
((mg1, mg2, mg3), key) <- acquireMany key set
(Ur str1, mg1) <- Mutex.read mg1
(Ur str2, mg2) <- Mutex.read mg2
(Ur str3, mg3) <- Mutex.read mg3
L.liftSystemIO do
str1 @?= "m1"
str2 @?= "m2"
str3 @?= "m3"
Mutex.release mg1
Mutex.release mg2
Mutex.release mg3
dropKeyAndReturn key ()
unit_write_lock_set :: IO ()
unit_write_lock_set = do
m1 <- Mutex.new 0 "m1"
m2 <- Mutex.new 0 "m2"
m3 <- Mutex.new 0 "m3"
set <- newLockSet (m3, m2, m1)
lockScope \key -> L.do
((mg3, mg2, mg1), key) <- acquireMany key set
mg3 <- Mutex.write mg3 "m3 updated"
mg2 <- Mutex.write mg2 "m2 updated"
mg1 <- Mutex.write mg1 "m1 updated"
Mutex.release mg3
Mutex.release mg2
Mutex.release mg1
dropKeyAndReturn key ()
lockScope \key -> L.do
((mg3, mg2, mg1), key) <- acquireMany key set
(Ur str3, mg3) <- Mutex.read mg3
(Ur str2, mg2) <- Mutex.read mg2
(Ur str1, mg1) <- Mutex.read mg1
L.liftSystemIO do
str3 @?= "m3 updated"
str2 @?= "m2 updated"
str1 @?= "m1 updated"
Mutex.release mg3
Mutex.release mg2
Mutex.release mg1
dropKeyAndReturn key ()
unit_assigns_unique_lock_ids :: IO ()
unit_assigns_unique_lock_ids = do
m1 <- Mutex.new 0 ""
m2 <- Mutex.new 0 ""
m3 <- Mutex.new 0 ""
m1.id `shouldNotBe` m2.id
m2.id `shouldNotBe` m3.id
m1.id `shouldNotBe` m3.id
unit_throws_when_lock_set_contains_duplicates :: IO ()
unit_throws_when_lock_set_contains_duplicates = do
m1 <- Mutex.new 0 ""
m2 <- Mutex.new 0 ""
newLockSet (m1, m2, m1) `shouldThrow` \(err :: IOError) -> err == userError "LockSet: duplicate locks are not allowed"
unit_sorts_locks_deterministically :: IO ()
unit_sorts_locks_deterministically = do
m1 <- Mutex.new 0 ""
m2 <- Mutex.new 0 ""
m3 <- Mutex.new 0 ""
newLockSet (m1, m2, m3) >>= \set -> sortedIndices set @?= VU.fromList [0, 1, 2]
newLockSet (m2, m1, m3) >>= \set -> sortedIndices set @?= VU.fromList [1, 0, 2]
newLockSet (m3, m1, m2) >>= \set -> sortedIndices set @?= VU.fromList [1, 2, 0]
newLockSet (m1, m3, m2) >>= \set -> sortedIndices set @?= VU.fromList [0, 2, 1]
newLockSet (m2, m3, m1) >>= \set -> sortedIndices set @?= VU.fromList [2, 0, 1]
newLockSet (m3, m2, m1) >>= \set -> sortedIndices set @?= VU.fromList [2, 1, 0]
where
sortedIndices :: forall set. LockSet set -> VU.Vector Int
sortedIndices (Internal.MkLockSet _ indices) = VU.map (\(Internal.LockSetIndex i) -> i) indices
unit_sets_can_have_mixed_lock_types :: IO ()
unit_sets_can_have_mixed_lock_types = do
m1 <- StrictMutex.new 0 "hello"
m2 <- Mutex.new @Int 0 99
m3 <- RWLock.new 0 True
set <- newLockSet (m1, m2, RWLock.AsRead (m3))
lockScope \key -> L.do
((g1, g2, g3), key) <- acquireMany key set
(Ur res1, g1) <- StrictMutex.read g1
(Ur res2, g2) <- Mutex.read g2
(Ur res3, g3) <- RWLock.read g3
L.liftSystemIO do
res1 @?= "hello"
res2 @?= 99
res3 @?= True
StrictMutex.release g1
Mutex.release g2
RWLock.releaseRead g3
dropKeyAndReturn key ()