System.Drawing: added email to icon and test file headers
[mono.git] / mcs / class / corlib / System.Threading / CancellationTokenSource.cs
1 // 
2 // CancellationTokenSource.cs
3 //  
4 // Authors:
5 //       Jérémie "Garuma" Laval <jeremie.laval@gmail.com>
6 //       Marek Safar (marek.safar@gmail.com)
7 // 
8 // Copyright (c) 2009 Jérémie "Garuma" Laval
9 // Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
10 // 
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:
17 // 
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 // 
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
27 // THE SOFTWARE.
28
29 #if NET_4_0 || MOBILE
30 using System.Collections.Generic;
31 using System.Collections.Concurrent;
32
33 namespace System.Threading
34 {
35 #if !NET_4_5
36         sealed
37 #endif
38         public class CancellationTokenSource : IDisposable
39         {
40                 bool canceled;
41                 bool disposed;
42                 
43                 int currId = int.MinValue;
44                 ConcurrentDictionary<CancellationTokenRegistration, Action> callbacks;
45
46                 ManualResetEvent handle;
47                 
48                 internal static readonly CancellationTokenSource NoneSource = new CancellationTokenSource ();
49                 internal static readonly CancellationTokenSource CanceledSource = new CancellationTokenSource ();
50                 
51 #if NET_4_5
52                 static readonly TimerCallback timer_callback;
53                 Timer timer;
54 #endif
55
56                 static CancellationTokenSource ()
57                 {
58                         CanceledSource.canceled = true;
59
60 #if NET_4_5
61                         timer_callback = token => {
62                                 var cts = (CancellationTokenSource) token;
63                                 cts.Cancel ();
64                         };
65 #endif
66                 }
67
68                 public CancellationTokenSource ()
69                 {
70                         callbacks = new ConcurrentDictionary<CancellationTokenRegistration, Action> ();
71                         handle = new ManualResetEvent (false);
72                 }
73
74 #if NET_4_5
75                 public CancellationTokenSource (int millisecondsDelay)
76                         : this ()
77                 {
78                         if (millisecondsDelay < -1)
79                                 throw new ArgumentOutOfRangeException ("millisecondsDelay");
80
81                         if (millisecondsDelay != Timeout.Infinite)
82                                 timer = new Timer (timer_callback, this, millisecondsDelay, Timeout.Infinite);
83                 }
84
85                 public CancellationTokenSource (TimeSpan delay)
86                         : this (CheckTimeout (delay))
87                 {
88                 }
89 #endif
90
91                 public CancellationToken Token {
92                         get {
93                                 CheckDisposed ();
94                                 return new CancellationToken (this);
95                         }
96                 }
97                 
98                 public bool IsCancellationRequested {
99                         get {
100                                 return canceled;
101                         }
102                 }
103                 
104                 internal WaitHandle WaitHandle {
105                         get {
106                                 CheckDisposed ();
107                                 return handle;
108                         }
109                 }
110                 
111                 public void Cancel ()
112                 {
113                         Cancel (false);
114                 }
115                 
116                 // If parameter is true we throw exception as soon as they appear otherwise we aggregate them
117                 public void Cancel (bool throwOnFirstException)
118                 {
119                         CheckDisposed ();
120
121                         canceled = true;
122                         handle.Set ();
123                         
124                         List<Exception> exceptions = null;
125                         
126                         try {
127                                 Action cb;
128                                 for (int id = int.MinValue + 1; id <= currId; id++) {
129                                         if (!callbacks.TryRemove (new CancellationTokenRegistration (id, this), out cb))
130                                                 continue;
131                                         if (cb == null)
132                                                 continue;
133
134                                         if (throwOnFirstException) {
135                                                 cb ();
136                                         } else {
137                                                 try {
138                                                         cb ();
139                                                 } catch (Exception e) {
140                                                         if (exceptions == null)
141                                                                 exceptions = new List<Exception> ();
142
143                                                         exceptions.Add (e);
144                                                 }
145                                         }
146                                 }
147                         } finally {
148                                 callbacks.Clear ();
149                         }
150
151                         if (exceptions != null)
152                                 throw new AggregateException (exceptions);
153                 }
154
155 #if NET_4_5
156                 public void CancelAfter (TimeSpan delay)
157                 {
158                         CancelAfter (CheckTimeout (delay));
159                 }
160
161                 public void CancelAfter (int millisecondsDelay)
162                 {
163                         if (millisecondsDelay < -1)
164                                 throw new ArgumentOutOfRangeException ("millisecondsDelay");
165
166                         CheckDisposed ();
167
168                         if (canceled || millisecondsDelay == Timeout.Infinite)
169                                 return;
170
171                         if (timer == null) {
172                                 // Have to be carefull not to create secondary background timer
173                                 var t = new Timer (timer_callback, this, Timeout.Infinite, Timeout.Infinite);
174                                 if (Interlocked.CompareExchange (ref timer, t, null) != null)
175                                         t.Dispose ();
176                         }
177
178                         timer.Change (millisecondsDelay, Timeout.Infinite);
179                 }
180 #endif
181
182                 public static CancellationTokenSource CreateLinkedTokenSource (CancellationToken token1, CancellationToken token2)
183                 {
184                         return CreateLinkedTokenSource (new [] { token1, token2 });
185                 }
186                 
187                 public static CancellationTokenSource CreateLinkedTokenSource (params CancellationToken[] tokens)
188                 {
189                         if (tokens == null)
190                                 throw new ArgumentNullException ("tokens");
191
192                         if (tokens.Length == 0)
193                                 throw new ArgumentException ("Empty tokens array");
194
195                         CancellationTokenSource src = new CancellationTokenSource ();
196                         Action action = src.Cancel;
197
198                         foreach (CancellationToken token in tokens) {
199                                 if (token.CanBeCanceled)
200                                         token.Register (action);
201                         }
202                         
203                         return src;
204                 }
205
206                 static int CheckTimeout (TimeSpan delay)
207                 {
208                         try {
209                                 return checked ((int) delay.TotalMilliseconds);
210                         } catch (OverflowException) {
211                                 throw new ArgumentOutOfRangeException ("delay");
212                         }
213                 }
214
215                 void CheckDisposed ()
216                 {
217                         if (disposed)
218                                 throw new ObjectDisposedException (GetType ().Name);
219                 }
220
221                 public void Dispose ()
222                 {
223                         Dispose (true);
224                 }
225
226 #if NET_4_5
227                 protected virtual
228 #endif
229                 void Dispose (bool disposing)
230                 {
231                         if (disposing && !disposed) {
232                                 disposed = true;
233                                 Thread.MemoryBarrier ();
234
235                                 callbacks = null;
236 #if NET_4_5
237                                 if (timer != null)
238                                         timer.Dispose ();
239 #endif
240                                 handle.Dispose ();
241                         }
242                 }
243                 
244                 internal CancellationTokenRegistration Register (Action callback, bool useSynchronizationContext)
245                 {
246                         CheckDisposed ();
247
248                         var tokenReg = new CancellationTokenRegistration (Interlocked.Increment (ref currId), this);
249
250                         /* If the source is already canceled we execute the callback immediately
251                          * if not, we try to add it to the queue and if it is currently being processed
252                          * we try to execute it back ourselves to be sure the callback is ran
253                          */
254                         if (canceled)
255                                 callback ();
256                         else {
257                                 callbacks.TryAdd (tokenReg, callback);
258                                 if (canceled && callbacks.TryRemove (tokenReg, out callback))
259                                         callback ();
260                         }
261                         
262                         return tokenReg;
263                 }
264
265                 internal void RemoveCallback (CancellationTokenRegistration reg)
266                 {
267                         // Ignore call if the source has been disposed
268                         if (disposed)
269                                 return;
270                         Action dummy;
271                         var cbs = callbacks;
272                         if (cbs != null)
273                                 cbs.TryRemove (reg, out dummy);
274                 }
275         }
276 }
277 #endif