locked
can awaiting Task.FromResult introduce concurrency

    Question

  • Hi All,

    Documentation is light on this stuff, so any help would be appreciated.

    This was my understanding on Task.FromResult.

    1. Creates a task that is already in a completed state.
    2. await Task.FromResult(42) will execute synchronously, will not introduce any additional concurrency.

    We are seeing behavior in certain environments (xunit test runner) that suggests my 2nd assumption is incorrect. 

    Help! ;)


    James Miles http://enumeratethis.com
    • Edited by James Miles Wednesday, February 01, 2012 2:44 AM
    Wednesday, February 01, 2012 1:58 AM

Answers

  • Hi James-

    Right, this is the same as with iterators in C# (and now in Visual Basic in .NET 4.5).

    A problem with throwing exceptions in the synchronous portion of the method is what's in that synchronous portion can be non-deterministic.  Consider:

    public async Task FooAsync()
    {
        await Task.Run(() => { ... });
        throw new Exception();
    }

    Does that Exception propagate out through the call to FooAsync or does it propagate out through the Task?  If all exceptions in the synchronous portion propagated out through the method call, this would then depend on how quickly the queued Task completed... if it completed so fast that it was done by the time we awaited it, then this method wouldn't yield, and we'd continue running synchronously to throw the exception, but if it didn't complete fast enough, then the throw would be in an asynchronous continuation.

    You're right that public APIs should consider using two separate methods like this, one that does argument validation and another that's delegated to that contains the bulk of the implementation.  That way you get to decide explicitly what's synchronous / wrapped in the Task and what's not, and this is what the new implementations of XxAsync methods in .NET 4.5 do. 

    Note that this is the same guidance as for iterators.  If you write:

    public IEnumerable<int> FooAsync(int arg)
    {
        if (arg < 0) throw new ArgumentOutOfRangeException("arg");
        for(int i=0; i<arg; i++) yield return i;
    }

    then the argument exception won't be thrown out of the call to FooAsync, but rather out of the call to MoveNext on the enumerator returned from calling GetEnumerator on the returned enumerable.  And so public APIs are guided to write this as two separate methods:

    public IEnumerable<int> FooAsync(int arg)
    {
        if (arg < 0) throw new ArgumentOutOfRangeException("arg");
        return FooAsyncCore(arg);
    }

    private IEnumerable<int> FooAsyncCore(int arg)
    {
        for(int i=0; i<arg; i++) yield return i;
    }

    That said, with async methods, there are other good reasons for often wanting to separate out a method like this.  As I wrote about at http://msdn.microsoft.com/en-us/magazine/hh456402.aspx, async methods do have some overhead associated with invoking them, and there are often cases where there's a fast path you can take that doesn't require using awaits.  You can separate out that fast path into the same non-async method wrapper that also does argument validation, then only delegating to the private async method that uses awaits for the "slow path".

    I hope that helps.

     

     

     

     

     

    Wednesday, February 01, 2012 3:32 PM

All replies

  • Hi James-

    Your understanding is correct, and the expression "await Task.FromResult(42)" will be functionally no different from the expression "42" and will not introduce any concurrency.  What are you seeing that makes you doubt this?

    Wednesday, February 01, 2012 7:43 AM
  • Hi Stephen,
     
    I've actually worked out what the problem is. I'm being tripped up by the subtle scheduling (synchronization context post).
     
    Consider the following.
     
     
     

    void Main()
    {
     try
     {
      Test();
     }
     catch(Exception ex)
     {
      Console.WriteLine("you will never see this...");
     }
    }
    
    public async void Test()
    {
     await TaskEx.FromResult(42);
     throw new Exception("boom!");
    }
    

     

    It seems a little odd to me, that even a completely synchronous method, doesn't throw the exception back to the caller.

    Consider another seemingly trivial example;
     
     
     

    public async void Foo(object bar)
    {
     if(bar == null) throw new ArgumentNullException("bar");
     
     // some coding using await
    }
    

    Based on the current implementation of the async CTP, this method is flawed. One would be required to break into two methods.

    public void Foo(object bar)
     {
      if(bar == null) throw new ArgumentNullException("bar");
      FooPrivate();
     }
    
     
    private async void FooPrivate(object bar)
     {
      // some coding using await 
     } 
    


    One could argue that this would be "good practice" however my feeling is this will trip a lot of people up ;)

    Cheers,

    • Edited by James Miles Wednesday, February 01, 2012 9:32 AM
    Wednesday, February 01, 2012 9:30 AM
  • Hi James-

    Right, this is the same as with iterators in C# (and now in Visual Basic in .NET 4.5).

    A problem with throwing exceptions in the synchronous portion of the method is what's in that synchronous portion can be non-deterministic.  Consider:

    public async Task FooAsync()
    {
        await Task.Run(() => { ... });
        throw new Exception();
    }

    Does that Exception propagate out through the call to FooAsync or does it propagate out through the Task?  If all exceptions in the synchronous portion propagated out through the method call, this would then depend on how quickly the queued Task completed... if it completed so fast that it was done by the time we awaited it, then this method wouldn't yield, and we'd continue running synchronously to throw the exception, but if it didn't complete fast enough, then the throw would be in an asynchronous continuation.

    You're right that public APIs should consider using two separate methods like this, one that does argument validation and another that's delegated to that contains the bulk of the implementation.  That way you get to decide explicitly what's synchronous / wrapped in the Task and what's not, and this is what the new implementations of XxAsync methods in .NET 4.5 do. 

    Note that this is the same guidance as for iterators.  If you write:

    public IEnumerable<int> FooAsync(int arg)
    {
        if (arg < 0) throw new ArgumentOutOfRangeException("arg");
        for(int i=0; i<arg; i++) yield return i;
    }

    then the argument exception won't be thrown out of the call to FooAsync, but rather out of the call to MoveNext on the enumerator returned from calling GetEnumerator on the returned enumerable.  And so public APIs are guided to write this as two separate methods:

    public IEnumerable<int> FooAsync(int arg)
    {
        if (arg < 0) throw new ArgumentOutOfRangeException("arg");
        return FooAsyncCore(arg);
    }

    private IEnumerable<int> FooAsyncCore(int arg)
    {
        for(int i=0; i<arg; i++) yield return i;
    }

    That said, with async methods, there are other good reasons for often wanting to separate out a method like this.  As I wrote about at http://msdn.microsoft.com/en-us/magazine/hh456402.aspx, async methods do have some overhead associated with invoking them, and there are often cases where there's a fast path you can take that doesn't require using awaits.  You can separate out that fast path into the same non-async method wrapper that also does argument validation, then only delegating to the private async method that uses awaits for the "slow path".

    I hope that helps.

     

     

     

     

     

    Wednesday, February 01, 2012 3:32 PM
  • We are seeing behavior in certain environments (xunit test runner) that suggests my 2nd assumption is incorrect. 

    James,

    Have you noticed the samples under "My Documents\Microsoft Visual Studio Async CTP\Samples\(C# Testing) Unit Testing"? I ask because they're easy to overlook.

    In the "AsyncTestUtilities" subfolder, there are three types that you can use in your unit tests to avoid surprises like this: GeneralThreadAffineContext (which should be used for all your tests unless you need another one), WindowsFormsContext (for components depending on Control.Invoke/BeginInvoke), and WpfContext (for components depending on Dispatcher.Invoke/BeginInvoke).

    I'm not aware of any unit test framework that (currently) supports async test methods. Consider the following two examples (written in MSTest):

     

    // A simple test that should pass; it throws an exception from an async continuation.
    [TestMethod]
    [ExpectedException(typeof(InvalidOperationException))]
    public async void ShouldPass()
    {
      await TaskEx.Yield(); // Change this to "Task.Yield" on VS 11.
      throw new InvalidOperationException();
    }
    
    // A simple test that should fail; it fails an assertion from an async continuation.
    [TestMethod]
    public async void ShouldFail()
    {
      await TaskEx.Yield(); // Change this to "Task.Yield" on VS 11.
      Assert.Fail();
    }
    
    

     


    You can use the unit testing contexts provided in the Async CTP to make the tests work as expected:

     

    // A simple test that should pass; it throws an exception from an async continuation.
    [TestMethod]
    [ExpectedException(typeof(InvalidOperationException))]
    public void ShouldPass()
    {
      GeneralThreadAffineContext.Run(async () =>
      {
        await TaskEx.Yield(); // Change this to "Task.Yield" on VS 11.
        throw new InvalidOperationException();
      });
    }
    
    // A simple test that should fail; it fails an assertion from an async continuation.
    [TestMethod]
    public void ShouldFail()
    {
      GeneralThreadAffineContext.Run(async () =>
      {
        await TaskEx.Yield(); // Change this to "Task.Yield" on VS 11.
        Assert.Fail();
      });
    }
    
    

     


    I am about to release an extension for MSTest that allows async test methods directly.

    I'm interested in doing similar extensions for NUnit and xUnit. Do you have any experience with xUnit extensions?

           -Steve


    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
      and How to Implement IDisposable and Finalizers: 3 Easy Rules
    Microsoft Certified Professional Developer

    How to get to Heaven according to the Bible
    Wednesday, February 01, 2012 4:14 PM
  • Yeah, I agree there is some benefit in keeping this behaviour consistent with iterators.

    I see one big difference between async & iterators.

    My experience is that iterator blocks (yield return) are not actually used much in day to day application code. They are normally used in infrastructure / framework code. Async / await is going to be used everywhere.

    (I've also noticed that this behaviour was changed in the async ctp refresh)
    James Miles http://enumeratethis.com
    • Edited by James Miles Thursday, February 02, 2012 1:21 AM
    Thursday, February 02, 2012 1:21 AM
  • Hi Stephen (Cleary),

    XUnit has recently been updated & supports tests that return Task & Task<T>.

    For some reason they've not introduced a syncrhonization context, it would be a massive improvement as it would allow asynchronous exceptions to be posted back to the test. Currently they just take down the test runner and it is difficult to work out which test caused the exception ;)


    James Miles http://enumeratethis.com
    Thursday, February 02, 2012 1:25 AM