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;
-            }
-        }
-
     }
 }