我正在写一个数据库库的种类。它输出的基本功能如下:异步异常的嵌套屏蔽
withDatabase :: FilePath -> (DBHandle -> IO a) -> IO a
它自动管理数据库句柄的生存期。
在内部,withDatabase
使用bracket
函数从Control.Exception。
withDatabase path f = bracket (openDatabase path) closeDatabase f
以我特定情况下,可以openDatabase
执行一些显著I/O,因此阻止了很长的时间。出于这个原因,我想运行异步异常的一部分unmasked。 (简化)实现可以是:
openDatabase :: FilePath -> IO DBHandle
openDatabase path = mask $ \restore -> do
h <- openFile path ReadWriteMode
restore (doLongStuff h) `onException` (hClose h)
...
return (DBHandle h)
我不知道该代码生成我打算效果。
让我们回头看看withDatabase
,此时它的定义替换bracket
:
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
:
请注意,传递给掩码参数的还原操作不一定会揭示异步异常,它只是将掩码状态恢复为封闭上下文的屏蔽状态。因此,如果异步异常已被屏蔽,则不能再使用屏蔽来取消屏蔽异常。
我对此描述的理解是,doLongStuff
将与异步异常一起使用,而不是像我想的那样屏蔽。
在我真正的代码,我不能动也不openFile
也不doLongStuff
出openDatabase
:其实openDatabase
可以打开任意数量的文件和/或“决定”,它处理它想回到withDatabase
之前执行各种I/O的。鉴于这种限制,是否有任何方法可以使doLongStuff
中断,即使它碰巧在嵌套的mask
调用中运行?