Haskell管道和屏蔽异步异常

时间:2015-03-30 00:31:07

标签: multithreading haskell exception asynchronous conduit

我有以下代码行使用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

我想确保此代码不会被任何异步异常中断。我担心中断可能会导致磁盘上的数据不完整/损坏。

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

谢谢!

1 个答案:

答案 0 :(得分:3)

我建议使用另一种与 conduit 无关的技术:

  • 创建一个新的临时文件
  • 在那里写下你的数据,
  • fsync该文件,以便所有内容都写入磁盘,
  • atomically rename临时文件到目标文件;在POSIX系统上这很容易,因为rename是专为它设计的,在this question上应该有所帮助。

使用此序列,无论发生什么,目标文件将保持不变或将包含完全写入磁盘的新数据。如果序列由于某种原因突然中断,它只会留下过时的临时文件。

unix Haskell库引入fsync only in version 2.7.1.0,但您可以自行添加调用。请参阅this module中的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