【发布时间】: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 可以处理被屏蔽的异步异常,而不是像我希望的那样被解除阻塞。
在我的真实代码中,我无法将openFile 和doLongStuff 移出openDatabase:事实上,openDatabase 可以在“决定”之前打开任意数量的文件和/或执行各种 I/O处理它想返回withDatabase。鉴于这种限制,有没有办法让doLongStuff 可中断,即使它恰好在嵌套的mask 调用中运行?
【问题讨论】:
标签: exception haskell asynchronous exception-handling