Skip to content

Commit 25e10e6

Browse files
committed
Add File internals that work directly with Path
1 parent 6b5c29d commit 25e10e6

6 files changed

Lines changed: 1227 additions & 0 deletions

File tree

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
module Streamly.Internal.FileSystem.File.Utils
2+
( openFile
3+
, withFile
4+
) where
5+
6+
-------------------------------------------------------------------------------
7+
-- Imports
8+
-------------------------------------------------------------------------------
9+
10+
import Control.Exception (mask, onException, try)
11+
import Control.Monad (when)
12+
import GHC.IO (catchException, unsafePerformIO)
13+
import GHC.IO.Exception (IOException(..))
14+
import GHC.IO.Handle.Internals (addHandleFinalizer, handleFinalizer)
15+
import Streamly.Internal.FileSystem.Path (Path)
16+
import System.IO (IOMode(..), Handle, hSetBinaryMode, hClose)
17+
18+
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
19+
import qualified Streamly.Internal.FileSystem.Windows.File as Platform
20+
#else
21+
import qualified Streamly.Internal.FileSystem.Posix.File as Platform
22+
#endif
23+
24+
import qualified Streamly.Internal.FileSystem.Path as Path
25+
26+
#if !(MIN_VERSION_base(4,16,0))
27+
import Control.Concurrent.MVar (MVar, addMVarFinalizer)
28+
import GHC.IO.Handle.Internals (debugIO)
29+
import GHC.IO.Handle.Types (Handle__, Handle(..))
30+
#endif
31+
32+
-------------------------------------------------------------------------------
33+
-- Utils
34+
-------------------------------------------------------------------------------
35+
36+
#if !(MIN_VERSION_base(4,16,0))
37+
type HandleFinalizer = FilePath -> MVar Handle__ -> IO ()
38+
39+
-- | Add a finalizer to a 'Handle'. Specifically, the finalizer
40+
-- will be added to the 'MVar' of a file handle or the write-side
41+
-- 'MVar' of a duplex handle. See Handle Finalizers for details.
42+
addHandleFinalizer :: Handle -> HandleFinalizer -> IO ()
43+
addHandleFinalizer handle finalizer = do
44+
debugIO $ "Registering finalizer: " ++ show filepath
45+
addMVarFinalizer mv (finalizer filepath mv)
46+
where
47+
!(filepath, !mv) = case handle of
48+
FileHandle fp m -> (fp, m)
49+
DuplexHandle fp _ write_m -> (fp, write_m)
50+
#endif
51+
52+
addFilePathToIOError :: String -> Path -> IOException -> IOException
53+
addFilePathToIOError fun fp ioe = unsafePerformIO $ do
54+
let fp' = Path.toString fp
55+
-- XXX Why is this important?
56+
-- deepseq will be introduced dependency because of this
57+
-- fp'' <- evaluate $ force fp'
58+
pure $ ioe{ ioe_location = fun, ioe_filename = Just fp' }
59+
60+
augmentError :: String -> Path -> IO a -> IO a
61+
augmentError str osfp = flip catchException (ioError . addFilePathToIOError str osfp)
62+
63+
withOpenFile'
64+
:: Path
65+
-> IOMode -> Bool -> Bool -> Bool
66+
-> (Handle -> IO r) -> Bool -> IO r
67+
withOpenFile' fp iomode binary existing cloExec action close_finally =
68+
mask $ \restore -> do
69+
hndl <- case (existing, cloExec) of
70+
(True, False) -> Platform.openExistingFile fp iomode
71+
(False, False) -> Platform.openFile fp iomode
72+
(True, True) -> Platform.openExistingFileWithCloseOnExec fp iomode
73+
(False, True) -> Platform.openFileWithCloseOnExec fp iomode
74+
addHandleFinalizer hndl handleFinalizer
75+
when binary $ hSetBinaryMode hndl True
76+
r <- restore (action hndl) `onException` hClose hndl
77+
when close_finally $ hClose hndl
78+
pure r
79+
80+
-- | Open a file and return the 'Handle'.
81+
openFile :: Path -> IOMode -> IO Handle
82+
openFile osfp iomode =
83+
augmentError "openFile" osfp $ withOpenFile' osfp iomode False False False pure False
84+
85+
-- | Run an action on a file.
86+
--
87+
-- The 'Handle' is automatically closed afther the action.
88+
withFile :: Path -> IOMode -> (Handle -> IO r) -> IO r
89+
withFile osfp iomode act = (augmentError "withFile" osfp
90+
$ withOpenFile' osfp iomode False False False (try . act) True)
91+
>>= either ioError pure

0 commit comments

Comments
 (0)