Suppose you want to find out if a collection is ordered. You write a parallel_for Body that will reset an atomic<bool> as soon as it finds an anomaly, and that reads the atomic from time to time to see if it's still doing useful work (one anomaly is sufficient). When the parallel_for returns, you read the atomic and take action accordingly, right? Wrong!
Another part of the program may have cancelled what you're doing, and that cancellation may or may not have propagated to that parallel_for call. It may have prevented examination of just that part of the sequence where the anomaly occurs, and you won't know about it... unless you also check for cancellation. That's right, you'll have to go over all your code, see if any of it is vulnerable to cancellation, and add checks to prevent any unfortunate outcome, or decide that they will be harmless.
To make matters worse, the current implementation of propagation has a potential problem: cancellation propagation travels bottom-up in the task context group tree. A propagating thread cancels a descendent task group context, which takes no action by itself, but before propagation goes all the way up the chain to the ancestor that was actually cancelled, the propagating thread goes to sleep, another thread checks whether cancellation might cause a problem, but it does not detect the problem because it has not got the message yet, and it does something based on wrong information. The programmer's task has become even harder because of it, because it is not enough anymore to check the critical places, but the information has to be synthesised up the tree, e.g., in the form of ternary logic.
What is the solution?
Probably cancellation propagation has to be top-down instead of bottom-up. This is an easy fix.
But that still does not fully alleviate the burden on the programmer to check and check and check again, and be constantly mindful of potential cancellation, even if it is just exceptional. But isn't that what exceptions are supposed to take care of? I think by now C++ programmers have grown accustomed to writing exception-safe code, so that shouldn't be a problem anymore. The only thing to remember is to provide an exception handler for the target of cancellation, i.e., only the ancestor itself. (It also fixes the problem about bottom-up propagation, but that's probably not an excuse not to fix that problem independently.)
So maybe cancellation should always cause an exception to be raised, even from inside TBB code like parallel_for? What do you think? Is it a real problem, and are exceptions the solution?