2 // CancellationTokenSource.cs
5 // Jérémie "Garuma" Laval <jeremie.laval@gmail.com>
6 // Marek Safar (marek.safar@gmail.com)
8 // Copyright (c) 2009 Jérémie "Garuma" Laval
9 // Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30 using System.Collections.Generic;
32 namespace System.Threading
37 public class CancellationTokenSource : IDisposable
43 int currId = int.MinValue;
45 Dictionary<CancellationTokenRegistration, Action> callbacks;
47 ManualResetEvent handle;
48 readonly object syncRoot = new object ();
50 internal static readonly CancellationTokenSource NoneSource = new CancellationTokenSource ();
51 internal static readonly CancellationTokenSource CanceledSource = new CancellationTokenSource ();
54 static readonly TimerCallback timer_callback;
58 static CancellationTokenSource ()
60 CanceledSource.processed = true;
61 CanceledSource.canceled = true;
64 timer_callback = token => {
65 var cts = (CancellationTokenSource) token;
71 public CancellationTokenSource ()
73 callbacks = new Dictionary<CancellationTokenRegistration, Action> ();
74 handle = new ManualResetEvent (false);
78 public CancellationTokenSource (int millisecondsDelay)
81 if (millisecondsDelay < -1)
82 throw new ArgumentOutOfRangeException ("millisecondsDelay");
84 if (millisecondsDelay != Timeout.Infinite)
85 timer = new Timer (timer_callback, this, millisecondsDelay, Timeout.Infinite);
88 public CancellationTokenSource (TimeSpan delay)
89 : this (CheckTimeout (delay))
94 public CancellationToken Token {
97 return new CancellationToken (this);
101 public bool IsCancellationRequested {
107 internal WaitHandle WaitHandle {
114 public void Cancel ()
119 // If parameter is true we throw exception as soon as they appear otherwise we aggregate them
120 public void Cancel (bool throwOnFirstException)
127 List<Exception> exceptions = null;
131 foreach (var item in callbacks) {
132 if (throwOnFirstException) {
137 } catch (Exception e) {
138 if (exceptions == null)
139 exceptions = new List<Exception> ();
150 Thread.MemoryBarrier ();
153 if (exceptions != null)
154 throw new AggregateException (exceptions);
158 public void CancelAfter (TimeSpan delay)
160 CancelAfter (CheckTimeout (delay));
163 public void CancelAfter (int millisecondsDelay)
165 if (millisecondsDelay < -1)
166 throw new ArgumentOutOfRangeException ("millisecondsDelay");
170 if (canceled || millisecondsDelay == Timeout.Infinite)
174 // Have to be carefull not to create secondary background timer
175 var t = new Timer (timer_callback, this, Timeout.Infinite, Timeout.Infinite);
176 if (Interlocked.CompareExchange (ref timer, t, null) != null)
180 timer.Change (millisecondsDelay, Timeout.Infinite);
184 public static CancellationTokenSource CreateLinkedTokenSource (CancellationToken token1, CancellationToken token2)
186 return CreateLinkedTokenSource (new [] { token1, token2 });
189 public static CancellationTokenSource CreateLinkedTokenSource (params CancellationToken[] tokens)
192 throw new ArgumentNullException ("tokens");
194 if (tokens.Length == 0)
195 throw new ArgumentException ("Empty tokens array");
197 CancellationTokenSource src = new CancellationTokenSource ();
198 Action action = src.Cancel;
200 foreach (CancellationToken token in tokens) {
201 if (token.CanBeCanceled)
202 token.Register (action);
208 static int CheckTimeout (TimeSpan delay)
211 return checked ((int) delay.TotalMilliseconds);
212 } catch (OverflowException) {
213 throw new ArgumentOutOfRangeException ("delay");
217 void CheckDisposed ()
220 throw new ObjectDisposedException (GetType ().Name);
223 public void Dispose ()
231 void Dispose (bool disposing)
233 if (disposing && !disposed) {
245 internal CancellationTokenRegistration Register (Action callback, bool useSynchronizationContext)
249 var tokenReg = new CancellationTokenRegistration (Interlocked.Increment (ref currId), this);
256 if (!(temp = canceled))
257 callbacks.Add (tokenReg, callback);
266 internal void RemoveCallback (CancellationTokenRegistration tokenReg)
271 callbacks.Remove (tokenReg);
277 SpinWait sw = new SpinWait ();