Mercurial > pub > ImplabNet
view Implab/CancellationToken.cs @ 240:fa6cbf4d8841 v3
refactoring, moving to dotnercore, simplifying promises
author | cin |
---|---|
date | Tue, 23 Jan 2018 19:39:21 +0300 |
parents | |
children | 5cb4826c2c2a |
line wrap: on
line source
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; } } } }