using System;
using Implab.Parallels;
using System.Threading;
using System.Reflection;
using System.Diagnostics;
namespace Implab {
///
/// Abstract class for creation of custom one-shot thread safe events.
///
///
///
/// An event is something that should happen in the future and the
/// triggering of the event causes execution of some pending actions
/// which are formely event handlers. One-shot events occur only once
/// and any handler added after the event is triggered should run
/// without a delay.
///
///
/// The lifecycle of the one-shot event is tipically consists of following
/// phases.
///
/// Pending state. This is the initial state of the event. Any
/// handler added to the event will be queued for the future execution.
///
/// Transitional state. This is intermediate state between pending
/// and fulfilled states, during this state internal initialization and storing
/// of the result occurs.
///
/// Fulfilled state. The event contains the result, all queued
/// handlers are signalled to run and newly added handlers are executed
/// immediatelly.
///
///
///
///
public abstract class AbstractEvent where THandler : class {
const int PENDING_SATE = 0;
const int TRANSITIONAL_STATE = 1;
const int FULFILLED_STATE = 2;
volatile int m_state;
THandler m_handler;
SimpleAsyncQueue m_extraHandlers;
public bool IsFulfilled {
get {
return m_state > TRANSITIONAL_STATE;
}
}
#region state managment
protected bool BeginTransit() {
return PENDING_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, PENDING_SATE);
}
protected void CompleteTransit() {
#if DEBUG
if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, FULFILLED_STATE, TRANSITIONAL_STATE))
throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
#else
m_state = state;
#endif
Signal();
}
protected void WaitTransition() {
if (m_state == TRANSITIONAL_STATE) {
SpinWait spin;
do {
spin.SpinOnce();
} while (m_state == TRANSITIONAL_STATE);
}
}
protected abstract void SignalHandler(THandler handler);
void Signal() {
THandler handler;
while (TryDequeueHandler(out handler))
SignalHandler(handler);
}
#endregion
protected abstract Signal GetFulfillSignal();
#region synchronization traits
protected void WaitResult(int timeout) {
if (!(IsFulfilled || GetFulfillSignal().Wait(timeout)))
throw new TimeoutException();
}
#endregion
#region handlers managment
protected void AddHandler(THandler handler) {
if (IsFulfilled) {
// the promise is in the resolved state, just invoke the handler
SignalHandler(handler);
} else {
EnqueueHandler(handler);
if (IsFulfilled && TryDequeueHandler(out handler))
// if the promise have been resolved while we was adding the handler to the queue
// we can't guarantee that someone is still processing it
// therefore we need to fetch a handler from the queue and execute it
// note that fetched handler may be not the one that we have added
// even we can fetch no handlers at all :)
SignalHandler(handler);
}
}
void EnqueueHandler(THandler handler) {
if (Interlocked.CompareExchange(ref m_handler, handler, null) != null) {
if (m_extraHandlers == null)
// compare-exchange will protect from loosing already created queue
Interlocked.CompareExchange(ref m_extraHandlers, new SimpleAsyncQueue(), null);
m_extraHandlers.Enqueue(handler);
}
}
bool TryDequeueHandler(out THandler handler) {
handler = Interlocked.Exchange(ref m_handler, null);
if (handler != null)
return true;
return m_extraHandlers != null && m_extraHandlers.TryDequeue(out handler);
}
#endregion
}
}