使用ResourceT清理资源的正确方法是什么?

时间:2014-05-21 23:46:58

标签: sockets haskell resources conduit

我一直在使用conduit-extra的UNIX软件包,它基本上允许使用UNIX域套接字轻松创建服务器,特别是使用runUnixServer funciton。< / p>

问题是在函数存在之后它不会清理套接字文件,这意味着它需要手动清理。这是一个简单的例子,它基本上创建了一个echo服务器。

main :: IO ()
main = do
  let settings = serverSettings "foobar.sock"
  runUnixServer settings (\ad -> (appSource ad) $$ (appSink ad))

我稍微谷歌了一下,发现在这里处理资源的正确方法是使用resourcet包。虽然问题在于资源中的大多数API都希望我自己分配资源,但runUnixSever的情况并非如此,而register并不会返回任何资源。

起初我以为我可以使用main :: IO () main = runResourceT $ do register $ removeLink "foobar.sock" let settings = serverSettings "foobar.sock" liftIO $ runUnixServer settings (\ad -> (appSource ad) $$ (appSink ad)) 来注册删除文件的函数,例如以下

allocate

这种方法存在问题,至少就register的文档说:

  

这与调用分配然后注册发布操作几乎完全相同,但这正确地处理了异步异常的屏蔽。

这是否意味着runUnixServer本身并不处理异步异常?如果是这样,当allocate(文档说它为每个客户端产生一个线程)产生的处理程序之一引发错误时,这可能是一个问题吗?

我提出的第三个也是最后一个解决方案是使用main :: IO () main = runResourceT $ do allocate (return 1) (const $ removeLink "foobar.sock") let settings = serverSettings "foobar.sock" liftIO $ runUnixServer settings (\ad -> (appSource ad) $$ (appSink ad)) ,以确保正确处理异步异常(我不确定在这种情况下是否真的有必要) )。

(return 1)

但这真的是最好的解决方案吗?由于我创建了一个我永远不会使用const的值,然后使用{{1}}函数忽略终结器中的值。

1 个答案:

答案 0 :(得分:9)

在解决resourcet问题之前:

    在这种情况下,不需要
  1. resourcet。你可以使用finally函数来做这样的事情,例如: runUnixServer settings (\ad -> ...)最后removeLink "foobar.sock"
  2. 这实际上看起来像是有问题的行为。管道中普遍接受的模式是,如果您分配资源,您负责清理它。我没有编写unix套接字代码,因此作者可能有理由在这里采用不同的方式。但是值得开一个错误报告。
  3. 也就是说,您使用register的初始代码很好。我看到的唯一问题是,如果在创建foobar.sock之前抛出异常,尽管我的finally解决方案也容易受到攻击。

    有关allocate vs register的注释与代码如下所示:

    handle <- openFile fp ReadMode
    register $ hClose handle
    

    此代码容易受到openFileregister调用之间抛出的异步异常的影响。由于您没有分配这样的资源,register没问题。