summaryrefslogtreecommitdiff
path: root/src/Mmap.hs
blob: bfe60429813546d21ca90cf6cac6a1d47031cd80 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
{-# LANGUAGE ForeignFunctionInterface #-}
module Mmap where

import Control.Exception
import Data.ByteString (ByteString)
import qualified Data.ByteString.Unsafe as BS
import Foreign
import Foreign.C.Types
import System.Posix.IO
import System.Posix.Types

foreign import ccall "ircbrowse_mmap"
  c_mmap :: CInt -> Ptr CSize -> IO (Ptr Word8)
  --        fd      out length

foreign import ccall "ircbrowse_munmap"
  c_munmap :: Ptr Word8 -> CSize -> IO ()
  --          addr         length

mapFile :: FilePath -> IO ByteString
mapFile path = mask_ $ do
  -- open can fail without repercussions
  Fd fd <- openFd path ReadOnly defaultFileFlags

  -- do the mmap; if it fails, close the file, ignoring exceptions there
  (addr, filelen) <-
    catchNoPropagate @SomeException
      (alloca $ \lengthp -> do
        addr <- c_mmap fd lengthp
        lengthval <- peek lengthp
        return (addr, lengthval))
      (\e -> do
          catch @SomeException (closeFd (Fd fd))
            (\_ -> return ())
          rethrowIO e)

  -- mmap succeeded, close the file
  catchNoPropagate @SomeException (closeFd (Fd fd))
    (\e -> do
      -- putStrLn ("[munmap " ++ show addr ++ " as close(2) handler]")
      c_munmap addr filelen
      rethrowIO e)

  -- close succeeded, we're safe now since bytestring construction will not
  -- fail (and no exceptions are coming from outside as we're masked)
  if addr == nullPtr
    then fail "mapFile: could not mmap"
    else BS.unsafePackCStringFinalizer addr (fromIntegral @CSize @Int filelen)
            (do -- putStrLn ("[munmap " ++ show addr ++ "]")
                c_munmap addr filelen)