summaryrefslogtreecommitdiff
path: root/libraries/base/System/Posix/Internals.hs
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/base/System/Posix/Internals.hs')
-rw-r--r--libraries/base/System/Posix/Internals.hs68
1 files changed, 66 insertions, 2 deletions
diff --git a/libraries/base/System/Posix/Internals.hs b/libraries/base/System/Posix/Internals.hs
index 4bdeabe1bd..2772a66c75 100644
--- a/libraries/base/System/Posix/Internals.hs
+++ b/libraries/base/System/Posix/Internals.hs
@@ -1,6 +1,9 @@
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE InterruptibleFFI #-}
{-# LANGUAGE CPP, NoImplicitPrelude, CApiFFI #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE UnboxedTuples #-}
+{-# LANGUAGE MagicHash #-}
{-# OPTIONS_HADDOCK not-home #-}
-----------------------------------------------------------------------------
@@ -356,9 +359,70 @@ type CFilePath = CWString
foreign import ccall unsafe "HsBase.h __hscore_open"
c_open :: CFilePath -> CInt -> CMode -> IO CInt
--- e.g. use `interruptible` rather than `safe` due to #17912.
+-- | The same as 'c_safe_open', but an /interruptible operation/
+-- as described in "Control.Exception"—it respects `uninterruptibleMask`
+-- but not `mask`.
+--
+-- We want to be able to interrupt an openFile call if
+-- it's expensive (NFS, FUSE, etc.), and we especially
+-- need to be able to interrupt a blocking open call.
+-- See #17912.
+c_interruptible_open :: CFilePath -> CInt -> CMode -> IO CInt
+c_interruptible_open filepath oflags mode =
+ getMaskingState >>= \case
+ -- If we're in an uninterruptible mask, there's basically
+ -- no point in using an interruptible FFI call. The open system call
+ -- will be interrupted, but the exception won't be delivered
+ -- unless the caller manually futzes with the masking state. So
+ -- then the caller (assuming they're following the usual conventions)
+ -- will retry the call (in response to EINTR), and we've just
+ -- wasted everyone's time.
+ MaskedUninterruptible -> c_safe_open_ filepath oflags mode
+ _ -> do
+ open_res <- c_interruptible_open_ filepath oflags mode
+ -- c_interruptible_open_ is an interruptible foreign call.
+ -- If the call is interrupted by an exception handler
+ -- before the system call has returned (so the file is
+ -- not yet open), we want to deliver the exception.
+ -- In point of fact, we deliver any pending exception
+ -- here regardless of the *reason* the system call
+ -- fails.
+ when (open_res == -1) $
+ if hostIsThreaded
+ then
+ -- Control.Exception.allowInterrupt, inlined to avoid
+ -- messing with any Haddock links.
+ interruptible (pure ())
+ else
+ -- Try to make this work somewhat better on the non-threaded
+ -- RTS. See #8684. This inlines the definition of `yield`; module
+ -- dependencies look pretty hairy here and I don't want to make
+ -- things worse for one little wrapper.
+ interruptible (IO $ \s -> (# yield# s, () #))
+ pure open_res
+
foreign import ccall interruptible "HsBase.h __hscore_open"
- c_safe_open :: CFilePath -> CInt -> CMode -> IO CInt
+ c_interruptible_open_ :: CFilePath -> CInt -> CMode -> IO CInt
+
+-- | Consult the RTS to find whether it is threaded.
+hostIsThreaded :: Bool
+hostIsThreaded = rtsIsThreaded_ /= 0
+
+foreign import ccall unsafe "rts_isThreaded" rtsIsThreaded_ :: Int
+
+c_safe_open :: CFilePath -> CInt -> CMode -> IO CInt
+c_safe_open filepath oflags mode =
+ getMaskingState >>= \case
+ -- When exceptions are unmasked, we use an interruptible
+ -- open call. If the system call is successfully
+ -- interrupted, the situation will be the same as if
+ -- the exception had arrived before this function was
+ -- called.
+ Unmasked -> c_interruptible_open_ filepath oflags mode
+ _ -> c_safe_open_ filepath oflags mode
+
+foreign import ccall safe "HsBase.h __hscore_open"
+ c_safe_open_ :: CFilePath -> CInt -> CMode -> IO CInt
foreign import ccall unsafe "HsBase.h __hscore_fstat"
c_fstat :: CInt -> Ptr CStat -> IO CInt