嵌套屏蔽异步异常

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调用中运行,有没有办法实现可中断?

Joh*_*n L 0

doLongStuff在数据库上做的吗?如果是这样,它可能已经是可中断的。请参阅有关可中断操作的部分,该部分指出任何可能阻塞的函数(包括大多数执行 IO 的函数)即使在掩码范围内也可以被中断。

如果您不确定是否doLongStuff可中断(取决于它使用的函数),那么您可以使用allowInterrupt轮询屏蔽代码中的异步异常,或者使用同步MVar从数据库读取。这是可行的,因为大多数MVar操作本身都是可中断的,从而允许较大的函数也可中断。