Estoy escribiendo una especie de biblioteca de base de datos. La función básica que exporta es la siguiente:Enmascaramiento anidado de excepciones asincrónicas
withDatabase :: FilePath -> (DBHandle -> IO a) -> IO a
que gestiona automáticamente la vida útil de los identificadores de la base de datos.
Internamente, withDatabase
utiliza la función bracket
de Control.Exception.
withDatabase path f = bracket (openDatabase path) closeDatabase f
En mi caso concreto, openDatabase
puede realizar alguna significativa de E/S y así bloquear durante mucho tiempo. Por esta razón, me gustaría ejecutar una parte de ella con las excepciones asíncronas sin máscara. Una aplicación (simplificado) podría ser:
openDatabase :: FilePath -> IO DBHandle
openDatabase path = mask $ \restore -> do
h <- openFile path ReadWriteMode
restore (doLongStuff h) `onException` (hClose h)
...
return (DBHandle h)
No estoy seguro de que este código está produciendo el efecto que pretendo.
Vamos a mirar hacia atrás en withDatabase
, esta vez reemplazando bracket
con su definición:
withDatabase path f = mask $ \restore -> do
h <- openDatabase path
r <- restore (f h) `onException` closeDatabase h
_ <- closeDatabase h
return r
En un momento determinado en la ejecución, la pila de llamadas se convierte en la siguiente:
\- withDatabase
\- mask
\- openDatabase
\- mask
\- restore
\- doLongStuff
La documentación para el Control.Exception módulo tiene algo sobre llamadas anidadas a mask
:
Tenga en cuenta que la acción de restauración pasada al argumento para enmascarar no desenmascara necesariamente las excepciones asincrónicas, simplemente restaura el estado de enmascaramiento al del contexto circundante. Por lo tanto, si las excepciones asincrónicas ya están enmascaradas, la máscara no se puede usar para volver a desenmascarar las excepciones.
Mi comprensión de esta descripción es que doLongStuff
funcionará con excepciones asíncronas enmascaradas y no, como me gustaría, desbloqueadas.
En mi código real, no me puedo mover ni openFile
ni doLongStuff
de openDatabase
: de hecho, openDatabase
puede abrir cualquier número de archivos y/o llevar a cabo varias señales de E/S antes de "decidir", que se encargue quiere volver a withDatabase
. Dado este contratiempo, ¿hay alguna forma de hacer que doLongStuff
sea interrumpible incluso si se ejecuta dentro de una llamada anidada mask
?