{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
module Control.Concurrent.STM.Delay (
Delay,
newDelay,
updateDelay,
cancelDelay,
waitDelay,
tryWaitDelay,
tryWaitDelayIO,
) where
import Control.Concurrent
import Control.Concurrent.STM
import Control.Exception (mask_)
import Control.Monad
#if MIN_VERSION_base(4,4,0) && !mingw32_HOST_OS && !ghcjs_HOST_OS
import qualified GHC.Event as Ev
#endif
data Delay = Delay
{ Delay -> TVar Bool
delayVar :: !(TVar Bool)
, Delay -> Int -> IO ()
delayUpdate :: !(Int -> IO ())
, Delay -> IO ()
delayCancel :: !(IO ())
}
instance Eq Delay where
== :: Delay -> Delay -> Bool
(==) Delay
a Delay
b = Delay -> TVar Bool
delayVar Delay
a TVar Bool -> TVar Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Delay -> TVar Bool
delayVar Delay
b
newDelay :: Int -> IO Delay
newDelay :: Int -> IO Delay
newDelay Int
t
| Int
t Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 = Int -> IO Delay
getDelayImpl Int
t
| Bool
otherwise = do
var <- Bool -> IO (TVar Bool)
forall a. a -> IO (TVar a)
newTVarIO Bool
True
return Delay
{ delayVar = var
, delayUpdate = \Int
_t -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
, delayCancel = return ()
}
updateDelay :: Delay -> Int -> IO ()
updateDelay :: Delay -> Int -> IO ()
updateDelay = Delay -> Int -> IO ()
delayUpdate
cancelDelay :: Delay -> IO ()
cancelDelay :: Delay -> IO ()
cancelDelay = Delay -> IO ()
delayCancel
waitDelay :: Delay -> STM ()
waitDelay :: Delay -> STM ()
waitDelay Delay
delay = do
expired <- Delay -> STM Bool
tryWaitDelay Delay
delay
if expired then return ()
else retry
tryWaitDelay :: Delay -> STM Bool
tryWaitDelay :: Delay -> STM Bool
tryWaitDelay = TVar Bool -> STM Bool
forall a. TVar a -> STM a
readTVar (TVar Bool -> STM Bool)
-> (Delay -> TVar Bool) -> Delay -> STM Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Delay -> TVar Bool
delayVar
tryWaitDelayIO :: Delay -> IO Bool
tryWaitDelayIO :: Delay -> IO Bool
tryWaitDelayIO = TVar Bool -> IO Bool
forall a. TVar a -> IO a
readTVarIO (TVar Bool -> IO Bool) -> (Delay -> TVar Bool) -> Delay -> IO Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Delay -> TVar Bool
delayVar
getDelayImpl :: Int -> IO Delay
#if MIN_VERSION_base(4,7,0) && !mingw32_HOST_OS && !ghcjs_HOST_OS
getDelayImpl :: Int -> IO Delay
getDelayImpl Int
t0 = do
m <- IO (Maybe EventManager)
Ev.getSystemEventManager
case m of
Maybe EventManager
Nothing -> Int -> IO Delay
implThread Int
t0
Just EventManager
_ -> do
mgr <- IO TimerManager
Ev.getSystemTimerManager
implEvent mgr t0
#elif MIN_VERSION_base(4,4,0) && !mingw32_HOST_OS && !ghcjs_HOST_OS
getDelayImpl t0 = do
m <- Ev.getSystemEventManager
case m of
Nothing -> implThread t0
Just mgr -> implEvent mgr t0
#else
getDelayImpl = implThread
#endif
#if MIN_VERSION_base(4,7,0) && !mingw32_HOST_OS && !ghcjs_HOST_OS
implEvent :: TimerManager -> Int -> IO Delay
implEvent TimerManager
mgr Int
t0 = do
var <- Bool -> IO (TVar Bool)
forall a. a -> IO (TVar a)
newTVarIO Bool
False
k <- Ev.registerTimeout mgr t0 $ atomically $ writeTVar var True
return Delay
{ delayVar = var
, delayUpdate = Ev.updateTimeout mgr k
, delayCancel = Ev.unregisterTimeout mgr k
}
#elif MIN_VERSION_base(4,4,0) && !mingw32_HOST_OS && !ghcjs_HOST_OS
implEvent :: Ev.EventManager -> Int -> IO Delay
implEvent mgr t0 = do
var <- newTVarIO False
k <- Ev.registerTimeout mgr t0 $ atomically $ writeTVar var True
return Delay
{ delayVar = var
, delayUpdate = Ev.updateTimeout mgr k
, delayCancel = Ev.unregisterTimeout mgr k
}
#endif
implThread :: Int -> IO Delay
implThread :: Int -> IO Delay
implThread Int
t0 = do
var <- Bool -> IO (TVar Bool)
forall a. a -> IO (TVar a)
newTVarIO Bool
False
let new Int
t = Int -> IO () -> IO TimeoutThread
forkTimeoutThread Int
t (IO () -> IO TimeoutThread) -> IO () -> IO TimeoutThread
forall a b. (a -> b) -> a -> b
$ STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ TVar Bool -> Bool -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar Bool
var Bool
True
mv <- new t0 >>= newMVar . Just
return Delay
{ delayVar = var
, delayUpdate = replaceThread mv . fmap Just . new
, delayCancel = replaceThread mv $ return Nothing
}
replaceThread :: MVar (Maybe TimeoutThread)
-> IO (Maybe TimeoutThread)
-> IO ()
replaceThread :: MVar (Maybe TimeoutThread) -> IO (Maybe TimeoutThread) -> IO ()
replaceThread MVar (Maybe TimeoutThread)
mv IO (Maybe TimeoutThread)
new =
IO (IO ()) -> IO ()
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join (IO (IO ()) -> IO ()) -> IO (IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ IO (IO ()) -> IO (IO ())
forall a. IO a -> IO a
mask_ (IO (IO ()) -> IO (IO ())) -> IO (IO ()) -> IO (IO ())
forall a b. (a -> b) -> a -> b
$ do
m <- MVar (Maybe TimeoutThread) -> IO (Maybe TimeoutThread)
forall a. MVar a -> IO a
takeMVar MVar (Maybe TimeoutThread)
mv
case m of
Maybe TimeoutThread
Nothing -> do
MVar (Maybe TimeoutThread) -> Maybe TimeoutThread -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar (Maybe TimeoutThread)
mv Maybe TimeoutThread
forall a. Maybe a
Nothing
IO () -> IO (IO ())
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (() -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ())
Just TimeoutThread
tt -> do
m' <- TimeoutThread -> IO (Maybe (IO ()))
stopTimeoutThread TimeoutThread
tt
case m' of
Maybe (IO ())
Nothing -> do
MVar (Maybe TimeoutThread) -> Maybe TimeoutThread -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar (Maybe TimeoutThread)
mv Maybe TimeoutThread
forall a. Maybe a
Nothing
IO () -> IO (IO ())
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (IO () -> IO (IO ())) -> IO () -> IO (IO ())
forall a b. (a -> b) -> a -> b
$ () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just IO ()
kill -> do
IO (Maybe TimeoutThread)
new IO (Maybe TimeoutThread) -> (Maybe TimeoutThread -> IO ()) -> IO ()
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= MVar (Maybe TimeoutThread) -> Maybe TimeoutThread -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar (Maybe TimeoutThread)
mv
IO () -> IO (IO ())
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return IO ()
kill
data TimeoutThread = TimeoutThread !ThreadId !(MVar ())
forkTimeoutThread :: Int -> IO () -> IO TimeoutThread
forkTimeoutThread :: Int -> IO () -> IO TimeoutThread
forkTimeoutThread Int
t IO ()
io = do
mv <- () -> IO (MVar ())
forall a. a -> IO (MVar a)
newMVar ()
tid <- compat_forkIOUnmasked $ do
threadDelay t
m <- tryTakeMVar mv
case m of
Maybe ()
Nothing -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just ()
_ -> IO ()
io
return (TimeoutThread tid mv)
stopTimeoutThread :: TimeoutThread -> IO (Maybe (IO ()))
stopTimeoutThread :: TimeoutThread -> IO (Maybe (IO ()))
stopTimeoutThread (TimeoutThread ThreadId
tid MVar ()
mv) =
Maybe (IO ()) -> (() -> Maybe (IO ())) -> Maybe () -> Maybe (IO ())
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Maybe (IO ())
forall a. Maybe a
Nothing (\()
_ -> IO () -> Maybe (IO ())
forall a. a -> Maybe a
Just (ThreadId -> IO ()
killThread ThreadId
tid)) (Maybe () -> Maybe (IO ())) -> IO (Maybe ()) -> IO (Maybe (IO ()))
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` MVar () -> IO (Maybe ())
forall a. MVar a -> IO (Maybe a)
tryTakeMVar MVar ()
mv
compat_forkIOUnmasked :: IO () -> IO ThreadId
#if MIN_VERSION_base(4,4,0)
compat_forkIOUnmasked :: IO () -> IO ThreadId
compat_forkIOUnmasked IO ()
io = ((forall a. IO a -> IO a) -> IO ()) -> IO ThreadId
forkIOWithUnmask (\forall a. IO a -> IO a
_ -> IO ()
io)
#else
compat_forkIOUnmasked = forkIOUnmasked
#endif