嵌套屏蔽异步异常

时间:2012-01-27 18:34:59

标签: exception haskell asynchronous exception-handling

我正在编写各种数据库库。它导出的基本功能如下:

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将用于屏蔽异步异常,而不是我想要解除阻塞。

在我的真实代码中,我无法将openFiledoLongStuff移出openDatabase:事实上,openDatabase可能会打开任意数量的文件和/或执行各种操作在“决定”处理它想要返回withDatabase之前的I / O.鉴于这种约束,有没有办法使doLongStuff可以中断,即使它恰好在嵌套的mask调用中运行?

1 个答案:

答案 0 :(得分:0)

是否在数据库上完成了doLongStuff?如果是这样,它可能已经可以中断了。请参阅Interruptible Operations部分,该部分声明可以阻止的任何功能,包括执行IO的大多数功能,即使在掩码范围内也可能被中断。

如果您不确定doLongStuff是否可以中断(取决于它使用的功能),那么您可以使用allowInterrupt轮询掩码中的异步异常,或者使用MVar来同步数据库中的读数。这样做是有效的,因为大多数MVar操作本身都是可中断的,允许更大的功能也可以中断。