bluefin-internal 0.9.1.0 → 0.9.2.0
raw patch · 6 files changed
+56/−14 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Bluefin.Internal.Vault: delete :: Key a -> Vault -> Vault
Files
- CHANGELOG.md +8/−0
- bluefin-internal.cabal +1/−1
- src/Bluefin/Internal.hs +9/−6
- src/Bluefin/Internal/CloneableHandle.hs +4/−4
- src/Bluefin/Internal/Vault.hs +7/−0
- test/Main.hs +27/−3
CHANGELOG.md view
@@ -1,3 +1,11 @@+# 0.9.2.0++* Bug fix: release `Reader` Vault keys when their handler scope exits.++* Make `Vault`'s `Key` type role representational. This is+ technically a PVP violation but since it's fixing a type safety bug+ we're not going to release a major version for it.+ # 0.9.1.0 * Add `yieldToPureList`
bluefin-internal.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: bluefin-internal-version: 0.9.1.0+version: 0.9.2.0 license: MIT license-file: LICENSE author: Tom Ellis
src/Bluefin/Internal.hs view
@@ -22,8 +22,8 @@ oneWayCoercion, trans3D, unsafeCoercionOfOneWayCoercion,- unsafeOneWayCoercible, unsafeOneWayCoerce,+ unsafeOneWayCoercible, ) import Bluefin.Internal.Vault (Vault) import Bluefin.Internal.Vault qualified as Vault@@ -1656,11 +1656,14 @@ (forall e. Reader r e -> Eff (e :& es) a) -> Eff es a runReader r f = do- k <- UnsafeMkEff $ \vault -> do- k <- Vault.newKey- modifyIORef' vault (\v -> Vault.insert k r v)- pure k- makeOp (f (MkReader k))+ bracket+ ( UnsafeMkEff $ \vault -> do+ k <- Vault.newKey+ modifyIORef' vault (\v -> Vault.insert k r v)+ pure k+ )+ (\k -> UnsafeMkEff $ \vault -> modifyIORef' vault (Vault.delete k))+ (\k -> makeOp (f (MkReader k))) -- | Read the value. Note that @ask@ has the property that these two -- operations are always equivalent:
src/Bluefin/Internal/CloneableHandle.hs view
@@ -117,10 +117,10 @@ hcHandleReader :: (CloneableHandle h) => HandleCloner (HandleReader h) (HandleReader h) e hcHandleReader = MkHandleCloner $ \hr k -> do- h <- askHandle hr- cloneHandleClass h $ \h' -> do- runHandleReader h' $ \hr' -> do- useImplIn k (mapHandle hr')+ asksHandle hr $ \h -> do+ cloneHandleClass h $ \h' -> do+ runHandleReader h' $ \hr' -> do+ useImplIn k (mapHandle hr') instance (TypeError (Text "Coroutine cannot be cloned. Perhaps you want an STM channel?")) =>
src/Bluefin/Internal/Vault.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE RoleAnnotations #-}+ module Bluefin.Internal.Vault ( module Bluefin.Internal.Vault, Vault,@@ -18,6 +20,8 @@ type Key :: Type -> Type newtype Key a = MkKey (Vault.Key Any) +type role Key representational+ fromMine :: Key a -> Vault.Key a fromMine (MkKey k) = unsafeCoerce k @@ -35,3 +39,6 @@ insert :: Key a -> a -> Vault -> Vault insert = Vault.insert . fromMine++delete :: Key a -> Vault -> Vault+delete = Vault.delete . fromMine
test/Main.hs view
@@ -4,8 +4,11 @@ module Main (main) where import Bluefin.Internal+import Bluefin.Internal.Vault qualified as Vault import Control.Monad (forever, when) import Data.Foldable (for_)+import Data.IORef (readIORef)+import Data.Maybe (isNothing) import Test.GeneralBracket (test_generalBracket) import Test.SpecH (SpecH, assertEqual, runSpecH) import Prelude hiding (break, read)@@ -44,6 +47,7 @@ ([20, 30], "Hello") test_localInHandler y+ test_readerCleanup y test_generalBracket io y test_streamConsumeReader y test_streamConsumeHandleReader y@@ -94,6 +98,26 @@ for_ as (yield y) pure r +test_readerCleanup :: (e <: es) => SpecH e -> Eff es ()+test_readerCleanup y = runReader @Int 1 $ \outer -> do+ for_ [False, True] $ \abort -> do+ key <- withEarlyReturn $ \ex ->+ runReader @Int 2 $ \(MkReader key) ->+ if abort then returnEarly ex key else pure key+ do+ -- Use the internals to check a property that cannot be tested without them.+ cleanedUp <- UnsafeMkEff $ \vault -> do+ contents <- readIORef vault+ pure (isNothing (Vault.lookup key contents))+ assertEqual+ y+ "Reader key released on normal and exceptional exit"+ True+ cleanedUp+ do+ outerValue <- ask outer+ assertEqual y "Outer Reader survives inner cleanup" 1 outerValue+ test_localInHandler :: (e <: es) => SpecH e -> Eff es () test_localInHandler y = runReader "global" $ \re -> forEach@@ -143,7 +167,7 @@ ) -- localHandle run in one branch of a streamConsume should not affect--- askHandle in the other branch.+-- asksHandle in the other branch. test_streamConsumeHandleReader :: (e <: es) => SpecH e -> Eff es () test_streamConsumeHandleReader spech = do runConstEffect @Int 0 $ \ce ->@@ -152,8 +176,8 @@ ( \y -> do let s = yield y () let check i = do- MkConstEffect i' <- askHandle r- assertEqual spech "HandleReader local" i i'+ asksHandle r $ \(MkConstEffect i') -> do+ assertEqual spech "HandleReader local" i i' check 0 s check 0