Proposal: Add newTimeoutKey and insertTimeout to System.Event
Bas van Dijk
v.dijk.bas at gmail.com
Wed Mar 16 12:09:23 CET 2011
I would like to propose adding and exporting the following functions
from System.Event.Manager and also export them from System.Event:
-- | Returns a unique 'TimeoutKey' that can be used by 'insertTimeout'.
newTimeoutKey :: EventManager -> IO TimeoutKey
newTimeoutKey = fmap TK . newUnique . emUniqueSource
-- | Like 'registerTimeout' but registers the timeout in the given number of
-- microseconds /at the specified key/ in the event manager. If the key was
-- already present the associated timeout is replaced with the given timeout.
-- Unique keys can be created using 'newTimeoutKey'.
insertTimeout :: EventManager -> TimeoutKey -> Int -> TimeoutCallback -> IO ()
insertTimeout mgr (TK key) us cb = do
expTime <- fromNow us
-- We intentionally do not evaluate the modified map to WHNF here.
-- Instead, we leave a thunk inside the IORef and defer its
-- evaluation until mkTimeout in the event loop. This is a
-- workaround for a nasty IORef contention problem that causes the
-- thread-delay benchmark to take 20 seconds instead of 0.2.
atomicModifyIORef (emTimeouts mgr) $ \f ->
let f' = (Q.insert key expTime cb) . f in (f', ())
'fromNow' is an internal utility function defined as:
-- | @fromNow us@ returns the time in seconds @us@ microseconds from now.
fromNow :: Int -> IO Double
fromNow us = do
now <- getCurrentTime
return $ fromIntegral us / 1000000.0 + now
The existing registerTimeout can be rewritten in terms of these:
registerTimeout :: EventManager -> Int -> TimeoutCallback -> IO TimeoutKey
registerTimeout mgr us cb = do
!tk <- newTimeoutKey mgr
if us <= 0
else insertTimeout mgr tk us cb
The reason I would like to have these is that with them I can write
more efficient resettable timeouts. A resettable timeout API looks
something like this:
register :: EventManager -> Int -> TimeoutCallback -> IO Key
pause :: Key -> IO ()
reset :: Key -> IO ()
cancel :: Key -> IO ()
'register mgr us cb' registers a callback 'cb' with the event manager
'mgr' to fire after 'us' microseconds. It returns a key which can be
used to control the timeout. When 'pause' is called on a key,
belonging to a timeout which hasn't fired yet, the timeout is
suspended indefinitely until it is reset or canceled. When 'reset' is
called on a key, belonging to a timeout which hasn't fired yet, the
timeout period will be extended with at least the original period.
'cancel' removes the timeout from the event manager.
The Warp web-server contains one implementation of resettable timeouts:
Note that their API is a bit more restricted but the idea is the same.
The implementation uses one thread which runs a loop which delays for
the timeout period then processes all the registered timeouts.
Registered timeouts are put in a list and stored in an IORef.
With the proposed additional functions for System.Event I can write a
simpler (and hopefully slightly more efficient) implementation of
resettable timeouts which don't need to forkIO and don't need to store
timeouts in a list:
I also wrote a CPS-like version:
Discussion deadline: 2 weeks.
A patch for base is attached.
-------------- next part --------------
A non-text attachment was scrubbed...
Size: 72993 bytes
Desc: not available
More information about the Libraries