예제 #1
0
        public static void DerivedCancellationTokenSource_Negative()
        {
            // Test the Dispose path for a class derived from CancellationTokenSource
            {
                var disposeTracker = new DisposeTracker();
                CancellationTokenSource c = new DerivedCTS(disposeTracker);

                c.Dispose();

                // Dispose() should have prevented the finalizer from running. Give the finalizer a chance to run. If this
                // results in Dispose(false) getting called, we'll catch the issue.
                GC.Collect();
                GC.WaitForPendingFinalizers();
                Assert.Throws<ObjectDisposedException>(
                    () =>
                    {
                        // Accessing the Token property should throw an ObjectDisposedException
                        if (c.Token.CanBeCanceled)
                            Assert.True(false, string.Format("DerivedCancellationTokenSource: Accessing the Token property should throw an ObjectDisposedException, but it did not."));
                        else
                            Assert.True(false, string.Format("DerivedCancellationTokenSource: Accessing the Token property should throw an ObjectDisposedException, but it did not."));
                    });
            }
        }
예제 #2
0
        public static void DerivedCancellationTokenSource()
        {
            // Verify that a derived CTS is functional
            {
                CancellationTokenSource c = new DerivedCTS(null);
                CancellationToken token = c.Token;

                var task = Task.Factory.StartNew(() => c.Cancel());
                task.Wait();

                Assert.True(token.IsCancellationRequested,
                   "DerivedCancellationTokenSource:  The token should have been cancelled.");
            }

            // Verify that callback list on a derived CTS is functional
            {
                CancellationTokenSource c = new DerivedCTS(null);
                CancellationToken token = c.Token;
                int callbackRan = 0;

                token.Register(() => Interlocked.Increment(ref callbackRan));

                var task = Task.Factory.StartNew(() => c.Cancel());
                task.Wait();
                SpinWait.SpinUntil(() => callbackRan > 0, 1000);

                Assert.True(callbackRan == 1,
                   "DerivedCancellationTokenSource:  Expected the callback to run once. Instead, it ran " + callbackRan + " times.");
            }

            // Test the Dispose path for a class derived from CancellationTokenSource
            {
                var disposeTracker = new DisposeTracker();
                CancellationTokenSource c = new DerivedCTS(disposeTracker);
                Assert.True(c.Token.CanBeCanceled,
                    "DerivedCancellationTokenSource:  The token should be cancellable.");

                c.Dispose();

                // Dispose() should have prevented the finalizer from running. Give the finalizer a chance to run. If this
                // results in Dispose(false) getting called, we'll catch the issue.
                GC.Collect();
                GC.WaitForPendingFinalizers();

                Assert.True(disposeTracker.DisposeTrueCalled,
                    "DerivedCancellationTokenSource:  Dispose(true) should have been called.");
                Assert.False(disposeTracker.DisposeFalseCalled,
                    "DerivedCancellationTokenSource:  Dispose(false) should not have been called.");
            }

            // Test the finalization code path for a class derived from CancellationTokenSource
            {
                var disposeTracker = new DisposeTracker();

                // Since the object is not assigned into a variable, it can be GC'd before the current method terminates.
                // (This is only an issue in the Debug build)
                new DerivedCTS(disposeTracker);

                // Wait until the DerivedCTS object is finalized
                SpinWait.SpinUntil(() =>
                {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    GC.Collect();
                    return disposeTracker.DisposeTrueCalled;
                }, 500);

                Assert.False(disposeTracker.DisposeTrueCalled,
                    "DerivedCancellationTokenSource:  Dispose(true) should not have been called.");
                Assert.True(disposeTracker.DisposeFalseCalled,
                    "DerivedCancellationTokenSource:  Dispose(false) should have been called.");
            }

            // Verify that Dispose(false) is a no-op on the CTS. Dispose(false) should only release any unmanaged resources, and
            // CTS does not currently hold any unmanaged resources.
            {
                var disposeTracker = new DisposeTracker();

                DerivedCTS c = new DerivedCTS(disposeTracker);
                c.DisposeUnmanaged();

                // No exception expected - the CancellationTokenSource should be valid
                Assert.True(c.Token.CanBeCanceled,
                   "DerivedCancellationTokenSource:  The token should still be cancellable.");

                Assert.False(disposeTracker.DisposeTrueCalled,
                   "DerivedCancellationTokenSource:  Dispose(true) should not have been called.");
                Assert.True(disposeTracker.DisposeFalseCalled,
                   "DerivedCancellationTokenSource:  Dispose(false) should have run.");
            }
        }