2012-01-27 80 views
5

我正在写一个数据库库的种类。它输出的基本功能如下:异步异常的嵌套屏蔽

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

它自动管理数据库句柄的生存期。

在内部,withDatabase使用bracket函数从Control.Exception

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

请注意,传递给掩码参数的还原操作不一定会揭示异步异常,它只是将掩码状态恢复为封闭上下文的屏蔽状态。因此,如果异步异常已被屏蔽,则不能再使用屏蔽来取消屏蔽异常。

我对此描述的理解是,doLongStuff将与异步异常一起使用,而不是像我想的那样屏蔽。

在我真正的代码,我不能动也不openFile也不doLongStuffopenDatabase:其实openDatabase可以打开任意数量的文件和/或“决定”,它处理它想回到withDatabase之前执行各种I/O的。鉴于这种限制,是否有任何方法可以使doLongStuff中断,即使它碰巧在嵌套的mask调用中运行?

回答

0

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

如果你不知道,如果是doLongStuff中断与否(取决于其使用的功能),那么您可以使用allowInterrupt轮询蒙面代码中的异步异常,或者使用MVar同步从数据库中读取。这是可行的,因为大多数MVar操作本身是可中断的,允许更大的功能也是可中断的。