Context:
I'm trying to understand the difference between cancel and uninterruptibleCancel from the Control.Concurent.Async package. I believe it has something to do with the underlying concepts of mask , uninterruptibleMask, and interruptible operations. Here's what I have understood so for:
- Asynchronous exceptions are thrown by thread-A, but need to be handled by thread-B. This is precisely what
throwTodoes. In some way, this can also be considered as a form of inter-thread communication. - Asynchronous exceptions are used by one thread to kill/cancel another thread.
- Handling aysnchronous exceptions creates a problem in the target/receiving thread, because one usually doesn't expect exceptions to be raised at any random point in the code. One puts
try/catcharound certain operations and expects/handles only certain exceptions. But, Asynchronous exceptions can be delivered when the target thread could be at any point in the execution. maskallows use to protect critical sections in the target/receiving thread from delivery of asynchronous exceptions. The operation protected bymaskdoesn't need to deal with asynchronous-exceptions till the point it callsrestore.
At this point uninterruptibleMask comes into the picture, and I start losing the plot. I thought the whole point of mask was to NOT deliver asynchronous-exceptions while executing a protected piece of code. However, here is what the docs say about "interruptible actions":
It is useful to think of mask not as a way to completely prevent asynchronous exceptions, but as a way to switch from asynchronous mode to polling mode. The main difficulty with asynchronous exceptions is that they normally can occur anywhere, but within a mask an asynchronous exception is only raised by operations that are interruptible (or call other interruptible operations). In many cases these operations may themselves raise exceptions, such as I/O errors, so the caller will usually be prepared to handle exceptions arising from the operation anyway. To perform an explicit poll for asynchronous exceptions inside mask, use allowInterrupt.
Questions:
- Even within a code-block protected by
mask, if there are some points where it is safe to handle asynchronous-exceptions, one can callallowInterrupt. This implicitly means, that, unlessallowInterruptis called, asynchronous exceptions will NOT be delivered while executingmasked code. What, then, is the purpose ofuninterruptibleMask? - Consequently, what is the need for
uninterruptibleCancel? IIUC, thread A is trying to cancel thread B, but thread A, itself, is trying to protect itself from some sort of asynchronous exceptions, which may possibly be initiated by a third thread C, right? In the code forcancel(given below), which part is so critical that it needs the ultimate form of protection from asynchronous exceptions? Isn'tthrowToan atomic/masked operation itself? Further, even if an asynchronous-exception is delivered to thread-A while executingwaitCatch, what difference does it make? Actually, if I think about it, why do we need to evenmaskthis code in the first place (let alone,uninterruptibleMask) ?
cancel a@(Async t _) = throwTo t AsyncCancelled <* waitCatch a