{-# LANGUAGE CPP #-}
#if defined(javascript_HOST_ARCH)
{-# LANGUAGE Safe #-}
module GHC.Internal.Event.TimerManager () where
#else
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE Trustworthy #-}
module GHC.Internal.Event.TimerManager
(
TimerManager
, new
, newWith
, newDefaultBackend
, emControl
, finished
, loop
, step
, shutdown
, cleanup
, wakeManager
, TimeoutCallback
, TimeoutKey
, registerTimeout
, updateTimeout
, unregisterTimeout
) where
#include "EventConfig.h"
import GHC.Internal.Control.Exception (finally)
import GHC.Internal.Data.Foldable (sequence_)
import GHC.Internal.Data.IORef (IORef, atomicModifyIORef', mkWeakIORef, newIORef, readIORef,
writeIORef)
import GHC.Internal.Base
import GHC.Internal.Clock (getMonotonicTimeNSec)
import GHC.Internal.Conc.Signal (runHandlers)
import GHC.Internal.Enum (maxBound)
import GHC.Internal.Num (Num(..))
import GHC.Internal.Real (quot, fromIntegral)
import GHC.Internal.Show (Show(..))
import GHC.Internal.Event.Control
import GHC.Internal.Event.Internal (Backend, Event, evtRead, Timeout(..))
import GHC.Internal.Event.Unique (UniqueSource, newSource, newUnique)
import GHC.Internal.Event.TimeOut
import GHC.Internal.System.Posix.Types (Fd)
import qualified GHC.Internal.Event.Internal as I
import qualified GHC.Internal.Event.PSQ as Q
#if defined(HAVE_POLL)
import qualified GHC.Internal.Event.Poll as Poll
#else
# error not implemented for this operating system
#endif
data State = Created
| Running
| Dying
| Finished
deriving ( State -> State -> Bool
(State -> State -> Bool) -> (State -> State -> Bool) -> Eq State
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: State -> State -> Bool
== :: State -> State -> Bool
$c/= :: State -> State -> Bool
/= :: State -> State -> Bool
Eq
, Int -> State -> ShowS
[State] -> ShowS
State -> String
(Int -> State -> ShowS)
-> (State -> String) -> ([State] -> ShowS) -> Show State
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> State -> ShowS
showsPrec :: Int -> State -> ShowS
$cshow :: State -> String
show :: State -> String
$cshowList :: [State] -> ShowS
showList :: [State] -> ShowS
Show
)
data TimerManager = TimerManager
{ TimerManager -> Backend
emBackend :: !Backend
, TimerManager -> IORef TimeoutQueue
emTimeouts :: {-# UNPACK #-} !(IORef TimeoutQueue)
, TimerManager -> IORef State
emState :: {-# UNPACK #-} !(IORef State)
, TimerManager -> UniqueSource
emUniqueSource :: {-# UNPACK #-} !UniqueSource
, TimerManager -> Control
emControl :: {-# UNPACK #-} !Control
}
handleControlEvent :: TimerManager -> Fd -> Event -> IO ()
handleControlEvent :: TimerManager -> Fd -> Event -> IO ()
handleControlEvent TimerManager
mgr Fd
fd Event
_evt = do
msg <- Control -> Fd -> IO ControlMessage
readControlMessage (TimerManager -> Control
emControl TimerManager
mgr) Fd
fd
case msg of
ControlMessage
CMsgWakeup -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
ControlMessage
CMsgDie -> IORef State -> State -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef (TimerManager -> IORef State
emState TimerManager
mgr) State
Finished
CMsgSignal ForeignPtr Word8
fp Signal
s -> ForeignPtr Word8 -> Signal -> IO ()
runHandlers ForeignPtr Word8
fp Signal
s
newDefaultBackend :: IO Backend
#if defined(HAVE_POLL)
newDefaultBackend :: IO Backend
newDefaultBackend = IO Backend
Poll.new
#else
newDefaultBackend = errorWithoutStackTrace "no back end for this platform"
#endif
new :: IO TimerManager
new :: IO TimerManager
new = Backend -> IO TimerManager
newWith (Backend -> IO TimerManager) -> IO Backend -> IO TimerManager
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO Backend
newDefaultBackend
newWith :: Backend -> IO TimerManager
newWith :: Backend -> IO TimerManager
newWith Backend
be = do
timeouts <- TimeoutQueue -> IO (IORef TimeoutQueue)
forall a. a -> IO (IORef a)
newIORef TimeoutQueue
forall v. IntPSQ v
Q.empty
ctrl <- newControl True
state <- newIORef Created
us <- newSource
_ <- mkWeakIORef state $ do
st <- atomicModifyIORef' state $ \State
s -> (State
Finished, State
s)
when (st /= Finished) $ do
I.delete be
closeControl ctrl
let mgr = TimerManager { emBackend :: Backend
emBackend = Backend
be
, emTimeouts :: IORef TimeoutQueue
emTimeouts = IORef TimeoutQueue
timeouts
, emState :: IORef State
emState = IORef State
state
, emUniqueSource :: UniqueSource
emUniqueSource = UniqueSource
us
, emControl :: Control
emControl = Control
ctrl
}
_ <- I.modifyFd be (controlReadFd ctrl) mempty evtRead
_ <- I.modifyFd be (wakeupReadFd ctrl) mempty evtRead
return mgr
shutdown :: TimerManager -> IO ()
shutdown :: TimerManager -> IO ()
shutdown TimerManager
mgr = do
state <- IORef State -> (State -> (State, State)) -> IO State
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' (TimerManager -> IORef State
emState TimerManager
mgr) ((State -> (State, State)) -> IO State)
-> (State -> (State, State)) -> IO State
forall a b. (a -> b) -> a -> b
$ \State
s -> (State
Dying, State
s)
when (state == Running) $ sendDie (emControl mgr)
finished :: TimerManager -> IO Bool
finished :: TimerManager -> IO Bool
finished TimerManager
mgr = (State -> State -> Bool
forall a. Eq a => a -> a -> Bool
== State
Finished) (State -> Bool) -> IO State -> IO Bool
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
`liftM` IORef State -> IO State
forall a. IORef a -> IO a
readIORef (TimerManager -> IORef State
emState TimerManager
mgr)
cleanup :: TimerManager -> IO ()
cleanup :: TimerManager -> IO ()
cleanup TimerManager
mgr = do
IORef State -> State -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef (TimerManager -> IORef State
emState TimerManager
mgr) State
Finished
Backend -> IO ()
I.delete (TimerManager -> Backend
emBackend TimerManager
mgr)
Control -> IO ()
closeControl (TimerManager -> Control
emControl TimerManager
mgr)
loop :: TimerManager -> IO ()
loop :: TimerManager -> IO ()
loop TimerManager
mgr = do
state <- IORef State -> (State -> (State, State)) -> IO State
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' (TimerManager -> IORef State
emState TimerManager
mgr) ((State -> (State, State)) -> IO State)
-> (State -> (State, State)) -> IO State
forall a b. (a -> b) -> a -> b
$ \State
s -> case State
s of
State
Created -> (State
Running, State
s)
State
_ -> (State
s, State
s)
case state of
State
Created -> IO ()
go IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO a
`finally` TimerManager -> IO ()
cleanup TimerManager
mgr
State
Dying -> TimerManager -> IO ()
cleanup TimerManager
mgr
State
_ -> do TimerManager -> IO ()
cleanup TimerManager
mgr
String -> IO ()
forall a. String -> a
errorWithoutStackTrace (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"GHC.Internal.Event.Manager.loop: state is already " String -> ShowS
forall a. [a] -> [a] -> [a]
++
State -> String
forall a. Show a => a -> String
show State
state
where
go :: IO ()
go = do running <- TimerManager -> IO Bool
step TimerManager
mgr
when running go
step :: TimerManager -> IO Bool
step :: TimerManager -> IO Bool
step TimerManager
mgr = do
timeout <- IO Timeout
mkTimeout
_ <- I.poll (emBackend mgr) (Just timeout) (handleControlEvent mgr)
state <- readIORef (emState mgr)
state `seq` return (state == Running)
where
mkTimeout :: IO Timeout
mkTimeout :: IO Timeout
mkTimeout = do
now <- IO Word64
getMonotonicTimeNSec
(expired, timeout) <- atomicModifyIORef' (emTimeouts mgr) $ \TimeoutQueue
tq ->
let ([Elem (IO ())]
expired, TimeoutQueue
tq') = Word64 -> TimeoutQueue -> ([Elem (IO ())], TimeoutQueue)
forall v. Word64 -> IntPSQ v -> ([Elem v], IntPSQ v)
Q.atMost Word64
now TimeoutQueue
tq
timeout :: Timeout
timeout = case TimeoutQueue -> Maybe (Elem (IO ()), TimeoutQueue)
forall v. IntPSQ v -> Maybe (Elem v, IntPSQ v)
Q.minView TimeoutQueue
tq' of
Maybe (Elem (IO ()), TimeoutQueue)
Nothing -> Timeout
Forever
Just (Q.E Key
_ Word64
t IO ()
_, TimeoutQueue
_) ->
let t' :: Word64
t' = Word64
t Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
now in Word64
t' Word64 -> Timeout -> Timeout
forall a b. a -> b -> b
`seq` Word64 -> Timeout
Timeout Word64
t'
in (TimeoutQueue
tq', ([Elem (IO ())]
expired, Timeout
timeout))
sequence_ $ map Q.value expired
return timeout
wakeManager :: TimerManager -> IO ()
wakeManager :: TimerManager -> IO ()
wakeManager TimerManager
mgr = Control -> IO ()
sendWakeup (TimerManager -> Control
emControl TimerManager
mgr)
expirationTime :: Int -> IO Q.Prio
expirationTime :: Int -> IO Word64
expirationTime Int
us = do
now <- IO Word64
getMonotonicTimeNSec
let expTime
| (Word64
forall a. Bounded a => a
maxBound Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
now) Word64 -> Word64 -> Word64
forall a. Integral a => a -> a -> a
`quot` Word64
1000 Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
< Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
us = Word64
forall a. Bounded a => a
maxBound
| Bool
otherwise = Word64
now Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
ns
where ns :: Word64
ns = Word64
1000 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
* Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
us
return expTime
registerTimeout :: TimerManager -> Int -> TimeoutCallback -> IO TimeoutKey
registerTimeout :: TimerManager -> Int -> IO () -> IO TimeoutKey
registerTimeout TimerManager
mgr Int
us IO ()
cb = do
!key <- UniqueSource -> IO Key
newUnique (TimerManager -> UniqueSource
emUniqueSource TimerManager
mgr)
if us <= 0 then cb
else do
expTime <- expirationTime us
editTimeouts mgr (Q.unsafeInsertNew key expTime cb)
return $ TK key
unregisterTimeout :: TimerManager -> TimeoutKey -> IO ()
unregisterTimeout :: TimerManager -> TimeoutKey -> IO ()
unregisterTimeout TimerManager
mgr (TK Key
key) =
TimerManager -> TimeoutEdit -> IO ()
editTimeouts TimerManager
mgr (Key -> TimeoutEdit
forall v. Key -> IntPSQ v -> IntPSQ v
Q.delete Key
key)
updateTimeout :: TimerManager -> TimeoutKey -> Int -> IO ()
updateTimeout :: TimerManager -> TimeoutKey -> Int -> IO ()
updateTimeout TimerManager
mgr (TK Key
key) Int
us = do
expTime <- Int -> IO Word64
expirationTime Int
us
editTimeouts mgr (Q.adjust (const expTime) key)
editTimeouts :: TimerManager -> TimeoutEdit -> IO ()
editTimeouts :: TimerManager -> TimeoutEdit -> IO ()
editTimeouts TimerManager
mgr TimeoutEdit
g = do
wake <- IORef TimeoutQueue
-> (TimeoutQueue -> (TimeoutQueue, Bool)) -> IO Bool
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' (TimerManager -> IORef TimeoutQueue
emTimeouts TimerManager
mgr) TimeoutQueue -> (TimeoutQueue, Bool)
f
when wake (wakeManager mgr)
where
f :: TimeoutQueue -> (TimeoutQueue, Bool)
f TimeoutQueue
q = (TimeoutQueue
q', Bool
wake)
where
q' :: TimeoutQueue
q' = TimeoutEdit
g TimeoutQueue
q
wake :: Bool
wake = case TimeoutQueue -> Maybe (Elem (IO ()), TimeoutQueue)
forall v. IntPSQ v -> Maybe (Elem v, IntPSQ v)
Q.minView TimeoutQueue
q of
Maybe (Elem (IO ()), TimeoutQueue)
Nothing -> Bool
True
Just (Q.E Key
_ Word64
t0 IO ()
_, TimeoutQueue
_) ->
case TimeoutQueue -> Maybe (Elem (IO ()), TimeoutQueue)
forall v. IntPSQ v -> Maybe (Elem v, IntPSQ v)
Q.minView TimeoutQueue
q' of
Just (Q.E Key
_ Word64
t1 IO ()
_, TimeoutQueue
_) ->
Word64
t0 Word64 -> Word64 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word64
t1
Maybe (Elem (IO ()), TimeoutQueue)
_ -> Bool
True
#endif