AbstractEvent.cs
130 lines
| 4.4 KiB
| text/x-csharp
|
CSharpLexer
/ Implab / AbstractEvent.cs
cin
|
r144 | using System; | ||
using Implab.Parallels; | ||||
using System.Threading; | ||||
using System.Reflection; | ||||
cin
|
r242 | using System.Diagnostics; | ||
cin
|
r144 | |||
namespace Implab { | ||||
cin
|
r243 | /// <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> | ||||
cin
|
r242 | public abstract class AbstractEvent<THandler> where THandler : class { | ||
cin
|
r244 | const int PendingState = 0; | ||
cin
|
r242 | |||
cin
|
r244 | const int TransitionalState = 1; | ||
cin
|
r243 | |||
cin
|
r244 | const int ResolvedState = 2; | ||
cin
|
r144 | |||
cin
|
r242 | volatile int m_state; | ||
cin
|
r144 | |||
cin
|
r242 | THandler m_handler; | ||
cin
|
r233 | SimpleAsyncQueue<THandler> m_extraHandlers; | ||
cin
|
r144 | |||
cin
|
r244 | public bool IsResolved { | ||
cin
|
r243 | get { | ||
cin
|
r244 | return m_state > TransitionalState; | ||
cin
|
r243 | } | ||
} | ||||
cin
|
r144 | #region state managment | ||
cin
|
r242 | protected bool BeginTransit() { | ||
cin
|
r244 | return PendingState == Interlocked.CompareExchange(ref m_state, TransitionalState, PendingState); | ||
cin
|
r144 | } | ||
cin
|
r243 | protected void CompleteTransit() { | ||
cin
|
r242 | #if DEBUG | ||
cin
|
r244 | if (TransitionalState != Interlocked.CompareExchange(ref m_state, ResolvedState, TransitionalState)) | ||
cin
|
r144 | throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state"); | ||
cin
|
r242 | #else | ||
m_state = state; | ||||
#endif | ||||
Signal(); | ||||
cin
|
r144 | } | ||
cin
|
r242 | protected void WaitTransition() { | ||
cin
|
r244 | if (m_state == TransitionalState) { | ||
cin
|
r248 | SpinWait spin = new SpinWait(); | ||
cin
|
r242 | do { | ||
spin.SpinOnce(); | ||||
cin
|
r244 | } while (m_state == TransitionalState); | ||
cin
|
r144 | } | ||
} | ||||
cin
|
r243 | protected abstract void SignalHandler(THandler handler); | ||
cin
|
r144 | |||
cin
|
r156 | void Signal() { | ||
cin
|
r242 | THandler handler; | ||
while (TryDequeueHandler(out handler)) | ||||
cin
|
r243 | SignalHandler(handler); | ||
cin
|
r144 | } | ||
#endregion | ||||
#region handlers managment | ||||
protected void AddHandler(THandler handler) { | ||||
cin
|
r244 | if (IsResolved) { | ||
cin
|
r144 | // the promise is in the resolved state, just invoke the handler | ||
cin
|
r243 | SignalHandler(handler); | ||
cin
|
r144 | } else { | ||
cin
|
r243 | EnqueueHandler(handler); | ||
cin
|
r144 | |||
cin
|
r244 | if (IsResolved && TryDequeueHandler(out handler)) | ||
cin
|
r144 | // 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 :) | ||||
cin
|
r243 | SignalHandler(handler); | ||
cin
|
r144 | } | ||
cin
|
r242 | |||
} | ||||
cin
|
r243 | 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); | ||||
} | ||||
} | ||||
cin
|
r242 | 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); | ||||
cin
|
r144 | } | ||
#endregion | ||||
} | ||||
} | ||||