Search code examples
.nettaskside-effects

Are side effects always visible in task continuations?


Are side effects guaranteed to be visible in task continuations?

object o = null;
Task.Run (() => o = new object ())
    .ContinueWith (t => o.ToString ());

The continuation may run on a different thread than the first task. If there is no kind of memory fence, the assignment to o might not be visible to the continuation. In that case, a NullReferenceException would be thrown. Is there anything that already prevents this in the above example?


Solution

  • Are side effects guaranteed to be visible in task continuations?

    If that's "is there some official documentation from MS somewhere that says so" then I'm afraid the answer is no.

    As a practical matter, continuations would not be useful if they couldn't rely on being able to observe all effects produced by the task they're continuing from1.

    Both the .NET Framework 4.8 source and the core clr have an internal method called FinishContinuations that is used to find the continuations a Task object has and cause them to be scheduled/executed. In both implementations, an Interlocked.Exchange is used to obtain the continuation object and hopefully you're already aware that that imposes a full memory barrier.


    I've also found this Blog Post (not from MS) that contains a list of items that force memory barriers. Task completions are on the list but there aren't any references to more authoritative sources.

    I also checked the BotR but couldn't find any statements on this specifically.


    1Bearing in mind that we can have Task.ContinueWith, not just Task<T>.ContinueWith - i.e. it supports scenarios where nothing is being directly delivered from the task to the continuation.