AbstractEvent.cs
130 lines
| 4.4 KiB
| text/x-csharp
|
CSharpLexer
cin
|
r289 | using System; | ||
using Implab.Parallels; | ||||
using System.Threading; | ||||
using System.Reflection; | ||||
using System.Diagnostics; | ||||
namespace Implab { | ||||
/// <summary> | ||||
/// Abstract class for creation of custom one-shot thread safe events. | ||||
/// </summary> | ||||
/// <remarks> | ||||
/// <para> | ||||
/// 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. | ||||
/// </para> | ||||
/// <para> | ||||
/// The lifecycle of the one-shot event is tipically consists of following | ||||
/// phases. | ||||
/// <list> | ||||
/// <description>Pending state. This is the initial state of the event. Any | ||||
/// handler added to the event will be queued for the future execution. | ||||
/// </description> | ||||
/// <description>Transitional state. This is intermediate state between pending | ||||
/// and fulfilled states, during this state internal initialization and storing | ||||
/// of the result occurs. | ||||
/// </description> | ||||
/// <description>Fulfilled state. The event contains the result, all queued | ||||
/// handlers are signalled to run and newly added handlers are executed | ||||
/// immediatelly. | ||||
/// </description> | ||||
/// </list> | ||||
/// </para> | ||||
/// </remarks> | ||||
public abstract class AbstractEvent<THandler> where THandler : class { | ||||
const int PendingState = 0; | ||||
const int TransitionalState = 1; | ||||
const int ResolvedState = 2; | ||||
volatile int m_state; | ||||
THandler m_handler; | ||||
SimpleAsyncQueue<THandler> m_extraHandlers; | ||||
public bool IsResolved { | ||||
get { | ||||
return m_state > TransitionalState; | ||||
} | ||||
} | ||||
#region state managment | ||||
protected bool BeginTransit() { | ||||
return PendingState == Interlocked.CompareExchange(ref m_state, TransitionalState, PendingState); | ||||
} | ||||
protected void CompleteTransit() { | ||||
#if DEBUG | ||||
if (TransitionalState != Interlocked.CompareExchange(ref m_state, ResolvedState, TransitionalState)) | ||||
throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state"); | ||||
#else | ||||
m_state = ResolvedState; | ||||
#endif | ||||
Signal(); | ||||
} | ||||
protected void WaitTransition() { | ||||
if (m_state == TransitionalState) { | ||||
SpinWait spin = new SpinWait(); | ||||
do { | ||||
spin.SpinOnce(); | ||||
} while (m_state == TransitionalState); | ||||
} | ||||
} | ||||
protected abstract void SignalHandler(THandler handler); | ||||
void Signal() { | ||||
THandler handler; | ||||
while (TryDequeueHandler(out handler)) | ||||
SignalHandler(handler); | ||||
} | ||||
#endregion | ||||
#region handlers managment | ||||
protected void AddHandler(THandler handler) { | ||||
if (IsResolved) { | ||||
// the promise is in the resolved state, just invoke the handler | ||||
SignalHandler(handler); | ||||
} else { | ||||
EnqueueHandler(handler); | ||||
if (IsResolved && 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<THandler>(), 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 | ||||
} | ||||
} | ||||