Search code examples
haskelltmp

Unable to read from temporary file


I'm trying to invoke an external process which writes to a temporary file which I obtain with withSystemTempFile. After the process exits, I'm able to cat the content of that file, but the application itself fails with openFile: resource busy (file is locked) error when trying to read that file with readFile. I followed suggestions in answers to this question and used the lazy version of readFile. Here's an example code:

module Main where

import Prelude hiding (readFile)
import System.Process (createProcess, shell)
import System.IO (Handle, hShow)
import System.IO.Temp (withSystemTempFile)
import System.IO.Strict (readFile)

main :: IO ()
main = withSystemTempFile "temp.txt" doSomeStuff
    where
        doSomeStuff :: FilePath -> Handle -> IO ()
        doSomeStuff tempFilePath tempFilePathHandle = do
            putStrLn tempFilePath
            createProcess $ shell $ "echo \"test\" >> " ++ tempFilePath
            getLine -- It's here just to stop program while I check that I can actually "cat" the temp file
            -- here I'm able to cat the temp file
            contents <- readFile tempFilePath -- here the command fails with /tmp/temp23900-0.txt: openFile: resource busy (file is locked)
            putStrLn contents

I'm still getting my head around Haskell, so I hope it's not something obvious, as I've run out of ideas. What gives?


Solution

  • This error is because withSystemTempFile locks the file upon entering. It gives you its handle in return (called tempFilePathHandle in your code), so you can read the file using the handle, like this:

    contents <- hGetContents tempFilePathHandle
    

    EDIT

    This is because GHC internally implements a readers-writer lock to track which files it has opened, and what permissions are required. A readers-writer lock allows either only one writer (exclusive access), or multiple readers (shared access).

    In this case, withSystemTempFile gets a writer lock on the temp file, and therefore readFile can't get the reader lock it needs (because, again, the writer lock prevents GHC from getting any reader locks on that file).

    Here is a link to the C code implementing the lock. As @luqui suggests in the comment below, it perhaps isn't an optimal solution, especially since GHC doesn't request any OS-level locks, so processes like cat can still access and modify the files. It might make more sense in a lazy context, where reading and writing on a file would produce hard-to-predict results.