Proposal: Add newTimeoutKey and insertTimeout to System.Event

Hello, 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', ()) wakeManager mgr '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 then cb else insertTimeout mgr tk us cb return tk 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: data Key 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: https://github.com/snoyberg/warp/blob/master/Timeout.hs 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: https://github.com/basvandijk/resettable-timeouts/blob/master/System/Timeout... I also wrote a CPS-like version: https://github.com/basvandijk/resettable-timeouts/blob/master/System/Timeout... Discussion deadline: 2 weeks. A patch for base is attached. Regards, Bas

Hi,
You might want to read
http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#code_ev_timer_code_rela...
. The current design is based on a subset of libev's API (i.e. we
don't support resettable or periodic timers at the moment).
On Wed, Mar 16, 2011 at 6:09 PM, Bas van Dijk
Hello,
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
I'm worried that if we expose this function in the API we'd have a hard time to switch the implementation to e.g. a mutable one that no longer uses Uniques. While we can manufacture some kind of token (e.g. a pointer) to represent a position in a mutable data structure it might not be possible to do so without actually inserting something into the data structure. Perhaps it's possible though. libev has a separate type representing timers themselves, rather than a TimeoutKey like we use. It seems like your use case can be implemented in terms of just registerTimeout and updateTimeout, so do you really need this function?
-- | 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 ()
I'd prefer to call this 'updateTimeout' as "insert" sounds more like an implementation detail.
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:
data Key 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.
Doesn't this mean that we'll have to store the period somewhere, together with the key?
The Warp web-server contains one implementation of resettable timeouts:
https://github.com/snoyberg/warp/blob/master/Timeout.hs
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 think it's important to figure out if this scheme is actually faster than using register/unregister. If the only gain is a bit of convenience in user code I'd be wary of adding complexity to the I/O manager itself, as it involves changes to base, which can only be release so often.
Discussion deadline: 2 weeks.
Note that the I/O manager is not managed under the libraries process, at least not yet. System.Event is still considered a GHC internal API (we never proposed it using the libraries process in the first place). It should probably have been renamed to GHC.Event until it was proposed but that never happened. I sent a patch to the GHC mailing list that updated the documentation to state that this API should be considered internal, but the patch was most likely lost somewhere along the road. Johan P.S. I'm in Thailand so I'm a bit slow in replying to emails.

On Sat, Mar 19, 2011 at 03:06:23PM +0700, Johan Tibell wrote:
It should probably have been renamed to GHC.Event until it was proposed but that never happened.
Is there any reason I shouldn't rename it in HEAD now?
I sent a patch to the GHC mailing list that updated the documentation to state that this API should be considered internal, but the patch was most likely lost somewhere along the road.
Looks like it's applied to me? Thanks Ian

On Sat, Mar 19, 2011 at 11:01 PM, Ian Lynagh
On Sat, Mar 19, 2011 at 03:06:23PM +0700, Johan Tibell wrote:
It should probably have been renamed to GHC.Event until it was proposed but that never happened.
Is there any reason I shouldn't rename it in HEAD now?
I wouldn't mind. Note that we might have to rename it back some time in the future. Bryan, any objections?
I sent a patch to the GHC mailing list that updated the documentation to state that this API should be considered internal, but the patch was most likely lost somewhere along the road.
Looks like it's applied to me?
Great! Johan

On Sun, Mar 20, 2011 at 09:37:50PM +0700, Johan Tibell wrote:
On Sat, Mar 19, 2011 at 11:01 PM, Ian Lynagh
wrote: On Sat, Mar 19, 2011 at 03:06:23PM +0700, Johan Tibell wrote:
It should probably have been renamed to GHC.Event until it was proposed but that never happened.
Is there any reason I shouldn't rename it in HEAD now?
I wouldn't mind. Note that we might have to rename it back some time in the future.
No problem. I've renamed it now to minimise confusion in the meantime. Thanks Ian
participants (3)
-
Bas van Dijk
-
Ian Lynagh
-
Johan Tibell