diff options
author | Ben Gamari <ben@smart-cactus.org> | 2019-04-07 12:50:18 -0400 |
---|---|---|
committer | Ben Gamari <ben@well-typed.com> | 2019-04-13 10:02:36 -0400 |
commit | 40848a43072768d5a0a41a1df05f7a8ffd85f345 (patch) | |
tree | ce9bc17ade171082d9f9a8665fc5fc1b8c5bfe94 /libraries/base/System | |
parent | 2e7b2e55de503d3b5086c0cec5f320667503f699 (diff) | |
download | haskell-40848a43072768d5a0a41a1df05f7a8ffd85f345.tar.gz |
base: Better document implementation implications of Data.Timeout
As noted in #16546 timeout uses asynchronous exceptions internally, an
implementation detail which can leak out in surprising ways. Note this
fact.
Also expose the `Timeout` tycon.
[skip ci]
Diffstat (limited to 'libraries/base/System')
-rw-r--r-- | libraries/base/System/Timeout.hs | 41 |
1 files changed, 25 insertions, 16 deletions
diff --git a/libraries/base/System/Timeout.hs b/libraries/base/System/Timeout.hs index e2b85658bb..df2c0f055a 100644 --- a/libraries/base/System/Timeout.hs +++ b/libraries/base/System/Timeout.hs @@ -16,7 +16,7 @@ -- ------------------------------------------------------------------------------- -module System.Timeout ( timeout ) where +module System.Timeout ( Timeout, timeout ) where #if !defined(mingw32_HOST_OS) import Control.Monad @@ -35,7 +35,11 @@ import Data.Unique (Unique, newUnique) -- interrupt the running IO computation when the timeout has -- expired. -newtype Timeout = Timeout Unique deriving Eq -- ^ @since 4.0 +-- | An exception thrown to a thread by 'timeout' to interrupt a timed-out +-- computation. +-- +-- @since 4.0 +newtype Timeout = Timeout Unique deriving Eq -- | @since 4.0 instance Show Timeout where @@ -67,20 +71,25 @@ instance Exception Timeout where -- another thread. -- -- A tricky implementation detail is the question of how to abort an @IO@ --- computation. This combinator relies on asynchronous exceptions internally. --- The technique works very well for computations executing inside of the --- Haskell runtime system, but it doesn't work at all for non-Haskell code. --- Foreign function calls, for example, cannot be timed out with this --- combinator simply because an arbitrary C function cannot receive --- asynchronous exceptions. When @timeout@ is used to wrap an FFI call that --- blocks, no timeout event can be delivered until the FFI call returns, which --- pretty much negates the purpose of the combinator. In practice, however, --- this limitation is less severe than it may sound. Standard I\/O functions --- like 'System.IO.hGetBuf', 'System.IO.hPutBuf', Network.Socket.accept, or --- 'System.IO.hWaitForInput' appear to be blocking, but they really don't --- because the runtime system uses scheduling mechanisms like @select(2)@ to --- perform asynchronous I\/O, so it is possible to interrupt standard socket --- I\/O or file I\/O using this combinator. +-- computation. This combinator relies on asynchronous exceptions internally +-- (namely throwing the computation the 'Timeout' exception). The technique +-- works very well for computations executing inside of the Haskell runtime +-- system, but it doesn't work at all for non-Haskell code. Foreign function +-- calls, for example, cannot be timed out with this combinator simply because +-- an arbitrary C function cannot receive asynchronous exceptions. When +-- @timeout@ is used to wrap an FFI call that blocks, no timeout event can be +-- delivered until the FFI call returns, which pretty much negates the purpose +-- of the combinator. In practice, however, this limitation is less severe than +-- it may sound. Standard I\/O functions like 'System.IO.hGetBuf', +-- 'System.IO.hPutBuf', Network.Socket.accept, or 'System.IO.hWaitForInput' +-- appear to be blocking, but they really don't because the runtime system uses +-- scheduling mechanisms like @select(2)@ to perform asynchronous I\/O, so it +-- is possible to interrupt standard socket I\/O or file I\/O using this +-- combinator. +--- +-- Note that 'timeout' cancels the computation by throwing it the 'Timeout' +-- exception. Consequently blanket exception handlers (e.g. catching +-- 'SomeException') within the computation will break the timeout behavior. timeout :: Int -> IO a -> IO (Maybe a) timeout n f | n < 0 = fmap Just f |