protected void ExecuteOnDispatcher(Func <Task> action) { if (!SingleThreadedTestSynchronizationContext.IsSingleThreadedSyncContext(SynchronizationContext.Current)) { SynchronizationContext.SetSynchronizationContext(SingleThreadedTestSynchronizationContext.New()); } var frame = SingleThreadedTestSynchronizationContext.NewFrame(); Exception failure = null; SynchronizationContext.Current.Post( async _ => { try { await action(); } catch (Exception ex) { failure = ex; } finally { frame.Continue = false; } }, null); SingleThreadedTestSynchronizationContext.PushFrame(SynchronizationContext.Current, frame); if (failure != null) { ExceptionDispatchInfo.Capture(failure).Throw(); } }
/// <summary> /// Runs an asynchronous task synchronously, using just the current thread to execute continuations. /// </summary> internal static void Run(Func <Task> func) { if (func == null) { throw new ArgumentNullException(nameof(func)); } var prevCtx = SynchronizationContext.Current; try { var syncCtx = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(syncCtx); var t = func(); if (t == null) { throw new InvalidOperationException(); } var frame = SingleThreadedTestSynchronizationContext.NewFrame(); t.ContinueWith(_ => { frame.Continue = false; }, TaskScheduler.Default); SingleThreadedTestSynchronizationContext.PushFrame(syncCtx, frame); t.GetAwaiter().GetResult(); } finally { SynchronizationContext.SetSynchronizationContext(prevCtx); } }
public void SynchronizationContextCaptured() { var syncContext = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(syncContext); Exception callbackError = null; var callback = new Action <GenericParameterHelper>( p => { try { Assert.Same(syncContext, SynchronizationContext.Current); } catch (Exception e) { callbackError = e; } }); var progress = new ProgressWithCompletion <GenericParameterHelper>(callback); IProgress <GenericParameterHelper> reporter = progress; Task.Run(delegate { reporter.Report(new GenericParameterHelper(1)); }); if (callbackError != null) { throw callbackError; } }
public void WithCancellationNoDeadlockFromSyncContext_Completed() { var dispatcher = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(dispatcher); WithCancellationSyncBlock(simulateCancellation: false); }
public void SignalAndWaitSynchronousBlockDoesNotHang() { SynchronizationContext.SetSynchronizationContext(SingleThreadedTestSynchronizationContext.New()); var evt = new AsyncCountdownEvent(1); Assert.True(evt.SignalAndWaitAsync().Wait(AsyncDelay), "Hang"); }
public void SynchronizationContextCaptured() { var syncContext = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(syncContext); TaskCompletionSource <object> callbackResult = new TaskCompletionSource <object>(); var frame = SingleThreadedTestSynchronizationContext.NewFrame(); var callback = new Action <GenericParameterHelper>( p => { try { Assert.NotNull(SynchronizationContext.Current); callbackResult.SetResult(null); } catch (Exception e) { callbackResult.SetException(e); } frame.Continue = false; }); var progress = new ProgressWithCompletion <GenericParameterHelper>(callback); IProgress <GenericParameterHelper> reporter = progress; Task.Run(delegate { reporter.Report(new GenericParameterHelper(1)); }); SingleThreadedTestSynchronizationContext.PushFrame(syncContext, frame); callbackResult.Task.GetAwaiter().GetResult(); }
public void WithCancellationNoncancelableNoDeadlockFromSyncContext() { var dispatcher = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(dispatcher); WithCancellationSyncBlockOnNoncancelableToken(); }
public void ConfigureAwaitRunInlineOfT_NoExtraThreadSwitching(NamedSyncContexts invokeOn, NamedSyncContexts completeOn) { // Set up various SynchronizationContexts that we may invoke or complete the async method with. var aSyncContext = SingleThreadedTestSynchronizationContext.New(); var bSyncContext = SingleThreadedTestSynchronizationContext.New(); var invokeOnSyncContext = invokeOn == NamedSyncContexts.None ? null : invokeOn == NamedSyncContexts.A ? aSyncContext : invokeOn == NamedSyncContexts.B ? bSyncContext : throw new ArgumentOutOfRangeException(nameof(invokeOn)); var completeOnSyncContext = completeOn == NamedSyncContexts.None ? null : completeOn == NamedSyncContexts.A ? aSyncContext : completeOn == NamedSyncContexts.B ? bSyncContext : throw new ArgumentOutOfRangeException(nameof(completeOn)); // Set up a single-threaded SynchronizationContext that we'll invoke the async method within. SynchronizationContext.SetSynchronizationContext(invokeOnSyncContext); var unblockAsyncMethod = new TaskCompletionSource <bool>(); var asyncTask = AwaitThenGetThreadAsync(unblockAsyncMethod.Task); SynchronizationContext.SetSynchronizationContext(completeOnSyncContext); unblockAsyncMethod.SetResult(true); // Confirm that setting the intermediate task allowed the async method to complete immediately, using our thread to do it. Assert.True(asyncTask.IsCompleted); Assert.Equal(Environment.CurrentManagedThreadId, asyncTask.Result); async Task <int> AwaitThenGetThreadAsync(Task <bool> antecedent) { bool result = await antecedent.ConfigureAwaitRunInline(); Assert.True(result); return(Environment.CurrentManagedThreadId); } }
public void DoesNotDeadlockWhenCallbackCapturesSyncContext(bool captureMainThreadContext) { var syncContext = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(syncContext); var jtc = new JoinableTaskContext(); const int expectedCallbackValue = 1; var actualCallbackValue = new TaskCompletionSource <int>(); Func <ProgressWithCompletion <GenericParameterHelper> > progressFactory = () => new ProgressWithCompletion <GenericParameterHelper>(async arg => { try { Assert.Equal(captureMainThreadContext, jtc.IsOnMainThread); // Ensure we have a main thread dependency even if we started on a threadpool thread. await jtc.Factory.SwitchToMainThreadAsync(this.TimeoutToken); actualCallbackValue.SetResult(arg.Data); } catch (Exception ex) { actualCallbackValue.SetException(ex); } }, jtc.Factory); ProgressWithCompletion <GenericParameterHelper> progress; if (captureMainThreadContext) { progress = progressFactory(); } else { var progressTask = Task.Run(progressFactory); progressTask.WaitWithoutInlining(); progress = progressTask.Result; } IProgress <GenericParameterHelper> progressReporter = progress; progressReporter.Report(new GenericParameterHelper(expectedCallbackValue)); Assert.False(actualCallbackValue.Task.IsCompleted); // Block the "main thread" while waiting for the reported progress to be executed. // Since the callback must execute on the main thread, this will deadlock unless // the underlying code is JTF aware, which is the whole point of this test to confirm. jtc.Factory.Run(async delegate { await progress.WaitAsync(this.TimeoutToken); Assert.True(actualCallbackValue.Task.IsCompleted); Assert.Equal(expectedCallbackValue, await actualCallbackValue.Task); }); }
public void WithCancellationNoncancelableNoDeadlockFromSyncContextWithinJTFRun() { var dispatcher = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(dispatcher); var jtc = new JoinableTaskContext(); jtc.Factory.Run(delegate { WithCancellationSyncBlockOnNoncancelableToken(); return(TplExtensions.CompletedTask); }); }
public void WithCancellationOfTNoncancelableNoDeadlockFromSyncContext() { var dispatcher = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(dispatcher); var tcs = new TaskCompletionSource <object>(); Task.Run(async delegate { await Task.Delay(AsyncDelay); tcs.SetResult(null); }); tcs.Task.WithCancellation(CancellationToken.None).Wait(TestTimeout); }
protected JoinableTaskTestBase(ITestOutputHelper logger) : base(logger) { this.dispatcherContext = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(this.dispatcherContext); this.context = this.CreateJoinableTaskContext(); this.joinableCollection = this.context.CreateCollection(); this.asyncPump = this.context.CreateFactory(this.joinableCollection); this.originalThreadManagedId = Environment.CurrentManagedThreadId; this.testFrame = SingleThreadedTestSynchronizationContext.NewFrame(); // Suppress the assert dialog that appears and causes test runs to hang. Trace.Listeners.OfType <DefaultTraceListener>().Single().AssertUiEnabled = false; }
protected void ExecuteOnDispatcher(Func <Task> action, bool staRequired = true) { Action worker = delegate { var frame = SingleThreadedTestSynchronizationContext.NewFrame(); Exception failure = null; SynchronizationContext.Current.Post( async _ => { try { await action(); } catch (Exception ex) { failure = ex; } finally { frame.Continue = false; } }, null); SingleThreadedTestSynchronizationContext.PushFrame(SynchronizationContext.Current, frame); if (failure != null) { ExceptionDispatchInfo.Capture(failure).Throw(); } }; if ((!staRequired || Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) && SingleThreadedTestSynchronizationContext.IsSingleThreadedSyncContext(SynchronizationContext.Current)) { worker(); } else { this.ExecuteOnSTA(() => { if (!SingleThreadedTestSynchronizationContext.IsSingleThreadedSyncContext(SynchronizationContext.Current)) { SynchronizationContext.SetSynchronizationContext(SingleThreadedTestSynchronizationContext.New()); } worker(); }); } }
public void WithCancellationOfTNoDeadlockFromSyncContext() { var dispatcher = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(dispatcher); var tcs = new TaskCompletionSource <object>(); var cts = new CancellationTokenSource(AsyncDelay / 4); try { tcs.Task.WithCancellation(cts.Token).Wait(TestTimeout); Assert.True(false, "Expected OperationCanceledException not thrown."); } catch (AggregateException ex) { ex.Handle(x => x is OperationCanceledException); } }
public void ValueFactoryRequiresMainThreadHeldByOtherSync(bool passJtfToLazyCtor) { var ctxt = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(ctxt); var context = new JoinableTaskContext(); var asyncPump = context.Factory; var originalThread = Thread.CurrentThread; var evt = new AsyncManualResetEvent(); var lazy = new AsyncLazy <object>( async delegate { // It is important that no await appear before this JTF.Run call, since // we're testing that the value factory is not invoked while the AsyncLazy // holds a private lock that would deadlock when called from another thread. asyncPump.Run(async delegate { await asyncPump.SwitchToMainThreadAsync(this.TimeoutToken); }); await Task.Yield(); return(new object()); }, passJtfToLazyCtor ? asyncPump : null); // mix it up to exercise all the code paths in the ctor. var backgroundRequest = Task.Run(async delegate { return(await lazy.GetValueAsync()); }); Thread.Sleep(AsyncDelay); // Give the background thread time to call GetValueAsync(), but it doesn't yield (when the test was written). var foregroundRequest = lazy.GetValueAsync(); var frame = SingleThreadedTestSynchronizationContext.NewFrame(); var combinedTask = Task.WhenAll(foregroundRequest, backgroundRequest); combinedTask.WithTimeout(UnexpectedTimeout).ContinueWith(_ => frame.Continue = false, TaskScheduler.Default); SingleThreadedTestSynchronizationContext.PushFrame(ctxt, frame); // Ensure that the test didn't simply timeout, and that the individual tasks did not throw. Assert.True(foregroundRequest.IsCompleted); Assert.True(backgroundRequest.IsCompleted); Assert.Same(foregroundRequest.GetAwaiter().GetResult(), backgroundRequest.GetAwaiter().GetResult()); }
public void ValueFactoryRequiresMainThreadHeldByOtherInJTFRun() { var ctxt = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(ctxt); var context = new JoinableTaskContext(); var asyncPump = context.Factory; var originalThread = Thread.CurrentThread; var evt = new AsyncManualResetEvent(); var lazy = new AsyncLazy <object>( async delegate { // It is important that no await appear before this JTF.Run call, since // we're testing that the value factory is not invoked while the AsyncLazy // holds a private lock that would deadlock when called from another thread. asyncPump.Run(async delegate { await asyncPump.SwitchToMainThreadAsync(this.TimeoutToken); }); await Task.Yield(); return(new object()); }, asyncPump); var backgroundRequest = Task.Run(async delegate { return(await lazy.GetValueAsync()); }); Thread.Sleep(AsyncDelay); // Give the background thread time to call GetValueAsync(), but it doesn't yield (when the test was written). asyncPump.Run(async delegate { var foregroundValue = await lazy.GetValueAsync(this.TimeoutToken); var backgroundValue = await backgroundRequest; Assert.Same(foregroundValue, backgroundValue); }); }
public void AsyncLazy_CompletesOnThreadWithValueFactory(NamedSyncContexts invokeOn, NamedSyncContexts completeOn) { // Set up various SynchronizationContexts that we may invoke or complete the async method with. var aSyncContext = SingleThreadedTestSynchronizationContext.New(); var bSyncContext = SingleThreadedTestSynchronizationContext.New(); var invokeOnSyncContext = invokeOn == NamedSyncContexts.None ? null : invokeOn == NamedSyncContexts.A ? aSyncContext : invokeOn == NamedSyncContexts.B ? bSyncContext : throw new ArgumentOutOfRangeException(nameof(invokeOn)); var completeOnSyncContext = completeOn == NamedSyncContexts.None ? null : completeOn == NamedSyncContexts.A ? aSyncContext : completeOn == NamedSyncContexts.B ? bSyncContext : throw new ArgumentOutOfRangeException(nameof(completeOn)); // Set up a single-threaded SynchronizationContext that we'll invoke the async method within. SynchronizationContext.SetSynchronizationContext(invokeOnSyncContext); var unblockAsyncMethod = new TaskCompletionSource <bool>(); var getValueTask = new AsyncLazy <int>(async delegate { await unblockAsyncMethod.Task.ConfigureAwaitRunInline(); return(Environment.CurrentManagedThreadId); }).GetValueAsync(); var verificationTask = getValueTask.ContinueWith( lazyValue => Environment.CurrentManagedThreadId, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); SynchronizationContext.SetSynchronizationContext(completeOnSyncContext); unblockAsyncMethod.SetResult(true); // Confirm that setting the intermediate task allowed the async method to complete immediately, using our thread to do it. Assert.True(verificationTask.IsCompleted); Assert.Equal(Environment.CurrentManagedThreadId, verificationTask.Result); }
private JoinableTaskContext InitializeJTCAndSC() { SynchronizationContext.SetSynchronizationContext(SingleThreadedTestSynchronizationContext.New()); return(new JoinableTaskContext()); }