Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions core/src/Streamly/Internal/FileSystem/File/Common.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
module Streamly.Internal.FileSystem.File.Common
( withFile
, openFile
) where

-------------------------------------------------------------------------------
-- Imports
-------------------------------------------------------------------------------

import Control.Exception (mask, onException, try)
import Control.Monad (when)
import GHC.IO (catchException)
import GHC.IO.Exception (IOException(..))
import GHC.IO.Handle.Internals (handleFinalizer)
import Streamly.Internal.FileSystem.Path (Path)
import System.IO (IOMode(..), Handle, hSetBinaryMode, hClose)

import qualified Streamly.Internal.FileSystem.Path as Path

#if MIN_VERSION_base(4,16,0)
import GHC.IO.Handle.Internals (addHandleFinalizer)
#else
import Control.Concurrent.MVar (MVar, addMVarFinalizer)
import GHC.IO.Handle.Types (Handle__, Handle(..))
#endif

-------------------------------------------------------------------------------
-- Utils
-------------------------------------------------------------------------------

#if !(MIN_VERSION_base(4,16,0))
type HandleFinalizer = FilePath -> MVar Handle__ -> IO ()

-- | Add a finalizer to a 'Handle'. Specifically, the finalizer
-- will be added to the 'MVar' of a file handle or the write-side
-- 'MVar' of a duplex handle. See Handle Finalizers for details.
addHandleFinalizer :: Handle -> HandleFinalizer -> IO ()
addHandleFinalizer handle finalizer = do
addMVarFinalizer mv (finalizer filepath mv)
where
!(filepath, !mv) = case handle of
FileHandle fp m -> (fp, m)
DuplexHandle fp _ write_m -> (fp, write_m)
#endif

{-# INLINE withOpenFile #-}
withOpenFile
:: Bool
-> Bool
-> (Path -> IOMode -> IO Handle)
-> Path
-> IOMode
-> (Handle -> IO r)
-> IO r
withOpenFile binary close_finally f fp iomode action =
mask $ \restore -> do
h <- f fp iomode
-- XXX In case of withFile it will be closed anyway, so do we even need
-- this?
addHandleFinalizer h handleFinalizer
when binary $ hSetBinaryMode h True
r <- restore (action h) `onException` hClose h
when close_finally $ hClose h
pure r

addFilePathToIOError :: String -> Path -> IOException -> IOException
addFilePathToIOError fun fp ioe =
let !str = Path.toString fp
in ioe
{ ioe_location = fun
, ioe_filename = Just str
}

{-# INLINE catchWith #-}
catchWith :: String -> Path -> IO a -> IO a
catchWith str path io =
catchException io (ioError . addFilePathToIOError str path)

{-# INLINE withFile #-}
withFile ::
Bool
-> (Path -> IOMode -> IO Handle)
-> Path
-> IOMode
-> (Handle -> IO r)
-> IO r
withFile binary f path iomode act =
catchWith "withFile" path
(withOpenFile binary True f path iomode (try . act))
>>= either ioError pure

{-# INLINE openFile #-}
openFile ::
Bool -> (Path -> IOMode -> IO Handle) -> Path -> IOMode -> IO Handle
openFile binary f path iomode =
catchWith "openFile" path
$ withOpenFile binary False f path iomode pure
Loading
Loading