Разница между отменой и uninterruptibleCancel (из библиотеки Async) - PullRequest
2 голосов
/ 13 октября 2019

Контекст:

Я пытаюсь понять разницу между cancel и uninterruptibleCancel из пакета Control.Concurent.Async. Я считаю, что это как-то связано с базовыми понятиями mask, uninterruptibleMask и прерываемых операций . Вот что я так понял:

  • Асинхронные исключения генерируются потоком-A, но должны обрабатываться потоком-B. Это именно то, что делает throwTo. В некотором смысле это также можно считать формой связи между потоками.
  • Асинхронные исключения используются одним потоком для уничтожения / отмены другого потока.
  • Обработка асинхронных исключений создает проблему в целевом / принимающем потоке, поскольку обычно не ожидается, что исключения будут возникать в любой произвольной точке кода. Каждый помещает try / catch вокруг определенных операций, а ожидает, что / обрабатывает только определенные исключения. Но асинхронные исключения могут быть доставлены, когда целевой поток может находиться в любой точке выполнения.
  • mask позволяет использовать защиту критических секций в целевом / принимающем потоке от доставки асинхронных исключений. Операция, защищенная mask, не должна иметь дело с асинхронными исключениями до тех пор, пока она не вызовет restore.

В этот момент uninterruptibleMask входит в картину, и я начинаю терятьсюжет. Я думал, что весь смысл mask состоит в том, чтобы НЕ доставлять асинхронные исключения при выполнении защищенного фрагмента кода. Однако вот что говорят в документах о «прерываемых действиях»:

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

Вопросы:

  • Даже в пределах блока кода, защищенного mask если есть моменты, когда безопасно обрабатывать асинхронные исключения, можно вызвать allowInterrupt. Это неявно означает, что, если не вызывается allowInterrupt, асинхронные исключения НЕ будут доставляться при выполнении mask кода. Какова же тогда цель uninterruptibleMask?
  • Следовательно, зачем нужна uninterruptibleCancel? IIUC, поток A пытается отменить поток B, но сам поток A пытается защитить себя от неких асинхронных исключений, которые могут быть инициированы третьим потоком C, верно? В коде для cancel (приведенном ниже) какая часть настолько критична, что она нуждается в окончательной форме защиты от асинхронных исключений? Разве throwTo не является самой атомарной / маскированной операцией? Кроме того, даже если асинхронное исключение доставляется в поток-A при выполнении waitCatch, какая разница? На самом деле, если я думаю об этом, зачем нам даже выравнивать mask этот код (не говоря уже о uninterruptibleMask)?
cancel a@(Async t _) = throwTo t AsyncCancelled <* waitCatch a

1 Ответ

3 голосов
/ 13 октября 2019

Без маскировки асинхронные исключения могут происходить где угодно. При mask асинхронные исключения могут появляться только из прерываемых действий (которые обычно блокируют). При uninterruptibleMask асинхронные исключения полностью отсутствуют. Также обратите внимание, что allowInterrupt является только одним из прерываемых действий;есть еще тонна, например, takeMVar. С помощью mask, например, невозможно заблокировать MVar, не открывая себя для исключений, но uninterruptibleMask позволяет вам это сделать (хотя вы не должны).

uninterruptibleCancel - этополезно, потому что cancel ожидает завершения целевого потока. Это операция блокировки, поэтому, как это принято, она также прерываема. Таким образом, когда вы используете cancel, вы открываете себя для получения неожиданных исключений, независимо от того, * * вы mask или нет. Когда вы используете uninterruptibleCancel, вы гарантированно не получите исключение на 100%. Вот и все. Помните, что исключения не являются локальными;даже если ничего в cancel не критично, то если оставить его незащищенным, исключение может проникнуть во что-то, что

mask $ do
  cancel something -- whoops, this can receive an exception, even though it's masked
  someCleanup -- therefore this might not get called

против

mask $ do
  uninterruptibleCancel something -- no exceptions
  someCleanup -- so this will definitely happen (assuming the target thread ends)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...