akb*_*der 5 haskell asynchronous exception-handling exception
我正在编写各种数据库库.它导出的基本功能如下:
withDatabase :: FilePath -> (DBHandle -> IO a) -> IO a
Run Code Online (Sandbox Code Playgroud)
它自动管理数据库句柄的生命周期.
在内部,withDatabase使用Control.Exception中的bracket函数.
withDatabase path f = bracket (openDatabase path) closeDatabase f
Run Code Online (Sandbox Code Playgroud)
在我的具体情况下,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)
Run Code Online (Sandbox Code Playgroud)
我不确定这段代码是否产生了我想要的效果.
让我们回顾一下withDatabase,这次取代bracket它的定义:
withDatabase path f = mask $ \restore -> do
h <- openDatabase path
r <- restore (f h) `onException` closeDatabase h
_ <- closeDatabase h
return r
Run Code Online (Sandbox Code Playgroud)
在执行的某个时刻,调用堆栈变为以下内容:
\- withDatabase
\- mask
\- openDatabase
\- mask
\- restore
\- doLongStuff
Run Code Online (Sandbox Code Playgroud)
Control.Exception模块的文档包含有关嵌套调用的内容mask:
请注意,传递给mask的参数的恢复操作不一定会取消屏蔽异步异常,它只是将屏蔽状态恢复为封闭上下文的状态.因此,如果已经屏蔽了异步异常,则不能使用掩码再次取消屏蔽异常.
我对这个描述的理解是,doLongStuff可以使用屏蔽的异步异常,而不是像我想的那样解除阻塞.
在我真正的代码,我不能移动既不openFile也不doLongStuff出来openDatabase:其实,openDatabase可以打开任意数量的文件和/或"决定",它处理它想要返回到之前执行各种I/O的withDatabase.鉴于这种约束,doLongStuff即使碰巧在嵌套mask调用中运行,有没有办法实现可中断?
是doLongStuff在数据库上做的吗?如果是这样,它可能已经是可中断的。请参阅有关可中断操作的部分,该部分指出任何可能阻塞的函数(包括大多数执行 IO 的函数)即使在掩码范围内也可以被中断。
如果您不确定是否doLongStuff可中断(取决于它使用的函数),那么您可以使用allowInterrupt轮询屏蔽代码中的异步异常,或者使用同步MVar从数据库读取。这是可行的,因为大多数MVar操作本身都是可中断的,从而允许较大的函数也可中断。