Haskell 管道和屏蔽异步异常

Haskell Conduit and masking async exceptions

我有以下代码行使用 aeson 序列化一个 IntMap 并将 JSON 保存到磁盘,全部在子线程上:

    import Data.Aeson (encode, toJSON)
    import Data.Conduit (($$), (=$), yield)
    import qualified Data.ByteString.Lazy as BL (toStrict)
    import qualified Data.Conduit.Binary as CB (sinkFile)
    import qualified Data.Conduit.List as CL (map)

    -- ...

    forkIO . runResourceT $ yield (toJSON intMap) $$ CL.map (BL.toStrict . encode) =$ CB.sinkFile file

我想确保这段代码不会被任何异步异常打断。我担心中断会导致 incomplete/corrupt 磁盘上的数据。

在这种情况下我该怎么做才能确保免受异步异常的影响?是否可以确保即使 main 想要终止也允许子线程完成?

谢谢!

我建议另一种技术,与 conduit 无关:

  • 创建一个新的临时文件,
  • 在那里写下你的数据,
  • fsync文件,让所有东西真正写入磁盘,
  • atomically rename the temporary file to the target one; on POSIX systems this is easy, as rename is designed for it, on Windows this question 应该有帮助。

按照这个序列,无论发生什么情况,目标文件要么保持完整,要么包含新数据,完全写入磁盘。如果序列由于某种原因突然中断,它只会留下一个陈旧的临时文件。

unix Haskell library introduces fsync only in version 2.7.1.0, but it's easy to add the call yourself. See this module in the Ganeti project(在 BSD2 下获得许可)。特别是

foreign import ccall "fsync" fsync :: CInt -> IO CInt

fsyncFile :: FilePath -> IO ()
fsyncFile path =
    bracket (openFd path ReadOnly Nothing defaultFileFlags) closeFd callfsync
  where
    callfsync (Fd fd) = throwErrnoPathIfMinus1_ "fsyncFile" path $ fsync fd