diff --git a/.github/workflows/haskell-ci.yml b/.github/workflows/haskell-ci.yml index 4fe0b5c6..ce539a3f 100644 --- a/.github/workflows/haskell-ci.yml +++ b/.github/workflows/haskell-ci.yml @@ -233,6 +233,12 @@ jobs: allow-newer: fused-effects:* allow-newer: polysemy:* + source-repository-package + type: git + location: https://github.com/arybczak/strict-mutable.git + tag: a60db945dcc3283da2c5d45dadf423fadc2ff745 + subdir: strict-mutable-base + package effectful-core ghc-options: -Werror diff --git a/cabal.project b/cabal.project index 7fe85a88..a661452a 100644 --- a/cabal.project +++ b/cabal.project @@ -9,3 +9,10 @@ allow-newer: cleff:* , freer-simple:* , fused-effects:* , polysemy:* + +-- strict-mutable-base 2.0.0.0 +source-repository-package + type: git + location: https://github.com/arybczak/strict-mutable.git + tag: a60db945dcc3283da2c5d45dadf423fadc2ff745 + subdir: strict-mutable-base diff --git a/effectful-core/CHANGELOG.md b/effectful-core/CHANGELOG.md index 876a7cc6..37d103de 100644 --- a/effectful-core/CHANGELOG.md +++ b/effectful-core/CHANGELOG.md @@ -9,6 +9,7 @@ * Tighten pre-requisites for `unconsEnv` and `unreplaceEnv`. * Add `localLendBorrow` to `Effectful.Dispatch.Dynamic`. * Require `primitive` >= 0.9.0.0. +* Require `strict-mutable-base` >= 2.0.0.0. * **Bugfixes**: - `restoreStorageData` no longer shrinks the capacity of the storage, which could result in out of bounds reads when out of date references to the diff --git a/effectful-core/effectful-core.cabal b/effectful-core/effectful-core.cabal index 7f615fe7..c0ad54ca 100644 --- a/effectful-core/effectful-core.cabal +++ b/effectful-core/effectful-core.cabal @@ -75,7 +75,7 @@ library , mtl >= 2.2.1 , monad-control >= 1.0.3 , primitive >= 0.9.0.0 - , strict-mutable-base >= 1.1.0.0 + , strict-mutable-base >= 2.0.0.0 && < 3 , transformers-base >= 0.4.6 , unliftio-core >= 0.2.0.1 diff --git a/effectful-core/src/Effectful/Internal/Env.hs b/effectful-core/src/Effectful/Internal/Env.hs index 21a458ea..e13c7885 100644 --- a/effectful-core/src/Effectful/Internal/Env.hs +++ b/effectful-core/src/Effectful/Internal/Env.hs @@ -58,7 +58,7 @@ module Effectful.Internal.Env import Control.Monad import Control.Monad.Primitive -import Data.IORef.Strict +import Data.IORef.Strict qualified as S import Data.Primitive.PrimArray import Data.Primitive.SmallArray import Data.Primitive.Types @@ -97,7 +97,7 @@ type role Env nominal data Env (es :: [Effect]) = Env { offset :: !Int , refs :: !(PrimArray Ref) - , storage :: !(IORef' Storage) + , storage :: !(S.IORef Storage) } -- | Reference to the effect in 'Storage'. @@ -180,18 +180,18 @@ data StorageData = StorageData -- | Clone the storage to use it in a different thread. -- -- @since 2.7.0.0 -cloneStorage :: HasCallStack => IORef' Storage -> IO (IORef' Storage) +cloneStorage :: HasCallStack => S.IORef Storage -> IO (S.IORef Storage) cloneStorage storage0 = do - Storage version storageData0 <- readIORef' storage0 + Storage version storageData0 <- S.readIORef storage0 storageData <- copyStorageData storageData0 - storage <- newIORef' $ Storage version storageData + storage <- S.newIORef $ Storage version storageData relinkStorageData storageData storage pure storage -- | Replace the storage of the environment. -- -- @since 2.7.0.0 -replaceStorage :: Env es -> IORef' Storage -> IO (Env es) +replaceStorage :: Env es -> S.IORef Storage -> IO (Env es) replaceStorage (Env offset refs _) storage = pure $ Env offset refs storage -- | Make a shallow copy of the 'StorageData'. @@ -214,7 +214,7 @@ copyStorageData (StorageData storageSize vs0 es0 fs0) = do -- | Relink effects in the storage data to the given storage. -- -- @since 2.7.0.0 -relinkStorageData :: HasCallStack => StorageData -> IORef' Storage -> IO () +relinkStorageData :: HasCallStack => StorageData -> S.IORef Storage -> IO () relinkStorageData (StorageData storageSize _ es fs) storage = go storageSize where go = \case @@ -234,7 +234,7 @@ relinkStorageData (StorageData storageSize _ es fs) storage = go storageSize -- @since 2.7.0.0 backupStorageData :: HasCallStack => Env es -> IO StorageData backupStorageData env = do - storageData <- copyStorageData . (.data_) =<< readIORef' env.storage + storageData <- copyStorageData . (.data_) =<< S.readIORef env.storage -- Relinking to the same storage might seem weird, but relinkers need to run -- and make a copy of mutable data associated with statically dispatched -- effects if appropriate. @@ -249,7 +249,7 @@ backupStorageData env = do -- @since 2.5.0.0 restoreStorageData :: HasCallStack => StorageData -> Env es -> IO () restoreStorageData (StorageData newSize vs1 es1 fs1) env = do - Storage version (StorageData oldSize vs0 es0 fs0) <- readIORef' env.storage + Storage version (StorageData oldSize vs0 es0 fs0) <- S.readIORef env.storage when (newSize /= oldSize) $ do error $ "newSize (" ++ show newSize ++ ") /= oldSize (" ++ show oldSize ++ ")" -- Since the time the backup was made the storage might've been grown by @@ -285,7 +285,7 @@ restoreStorageData (StorageData newSize vs1 es1 fs1) env = do copySmallMutableArray fs 0 fs1 0 newSize pure fs else pure fs1 - writeIORef' env.storage $ Storage version (StorageData newSize vs es fs) + S.writeIORef env.storage $ Storage version (StorageData newSize vs es fs) ---------------------------------------- -- Relinker @@ -328,7 +328,7 @@ type family EffectRep (d :: Dispatch) :: Effect -> Type emptyEnv :: HasCallStack => IO (Env '[]) emptyEnv = Env 0 <$> (unsafeFreezePrimArray =<< newPrimArray 0) - <*> (newIORef' =<< emptyStorage) + <*> (S.newIORef =<< emptyStorage) -- | Clone the environment to use it in a different thread. cloneEnv :: HasCallStack => Env es -> IO (Env es) @@ -504,7 +504,7 @@ getLocation => Env es -> IO (Int, SmallMutableArray RealWorld AnyEffect) getLocation (Env offset refs storage) = do - Storage _ (StorageData _ vs es _) <- readIORef' storage + Storage _ (StorageData _ vs es _) <- S.readIORef storage storageVersion <- readPrimArray vs ref -- If version of the reference is different than version in the storage, it -- means that the effect in the storage is not the one that was initially @@ -534,13 +534,13 @@ emptyStorage = Storage initialVersion <$> storageData -- | Insert an effect into the storage and return its reference. insertEffect :: HasCallStack - => IORef' Storage + => S.IORef Storage -> EffectRep (DispatchOf e) e -- ^ The representation of the effect. -> Relinker (EffectRep (DispatchOf e)) e -> IO Ref insertEffect storage e f = do - Storage version (StorageData size vs0 es0 fs0) <- readIORef' storage + Storage version (StorageData size vs0 es0 fs0) <- S.readIORef storage len0 <- getSizeofSmallMutableArray es0 case size `compare` len0 of GT -> error $ "size (" ++ show size ++ ") > len0 (" ++ show len0 ++ ")" @@ -548,7 +548,7 @@ insertEffect storage e f = do writePrimArray vs0 size version writeSmallArray' es0 size (toAnyEffect e) writeSmallArray' fs0 size (toAnyRelinker f) - writeIORef' storage $ + S.writeIORef storage $ Storage (bumpVersion version) (StorageData (size + 1) vs0 es0 fs0) pure $ Ref size version EQ -> do @@ -566,15 +566,15 @@ insertEffect storage e f = do writePrimArray vs size version writeSmallArray' es size (toAnyEffect e) writeSmallArray' fs size (toAnyRelinker f) - writeIORef' storage $ + S.writeIORef storage $ Storage (bumpVersion version) (StorageData (size + 1) vs es fs) pure $ Ref size version -- | Given a reference to an effect from the top of the stack, delete it from -- the storage. -deleteEffect :: HasCallStack => IORef' Storage -> Ref -> IO () +deleteEffect :: HasCallStack => S.IORef Storage -> Ref -> IO () deleteEffect storage (Ref ref version) = do - Storage currentVersion (StorageData size vs es fs) <- readIORef' storage + Storage currentVersion (StorageData size vs es fs) <- S.readIORef storage when (ref /= size - 1) $ do error $ "ref (" ++ show ref ++ ") /= size - 1 (" ++ show (size - 1) ++ ")" storageVersion <- readPrimArray vs ref @@ -584,10 +584,10 @@ deleteEffect storage (Ref ref version) = do writePrimArray vs ref undefinedVersion writeSmallArray es ref undefinedEffect writeSmallArray fs ref undefinedRelinker - writeIORef' storage $ Storage currentVersion (StorageData (size - 1) vs es fs) + S.writeIORef storage $ Storage currentVersion (StorageData (size - 1) vs es fs) -- | Relink the environment to use the new storage. -relinkEnv :: IORef' Storage -> Env es -> IO (Env es) +relinkEnv :: S.IORef Storage -> Env es -> IO (Env es) relinkEnv storage (Env offset refs _) = pure $ Env offset refs storage -- | Version of an unused slot. diff --git a/effectful-core/src/Effectful/Internal/Unlift.hs b/effectful-core/src/Effectful/Internal/Unlift.hs index 4548cf21..947b3149 100644 --- a/effectful-core/src/Effectful/Internal/Unlift.hs +++ b/effectful-core/src/Effectful/Internal/Unlift.hs @@ -22,7 +22,7 @@ module Effectful.Internal.Unlift ) where import Control.Concurrent -import Control.Concurrent.MVar.Strict +import Control.Concurrent.MVar.Strict qualified as S import Control.Monad import Data.Coerce import Data.IntMap.Strict qualified as IM @@ -163,10 +163,10 @@ ephemeralConcLimitedUnlift es0 uses k = do -- use. This can't be done from inside the callback as the environment might -- have already changed by then. esTemplate <- cloneEnv es0 - mvUses <- newMVar' uses + mvUses <- S.newMVar uses let getEs = myThreadId >>= \case tid | tid0 == tid -> pure es0 - _ -> modifyMVar' mvUses $ \case + _ -> S.modifyMVar mvUses $ \case 0 -> error $ "Number of permitted calls (" ++ show uses ++ ") to the unlifting " ++ "function in other threads was exceeded. Please increase the limit " @@ -217,18 +217,18 @@ persistentConcUnlift es0 cleanUp threads k = do -- use. This can't be done from inside the callback as the environment might -- have already changed by then. esTemplate <- cloneEnv es0 - mvEntries <- newMVar' $ ThreadEntries threads IM.empty + mvEntries <- S.newMVar $ ThreadEntries threads IM.empty let getEs = myThreadId >>= \case tid | tid0 == tid -> pure es0 tid -> do - te0 <- readMVar' mvEntries + te0 <- S.readMVar mvEntries let wkTid = weakThreadId tid case wkTid `IM.lookup` te0.entries of Just wkEs -> getWkTidEnv wkEs -- If the environment is not in the map, there is no point checking - -- again within modifyMVar' below, because this is the only thread + -- again within modifyMVar below, because this is the only thread -- that can put it there. - Nothing -> modifyMVar' mvEntries $ \te -> case te.capacity of + Nothing -> S.modifyMVar mvEntries $ \te -> case te.capacity of 0 -> noCapacityError threads 1 -> do wkTidEs <- mkWeakThreadIdEnv tid wkTid esTemplate mvEntries cleanUp @@ -264,13 +264,13 @@ persistentConcSingleUnlift es0 k = do -- by then. es <- cloneEnv es0 -- GHC never labels threads as 0. - mvWeakTid <- newMVar' 0 + mvWeakTid <- S.newMVar 0 let getEs = myThreadId >>= \case tid | tid0 == tid -> pure es0 tid -> do let wkTid = weakThreadId tid - readMVar' mvWeakTid >>= \case - 0 -> modifyMVar' mvWeakTid $ \case + S.readMVar mvWeakTid >>= \case + 0 -> S.modifyMVar mvWeakTid $ \case 0 -> pure (wkTid, es) _ -> noCapacityError 1 v | v == wkTid -> pure es @@ -304,18 +304,18 @@ persistentConcUnlifts es0 les0 cleanUp threads k = do storageTemplate <- cloneStorage es0.storage esTemplate <- replaceStorage es0 storageTemplate lesTemplate <- replaceStorage les0 storageTemplate - mvEntries <- newMVar' $ ThreadEntries threads IM.empty + mvEntries <- S.newMVar $ ThreadEntries threads IM.empty let getEsLes = myThreadId >>= \case tid | tid0 == tid -> pure (es0, les0) tid -> do - te0 <- readMVar' mvEntries + te0 <- S.readMVar mvEntries let wkTid = weakThreadId tid case wkTid `IM.lookup` te0.entries of Just wkEsLes -> getWkTidEnv wkEsLes -- If the environments are not in the map, there is no point - -- checking again within modifyMVar' below, because this is the only + -- checking again within modifyMVar below, because this is the only -- thread that can put them there. - Nothing -> modifyMVar' mvEntries $ \te -> case te.capacity of + Nothing -> S.modifyMVar mvEntries $ \te -> case te.capacity of 0 -> noCapacityError threads 1 -> do wkTidEsLes <- mkWeakThreadIdEnv tid wkTid (esTemplate, lesTemplate) mvEntries cleanUp @@ -360,13 +360,13 @@ persistentConcSingleUnlifts es0 les0 k = do es <- replaceStorage es0 storage les <- replaceStorage les0 storage -- GHC never labels threads as 0. - mvWeakTid <- newMVar' 0 + mvWeakTid <- S.newMVar 0 let getEsLes = myThreadId >>= \case tid | tid0 == tid -> pure (es0, les0) tid -> do let wkTid = weakThreadId tid - readMVar' mvWeakTid >>= \case - 0 -> modifyMVar' mvWeakTid $ \case + S.readMVar mvWeakTid >>= \case + 0 -> S.modifyMVar mvWeakTid $ \case 0 -> pure (wkTid, (es, les)) _ -> noCapacityError 1 v | v == wkTid -> pure (es, les) @@ -398,7 +398,7 @@ mkWeakThreadIdEnv :: ThreadId -> Int -> a - -> MVar' (ThreadEntries a) + -> S.MVar (ThreadEntries a) -> Bool -> IO (Weak a) mkWeakThreadIdEnv (ThreadId t#) wkTid es v = \case @@ -409,7 +409,7 @@ mkWeakThreadIdEnv (ThreadId t#) wkTid es v = \case case mkWeakNoFinalizer# t# es s0 of (# s1, w #) -> (# s1, Weak w #) where - IO finalizer = modifyMVar'_ v $ \te -> do + IO finalizer = S.modifyMVar_ v $ \te -> do pure ThreadEntries { capacity = case te.capacity of -- If the template copy of the environment hasn't been consumed diff --git a/effectful-core/src/Effectful/State/Static/Shared.hs b/effectful-core/src/Effectful/State/Static/Shared.hs index 40f6067b..d4bab8ac 100644 --- a/effectful-core/src/Effectful/State/Static/Shared.hs +++ b/effectful-core/src/Effectful/State/Static/Shared.hs @@ -47,7 +47,7 @@ module Effectful.State.Static.Shared , modifyM ) where -import Control.Concurrent.MVar.Strict +import Control.Concurrent.MVar.Strict qualified as S import Data.Kind import Effectful @@ -58,55 +58,55 @@ import Effectful.Dispatch.Static.Primitive data State (s :: Type) :: Effect type instance DispatchOf (State s) = Static NoSideEffects -newtype instance StaticRep (State s) = State (MVar' s) +newtype instance StaticRep (State s) = State (S.MVar s) -- | Run the 'State' effect with the given initial state and return the final -- value along with the final state. runState :: HasCallStack => s -> Eff (State s : es) a -> Eff es (a, s) runState s m = do - v <- unsafeEff_ $ newMVar' s + v <- unsafeEff_ $ S.newMVar s a <- evalStaticRep (State v) m - (a, ) <$> unsafeEff_ (readMVar' v) + (a, ) <$> unsafeEff_ (S.readMVar v) -- | Run the 'State' effect with the given initial state and return the final -- value, discarding the final state. evalState :: HasCallStack => s -> Eff (State s : es) a -> Eff es a evalState s m = do - v <- unsafeEff_ $ newMVar' s + v <- unsafeEff_ $ S.newMVar s evalStaticRep (State v) m -- | Run the 'State' effect with the given initial state and return the final -- state, discarding the final value. execState :: HasCallStack => s -> Eff (State s : es) a -> Eff es s execState s m = do - v <- unsafeEff_ $ newMVar' s + v <- unsafeEff_ $ S.newMVar s _ <- evalStaticRep (State v) m - unsafeEff_ $ readMVar' v + unsafeEff_ $ S.readMVar v --- | Run the 'State' effect with the given initial state 'MVar'' and return the +-- | Run the 'State' effect with the given initial state 'S.MVar' and return the -- final value along with the final state. -runStateMVar :: HasCallStack => MVar' s -> Eff (State s : es) a -> Eff es (a, s) +runStateMVar :: HasCallStack => S.MVar s -> Eff (State s : es) a -> Eff es (a, s) runStateMVar v m = do a <- evalStaticRep (State v) m - (a, ) <$> unsafeEff_ (readMVar' v) + (a, ) <$> unsafeEff_ (S.readMVar v) --- | Run the 'State' effect with the given initial state 'MVar'' and return the +-- | Run the 'State' effect with the given initial state 'S.MVar' and return the -- final value, discarding the final state. -evalStateMVar :: HasCallStack => MVar' s -> Eff (State s : es) a -> Eff es a +evalStateMVar :: HasCallStack => S.MVar s -> Eff (State s : es) a -> Eff es a evalStateMVar v = evalStaticRep (State v) --- | Run the 'State' effect with the given initial state 'MVar'' and return the +-- | Run the 'State' effect with the given initial state 'S.MVar' and return the -- final state, discarding the final value. -execStateMVar :: HasCallStack => MVar' s -> Eff (State s : es) a -> Eff es s +execStateMVar :: HasCallStack => S.MVar s -> Eff (State s : es) a -> Eff es s execStateMVar v m = do _ <- evalStaticRep (State v) m - unsafeEff_ $ readMVar' v + unsafeEff_ $ S.readMVar v -- | Fetch the current value of the state. get :: (HasCallStack, State s :> es) => Eff es s get = unsafeEff $ \es -> do State v <- getEnv es - readMVar' v + S.readMVar v -- | Get a function of the current state. -- @@ -118,7 +118,7 @@ gets f = f <$> get put :: (HasCallStack, State s :> es) => s -> Eff es () put s = unsafeEff $ \es -> do State v <- getEnv es - modifyMVar'_ v $ \_ -> pure s + S.modifyMVar_ v $ \_ -> pure s -- | Apply the function to the current state and return a value. -- @@ -126,7 +126,7 @@ put s = unsafeEff $ \es -> do state :: (HasCallStack, State s :> es) => (s -> (a, s)) -> Eff es a state f = unsafeEff $ \es -> do State v <- getEnv es - modifyMVar' v $ \s0 -> let (a, s) = f s0 in pure (s, a) + S.modifyMVar v $ \s0 -> let (a, s) = f s0 in pure (s, a) -- | Apply the function to the current state. -- @@ -142,7 +142,7 @@ modify f = state (\s -> ((), f s)) stateM :: (HasCallStack, State s :> es) => (s -> Eff es (a, s)) -> Eff es a stateM f = unsafeEff $ \es -> do State v <- getEnv es - modifyMVar' v $ \s0 -> do + S.modifyMVar v $ \s0 -> do (a, s) <- unEff (f s0) es pure (s, a) diff --git a/effectful-core/src/Effectful/Writer/Static/Shared.hs b/effectful-core/src/Effectful/Writer/Static/Shared.hs index f328ccdf..d9c5f1e6 100644 --- a/effectful-core/src/Effectful/Writer/Static/Shared.hs +++ b/effectful-core/src/Effectful/Writer/Static/Shared.hs @@ -27,7 +27,7 @@ module Effectful.Writer.Static.Shared , listens ) where -import Control.Concurrent.MVar.Strict +import Control.Concurrent.MVar.Strict qualified as S import Control.Exception (onException, uninterruptibleMask) import Data.Kind @@ -39,29 +39,29 @@ import Effectful.Dispatch.Static.Primitive data Writer (w :: Type) :: Effect type instance DispatchOf (Writer w) = Static NoSideEffects -newtype instance StaticRep (Writer w) = Writer (MVar' w) +newtype instance StaticRep (Writer w) = Writer (S.MVar w) -- | Run a 'Writer' effect and return the final value along with the final -- output. runWriter :: (HasCallStack, Monoid w) => Eff (Writer w : es) a -> Eff es (a, w) runWriter m = do - v <- unsafeEff_ $ newMVar' mempty + v <- unsafeEff_ $ S.newMVar mempty a <- evalStaticRep (Writer v) m - (a, ) <$> unsafeEff_ (readMVar' v) + (a, ) <$> unsafeEff_ (S.readMVar v) -- | Run a 'Writer' effect and return the final output, discarding the final -- value. execWriter :: (HasCallStack, Monoid w) => Eff (Writer w : es) a -> Eff es w execWriter m = do - v <- unsafeEff_ $ newMVar' mempty + v <- unsafeEff_ $ S.newMVar mempty _ <- evalStaticRep (Writer v) m - unsafeEff_ $ readMVar' v + unsafeEff_ $ S.readMVar v -- | Append the given output to the overall output of the 'Writer'. tell :: (HasCallStack, Writer w :> es, Monoid w) => w -> Eff es () tell w1 = unsafeEff $ \es -> do Writer v <- getEnv es - modifyMVar'_ v $ \w0 -> let w = w0 <> w1 in pure w + S.modifyMVar_ v $ \w0 -> pure (w0 <> w1) -- | Execute an action and append its output to the overall output of the -- 'Writer'. @@ -108,7 +108,7 @@ listen m = unsafeEff $ \es -> do -- might block and if an async exception is received while waiting, w1 will be -- lost. uninterruptibleMask $ \unmask -> do - v1 <- newMVar' mempty + v1 <- S.newMVar mempty -- Replace thread local MVar with a fresh one for isolated listening. v0 <- stateEnv es $ \(Writer v) -> (v, Writer v1) a <- unmask (unEff m es) `onException` merge es v0 v1 @@ -118,8 +118,8 @@ listen m = unsafeEff $ \es -> do -- exception was received while listening, merge results recorded so far. merge es v0 v1 = do putEnv es $ Writer v0 - w1 <- readMVar' v1 - modifyMVar'_ v0 $ \w0 -> let w = w0 <> w1 in pure w + w1 <- S.readMVar v1 + S.modifyMVar_ v0 $ \w0 -> pure (w0 <> w1) pure w1 -- | Execute an action and append its output to the overall output of the diff --git a/effectful/CHANGELOG.md b/effectful/CHANGELOG.md index c10f316b..58820a26 100644 --- a/effectful/CHANGELOG.md +++ b/effectful/CHANGELOG.md @@ -9,6 +9,7 @@ * Tighten pre-requisites for `unconsEnv` and `unreplaceEnv`. * Add `localLendBorrow` to `Effectful.Dispatch.Dynamic`. * Require `primitive` >= 0.9.0.0. +* Require `strict-mutable-base` >= 2.0.0.0. * **Bugfixes**: - `restoreStorageData` no longer shrinks the capacity of the storage, which could result in out of bounds reads when out of date references to the diff --git a/effectful/effectful.cabal b/effectful/effectful.cabal index f57a8ee1..362e0067 100644 --- a/effectful/effectful.cabal +++ b/effectful/effectful.cabal @@ -78,7 +78,7 @@ library , directory >= 1.3.2 , effectful-core >= 2.7.0.0 && < 2.7.1.0 , process >= 1.6.9 - , strict-mutable-base >= 1.1.0.0 + , strict-mutable-base >= 2.0.0.0 && < 3 , time >= 1.9.2 , stm >= 2.5.1.0 , unliftio >= 0.2.20 diff --git a/effectful/src/Effectful/Concurrent/Chan/Strict.hs b/effectful/src/Effectful/Concurrent/Chan/Strict.hs index 26a5465a..becb14ae 100644 --- a/effectful/src/Effectful/Concurrent/Chan/Strict.hs +++ b/effectful/src/Effectful/Concurrent/Chan/Strict.hs @@ -9,42 +9,42 @@ module Effectful.Concurrent.Chan.Strict , runConcurrent -- * Chan - , Chan' - , newChan' - , writeChan' - , readChan' - , dupChan' - , getChan'Contents - , writeList2Chan' + , Chan + , newChan + , writeChan + , readChan + , dupChan + , getChanContents + , writeList2Chan ) where -import Control.Concurrent.Chan.Strict (Chan') +import Control.Concurrent.Chan.Strict (Chan) import Control.Concurrent.Chan.Strict qualified as C import Effectful import Effectful.Concurrent.Effect import Effectful.Dispatch.Static --- | Lifted 'C.newChan''. -newChan' :: Concurrent :> es => Eff es (Chan' a) -newChan' = unsafeEff_ C.newChan' +-- | Lifted 'C.newChan'. +newChan :: Concurrent :> es => Eff es (Chan a) +newChan = unsafeEff_ C.newChan --- | Lifted 'C.writeChan''. -writeChan' :: Concurrent :> es => Chan' a -> a -> Eff es () -writeChan' c = unsafeEff_ . C.writeChan' c +-- | Lifted 'C.writeChan'. +writeChan :: Concurrent :> es => Chan a -> a -> Eff es () +writeChan c = unsafeEff_ . C.writeChan c --- | Lifted 'C.readChan''. -readChan' :: Concurrent :> es => Chan' a -> Eff es a -readChan' = unsafeEff_ . C.readChan' +-- | Lifted 'C.readChan'. +readChan :: Concurrent :> es => Chan a -> Eff es a +readChan = unsafeEff_ . C.readChan --- | Lifted 'C.dupChan''. -dupChan' :: Concurrent :> es => Chan' a -> Eff es (Chan' a) -dupChan' = unsafeEff_ . C.dupChan' +-- | Lifted 'C.dupChan'. +dupChan :: Concurrent :> es => Chan a -> Eff es (Chan a) +dupChan = unsafeEff_ . C.dupChan --- | Lifted 'C.getChan'Contents'. -getChan'Contents :: Concurrent :> es => Chan' a -> Eff es [a] -getChan'Contents = unsafeEff_ . C.getChan'Contents +-- | Lifted 'C.getChanContents'. +getChanContents :: Concurrent :> es => Chan a -> Eff es [a] +getChanContents = unsafeEff_ . C.getChanContents --- | Lifted 'C.writeList2Chan''. -writeList2Chan' :: Concurrent :> es => Chan' a -> [a] -> Eff es () -writeList2Chan' c = unsafeEff_ . C.writeList2Chan' c +-- | Lifted 'C.writeList2Chan'. +writeList2Chan :: Concurrent :> es => Chan a -> [a] -> Eff es () +writeList2Chan c = unsafeEff_ . C.writeList2Chan c diff --git a/effectful/src/Effectful/Concurrent/MVar/Strict.hs b/effectful/src/Effectful/Concurrent/MVar/Strict.hs index 38690c2a..b518727a 100644 --- a/effectful/src/Effectful/Concurrent/MVar/Strict.hs +++ b/effectful/src/Effectful/Concurrent/MVar/Strict.hs @@ -9,28 +9,28 @@ module Effectful.Concurrent.MVar.Strict , runConcurrent -- * MVar - , MVar' - , newEmptyMVar' - , newMVar' - , takeMVar' - , putMVar' - , readMVar' - , swapMVar' - , tryTakeMVar' - , tryPutMVar' - , tryReadMVar' - , isEmptyMVar' - , withMVar' - , withMVar'Masked - , modifyMVar' - , modifyMVar'_ - , modifyMVar'Masked - , modifyMVar'Masked_ - , mkWeakMVar' + , MVar + , newEmptyMVar + , newMVar + , takeMVar + , putMVar + , readMVar + , swapMVar + , tryTakeMVar + , tryPutMVar + , tryReadMVar + , isEmptyMVar + , withMVar + , withMVarMasked + , modifyMVar + , modifyMVar_ + , modifyMVarMasked + , modifyMVarMasked_ + , mkWeakMVar ) where import System.Mem.Weak (Weak) -import Control.Concurrent.MVar.Strict (MVar') +import Control.Concurrent.MVar.Strict (MVar) import Control.Concurrent.MVar.Strict qualified as M import Effectful @@ -39,90 +39,90 @@ import Effectful.Dispatch.Static import Effectful.Dispatch.Static.Primitive import Effectful.Dispatch.Static.Unsafe --- | Lifted 'M.newEmptyMVar''. -newEmptyMVar' :: Concurrent :> es => Eff es (MVar' a) -newEmptyMVar' = unsafeEff_ M.newEmptyMVar' - --- | Lifted 'M.newMVar''. -newMVar' :: Concurrent :> es => a -> Eff es (MVar' a) -newMVar' = unsafeEff_ . M.newMVar' - --- | Lifted 'M.takeMVar''. -takeMVar' :: Concurrent :> es => MVar' a -> Eff es a -takeMVar' = unsafeEff_ . M.takeMVar' - --- | Lifted 'M.putMVar''. -putMVar' :: Concurrent :> es => MVar' a -> a -> Eff es () -putMVar' var = unsafeEff_ . M.putMVar' var - --- | Lifted 'M.readMVar''. -readMVar' :: Concurrent :> es => MVar' a -> Eff es a -readMVar' = unsafeEff_ . M.readMVar' - --- | Lifted 'M.swapMVar''. -swapMVar' :: Concurrent :> es => MVar' a -> a -> Eff es a -swapMVar' var = unsafeEff_ . M.swapMVar' var - --- | Lifted 'M.tryTakeMVar''. -tryTakeMVar' :: Concurrent :> es => MVar' a -> Eff es (Maybe a) -tryTakeMVar' = unsafeEff_ . M.tryTakeMVar' - --- | Lifted 'M.tryPutMVar''. -tryPutMVar' :: Concurrent :> es => MVar' a -> a -> Eff es Bool -tryPutMVar' var = unsafeEff_ . M.tryPutMVar' var - --- | Lifted 'M.tryReadMVar''. -tryReadMVar' :: Concurrent :> es => MVar' a -> Eff es (Maybe a) -tryReadMVar' = unsafeEff_ . M.tryReadMVar' - --- | Lifted 'M.isEmptyMVar''. -isEmptyMVar' :: Concurrent :> es => MVar' a -> Eff es Bool -isEmptyMVar' = unsafeEff_ . M.isEmptyMVar' - --- | Lifted 'M.withMVar''. -withMVar' :: Concurrent :> es => MVar' a -> (a -> Eff es b) -> Eff es b -withMVar' var f = reallyUnsafeUnliftIO $ \unlift -> do - M.withMVar' var $ unlift . f -{-# INLINE withMVar' #-} - --- | Lifted 'M.withMVar'Masked'. -withMVar'Masked :: Concurrent :> es => MVar' a -> (a -> Eff es b) -> Eff es b -withMVar'Masked var f = reallyUnsafeUnliftIO $ \unlift -> do - M.withMVar'Masked var $ unlift . f -{-# INLINE withMVar'Masked #-} - --- | Lifted 'M.modifyMVar'_'. -modifyMVar'_ :: Concurrent :> es => MVar' a -> (a -> Eff es a) -> Eff es () -modifyMVar'_ var f = reallyUnsafeUnliftIO $ \unlift -> do - M.modifyMVar'_ var $ unlift . f -{-# INLINE modifyMVar'_ #-} - --- | Lifted 'M.modifyMVar''. -modifyMVar' :: Concurrent :> es => MVar' a -> (a -> Eff es (a, b)) -> Eff es b -modifyMVar' var f = reallyUnsafeUnliftIO $ \unlift -> do - M.modifyMVar' var $ unlift . f -{-# INLINE modifyMVar' #-} - --- | Lifted 'M.modifyMVar'Masked_'. -modifyMVar'Masked_ :: Concurrent :> es => MVar' a -> (a -> Eff es a) -> Eff es () -modifyMVar'Masked_ var f = reallyUnsafeUnliftIO $ \unlift -> do - M.modifyMVar'Masked_ var $ unlift . f -{-# INLINE modifyMVar'Masked_ #-} - --- | Lifted 'M.modifyMVar'Masked'. -modifyMVar'Masked :: Concurrent :> es => MVar' a -> (a -> Eff es (a, b)) -> Eff es b -modifyMVar'Masked var f = reallyUnsafeUnliftIO $ \unlift -> do - M.modifyMVar'Masked var $ unlift . f -{-# INLINE modifyMVar'Masked #-} - --- | Lifted 'M.mkWeakMVar''. +-- | Lifted 'M.newEmptyMVar'. +newEmptyMVar :: Concurrent :> es => Eff es (MVar a) +newEmptyMVar = unsafeEff_ M.newEmptyMVar + +-- | Lifted 'M.newMVar'. +newMVar :: Concurrent :> es => a -> Eff es (MVar a) +newMVar = unsafeEff_ . M.newMVar + +-- | Lifted 'M.takeMVar'. +takeMVar :: Concurrent :> es => MVar a -> Eff es a +takeMVar = unsafeEff_ . M.takeMVar + +-- | Lifted 'M.putMVar'. +putMVar :: Concurrent :> es => MVar a -> a -> Eff es () +putMVar var = unsafeEff_ . M.putMVar var + +-- | Lifted 'M.readMVar'. +readMVar :: Concurrent :> es => MVar a -> Eff es a +readMVar = unsafeEff_ . M.readMVar + +-- | Lifted 'M.swapMVar'. +swapMVar :: Concurrent :> es => MVar a -> a -> Eff es a +swapMVar var = unsafeEff_ . M.swapMVar var + +-- | Lifted 'M.tryTakeMVar'. +tryTakeMVar :: Concurrent :> es => MVar a -> Eff es (Maybe a) +tryTakeMVar = unsafeEff_ . M.tryTakeMVar + +-- | Lifted 'M.tryPutMVar'. +tryPutMVar :: Concurrent :> es => MVar a -> a -> Eff es Bool +tryPutMVar var = unsafeEff_ . M.tryPutMVar var + +-- | Lifted 'M.tryReadMVar'. +tryReadMVar :: Concurrent :> es => MVar a -> Eff es (Maybe a) +tryReadMVar = unsafeEff_ . M.tryReadMVar + +-- | Lifted 'M.isEmptyMVar'. +isEmptyMVar :: Concurrent :> es => MVar a -> Eff es Bool +isEmptyMVar = unsafeEff_ . M.isEmptyMVar + +-- | Lifted 'M.withMVar'. +withMVar :: Concurrent :> es => MVar a -> (a -> Eff es b) -> Eff es b +withMVar var f = reallyUnsafeUnliftIO $ \unlift -> do + M.withMVar var $ unlift . f +{-# INLINE withMVar #-} + +-- | Lifted 'M.withMVarMasked'. +withMVarMasked :: Concurrent :> es => MVar a -> (a -> Eff es b) -> Eff es b +withMVarMasked var f = reallyUnsafeUnliftIO $ \unlift -> do + M.withMVarMasked var $ unlift . f +{-# INLINE withMVarMasked #-} + +-- | Lifted 'M.modifyMVar_'. +modifyMVar_ :: Concurrent :> es => MVar a -> (a -> Eff es a) -> Eff es () +modifyMVar_ var f = reallyUnsafeUnliftIO $ \unlift -> do + M.modifyMVar_ var $ unlift . f +{-# INLINE modifyMVar_ #-} + +-- | Lifted 'M.modifyMVar'. +modifyMVar :: Concurrent :> es => MVar a -> (a -> Eff es (a, b)) -> Eff es b +modifyMVar var f = reallyUnsafeUnliftIO $ \unlift -> do + M.modifyMVar var $ unlift . f +{-# INLINE modifyMVar #-} + +-- | Lifted 'M.modifyMVarMasked_'. +modifyMVarMasked_ :: Concurrent :> es => MVar a -> (a -> Eff es a) -> Eff es () +modifyMVarMasked_ var f = reallyUnsafeUnliftIO $ \unlift -> do + M.modifyMVarMasked_ var $ unlift . f +{-# INLINE modifyMVarMasked_ #-} + +-- | Lifted 'M.modifyMVarMasked'. +modifyMVarMasked :: Concurrent :> es => MVar a -> (a -> Eff es (a, b)) -> Eff es b +modifyMVarMasked var f = reallyUnsafeUnliftIO $ \unlift -> do + M.modifyMVarMasked var $ unlift . f +{-# INLINE modifyMVarMasked #-} + +-- | Lifted 'M.mkWeakMVar'. -- -- /Note:/ the finalizer will run a cloned environment, so any changes it makes -- to thread local data will not be visible outside of it. -mkWeakMVar' +mkWeakMVar :: (HasCallStack, Concurrent :> es) - => MVar' a -> Eff es () - -> Eff es (Weak (MVar' a)) -mkWeakMVar' var f = unsafeEff $ \es -> do + => MVar a -> Eff es () + -> Eff es (Weak (MVar a)) +mkWeakMVar var f = unsafeEff $ \es -> do -- The finalizer can run at any point and in any thread. - M.mkWeakMVar' var . unEff f =<< cloneEnv es + M.mkWeakMVar var . unEff f =<< cloneEnv es diff --git a/effectful/src/Effectful/Prim/IORef/Strict.hs b/effectful/src/Effectful/Prim/IORef/Strict.hs index 0adaf212..9230f97e 100644 --- a/effectful/src/Effectful/Prim/IORef/Strict.hs +++ b/effectful/src/Effectful/Prim/IORef/Strict.hs @@ -9,17 +9,17 @@ module Effectful.Prim.IORef.Strict , runPrim -- * IORef - , IORef' - , newIORef' - , readIORef' - , writeIORef' - , modifyIORef' - , atomicModifyIORef' - , atomicWriteIORef' - , mkWeakIORef' + , IORef + , newIORef + , readIORef + , writeIORef + , modifyIORef + , atomicModifyIORef + , atomicWriteIORef + , mkWeakIORef ) where -import Data.IORef.Strict (IORef') +import Data.IORef.Strict (IORef) import Data.IORef.Strict qualified as Ref import System.Mem.Weak (Weak) @@ -28,39 +28,39 @@ import Effectful.Dispatch.Static import Effectful.Dispatch.Static.Primitive import Effectful.Prim --- | Lifted 'Ref.newIORef''. -newIORef' :: Prim :> es => a -> Eff es (IORef' a) -newIORef' = unsafeEff_ . Ref.newIORef' +-- | Lifted 'Ref.newIORef'. +newIORef :: Prim :> es => a -> Eff es (IORef a) +newIORef = unsafeEff_ . Ref.newIORef --- | Lifted 'Ref.readIORef''. -readIORef' :: Prim :> es => IORef' a -> Eff es a -readIORef' = unsafeEff_ . Ref.readIORef' +-- | Lifted 'Ref.readIORef'. +readIORef :: Prim :> es => IORef a -> Eff es a +readIORef = unsafeEff_ . Ref.readIORef --- | Lifted 'Ref.writeIORef''. -writeIORef' :: Prim :> es => IORef' a -> a -> Eff es () -writeIORef' var = unsafeEff_ . Ref.writeIORef' var +-- | Lifted 'Ref.writeIORef'. +writeIORef :: Prim :> es => IORef a -> a -> Eff es () +writeIORef var = unsafeEff_ . Ref.writeIORef var --- | Lifted 'Ref.modifyIORef''. -modifyIORef' :: Prim :> es => IORef' a -> (a -> a) -> Eff es () -modifyIORef' var = unsafeEff_ . Ref.modifyIORef' var +-- | Lifted 'Ref.modifyIORef'. +modifyIORef :: Prim :> es => IORef a -> (a -> a) -> Eff es () +modifyIORef var = unsafeEff_ . Ref.modifyIORef var --- | Lifted 'Ref.atomicModifyIORef''. -atomicModifyIORef' :: Prim :> es => IORef' a -> (a -> (a, b)) -> Eff es b -atomicModifyIORef' var = unsafeEff_ . Ref.atomicModifyIORef' var +-- | Lifted 'Ref.atomicModifyIORef'. +atomicModifyIORef :: Prim :> es => IORef a -> (a -> (a, b)) -> Eff es b +atomicModifyIORef var = unsafeEff_ . Ref.atomicModifyIORef var --- | Lifted 'Ref.atomicWriteIORef''. -atomicWriteIORef' :: Prim :> es => IORef' a -> a -> Eff es () -atomicWriteIORef' var = unsafeEff_ . Ref.atomicWriteIORef' var +-- | Lifted 'Ref.atomicWriteIORef'. +atomicWriteIORef :: Prim :> es => IORef a -> a -> Eff es () +atomicWriteIORef var = unsafeEff_ . Ref.atomicWriteIORef var --- | Lifted 'Ref.mkWeakIORef''. +-- | Lifted 'Ref.mkWeakIORef'. -- -- /Note:/ the finalizer will run a cloned environment, so any changes it makes -- to thread local data will not be visible outside of it. -mkWeakIORef' +mkWeakIORef :: (HasCallStack, Prim :> es) - => IORef' a + => IORef a -> Eff es () - -> Eff es (Weak (IORef' a)) -mkWeakIORef' var f = unsafeEff $ \es -> do + -> Eff es (Weak (IORef a)) +mkWeakIORef var f = unsafeEff $ \es -> do -- The finalizer can run at any point and in any thread. - Ref.mkWeakIORef' var . unEff f =<< cloneEnv es + Ref.mkWeakIORef var . unEff f =<< cloneEnv es diff --git a/effectful/tests/NonDetTests.hs b/effectful/tests/NonDetTests.hs index 37bcd6f7..c23b320a 100644 --- a/effectful/tests/NonDetTests.hs +++ b/effectful/tests/NonDetTests.hs @@ -1,6 +1,6 @@ module NonDetTests (nonDetTests) where -import Data.IORef.Strict +import Data.IORef.Strict qualified as S import Data.Primitive.PrimArray import Test.Tasty import Test.Tasty.HUnit @@ -105,15 +105,15 @@ test_independentHandlers step = runEff $ do -- which case the rollback must not shrink its capacity. test_staleReferenceAfterRollback :: Assertion test_staleReferenceAfterRollback = runEff $ do - ref <- liftIO $ newIORef' Nothing + ref <- liftIO $ S.newIORef Nothing result <- runNonDet OnEmptyRollback $ (do stale <- captureStaleReference 16 capacity <- getStorageCapacity - liftIO $ writeIORef' ref $ Just (capacity, stale) + liftIO $ S.writeIORef ref $ Just (capacity, stale) emptyEff) <|> pure True U.assertEqual "result" (Just True) (dropLeft result) - liftIO (readIORef' ref) >>= \case + liftIO (S.readIORef ref) >>= \case Nothing -> U.assertFailure "stale reference not captured" Just (capacity, stale) -> do -- If the rollback shrunk the capacity of the storage, evaluation of the @@ -132,7 +132,7 @@ captureStaleReference n = evalStateLocal @Int n $ -- | Get the current capacity of the underlying storage of effects. getStorageCapacity :: Eff es Int getStorageCapacity = unsafeEff $ \es -> do - I.Storage _ storageData <- readIORef' es.storage + I.Storage _ storageData <- S.readIORef es.storage getSizeofMutablePrimArray storageData.versions ---------------------------------------- @@ -140,11 +140,11 @@ getStorageCapacity = unsafeEff $ \es -> do data MutInt :: Effect type instance DispatchOf MutInt = Static NoSideEffects -newtype instance StaticRep MutInt = MutInt (IORef' Int) +newtype instance StaticRep MutInt = MutInt (S.IORef Int) runMutInt :: Int -> Eff (MutInt : es) a -> Eff es a runMutInt s action = unsafeEff $ \es -> do - ref <- newIORef' s + ref <- S.newIORef s inlineBracket (consEnv (MutInt ref) relinkMutInt es) unconsEnv @@ -152,14 +152,14 @@ runMutInt s action = unsafeEff $ \es -> do where relinkMutInt :: Relinker StaticRep MutInt relinkMutInt = Relinker $ \_ (MutInt ref0) -> do - ref <- newIORef' =<< readIORef' ref0 + ref <- S.newIORef =<< S.readIORef ref0 pure $ MutInt ref stateMutInt :: MutInt :> es => (Int -> (a, Int)) -> Eff es a stateMutInt f = unsafeEff $ \es -> do MutInt ref <- getEnv es - (r, s) <- f <$> readIORef' ref - writeIORef' ref s + (r, s) <- f <$> S.readIORef ref + S.writeIORef ref s pure r modifyMutInt :: MutInt :> es => (Int -> Int) -> Eff es () diff --git a/effectful/tests/StateTests.hs b/effectful/tests/StateTests.hs index c269572b..d345d270 100644 --- a/effectful/tests/StateTests.hs +++ b/effectful/tests/StateTests.hs @@ -4,7 +4,7 @@ import Control.Exception.Lifted qualified as LE import Control.Exception.Safe qualified as Safe import Control.Monad import Control.Monad.Catch qualified as C -import Data.IORef.Strict +import Data.IORef.Strict qualified as S import Test.Tasty import Test.Tasty.HUnit import UnliftIO.Exception qualified as UE @@ -51,7 +51,7 @@ test_stateM = runEff $ do U.assertEqual "correct a" "hi" a U.assertEqual "correct b" "hi!!!" b where - getEffectReps = unsafeEff $ \es -> (.effects) . (.data_) <$> readIORef' es.storage + getEffectReps = unsafeEff $ \es -> (.effects) . (.data_) <$> S.readIORef es.storage test_deepStack :: Assertion test_deepStack = runEff $ do