我正在编写各种数据库库。它导出的基本功能如下:
withDatabase :: FilePath -> (DBHandle -> IO a) -> IO a
自动管理数据库句柄的生命周期。
在内部,withDatabase
使用 Control.Exception 中的bracket
函数。
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
的嵌套调用的内容:
请注意,传递给mask的参数的恢复操作不一定会取消屏蔽异步异常,它只是将屏蔽状态恢复为封闭上下文的状态。因此,如果已经屏蔽了异步异常,则不能使用掩码再次屏蔽异常。
我对此描述的理解是doLongStuff
将用于屏蔽异步异常,而不是我想要解除阻塞。
在我的真实代码中,我无法将openFile
和doLongStuff
移出openDatabase
:事实上,openDatabase
可能会打开任意数量的文件和/或执行各种操作在“决定”处理它想要返回withDatabase
之前的I / O.鉴于这种约束,有没有办法使doLongStuff
可以中断,即使它恰好在嵌套的mask
调用中运行?
答案 0 :(得分:0)
是否在数据库上完成了doLongStuff
?如果是这样,它可能已经可以中断了。请参阅Interruptible Operations部分,该部分声明可以阻止的任何功能,包括执行IO的大多数功能,即使在掩码范围内也可能被中断。
如果您不确定doLongStuff
是否可以中断(取决于它使用的功能),那么您可以使用allowInterrupt
轮询掩码中的异步异常,或者使用MVar
来同步数据库中的读数。这样做是有效的,因为大多数MVar
操作本身都是可中断的,允许更大的功能也可以中断。