7

の UNIX パッケージをいじってみました。これは基本的に、UNIX ドメイン ソケットを使用して、特にfuncitonconduit-extraを使用してサーバーを簡単に作成できるようにします。runUnixServer

問題は、関数が存在した後、ソケット ファイルをクリーンアップしないことです。つまり、手動でクリーンアップする必要があります。基本的にエコーサーバーを作成する簡単な例を次に示します。

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、それ自体では非同期例外を処理しないということですか? もしそうなら、それによって生成されたハンドラーの1つがrunUnixServerエラーを発生させたときに問題になる可能性がありますか?

私が思いついた 3 番目の最終的な解決策は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 ソケットのコードを書いていません。しかし、バグレポートを開く価値があります。

とはいえ、最初のコードregisterは問題ありません。foobar.sock私が目にする唯一の問題は、作成される前に例外がスローされた場合ですが、私のfinallyソリューションもそれに対して脆弱です。

割り当てと登録に関するコメントは、次のようなコードに関係しています。

handle <- openFile fp ReadMode
register $ hClose handle

openFileこのコードは、とのregister呼び出しの間にスローされる非同期例外に対して脆弱です。このようにリソースを割り当てていないので、問題ありませんregister

于 2014-05-22T04:17:14.363 に答える