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

Jak*_*old 7 sockets resources haskell conduit

我一直在使用conduit-extraUNIX软件包,它基本上允许使用UNIX域套接字轻松创建服务器,特别是使用runUnixServer函数.

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

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

我已经谷歌了一下,发现在这里处理资源的正确方法是使用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))
Run Code Online (Sandbox Code Playgroud)

但是这种方法存在问题,至少就文档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))
Run Code Online (Sandbox Code Playgroud)

但这真的是最好的解决方案吗?因为我正在创建一个我永远不会使用的值,(return 1)然后使用const函数来忽略终结器中的值.

Mic*_*man 9

在解决resourcet问题之前:

  1. resourcet在这种情况下不需要.你可以使用这个finally功能,例如runUnixServer settings (\ad -> ...)最后removeLink "foobar.sock".
  2. 这实际上看起来像有问题的行为.管道中普遍接受的模式是,如果您分配资源,您负责清理它.我没有编写unix套接字代码,因此作者可能有理由在这里采用不同的方式.但值得开放一个错误报告.

也就是说,您的初始代码register很好.我看到的唯一问题是如果在foobar.sock创建之前抛出异常,尽管我的finally解决方案也容易受到攻击.

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

handle <- openFile fp ReadMode
register $ hClose handle
Run Code Online (Sandbox Code Playgroud)

此代码容易受到在openFileregister调用之间抛出的异步异常的影响.既然你没有像这样分配资源,那register很好.