Bug 16680 - await can rethrow AggregateException instead of getting first exception
Summary: await can rethrow AggregateException instead of getting first exception
Status: RESOLVED FIXED
Alias: None
Product: Android
Classification: Xamarin
Component: BCL Class Libraries ()
Version: 4.10.0.x
Hardware: PC Windows
: --- normal
Target Milestone: ---
Assignee: Jonathan Pryor
URL:
Depends on:
Blocks:
 
Reported: 2013-12-09 13:39 UTC by Grigory (Playtika)
Modified: 2014-04-22 09:35 UTC (History)
5 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description Grigory (Playtika) 2013-12-09 13:39:07 UTC
Seems that there is an issue with await.

I see reports like this:
System.AggregateException:  ---> Slots.Common.Exceptions.SessionLostException: Server returned error -1101 ()
  at Slots.Common.Network.ApiServiceBase+<MakeRequestAsync>d__0`1[Slots.Common.Lobby.Model.HighscoresMessage].MoveNext () [0x00000] in <filename unknown>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <filename unknown>:0 
  at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1+ConfiguredTaskAwaiter[Slots.Common.Lobby.Model.HighscoresMessage].GetResult () [0x00000] in <filename unknown>:0 
  at Slots.Common.Lobby.LobbyService+<GetHighScores>d__2f.MoveNext () [0x00000] in <filename unknown>:0 
  --- End of inner exception stack trace ---
 --> (Inner exception 0) Slots.Common.Exceptions.SessionLostException: Server returned error -1101 ()
  at Slots.Common.Network.ApiServiceBase+<MakeRequestAsync>d__0`1[Slots.Common.Lobby.Model.HighscoresMessage].MoveNext () [0x00000] in <filename unknown>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <filename unknown>:0 
  at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1+ConfiguredTaskAwaiter[Slots.Common.Lobby.Model.HighscoresMessage].GetResult () [0x00000] in <filename unknown>:0 
  at Slots.Common.Lobby.LobbyService+<GetHighScores>d__2f.MoveNext () [0x00000] in <filename unknown>:0 

I don't know how to repro this, probably this is a bug in compiler or something like that.
await shouldn't rethrow any AggregateException but should take first exception.

http://stiller.co.il/blog/2012/12/accessing-the-aggregateexception-with-await/
Comment 1 Marek Safar 2013-12-10 05:22:06 UTC
That claim is not correct. Await can re-throw AggregateException when that's the exception which occurred. Without any source code it's hard to guess what is really going on.

Do you have sample code which reproduce (shows) the issue?
Comment 2 Grigory (Playtika) 2013-12-10 07:07:34 UTC
I have exactly same code running on Win32 (.NET 4.5) it doesn't throw any AggregateExceptions. It is definitely a bug in Mono. 
I never call Task.Wait() or Task.Result. Tasks are always awaited. There are no places where AggregateException is thrown.

Bug doesn't reproduce easily. 

But I'll try to write a repro project.
Comment 3 Jonathan Pryor 2013-12-10 12:29:12 UTC
I suspect this may be a dupe of Bug #14824.
Comment 4 Grigory (Playtika) 2013-12-10 12:36:14 UTC
Xamarin is 4.10.1
Comment 5 Grigory (Playtika) 2013-12-11 11:58:18 UTC
In mscorlib that is baked into APK i see the following:
The funny thing that on .NET, Mono and Xamarin.Android this code is different. 

public override Exception GetBaseException()
{
    if ((this.innerExceptions != null) && (this.innerExceptions.Count == 1))
    {
        return this.innerExceptions.get_Item(0).GetBaseException();
    }
    return this;
}

 
I think that Xamarin.Android version is completely incorrect.
Comment 6 Marek Safar 2013-12-12 04:57:18 UTC
It's likely you are running Xamarin.Android version which didn't pick up the fix.
Comment 7 Grigory (Playtika) 2013-12-12 05:27:55 UTC
It's likely that this logic is broken in 4.10.x
I've checked in 4.8.1 - the logic is correct there. 

This is a ridiculous regression ... Fix this ASAP please.
Comment 8 Marek Safar 2013-12-12 05:39:21 UTC
I am not sure why do you think the logic is broken.
Comment 9 Grigory (Playtika) 2013-12-12 05:44:56 UTC
Because behavior differs from .NET. This is the only reason for me.
Basically im not event sure that issue is in this method. Probably it is another issue, but anyway AggregateExceptions shouldn't be thrown from async Task methods. (Of course if actual exception is not AggregateException)
Comment 10 Jonathan Pryor 2013-12-12 15:32:40 UTC
@Grigory: Please provide a test case which demonstrates the bug, based on the blog post you mentioned:

http://stiller.co.il/blog/2012/12/accessing-the-aggregateexception-with-await/

  using System;
  using System.Linq;
  using System.Threading;
  using System.Threading.Tasks;
  using Android.App;
  using Android.Content;
  using Android.Runtime;
  using Android.Views;
  using Android.Widget;
  using Android.OS;

  namespace Scratch.Bxc16680 {
    [Activity (Label = "Scratch.Bxc16680", MainLauncher = true)]
    public class MainActivity : Activity {
      protected override async void OnCreate (Bundle bundle)
      {
        base.OnCreate (bundle);

        Console.WriteLine ("# jonp: start DoWork()");
        await DoWork ("abcdef"); // .Wait ();
        Console.WriteLine ("# jonp:   end DoWork()");
      }
    
      static async Task DoWork(string s)
      {
        if (s == null)
          throw new ArgumentNullException("s"); 

        // Setup task 
        try
        {
          // Remember that a String is also an IEnumerable
          var tasks = s.Select(c => Task.Run(() => {
              Thread.Sleep(1000); // This task is very complicated!!
              if (c % 2 == 0)
                throw new InvalidOperationException("Cannot process " + c);
          }));
          await Task.WhenAll(tasks); 
        }
        catch (InvalidOperationException ex)
        {
          Console.WriteLine("Caught only the first exception: {0}", ex);
        }
        catch (Exception e) {
          Console.WriteLine ("# BAD!!! caught: {0}", e);
        }
      }
    }
  }

Note: I don't use DoWork("abcdef").Wait() because that results in deadlock, as async/await from the Main/UI thread will post back to the Main/UI thread, which is blocked waiting for DoWork() to complete, which is...

When I run it, it is clearly hitting the InvalidOperationException catch block, as it's supposed to do:

> # jonp: start DoWork()
> Caught only the first exception: System.InvalidOperationException: Cannot process b
>   at Scratch.Bxc16680.MainActivity+<DoWork>c__async1+<DoWork>c__AnonStorey3.<>m__2 ()
>   at System.Threading.Tasks.TaskActionInvoker+ActionInvoke.Invoke (System.Threading.Tasks.Task owner, System.Object state, System.Threading.Tasks.Task context)
>   at System.Threading.Tasks.Task.InnerInvoke ()
>   at System.Threading.Tasks.Task.ThreadStart ()
> --- End of stack trace from previous location where exception was thrown ---
>   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw ()
>   at System.Runtime.CompilerServices.TaskAwaiter.GetResult ()
>   at Scratch.Bxc16680.MainActivity+<DoWork>c__async1.MoveNext ()
> # jonp:   end DoWork()

Furthermore, the above is the output I get with Xamarin.Android 4.10 (and what will hopefully become the beta for 4.12).
Comment 11 Grigory (Playtika) 2014-04-22 09:35:11 UTC
Doesn't reproduce anymore in 4.12/7.2.0