Beispiel #1
0
        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);
            }
        }
Beispiel #3
0
        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");
        }
Beispiel #6
0
        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);
            }
        }
Beispiel #9
0
        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;
        }
Beispiel #13
0
        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());
 }