-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Description
When an exception is re-thrown from an await
from multiple observers (think a TaskCompletionSource
where there are more than one caller await
ing on the result), an ArgumentException
can be thrown from Exception.CaptureDispatchState
due to the member field this.foreignExceptionsFrames
being changed by Exception.RestoreDispatchState
during the re-throw of the exception from await
ing the TaskCompletionSource
.
I believe the issue is that Exception.CaptureDispatchState
is accessing this.foreignExceptionsFrames
in a non-thread-safe way, and due to the this.foreignExceptionsFrames.Length
property of the array changing during execution of the method, insufficient array space is allocated to the variable monoStackFrameArray
then Array.Copy
fails due to a change in this.foreignExceptionsFrames.Length
.
This issue happens on Android (net6.0-android), but may also happen on iOS (net6.0-ios) if it shares the same implementation (untested).
This issue does not happen on Windows as it appears that Exception.CaptureDispatchState
is handled by some extern call.
This issue does not happen on Xamarin.iOS (xamarinios) or Xamarin.Android (monoandroid), in either Xamarin.Native or Xamarin.Forms apps.
FWIW this appears to be a fairly serious bug and will block me from switching our Xam apps to MAUI.
Steps to Reproduce
- Create a new Android MAUI Hello World! app
- Change the body of
OnCounterClicked
to the following:
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
CounterLabel.Text = $"Current count: {count}";
SemanticScreenReader.Announce(CounterLabel.Text);
var theException = new Exception();
var tasks = new List<Task>();
var resetEvent = new ManualResetEventSlim();
for (var awaits = 0; awaits < 10; ++awaits)
{
tasks.Add(Task.Run(async () =>
{
resetEvent.Wait();
var tcs = new TaskCompletionSource();
try
{
// One thread calls SetException on the TCS which calls CaptureDispatchState on the Exception which accesses foreignExceptionsFrames in a non-atomic way
tcs.SetException(theException);
}
catch (ArgumentException ex)
{
// THIS SHOULD NOT HAPPEN
Log.Wtf("TaskAwaitCrash", ex.ToString());
}
// Another thread re-throws the Exception which calls RestoreDispatchState which sets foreignExceptionsFrames to a new array of a different length
await tcs.Task;
}));
}
resetEvent.Set();
try
{
Task.WhenAll(tasks).Wait();
}
catch
{
}
}
- Run the app and click the
Click me
button repeatedly until the WTF error is seen in the logcat output.
Version with bug
Release Candidate 3 (current)
Last version that worked well
Unknown/Other
Affected platforms
Android
Affected platform versions
Android 12
Did you find any workaround?
No workaround :(
Relevant log output
05-18 12:19:19.680 E/TaskAwaitCrash(19152): System.ArgumentException: Destination array was not long enough. Check destIndex and length, and the array's lower bounds (Parameter 'destinationArray')
05-18 12:19:19.680 E/TaskAwaitCrash(19152): at System.Array.Copy(Array sourceArray, Int32 sourceIndex, Array destinationArray, Int32 destinationIndex, Int32 length, Boolean reliable)
05-18 12:19:19.680 E/TaskAwaitCrash(19152): at System.Array.Copy(Array sourceArray, Int32 sourceIndex, Array destinationArray, Int32 destinationIndex, Int32 length)
05-18 12:19:19.680 E/TaskAwaitCrash(19152): at System.Exception.CaptureDispatchState()
05-18 12:19:19.680 E/TaskAwaitCrash(19152): at System.Runtime.ExceptionServices.ExceptionDispatchInfo..ctor(Exception exception)
05-18 12:19:19.680 E/TaskAwaitCrash(19152): at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(Exception source)
05-18 12:19:19.680 E/TaskAwaitCrash(19152): at System.Threading.Tasks.TaskExceptionHolder.AddFaultException(Object exceptionObject)
05-18 12:19:19.680 E/TaskAwaitCrash(19152): at System.Threading.Tasks.TaskExceptionHolder.Add(Object exceptionObject, Boolean representsCancellation)
05-18 12:19:19.680 E/TaskAwaitCrash(19152): at System.Threading.Tasks.Task.AddException(Object exceptionObject, Boolean representsCancellation)
05-18 12:19:19.680 E/TaskAwaitCrash(19152): at System.Threading.Tasks.Task.AddException(Object exceptionObject)
05-18 12:19:19.680 E/TaskAwaitCrash(19152): at System.Threading.Tasks.Task.TrySetException(Object exceptionObject)
05-18 12:19:19.680 E/TaskAwaitCrash(19152): at System.Threading.Tasks.TaskCompletionSource.TrySetException(Exception exception)
05-18 12:19:19.680 E/TaskAwaitCrash(19152): at System.Threading.Tasks.TaskCompletionSource.SetException(Exception exception)
05-18 12:19:19.680 E/TaskAwaitCrash(19152): at TaskAwaitCrash.MainPage.<>c__DisplayClass2_0.<<OnCounterClicked>b__0>d.MoveNext() in C:\Users\JahmaiLay\source\repos\TaskAwaitCrash\MainPage.xaml.cs:line 40