##// END OF EJS Templates
Fixed promise rejection when there is not specified error handler in the reaction....
Fixed promise rejection when there is not specified error handler in the reaction. FIXED SPELLING IN THE XML CONTAINER CONFIGURATION signleton->singleton Code cleanup Update tests make them working on dotnet core

File last commit:

r295:28af686e24f7 default
r295:28af686e24f7 default
Show More
AbstractEvent.cs
128 lines | 4.4 KiB | text/x-csharp | CSharpLexer
cin
Added tests for Implab.ServiceHost.Unity configuration loader.
r289 using System;
using Implab.Parallels;
using System.Threading;
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
}
}