summaryrefslogtreecommitdiff
path: root/libraries/base/System
diff options
context:
space:
mode:
authorBen Gamari <ben@smart-cactus.org>2019-04-07 12:50:18 -0400
committerBen Gamari <ben@well-typed.com>2019-04-13 10:02:36 -0400
commit40848a43072768d5a0a41a1df05f7a8ffd85f345 (patch)
treece9bc17ade171082d9f9a8665fc5fc1b8c5bfe94 /libraries/base/System
parent2e7b2e55de503d3b5086c0cec5f320667503f699 (diff)
downloadhaskell-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.hs41
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