240
|
1 using System;
|
|
2 using System.Threading;
|
|
3 using Implab.Parallels;
|
|
4
|
|
5 namespace Implab {
|
|
6 public class CancellationToken : ICancellationToken {
|
|
7 const int CANCEL_NOT_REQUESTED = 0;
|
|
8 const int CANCEL_REQUESTING = 1;
|
|
9 const int CANCEL_REQUESTED = 2;
|
|
10
|
|
11 volatile int m_state = CANCEL_NOT_REQUESTED;
|
|
12
|
|
13 Action<Exception> m_handler;
|
|
14
|
|
15 Parallels.SimpleAsyncQueue<Action<Exception>> m_handlers;
|
|
16
|
|
17 public bool IsCancellationRequested {
|
|
18 get { return m_state == CANCEL_REQUESTED; }
|
|
19 }
|
|
20
|
|
21 public Exception CancellationReason {
|
|
22 get; set;
|
|
23 }
|
|
24
|
|
25 public void CancellationRequested(Action<Exception> handler) {
|
|
26 Safe.ArgumentNotNull(handler, nameof(handler));
|
|
27 if (IsCancellationRequested) {
|
|
28 handler(CancellationReason);
|
|
29 } else {
|
|
30 EnqueueHandler(handler);
|
|
31 if (IsCancellationRequested && TryDequeueHandler(out handler))
|
|
32 handler(CancellationReason);
|
|
33 }
|
|
34 }
|
|
35
|
|
36 bool TryDequeueHandler(out Action<Exception> handler) {
|
|
37 handler = Interlocked.Exchange(ref m_handler, null);
|
|
38 if (handler != null)
|
|
39 return true;
|
|
40 else if (m_handlers != null)
|
|
41 return m_handlers.TryDequeue(out handler);
|
|
42 else
|
|
43 return false;
|
|
44 }
|
|
45
|
|
46 void EnqueueHandler(Action<Exception> handler) {
|
|
47 if (Interlocked.CompareExchange(ref m_handler, handler, null) != null) {
|
|
48 if (m_handlers == null)
|
|
49 // compare-exchange will fprotect from loosing already created queue
|
|
50 Interlocked.CompareExchange(ref m_handlers, new SimpleAsyncQueue<Action<Exception>>(), null);
|
|
51 m_handlers.Enqueue(handler);
|
|
52 }
|
|
53 }
|
|
54
|
|
55 void RequestCancellation(Exception reason) {
|
|
56 if (Interlocked.CompareExchange(ref m_state, CANCEL_REQUESTING, CANCEL_NOT_REQUESTED) == CANCEL_NOT_REQUESTED) {
|
|
57 if (reason == null)
|
|
58 reason = new OperationCanceledException();
|
|
59 CancellationReason = reason;
|
|
60 m_state = CANCEL_REQUESTED;
|
|
61 }
|
|
62 }
|
|
63
|
|
64 }
|
|
65 } |