Title: Selective AttachedToParent for recursive calls
Tags: .net;.net-4.0;task-parallel-library
Question: I've implemented helper method for running heavy tasks in background
It accepts several delegates and doesn't block:
async part
success part(GUI)
on failure in async part (GUI)
finally (GUI)  (optional)
Each gui part can start its own sequence i.e. recursive calls to my helper method occur. But I want to treat any part as completed when attached tasks are olso finished.
This picture explains what I mean:
To provide this condition I create my async task as Attached to parent and this gives me what I need.
The only problem I have is that in TPL for .NET v4 DenyChildAttach option doesn't exist so when my method is called inside other task it attaches to this task.
I'm going to create an overload with parameter to indicate the need of attach, nonetheless I want the following default behavior:
Attach if recursive call, otherwise do not attach unless explicitly stated.
Definetely I should set some context indicating recursion but do not understand how...
P.S. Now method looks like this (has many oveloads for Action, Func):
public static CancellationTokenSource ExecuteBlockingOperation(Action action,
Action completition,
Action<AggregateException&gt; onException,
Action<bool, bool, bool&gt; @finally = null)
{
if (action == null)
throw new ArgumentNullException("action");
if (completition == null)
throw new ArgumentNullException("completition");
if (onException == null)
throw new ArgumentNullException("onException");
var cts = new CancellationTokenSource();
var token = cts.Token;
var scheduler = SynchronizationContext.Current == null? TaskScheduler.Default : TaskScheduler.FromCurrentSynchronizationContext();
var successfullyCompleted = false;
var task = new Task(action, TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent);
var onFault = task.ContinueWith(asyncPartTask =&gt; onException(asyncPartTask.Exception),
CancellationToken.None,
TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.AttachedToParent,
scheduler);
var onSuccess = task.ContinueWith(asyncPart =&gt;
{
if (!token.IsCancellationRequested)
{
completition();
successfullyCompleted = true;
}
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent, scheduler);
if (@finally != null)
{
Task.Factory.ContinueWhenAll(new[] { onSuccess, onFault },
tasks =&gt;
{
var isSuccess = task.Status == TaskStatus.RanToCompletion;
@finally(isSuccess, token.IsCancellationRequested, successfullyCompleted);
},
CancellationToken.None,
TaskContinuationOptions.AttachedToParent, scheduler);
}
task.Start();
return cts;
}
Comment: This method serves mostly the case of long operation the result of which can be abandoned. Completition part usually changes GUI but only if the sequence was not cancelled. That is why I care when task completes.

Comment: Why do you even care when does the `Task` complete, since the user will never see it?

Comment: Also, the usual pattern is to let the user pass (optional) `CancellationToken` in, not to return a `CancellationTokenSource`


Here is another answer: I think using AttachedToParent is quite clumsy. What you could do instead is to make the child Task explicit by changing completition into a Func<Task&gt;. This way, you can control what continuation executes when.
To do this, you would need to change onSuccess to something like:
var onSuccess = task.ContinueWith(asyncPart =&gt;
token.ThrowIfCancellationRequested();
return completion();
}, token, TaskContinuationOptions.OnlyOnRanToCompletion, scheduler).Unwrap();
This also most likely means ExecuteBlockingOperation() will need to return the Task that executes @finally, but I think that shouldn't be a problem.

{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 1, 'CommentCount': 3, 'ContentLicense': 'CC BY-SA 3.0', 'CreationDate': '2013:00:04/03/13', 'FavoriteCount': 1.0, 'PostTypeId': 1, 'Tags': '<.net><.net-4.0><task-parallel-library>', 'Title': 'Selective AttachedToParent for recursive calls', 'ViewCount': 76, 'id': 15788762, 'user_AboutMe': '<p>Nice videos worth watching:</p>\n<ul>\n<li><a href="https://vimeo.com/204075759" rel="nofollow noreferrer">What were they thinking? Language design choices that seem wrong, until they don’t - Bill Wagner</a></li>\n<li><a href="https://vimeo.com/97329157" rel="nofollow noreferrer">Seven Ineffective Coding Habits of Many Programmers - Kevlin Henney</a></li>\n<li><a href="https://vimeo.com/97471514" rel="nofollow noreferrer">Code that fits your brain - Adam Tornhill</a></li>\n<li><a href="https://vimeo.com/157710445" rel="nofollow noreferrer">Clean Code: A Reader-Centered Approach - Matthew Renze</a></li>\n<li><a href="https://vimeo.com/157708450" rel="nofollow noreferrer">SOLID Deconstruction - Kevlin Henney</a></li>\n<li><a href="https://vimeo.com/113594282" rel="nofollow noreferrer">Self-documenting Code: A Mob Powered Vocabulary Lesson - Cory House</a></li>\n<li><a href="https://skillsmatter.com/skillscasts/4971-domain-driven-design-with-scott-wlaschin" rel="nofollow noreferrer">DDD, F# and Types</a></li>\n</ul>\n', 'user_AccountId': 558599.0, 'user_DisplayName': 'Pavel Voronin', 'user_Reputation': 12923.0, 'user_Views': 1369.0, 'user_id': 921054.0}}