|
|
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;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
}
|