I think there's a bug in the enqueue operation or at least a lack of documentation. I also think that the behaviour there has changed recently and it had not been an issue with previous versions of TBB. If you call enqueue on a task allocated as root, it inherits the task group context. If the original root task that has "opened" this task group context terminates, the task context seems to be destroyed even though there still are some tasks enqueued. It leads to
Assertion tgc->my_cancellation_requested == 0 || tgc->my_cancellation_requested == 1 failed on line 443 of file ../../src/tbb/scheduler.h
Detailed description: task_group_context is invalid
(or a seg fault if you forget to link to tbb_malloc). I'm not sure whether this behaviour of the task context is correct. The workaround is obvious once you are aware of the problem:
static tbb::task_group_context mycontext;
Task* tbbTask = new(tbb::task::allocate_root(mycontext)) MyFancyTask();
But I think this at least deserves a comment in enqueue. Instead, one could make enqueue always create its own context or ensure that the surrounding context is not destroyed if any tasks still is enqueued.
I am sorry that you observed this complicated issue. It is not the issue of tbb::task::enqueue; however, the semantics of tbb::task::enqueue leads to error-prone approaches. According to the documentation of the tbb::task_group_context:
As you already mentioned, tbb::task_group_context (cancellation group) is captured by tbb::task:allocate_root:
So, you can observe the same issue with a spawned task; however, it is usually used with tbb::task::spawn_root_and_wait interface that forces the task to be executed before the scope is closed and the context is destroyed.
The Intel TBB Task Scheduler interface is very complicated and has many particularities that has to be considered to implement the correct algorithm. We usually recommend using high level parallel algorithms (e.g. tbb::parallel_for, tbb::task_group, tbb::flow::graph and others) if possible. Feel free to describe your algorithm that we can try to consider a solution without explicit tasks or recommend some techniques to avoid errors in future.