purescript-threading/test/Test/Threading.Data.RWLock.purs

204 lines
7.6 KiB
Haskell
Raw Permalink Normal View History

2024-07-16 17:55:45 +00:00
module Test.Threading.Data.RWLock where
import Prelude
import Control.Monad.Error.Class (liftMaybe)
import Data.Maybe (isNothing)
import Data.Time.Duration (Milliseconds(..))
import Data.Traversable (for_)
import Effect.Aff as Aff
import Effect.Class (liftEffect)
import Effect.Console as Console
import Effect.Exception (error)
import Effect.Ref as Ref
import Test.Spec (Spec, describe, it)
import Test.Spec.Assertions (expectError, shouldEqual)
import Threading.Data.RWLock as RWLock
spec :: Spec Unit
spec =
describe "Threading.Data.RWLock" do
describe "rwLock" do
it "creates" $ liftEffect $ void $ RWLock.rwLock 0
describe "read" do
it "reads the value" do
m <- liftEffect $ RWLock.rwLock 0
g <- RWLock.lockRead m
v <- RWLock.read g
v `shouldEqual` 0
it "throws if released" do
m <- liftEffect $ RWLock.rwLock 0
g <- RWLock.lockRead m
RWLock.release g
expectError $ RWLock.read g
describe "write" do
it "writes the value" do
m <- liftEffect $ RWLock.rwLock 0
g <- RWLock.lockWrite m
liftEffect $ RWLock.write g 1
v <- RWLock.read g
v `shouldEqual` 1
it "throws if released" do
m <- liftEffect $ RWLock.rwLock 0
g <- RWLock.lockWrite m
RWLock.release g
expectError $ liftEffect $ RWLock.write g 1
describe "get" do
it "yields immediately when unlocked" do
m <- liftEffect $ RWLock.rwLock 0
val <- RWLock.get m
val `shouldEqual` 0
it "blocks until unlocked" do
m <- liftEffect $ RWLock.rwLock 0
g <- RWLock.lockWrite m
getFiber <- Aff.forkAff $ RWLock.get m
liftEffect $ RWLock.write g 1
RWLock.release g
read <- Aff.joinFiber getFiber
read `shouldEqual` 1
describe "put" do
it "yields immediately when unlocked" do
m <- liftEffect $ RWLock.rwLock 0
RWLock.put m 1
val <- RWLock.get m
val `shouldEqual` 1
it "blocks until unlocked" do
m <- liftEffect $ RWLock.rwLock 0
g <- RWLock.lockWrite m
getFiber <- Aff.forkAff $ RWLock.put m 2
liftEffect $ RWLock.write g 1
RWLock.release g
Aff.joinFiber getFiber
val <- RWLock.get m
val `shouldEqual` 2
describe "modify" do
it "yields immediately when unlocked" do
m <- liftEffect $ RWLock.rwLock 0
val <- RWLock.modify m (_ + 1)
val `shouldEqual` 1
it "blocks until unlocked" do
m <- liftEffect $ RWLock.rwLock 0
g <- RWLock.lockWrite m
getFiber <- Aff.forkAff $ RWLock.modify m (_ * 10)
liftEffect $ RWLock.write g 1
RWLock.release g
val <- Aff.joinFiber getFiber
val `shouldEqual` 10
describe "lockRead" do
it "yields immediately when unlocked" do
m <- liftEffect $ RWLock.rwLock 0
void $ RWLock.lockRead m
it "blocks when write locked" do
m <- liftEffect $ RWLock.rwLock 0
g <- RWLock.lockWrite m
finished <- liftEffect $ Ref.new false
fiber <- Aff.forkAff do
void $ RWLock.lockRead m
void $ liftEffect $ Ref.write true finished
Aff.delay $ Milliseconds 5.0
f1 <- liftEffect $ Ref.read finished
f1 `shouldEqual` false
RWLock.release g
Aff.joinFiber fiber
f2 <- liftEffect $ Ref.read finished
f2 `shouldEqual` true
it "does not block when read locked" do
m <- liftEffect $ RWLock.rwLock 0
void $ Aff.forkAff $ void $ RWLock.lockRead m
void $ Aff.forkAff $ void $ RWLock.lockRead m
void $ RWLock.lockRead m
n <- RWLock.get m
n `shouldEqual` 0
it "blocks when write locked" do
m <- liftEffect $ RWLock.rwLock 0
g <- RWLock.lockWrite m
finished <- liftEffect $ Ref.new false
fiber <- Aff.forkAff do
g' <- RWLock.lockRead m
liftEffect $ Ref.write true finished
RWLock.read g'
liftEffect $ RWLock.write g 1
f <- liftEffect $ Ref.read finished
f `shouldEqual` false
RWLock.release g
n <- Aff.joinFiber fiber
n `shouldEqual` 1
describe "lockWrite" do
it "yields immediately when unlocked" do
m <- liftEffect $ RWLock.rwLock 0
void $ RWLock.lockWrite m
it "blocks when write locked" do
m <- liftEffect $ RWLock.rwLock 0
g <- RWLock.lockWrite m
finished <- liftEffect $ Ref.new false
fiber <- Aff.forkAff do
void $ RWLock.lockWrite m
void $ liftEffect $ Ref.write true finished
Aff.delay $ Milliseconds 5.0
f1 <- liftEffect $ Ref.read finished
f1 `shouldEqual` false
RWLock.release g
Aff.joinFiber fiber
f2 <- liftEffect $ Ref.read finished
f2 `shouldEqual` true
it "blocks when read locked" do
m <- liftEffect $ RWLock.rwLock 0
g <- RWLock.lockRead m
finished <- liftEffect $ Ref.new false
fiber <- Aff.forkAff do
void $ RWLock.lockWrite m
void $ liftEffect $ Ref.write true finished
Aff.delay $ Milliseconds 5.0
f1 <- liftEffect $ Ref.read finished
f1 `shouldEqual` false
RWLock.release g
Aff.joinFiber fiber
f2 <- liftEffect $ Ref.read finished
f2 `shouldEqual` true
it "locks are acquired in the order they were requested" do
m <- liftEffect $ RWLock.rwLock 0
g <- RWLock.lockWrite m
a <- Aff.forkAff $ RWLock.modify_ m (_ + 1) -- 1
b <- Aff.forkAff $ RWLock.modify_ m (_ * 10) -- 10
c <- Aff.forkAff $ RWLock.modify_ m (_ + 10) -- 20
d <- Aff.forkAff $ RWLock.modify_ m (_ * 10) -- 200
RWLock.release g
for_ [ a, b, c, d ] Aff.joinFiber
n <- RWLock.get m
n `shouldEqual` 200
describe "tryLockWrite" do
it "returns Just when unlocked" do
m <- liftEffect $ RWLock.rwLock 0
void $ liftMaybe (error $ "RWLock.tryLockWrite returned Nothing on new mutex") =<< RWLock.tryLockWrite m
it "returns Nothing when locked" do
m <- liftEffect $ RWLock.rwLock 0
_ <- liftMaybe (error $ "RWLock.tryLockWrite returned Nothing on new mutex") =<< RWLock.tryLockWrite m
g <- RWLock.tryLockWrite m
isNothing g `shouldEqual` true
it "returns Just after release" do
m <- liftEffect $ RWLock.rwLock 0
g <- liftMaybe (error $ "RWLock.tryLockWrite returned Nothing on new mutex") =<< RWLock.tryLockWrite m
RWLock.release g
void $ liftMaybe (error $ "RWLock.tryLockWrite returned Nothing after lock released") =<< RWLock.tryLockWrite m
describe "locked" do
it "Unlocked" do
m <- liftEffect $ RWLock.rwLock 0
l <- liftEffect $ RWLock.locked m
l `shouldEqual` RWLock.Unlocked
it "LockedWriting" do
m <- liftEffect $ RWLock.rwLock 0
_ <- liftMaybe (error $ "RWLock.tryLockWrite returned Nothing on new mutex") =<< RWLock.tryLockWrite m
l <- liftEffect $ RWLock.locked m
l `shouldEqual` RWLock.LockedWriting
it "LockedReading" do
m <- liftEffect $ RWLock.rwLock 0
_ <- liftMaybe (error $ "RWLock.tryLockRead returned Nothing on new mutex") =<< RWLock.tryLockRead m
l <- liftEffect $ RWLock.locked m
l `shouldEqual` RWLock.LockedReading
it "Unlocked after lock released" do
m <- liftEffect $ RWLock.rwLock 0
g <- liftMaybe (error $ "RWLock.tryLockWrite returned Nothing on new mutex") =<< RWLock.tryLockWrite m
RWLock.release g
l' <- liftEffect $ RWLock.locked m
l' `shouldEqual` RWLock.Unlocked