Mercurial > pub > ImplabNet
diff Implab/AbstractEvent.cs @ 243:b1e0ffdf3451 v3
working on promises
author | cin |
---|---|
date | Wed, 24 Jan 2018 19:24:10 +0300 |
parents | cbe10ac0731e |
children | eee3e49dd1ff |
line wrap: on
line diff
--- a/Implab/AbstractEvent.cs Wed Jan 24 03:03:21 2018 +0300 +++ b/Implab/AbstractEvent.cs Wed Jan 24 19:24:10 2018 +0300 @@ -5,28 +5,61 @@ 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 PENDING_SATE = 0; - protected const int TRANSITIONAL_STATE = 1; - protected const int SUCCEEDED_STATE = 2; - protected const int REJECTED_STATE = 3; + const int TRANSITIONAL_STATE = 1; + + const int FULFILLED_STATE = 2; volatile int m_state; - Exception m_error; THandler m_handler; SimpleAsyncQueue<THandler> 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(int state) { + protected void CompleteTransit() { #if DEBUG - if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, state, TRANSITIONAL_STATE)) + 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; @@ -43,47 +76,13 @@ } } - protected bool BeginSetResult() { - if (!BeginTransit()) { - WaitTransition(); - return false; - } - return true; - } - protected void EndSetResult() { - CompleteTransit(SUCCEEDED_STATE); - } - - - - /// <summary> - /// Выполняет обещание, сообщая об ошибке - /// </summary> - /// <remarks> - /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков - /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные - /// будут проигнорированы. - /// </remarks> - /// <param name="error">Исключение возникшее при выполнении операции</param> - /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception> - protected void SetError(Exception error) { - if (BeginTransit()) { - m_error = error; - CompleteTransit(REJECTED_STATE); - } else { - WaitTransition(); - if (m_state == SUCCEEDED_STATE) - throw new InvalidOperationException("The promise is already resolved"); - } - } - - protected abstract void SignalHandler(THandler handler, int signal); + protected abstract void SignalHandler(THandler handler); void Signal() { THandler handler; while (TryDequeueHandler(out handler)) - SignalHandler(handler, m_state); + SignalHandler(handler); } #endregion @@ -94,46 +93,41 @@ protected void WaitResult(int timeout) { if (!(IsFulfilled || GetFulfillSignal().Wait(timeout))) throw new TimeoutException(); - - if (IsRejected) - Rethrow(); } - protected void Rethrow() { - Debug.Assert(m_error != null); - if (m_error is OperationCanceledException) - throw new OperationCanceledException("Operation cancelled", m_error); - else - throw new TargetInvocationException(m_error); - } + #endregion #region handlers managment protected void AddHandler(THandler handler) { - if (m_state > 1) { + if (IsFulfilled) { // the promise is in the resolved state, just invoke the handler - SignalHandler(handler, m_state); + SignalHandler(handler); } else { - if (Interlocked.CompareExchange(ref m_handler, handler, null) != null) { - if (m_extraHandlers == null) - // compare-exchange will fprotect from loosing already created queue - Interlocked.CompareExchange(ref m_extraHandlers, new SimpleAsyncQueue<THandler>(), null); - m_extraHandlers.Enqueue(handler); - } + EnqueueHandler(handler); - if (m_state > 1 && TryDequeueHandler(out 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, m_state); + 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) @@ -142,29 +136,6 @@ } #endregion - - #region IPromise implementation - - public bool IsFulfilled { - get { - return m_state > TRANSITIONAL_STATE; - } - } - - public bool IsRejected { - get { - return m_state == REJECTED_STATE; - } - } - - #endregion - - public Exception RejectReason { - get { - return m_error; - } - } - } }