Mercurial > pub > ImplabNet
view Implab/AbstractEvent.cs @ 249:d82909310094 v3
Implab.Test moved to xunit
Complete set of PromiseHelpers (Then, Catch, Finally)
Removed obsolete types ICancellable, ICancellationToken
author | cin |
---|---|
date | Wed, 31 Jan 2018 11:28:38 +0300 |
parents | 5cb4826c2c2a |
children | 34df34841225 |
line wrap: on
line source
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 = state; #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 } }