【问题标题】:Nested masking of asynchronous exceptions异步异常的嵌套屏蔽
【发布时间】:2012-02-20 17:42:03
【问题描述】:

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

withDatabase :: FilePath -> (DBHandle -> IO a) -> IO a

自动管理数据库句柄的生命周期。

在内部,withDatabase 使用 Control.Exception 中的 bracket 函数。

withDatabase path f = bracket (openDatabase path) closeDatabase f

在我的具体情况下,openDatabase 可能会执行一些重要的 I/O,因此会阻塞很长时间。出于这个原因,我想在未屏蔽异步异常的情况下运行它的某些部分。一个(简化的)实现可以是:

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 参数的恢复操作不一定会取消屏蔽异步异常,它只是将屏蔽状态恢复到封闭上下文的状态。因此,如果异步异常已被屏蔽,则无法再次使用 mask 取消屏蔽异常。

我对这个描述的理解是doLongStuff 可以处理被屏蔽的异步异常,而不是像我希望的那样被解除阻塞。

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

【问题讨论】:

    标签: exception haskell asynchronous exception-handling


    【解决方案1】:

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

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

    【讨论】:

      猜你喜欢
      • 2015-06-02
      • 1970-01-01
      • 2015-02-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多