Вложенная маскировка асинхронных исключений - PullRequest
5 голосов
/ 27 января 2012

Я пишу базу данных своего рода. Основная функция, которую он экспортирует, следующая:

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

, который автоматически управляет временем жизни дескрипторов базы данных.

Внутренне, withDatabase использует функцию bracket из Control.Exception .

withDatabase path f = bracket (openDatabase path) closeDatabase f

В моем конкретном случае openDatabase может выполнять некоторые существенные операции ввода-вывода и, таким образом, блокировать в течение длительного времени. По этой причине я хотел бы запустить некоторую его часть с асинхронными исключениями без маскировки. (Упрощенная) реализация может быть:

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, не обязательно снимает маску с асинхронных исключений, оно просто восстанавливает состояние маскирования до состояния окружающего контекста. Таким образом, если асинхронные исключения уже замаскированы, маска не может быть использована для повторной маскировки исключений.

Мое понимание этого описания состоит в том, что doLongStuff будет работать с асинхронными исключениями, замаскированными и не, как мне хотелось бы, разблокированными.

В моем реальном коде я не могу переместить ни openFile, ни doLongStuff из openDatabase: на самом деле openDatabase может открыть любое количество файлов и / или выполнить различные операции ввода-вывода, прежде чем "решить", какие обработать его хочет вернуться к withDatabase. Учитывая это противоречие, есть ли способ сделать doLongStuff прерываемым, даже если он выполняется внутри вложенного mask вызова?

1 Ответ

0 голосов
/ 28 января 2012

В базе данных выполняется doLongStuff? Если это так, это может быть уже прервано. См. Раздел Прерываемые операции , в котором говорится, что любая функция, которая может блокировать, включая большинство функций, выполняющих ввод-вывод, может быть прервана даже в рамках маски.

Если вы не уверены, является ли doLongStuff прерываемым или нет (зависит от того, какие функции он использует), то вы можете либо использовать allowInterrupt для опроса асинхронных исключений в замаскированном коде, либо использовать MVar для синхронизировать чтение из БД. Это сработает, потому что большинство MVar операций сами прерываются, что позволяет большей функции также быть прерываемой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...