7

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

问题是函数存在后并没有清理套接字文件,这意味着它需要手动清理。这是一个简单的示例,它基本上创建了一个回显服务器。

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函数在终结器中忽略该值。

4

1 回答 1

9

在解决resourcet问题之前:

  1. resourcet在这种情况下不需要。你可以只使用这个finally函数来做这样的事情,例如runUnixServer settings (\ad -> ...)finally removeLink "foobar.sock"
  2. 这实际上看起来像是有问题的行为。管道中普遍接受的模式是,如果您分配资源,您有责任清理它。unix socket 代码不是我写的,所以作者这里可能有不同的做法。但值得打开一个错误报告。

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

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

handle <- openFile fp ReadMode
register $ hClose handle

此代码易受在openFileandregister调用之间引发的异步异常的影响。由于您没有分配这样的资源,register所以很好。

于 2014-05-22T04:17:14.363 回答