File: CancellationTokenHelper.cs

package info (click to toggle)
mono 6.8.0.105%2Bdfsg-3.3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,284,512 kB
  • sloc: cs: 11,172,132; xml: 2,850,069; ansic: 671,653; cpp: 122,091; perl: 59,366; javascript: 30,841; asm: 22,168; makefile: 20,093; sh: 15,020; python: 4,827; pascal: 925; sql: 859; sed: 16; php: 1
file content (102 lines) | stat: -rw-r--r-- 4,590 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//------------------------------------------------------------------------------
// <copyright file="CancellationTokenHelper.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------

namespace System.Web.Util {
    using System;
    using System.Threading;

    // Helper class for dealing with CancellationToken instances. Our Cancel and Dispose methods
    // are fully thread-safe and will never block or throw, while a normal CancellationTokenSource
    // doesn't make these guarantees.

    internal sealed class CancellationTokenHelper : IDisposable {

        private const int STATE_CREATED = 0;
        private const int STATE_CANCELING = 1;
        private const int STATE_CANCELED = 2;
        private const int STATE_DISPOSING = 3;
        private const int STATE_DISPOSED = 4; // terminal state

        // A CancellationTokenHelper which is already marked as disposed; useful for avoiding
        // allocations of CancellationTokenHelper instances which are never observed.
        internal static readonly CancellationTokenHelper StaticDisposed = GetStaticDisposedHelper();

        private readonly CancellationTokenSource _cts = new CancellationTokenSource();
        private int _state;

        public CancellationTokenHelper(bool canceled) {
            if (canceled) {
                _cts.Cancel();
            }
            _state = (canceled) ? STATE_CANCELED : STATE_CREATED;
        }

        internal bool IsCancellationRequested {
            get { return _cts.IsCancellationRequested; }
        }

        internal CancellationToken Token {
            get { return _cts.Token; }
        }

        // Cancels the token.
        public void Cancel() {
            if (Interlocked.CompareExchange(ref _state, STATE_CANCELING, STATE_CREATED) == STATE_CREATED) {
                // Only allow cancellation if the token hasn't yet been canceled or disposed.
                // Cancel on a ThreadPool thread so that we can release the original thread back to IIS.
                // We can use UnsafeQUWI to avoid an extra ExecutionContext capture since CancellationToken already captures it.
                ThreadPool.UnsafeQueueUserWorkItem(_ => {
                    try {
                        _cts.Cancel();
                    }
                    catch {
                        // ---- all exceptions to avoid killing the worker process.
                    }
                    finally {
                        if (Interlocked.CompareExchange(ref _state, STATE_CANCELED, STATE_CANCELING) == STATE_DISPOSING) {
                            // A call to Dispose() came in on another thread while we were in the middle of a cancel
                            // operation. That thread will no-op, so we'll dispose of it here.
                            _cts.Dispose();
                            Interlocked.Exchange(ref _state, STATE_DISPOSED);
                        }
                    }
                }, null);
            }
        }

        // Disposes of the token.
        public void Dispose() {
            // Only allow a single call to Dispose.
            int originalState = Interlocked.Exchange(ref _state, STATE_DISPOSING);
            switch (originalState) {
                case STATE_CREATED:
                case STATE_CANCELED:
                    // If Cancel() hasn't yet been called or has already run to completion,
                    // the underlying CTS guarantees that the Dispose method won't block
                    // or throw, so we can just call it directly.
                    _cts.Dispose();
                    Interlocked.Exchange(ref _state, STATE_DISPOSED);
                    break;

                case STATE_DISPOSED:
                    // If the object was already disposed, we need to reset the flag here
                    // since we accidentally blew it away with the original Exchange.
                    Interlocked.Exchange(ref _state, STATE_DISPOSED);
                    break;

                // Otherwise, the object is already canceling or disposing, so the
                // other thread will handle the call to Dispose().
            }
        }

        private static CancellationTokenHelper GetStaticDisposedHelper() {
            CancellationTokenHelper helper = new CancellationTokenHelper(false);
            helper.Dispose();
            return helper;
        }

    }
}