view Implab/CancellationToken.cs @ 243:b1e0ffdf3451 v3

working on promises
author cin
date Wed, 24 Jan 2018 19:24:10 +0300
parents fa6cbf4d8841
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;
            }
        }
        
    }
}