Mercurial > pub > ImplabNet
annotate Implab/AbstractEvent.cs @ 274:22629bf26121 v3
Unity xml configuration, alpha2
| author | cin |
|---|---|
| date | Fri, 27 Apr 2018 04:47:52 +0300 |
| parents | 34df34841225 |
| children |
| rev | line source |
|---|---|
| 144 | 1 using System; |
| 2 using Implab.Parallels; | |
| 3 using System.Threading; | |
| 4 using System.Reflection; | |
| 242 | 5 using System.Diagnostics; |
| 144 | 6 |
| 7 namespace Implab { | |
| 243 | 8 /// <summary> |
| 9 /// Abstract class for creation of custom one-shot thread safe events. | |
| 10 /// </summary> | |
| 11 /// <remarks> | |
| 12 /// <para> | |
| 13 /// An event is something that should happen in the future and the | |
| 14 /// triggering of the event causes execution of some pending actions | |
| 15 /// which are formely event handlers. One-shot events occur only once | |
| 16 /// and any handler added after the event is triggered should run | |
| 17 /// without a delay. | |
| 18 /// </para> | |
| 19 /// <para> | |
| 20 /// The lifecycle of the one-shot event is tipically consists of following | |
| 21 /// phases. | |
| 22 /// <list> | |
| 23 /// <description>Pending state. This is the initial state of the event. Any | |
| 24 /// handler added to the event will be queued for the future execution. | |
| 25 /// </description> | |
| 26 /// <description>Transitional state. This is intermediate state between pending | |
| 27 /// and fulfilled states, during this state internal initialization and storing | |
| 28 /// of the result occurs. | |
| 29 /// </description> | |
| 30 /// <description>Fulfilled state. The event contains the result, all queued | |
| 31 /// handlers are signalled to run and newly added handlers are executed | |
| 32 /// immediatelly. | |
| 33 /// </description> | |
| 34 /// </list> | |
| 35 /// </para> | |
| 36 /// </remarks> | |
| 242 | 37 public abstract class AbstractEvent<THandler> where THandler : class { |
| 244 | 38 const int PendingState = 0; |
| 242 | 39 |
| 244 | 40 const int TransitionalState = 1; |
| 243 | 41 |
| 244 | 42 const int ResolvedState = 2; |
| 144 | 43 |
| 242 | 44 volatile int m_state; |
| 144 | 45 |
| 242 | 46 THandler m_handler; |
| 233 | 47 SimpleAsyncQueue<THandler> m_extraHandlers; |
| 144 | 48 |
| 244 | 49 public bool IsResolved { |
| 243 | 50 get { |
| 244 | 51 return m_state > TransitionalState; |
| 243 | 52 } |
| 53 } | |
| 54 | |
| 144 | 55 #region state managment |
| 242 | 56 protected bool BeginTransit() { |
| 244 | 57 return PendingState == Interlocked.CompareExchange(ref m_state, TransitionalState, PendingState); |
| 144 | 58 } |
| 59 | |
| 243 | 60 protected void CompleteTransit() { |
| 242 | 61 #if DEBUG |
| 244 | 62 if (TransitionalState != Interlocked.CompareExchange(ref m_state, ResolvedState, TransitionalState)) |
| 144 | 63 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state"); |
| 242 | 64 #else |
| 253 | 65 m_state = ResolvedState; |
| 242 | 66 #endif |
| 67 Signal(); | |
| 144 | 68 } |
| 69 | |
| 242 | 70 protected void WaitTransition() { |
| 244 | 71 if (m_state == TransitionalState) { |
| 248 | 72 SpinWait spin = new SpinWait(); |
| 242 | 73 do { |
| 74 spin.SpinOnce(); | |
| 244 | 75 } while (m_state == TransitionalState); |
| 144 | 76 } |
| 77 } | |
| 78 | |
| 79 | |
| 243 | 80 protected abstract void SignalHandler(THandler handler); |
| 144 | 81 |
|
156
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
148
diff
changeset
|
82 void Signal() { |
| 242 | 83 THandler handler; |
| 84 while (TryDequeueHandler(out handler)) | |
| 243 | 85 SignalHandler(handler); |
| 144 | 86 } |
| 87 | |
| 88 #endregion | |
| 89 | |
| 90 #region handlers managment | |
| 91 | |
| 92 protected void AddHandler(THandler handler) { | |
| 93 | |
| 244 | 94 if (IsResolved) { |
| 144 | 95 // the promise is in the resolved state, just invoke the handler |
| 243 | 96 SignalHandler(handler); |
| 144 | 97 } else { |
| 243 | 98 EnqueueHandler(handler); |
| 144 | 99 |
| 244 | 100 if (IsResolved && TryDequeueHandler(out handler)) |
| 144 | 101 // if the promise have been resolved while we was adding the handler to the queue |
| 102 // we can't guarantee that someone is still processing it | |
| 103 // therefore we need to fetch a handler from the queue and execute it | |
| 104 // note that fetched handler may be not the one that we have added | |
| 105 // even we can fetch no handlers at all :) | |
| 243 | 106 SignalHandler(handler); |
| 144 | 107 } |
| 242 | 108 |
| 109 } | |
| 110 | |
| 243 | 111 void EnqueueHandler(THandler handler) { |
| 112 if (Interlocked.CompareExchange(ref m_handler, handler, null) != null) { | |
| 113 if (m_extraHandlers == null) | |
| 114 // compare-exchange will protect from loosing already created queue | |
| 115 Interlocked.CompareExchange(ref m_extraHandlers, new SimpleAsyncQueue<THandler>(), null); | |
| 116 m_extraHandlers.Enqueue(handler); | |
| 117 } | |
| 118 } | |
| 119 | |
| 242 | 120 bool TryDequeueHandler(out THandler handler) { |
| 121 handler = Interlocked.Exchange(ref m_handler, null); | |
| 122 if (handler != null) | |
| 123 return true; | |
| 124 return m_extraHandlers != null && m_extraHandlers.TryDequeue(out handler); | |
| 144 | 125 } |
| 126 | |
| 127 #endregion | |
| 128 } | |
| 129 } | |
| 130 |
