Skip to content

Commit 33ada20

Browse files
author
Lifeng Lu
committed
Add a unit test to capture problems that IsMainThreadBlocked may return wrong value.
1 parent 26830dc commit 33ada20

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed

test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskTests.cs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4116,6 +4116,44 @@ public void GetAwaiterCompletedTDoesNotLock()
41164116
lockHolder.Wait();
41174117
}
41184118

4119+
[Fact]
4120+
public void JoinableTaskCleanUpDependenciesForLoopDependencies()
4121+
{
4122+
this.SimulateUIThread(async delegate
4123+
{
4124+
var joinableTaskCollection = new JoinableTaskCollection(this.context, refCountAddedJobs: true);
4125+
JoinableTaskFactory taskFactory = this.context.CreateFactory(joinableTaskCollection);
4126+
((DerivedJoinableTaskFactory)taskFactory).AssumeConcurrentUse = true;
4127+
4128+
bool isMainThreadBlockedResult = true;
4129+
var unblockTask2 = new AsyncManualResetEvent();
4130+
JoinableTask? childTask = null;
4131+
4132+
this.context.Factory.Run(async () =>
4133+
{
4134+
await TaskScheduler.Default.SwitchTo(alwaysYield: true);
4135+
4136+
childTask = await this.SpinOffMainThreadTaskForJoinableTaskDependenciesHandledAfterTaskCompletion(taskFactory, joinableTaskCollection, unblockTask2);
4137+
});
4138+
4139+
using (this.context.SuppressRelevance())
4140+
{
4141+
isMainThreadBlockedResult = await taskFactory.RunAsync(() =>
4142+
{
4143+
return Task.FromResult(this.context.IsMainThreadBlocked());
4144+
});
4145+
}
4146+
4147+
using (this.context.SuppressRelevance())
4148+
{
4149+
unblockTask2.Set();
4150+
childTask!.Task.Wait();
4151+
}
4152+
4153+
Assert.False(isMainThreadBlockedResult);
4154+
});
4155+
}
4156+
41194157
protected override JoinableTaskContext CreateJoinableTaskContext()
41204158
{
41214159
return new DerivedJoinableTaskContext();
@@ -4126,6 +4164,123 @@ private static async void SomeFireAndForgetMethod()
41264164
await Task.Yield();
41274165
}
41284166

4167+
[MethodImpl(MethodImplOptions.NoInlining)] // We need locals to surely be popped off the stack for a reliable test
4168+
private async Task<JoinableTask> SpinOffMainThreadTaskForJoinableTaskDependenciesHandledAfterTaskCompletion(
4169+
JoinableTaskFactory joinableTaskFactory,
4170+
JoinableTaskCollection joinableTaskCollection,
4171+
AsyncManualResetEvent unblockTask2)
4172+
{
4173+
JoinableTask? task2 = null;
4174+
Task? spinOffTask = null;
4175+
4176+
JoinableTask? joinableTask = null;
4177+
4178+
var mainThreadJoinedCollection = new JoinableTaskCollection(this.context);
4179+
await TaskScheduler.Default.SwitchTo(alwaysYield: true);
4180+
4181+
using (this.context.SuppressRelevance())
4182+
{
4183+
task2 = joinableTaskFactory.RunAsync(async () =>
4184+
{
4185+
joinableTaskCollection.Join();
4186+
await unblockTask2.WaitAsync();
4187+
});
4188+
4189+
var unblockTask1 = new AsyncManualResetEvent();
4190+
var spinOffIsReady = new AsyncManualResetEvent();
4191+
var spinOffIsDone = new AsyncManualResetEvent();
4192+
4193+
joinableTask = joinableTaskFactory.RunAsync(async () =>
4194+
{
4195+
joinableTaskCollection.Join();
4196+
4197+
spinOffTask = Task.Run(async () =>
4198+
{
4199+
JoinableTaskFactory.MainThreadAwaiter awaiter = this.context.Factory.SwitchToMainThreadAsync().GetAwaiter();
4200+
awaiter.OnCompleted(() =>
4201+
{
4202+
spinOffIsDone.Set();
4203+
});
4204+
4205+
spinOffIsReady.Set();
4206+
4207+
await spinOffIsDone.WaitAsync();
4208+
});
4209+
4210+
await spinOffIsReady.WaitAsync();
4211+
await unblockTask1.WaitAsync();
4212+
});
4213+
4214+
// Increase refcount
4215+
mainThreadJoinedCollection.Add(joinableTask);
4216+
joinableTaskCollection.Add(joinableTask);
4217+
4218+
unblockTask1.Set();
4219+
4220+
await joinableTask.Task;
4221+
}
4222+
4223+
await this.context.Factory.SwitchToMainThreadAsync();
4224+
4225+
using (joinableTaskCollection.Join())
4226+
{
4227+
}
4228+
4229+
using (mainThreadJoinedCollection.Join())
4230+
{
4231+
await this.context.Factory.RunAsync(() =>
4232+
{
4233+
return Task.FromResult(this.context.IsMainThreadBlocked());
4234+
});
4235+
4236+
await spinOffTask!;
4237+
}
4238+
4239+
return task2;
4240+
}
4241+
4242+
[MethodImpl(MethodImplOptions.NoInlining)] // We need locals to surely be popped off the stack for a reliable test
4243+
private async Task<(Task SpinOffTask, WeakReference WeakJoinableTask)> SpinOffMainThreadTaskForJoinableTaskDependenciesHandledAfterTaskCompletionInnerTask(
4244+
JoinableTaskFactory joinableTaskFactory,
4245+
JoinableTaskCollection joinableTaskCollection)
4246+
{
4247+
var unblockTask1 = new AsyncManualResetEvent();
4248+
var spinOffIsReady = new AsyncManualResetEvent();
4249+
var spinOffIsDone = new AsyncManualResetEvent();
4250+
Task? spinOffTask = null;
4251+
4252+
JoinableTask joinableTask = joinableTaskFactory.RunAsync(async () =>
4253+
{
4254+
spinOffTask = Task.Run(async () =>
4255+
{
4256+
JoinableTaskFactory.MainThreadAwaiter awaiter = this.context.Factory.SwitchToMainThreadAsync().GetAwaiter();
4257+
awaiter.OnCompleted(() =>
4258+
{
4259+
spinOffIsDone.Set();
4260+
});
4261+
4262+
spinOffIsReady.Set();
4263+
4264+
await spinOffIsDone.WaitAsync();
4265+
4266+
// Add loop dependency
4267+
joinableTaskCollection.Join();
4268+
});
4269+
4270+
await spinOffIsReady.WaitAsync();
4271+
await unblockTask1.WaitAsync();
4272+
});
4273+
4274+
// Increase refcount
4275+
joinableTaskCollection.Add(joinableTask);
4276+
4277+
unblockTask1.Set();
4278+
4279+
await joinableTask.Task;
4280+
4281+
return (spinOffTask!, new WeakReference(joinableTask.Task));
4282+
}
4283+
41294284
private async Task SomeOperationThatMayBeOnMainThreadAsync()
41304285
{
41314286
await Task.Yield();

0 commit comments

Comments
 (0)