CancellationToken.cs
64 lines
| 2.2 KiB
| text/x-csharp
|
CSharpLexer
/ Implab / CancellationToken.cs
|
|
r240 | using System; | |
| using System.Threading; | |||
| using Implab.Parallels; | |||
| namespace Implab { | |||
| public class CancellationToken : ICancellationToken { | |||
| const int CANCEL_NOT_REQUESTED = 0; | |||
| const int CANCEL_REQUESTING = 1; | |||
| const int CANCEL_REQUESTED = 2; | |||
| volatile int m_state = CANCEL_NOT_REQUESTED; | |||
| Action<Exception> m_handler; | |||
| Parallels.SimpleAsyncQueue<Action<Exception>> m_handlers; | |||
| public bool IsCancellationRequested { | |||
| get { return m_state == CANCEL_REQUESTED; } | |||
| } | |||
| public Exception CancellationReason { | |||
| get; set; | |||
| } | |||
| public void CancellationRequested(Action<Exception> handler) { | |||
| Safe.ArgumentNotNull(handler, nameof(handler)); | |||
| if (IsCancellationRequested) { | |||
| handler(CancellationReason); | |||
| } else { | |||
| EnqueueHandler(handler); | |||
| if (IsCancellationRequested && TryDequeueHandler(out handler)) | |||
| handler(CancellationReason); | |||
| } | |||
| } | |||
| bool TryDequeueHandler(out Action<Exception> handler) { | |||
| handler = Interlocked.Exchange(ref m_handler, null); | |||
| if (handler != null) | |||
| return true; | |||
| else if (m_handlers != null) | |||
| return m_handlers.TryDequeue(out handler); | |||
| else | |||
| return false; | |||
| } | |||
| void EnqueueHandler(Action<Exception> handler) { | |||
| if (Interlocked.CompareExchange(ref m_handler, handler, null) != null) { | |||
| if (m_handlers == null) | |||
| // compare-exchange will fprotect from loosing already created queue | |||
| Interlocked.CompareExchange(ref m_handlers, new SimpleAsyncQueue<Action<Exception>>(), null); | |||
| m_handlers.Enqueue(handler); | |||
| } | |||
| } | |||
| void RequestCancellation(Exception reason) { | |||
| if (Interlocked.CompareExchange(ref m_state, CANCEL_REQUESTING, CANCEL_NOT_REQUESTED) == CANCEL_NOT_REQUESTED) { | |||
| if (reason == null) | |||
| reason = new OperationCanceledException(); | |||
| CancellationReason = reason; | |||
| m_state = CANCEL_REQUESTED; | |||
| } | |||
| } | |||
| } | |||
| } |
