私はソートのデータベースライブラリを作成しています。自動的にデータベースハンドルの寿命を管理非同期例外のネストされたマスキング
withDatabase :: FilePath -> (DBHandle -> IO a) -> IO a
:それは輸出する基本的な機能は以下の通りです。
内部では、withDatabase
は、のbracket
機能を使用します.Control.Exception。私の特定の場合には
withDatabase path f = bracket (openDatabase path) closeDatabase f
、openDatabase
は、いくつかの重要なI/Oを実行するため、長い時間のためにブロックすることがあります。このため、非マスク例外を使用して非同期例外の一部を実行したいと考えています。
openDatabase :: FilePath -> IO DBHandle
openDatabase path = mask $ \restore -> do
h <- openFile path ReadWriteMode
restore (doLongStuff h) `onException` (hClose h)
...
return (DBHandle h)
私は、このコードが意図する効果を生み出しているかどうかわかりません。
今回は、その定義とbracket
を交換する、のはwithDatabase
を振り返ってみましょう:
withDatabase path f = mask $ \restore -> do
h <- openDatabase path
r <- restore (f h) `onException` closeDatabase h
_ <- closeDatabase h
return r
実行中のある時点で、コールスタックは、次のようになります:
\- withDatabase
\- mask
\- openDatabase
\- mask
\- restore
\- doLongStuff
ドキュメントについてControl.Exceptionモジュールは、mask
へのネストされた呼び出しについて何かを持っています:
maskへの引数に渡されるrestoreアクションは、必ずしも非同期例外のマスクを解除するとは限らず、マスキング状態を囲むコンテキストの状態に復元するだけであることに注意してください。したがって、非同期例外が既にマスクされている場合、マスクを使用して例外のマスクを解除することはできません。
doLongStuff
は、非同期例外がマスクされていても動作しますが、私が望むように、ブロックされていません。私の実際のコードで
、私はopenDatabase
の外にopenFile
もdoLongStuff
どちらを移動することはできません。実際には、openDatabase
は、任意の数のファイルを開くことができ、および/またはそれがwithDatabase
に復帰したい取り扱う「決定」する前に、さまざまなI/Oを実行します。この制約を受けて、mask
コールの内部で実行されても、doLongStuff
を割り込み可能にする方法はありますか?