Safe Haskell | None |
---|---|
Language | Haskell2010 |
An alternative implementation of timeout
for platforms
(i.e. Windows) where the standard implementation is too expensive.
The implementation provided here is for the special case where only one timeout is active at once. A concurrent implementation would be possible but is not currently needed.
Synopsis
- type TimeoutFn (m :: Type -> Type) = forall a. DiffTime -> m a -> m (Maybe a)
- withTimeoutSerial :: (MonadAsync m, MonadFork m, MonadMonotonicTime m, MonadTimer m, MonadMask m, MonadThrow (STM m)) => (TimeoutFn m -> m b) -> m b
- withTimeoutSerialNative :: (MonadAsync m, MonadFork m, MonadMonotonicTime m, MonadTimer m, MonadMask m, MonadThrow (STM m)) => (TimeoutFn m -> m b) -> m b
- withTimeoutSerialAlternative :: (MonadAsync m, MonadFork m, MonadMonotonicTime m, MonadTimer m, MonadMask m, MonadThrow (STM m)) => (TimeoutFn m -> m b) -> m b
Documentation
type TimeoutFn (m :: Type -> Type) = forall a. DiffTime -> m a -> m (Maybe a) Source #
The type of the timeout
function.
withTimeoutSerial :: (MonadAsync m, MonadFork m, MonadMonotonicTime m, MonadTimer m, MonadMask m, MonadThrow (STM m)) => (TimeoutFn m -> m b) -> m b Source #
A timeout
that is reasonably efficient for all platforms.
On Unix it uses exactly timeout
and on Windows it uses
withTimeoutSerialAlternative
.
withTimeoutSerial $ \timeout -> -- now use timeout as one would use System.Timeout.timeout -- but not concurrently!
The implementation has a serial constraint: the body action that calls
timeout
can only do so from one thread at once.
withTimeoutSerialNative :: (MonadAsync m, MonadFork m, MonadMonotonicTime m, MonadTimer m, MonadMask m, MonadThrow (STM m)) => (TimeoutFn m -> m b) -> m b Source #
A timeout
that is reasonably efficient for all platforms.
On Unix it uses exactly timeout
and on Windows it uses
withTimeoutSerialAlternative
.
withTimeoutSerial $ \timeout -> -- now use timeout as one would use System.Timeout.timeout -- but not concurrently!
The implementation has a serial constraint: the body action that calls
timeout
can only do so from one thread at once.
This version simply passes the native platform's timeout
.
withTimeoutSerialAlternative :: (MonadAsync m, MonadFork m, MonadMonotonicTime m, MonadTimer m, MonadMask m, MonadThrow (STM m)) => (TimeoutFn m -> m b) -> m b Source #
An alternative implementation of timeout
for platforms
where the standard implementation is too expensive.
withTimeoutSerial $ \timeout -> -- now use timeout as one would use System.Timeout.timeout -- but not concurrently!
This implementation has a serial constraint: the body action that calls
timeout
can only do so from one thread at once.
Further details for the curious:
the problem with System.Timeout.timeout
is that (as of base 4.12) it has
two implementations, one for Unix with the threaded RTS, and one for all
other configurations.
The Unix threaded RTS implementation is rather clever and very fast. In the
normal case of no timeout, it only has to allocate a timer entry. Only in
the case the timeout occurs does it allocate a forkIO
thread to do the
potentially-blocking operation of sending the asynchronous exception to
interrupt the action under timeout.
The implementation for all other configurations has to allocate a forkIO
thread up front, whether or not the timeout fires. This is fine for many
applications but not for network timers which have to be created and
altered/cancelled with very high frequency.
The implementation here only relies upon threadDelay
which has an
efficient implementation on all platforms. It uses a separate monitoring
thread which will throw an exception to terminate the action if the
timeout expires. This is why it uses the "with" style: because it keeps
a monitoring thread over a region of execution that may use many timeouts.
The cost of allocating this thread is amortised over all the timeouts used.
This implementation is simplified by the constraint that the timeouts only be used serially. In addition, it has the limitation that timeouts may not always be detected promptly: e.g. a 10s timeout on an action that finishes immediately, followed by a 5s timeout on an action will only actually be interrupted after 10s. So it's possible that action with the 5s timeout runs for up to 10s. This is ok for many applications provided that the variance in timeouts is not too large and the timeouts don't need to be too tight.