Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IORefs for io-sim and io-sim-por #145

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions io-classes/io-classes.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ library
Control.Monad.Class.MonadAsync
Control.Monad.Class.MonadEventlog
Control.Monad.Class.MonadFork
Control.Monad.Class.MonadIORef
Control.Monad.Class.MonadSay
Control.Monad.Class.MonadST
Control.Monad.Class.MonadSTM
Expand Down
79 changes: 79 additions & 0 deletions io-classes/src/Control/Monad/Class/MonadIORef.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}

module Control.Monad.Class.MonadIORef where

import Control.Monad.Reader
import Data.IORef qualified as IO
import Data.Kind

class Monad m => MonadIORef m where
{-# MINIMAL newIORef, readIORef, writeIORef, atomicModifyIORef, atomicModifyIORef', atomicWriteIORef #-}

type IORef m :: Type -> Type

-- | See 'IO.newIORef'.
newIORef :: a -> m (IORef m a)
-- | See 'IO.readIORef'.
readIORef :: IORef m a -> m a
-- | See 'IO.writeIORef'.
writeIORef :: IORef m a -> a -> m ()
-- | See 'IO.modifyIORef'.
modifyIORef :: IORef m a -> (a -> a) -> m ()
-- | See 'IO.modifyRef''.
modifyIORef' :: IORef m a -> (a -> a) -> m ()
-- | See 'IO.atomicModifyIORef'.
atomicModifyIORef :: IORef m a -> (a -> (a, b)) -> m b
-- | See 'IO.atomicModifyIORef''.
atomicModifyIORef' :: IORef m a -> (a -> (a, b)) -> m b
-- | See 'IO.atomicWriteIORef'.
atomicWriteIORef :: IORef m a -> a -> m ()

default modifyIORef :: IORef m a -> (a -> a) -> m ()
modifyIORef ref f = readIORef ref >>= writeIORef ref . f

default modifyIORef' :: IORef m a -> (a -> a) -> m ()
modifyIORef' ref f = do
x <- readIORef ref
let x' = f x
x' `seq` writeIORef ref x'

--
-- IO instance
--

instance MonadIORef IO where
type IORef IO = IO.IORef
newIORef = IO.newIORef
readIORef = IO.readIORef
writeIORef = IO.writeIORef
modifyIORef = IO.modifyIORef
modifyIORef' = IO.modifyIORef'
atomicModifyIORef = IO.atomicModifyIORef
atomicModifyIORef' = IO.atomicModifyIORef'
atomicWriteIORef = IO.atomicWriteIORef

--
-- ReaderT instance
--

instance MonadIORef m => MonadIORef (ReaderT r m) where
type IORef (ReaderT r m) = IORef m
newIORef = lift . newIORef
readIORef = lift . readIORef
writeIORef = lift .: writeIORef
modifyIORef = lift .: modifyIORef
modifyIORef' = lift .: modifyIORef'
atomicModifyIORef = lift .: atomicModifyIORef
atomicModifyIORef' = lift .: atomicModifyIORef'
atomicWriteIORef = lift .: atomicWriteIORef
Comment on lines +63 to +72
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be moved to io-classes-mtl?


--
-- Utilities
--

(.:) :: (c -> d) -> (a -> b -> c) -> (a -> b -> d)
(f .: g) x y = f (g x y)
1 change: 1 addition & 0 deletions io-sim/io-sim.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ library
Control.Monad.IOSim.Internal,
Control.Monad.IOSim.InternalTypes,
Control.Monad.IOSim.STM,
Control.Monad.IOSim.IORef,
Control.Monad.IOSimPOR.Internal,
Control.Monad.IOSimPOR.Types,
Control.Monad.IOSimPOR.QuickCheckUtils,
Expand Down
70 changes: 70 additions & 0 deletions io-sim/src/Control/Monad/IOSim/IORef.hs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementations in this module are based on Data.IORef and GHC.IORef from base-4.19.0.0

Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UnboxedTuples #-}

{-# OPTIONS_GHC -Wno-orphans #-}

module Control.Monad.IOSim.IORef where

import Control.Monad.Class.MonadIORef
import Control.Monad.Class.MonadST
import Control.Monad.IOSim.Types
import GHC.Exts
import GHC.ST
import GHC.STRef

newtype IOSimRef s a = IORef (STRef s a)

instance MonadIORef (IOSim s) where
type IORef (IOSim s) = IOSimRef s
newIORef v = stToIO (newSTRef v) >>= \ var -> return (IORef var)
readIORef (IORef v) = stToIO (readSTRef v)
writeIORef (IORef var) v = stToIO (writeSTRef var v)
modifyIORef ref f = readIORef ref >>= writeIORef ref . f
modifyIORef' ref f = do
x <- readIORef ref
let x' = f x
x' `seq` writeIORef ref x'
atomicModifyIORef ref f = do
(_old, (_new, res)) <- atomicModifyIORef2 ref f
pure res
atomicModifyIORef' = Control.Monad.IOSim.IORef.atomicModifyIORef'
atomicWriteIORef ref a = do
_ <- atomicSwapIORef ref a
pure ()

atomicModifyIORef2Lazy :: IORef (IOSim s) a -> (a -> (a,b)) -> IOSim s (a, (a, b))
atomicModifyIORef2Lazy (IORef (STRef r#)) f = stToIO $
ST (\s -> case atomicModifyMutVar2# r# f s of
(# s', old, res #) -> (# s', (old, res) #))
Comment on lines +37 to +40
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since IORefs are STRefs, we can copy the code from base. However, this is probably overkill -- @coot, do you have pointers for how I should proceed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, these implementations might change slightly between GHC releases. I see that GHA is failing because GHCs before 9.8 don't have access to an atomicSwapMutVar# function


atomicModifyIORef2 :: IORef (IOSim s) a -> (a -> (a,b)) -> IOSim s (a, (a, b))
atomicModifyIORef2 ref f = do
r@(_old, (_new, _res)) <- atomicModifyIORef2Lazy ref f
return r

atomicModifyIORefP :: IORef (IOSim s) a -> (a -> (a,b)) -> IOSim s b
atomicModifyIORefP ref f = do
(_old, (_,r)) <- atomicModifyIORef2 ref f
pure r

atomicModifyIORefLazy_ :: IORef (IOSim s) a -> (a -> a) -> IOSim s (a, a)
atomicModifyIORefLazy_ (IORef (STRef ref)) f = stToIO $ ST $ \s ->
case atomicModifyMutVar_# ref f s of
(# s', old, new #) -> (# s', (old, new) #)

atomicModifyIORef'_ :: IORef (IOSim s) a -> (a -> a) -> IOSim s (a, a)
atomicModifyIORef'_ ref f = do
(old, !new) <- atomicModifyIORefLazy_ ref f
return (old, new)

atomicSwapIORef :: IORef (IOSim s) a -> a -> IOSim s a
atomicSwapIORef (IORef (STRef ref)) new = stToIO $ ST (atomicSwapMutVar# ref new)

atomicModifyIORef' :: IORef (IOSim s) a -> (a -> (a,b)) -> IOSim s b
atomicModifyIORef' ref f = do
(_old, (_new, !res)) <- atomicModifyIORef2 ref $
\old -> case f old of
r@(!_new, _res) -> r
pure res
Loading