Mercurial > pub > ImplabNet
annotate Implab/AbstractEvent.cs @ 255:b00441e04738 v3
Adde workaround to the behaviour of the logical operations stack in conjuction
with async/await methods
author | cin |
---|---|
date | Wed, 04 Apr 2018 15:38:48 +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 |