Bug description
The JoinableTask.Task property throws if wrappedTask == null, which it will be until its initialDelegate returns a Task to assign to that field. It might seem this is OK since JTF.RunAsync hasn't returned the object to the caller to observe and call the property on, but this is incorrect. The JoinableTask is observable even before the caller sees it from another thread that enumerates the contents of a JoinableTaskCollection that the original JoinableTaskFactory.RunAsync added the object to.
Repro steps
Here is a test that demonstrates the bug:
[Fact]
public async Task JoinableTask_TaskPropertyBeforeReturning()
{
var unblockJoinableTask = new ManualResetEventSlim();
var joinableTaskStarted = new AsyncManualResetEvent(allowInliningAwaiters: false);
Task<int> observedWrappedTask = null;
var assertingTask = Task.Run(async delegate
{
try
{
await joinableTaskStarted.WaitAsync();
var observableJoinableTask = (JoinableTask<int>)this.joinableCollection.Single();
observedWrappedTask = observableJoinableTask.Task;
}
finally
{
unblockJoinableTask.Set();
}
});
var joinableTask = this.asyncPump.RunAsync(delegate
{
joinableTaskStarted.Set();
// Synchronously block *BEFORE* yielding.
unblockJoinableTask.Wait();
return Task.FromResult(3);
});
await assertingTask; // observe failures.
await joinableTask;
Assert.Same(observedWrappedTask, joinableTask.Task);
Assert.Equal(3, await observedWrappedTask);
}
Expected behavior
A Task is synthesized and returned from JoinableTask.Task.
Actual behavior
JoinableTask.Task throws:
Microsoft.Assumes+InternalErrorException : An internal error occurred. Please contact Microsoft Product Support Services.
Bug description
The
JoinableTask.Taskproperty throws ifwrappedTask == null, which it will be until itsinitialDelegatereturns aTaskto assign to that field. It might seem this is OK sinceJTF.RunAsynchasn't returned the object to the caller to observe and call the property on, but this is incorrect. TheJoinableTaskis observable even before the caller sees it from another thread that enumerates the contents of aJoinableTaskCollectionthat the originalJoinableTaskFactory.RunAsyncadded the object to.Repro steps
Here is a test that demonstrates the bug:
Expected behavior
A
Taskis synthesized and returned fromJoinableTask.Task.Actual behavior
JoinableTask.Taskthrows: