2012-01-27 21 views
5

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?

Respuesta

0

¿Se realiza doLongStuff en la base de datos? Si es así, puede que ya sea interrumpible. Consulte la sección en Interruptible Operations, que establece que cualquier función que pueda bloquear, incluida la mayoría de las funciones que realizan IO, puede interrumpirse incluso dentro del alcance de una máscara.

Si no está seguro de si doLongStuff es interrumpible o no (depende qué funciones se utiliza), entonces usted puede utilizar para sondear allowInterrupt excepciones asíncronas dentro de código de máscaras, o alternativamente usar un MVar para sincronizar la lectura de la BD . Esto funcionaría porque la mayoría de las operaciones MVar son interrumpibles por sí mismas, lo que permite que la función más grande también sea interrumpible.