Mercurial > pub > ImplabNet
annotate Implab/AbstractEvent.cs @ 243:b1e0ffdf3451 v3
working on promises
author | cin |
---|---|
date | Wed, 24 Jan 2018 19:24:10 +0300 |
parents | cbe10ac0731e |
children | eee3e49dd1ff |
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 { |
38 const int PENDING_SATE = 0; | |
39 | |
243 | 40 const int TRANSITIONAL_STATE = 1; |
41 | |
42 const int FULFILLED_STATE = 2; | |
144 | 43 |
242 | 44 volatile int m_state; |
144 | 45 |
242 | 46 THandler m_handler; |
233 | 47 SimpleAsyncQueue<THandler> m_extraHandlers; |
144 | 48 |
243 | 49 public bool IsFulfilled { |
50 get { | |
51 return m_state > TRANSITIONAL_STATE; | |
52 } | |
53 } | |
54 | |
144 | 55 #region state managment |
242 | 56 protected bool BeginTransit() { |
57 return PENDING_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, PENDING_SATE); | |
144 | 58 } |
59 | |
243 | 60 protected void CompleteTransit() { |
242 | 61 #if DEBUG |
243 | 62 if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, FULFILLED_STATE, TRANSITIONAL_STATE)) |
144 | 63 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state"); |
242 | 64 #else |
65 m_state = state; | |
66 #endif | |
67 Signal(); | |
144 | 68 } |
69 | |
242 | 70 protected void WaitTransition() { |
71 if (m_state == TRANSITIONAL_STATE) { | |
72 SpinWait spin; | |
73 do { | |
74 spin.SpinOnce(); | |
75 } while (m_state == TRANSITIONAL_STATE); | |
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 | |
242 | 90 protected abstract Signal GetFulfillSignal(); |
144 | 91 |
92 #region synchronization traits | |
93 protected void WaitResult(int timeout) { | |
242 | 94 if (!(IsFulfilled || GetFulfillSignal().Wait(timeout))) |
148 | 95 throw new TimeoutException(); |
242 | 96 } |
97 | |
243 | 98 |
144 | 99 #endregion |
100 | |
101 #region handlers managment | |
102 | |
103 protected void AddHandler(THandler handler) { | |
104 | |
243 | 105 if (IsFulfilled) { |
144 | 106 // the promise is in the resolved state, just invoke the handler |
243 | 107 SignalHandler(handler); |
144 | 108 } else { |
243 | 109 EnqueueHandler(handler); |
144 | 110 |
243 | 111 if (IsFulfilled && TryDequeueHandler(out handler)) |
144 | 112 // if the promise have been resolved while we was adding the handler to the queue |
113 // we can't guarantee that someone is still processing it | |
114 // therefore we need to fetch a handler from the queue and execute it | |
115 // note that fetched handler may be not the one that we have added | |
116 // even we can fetch no handlers at all :) | |
243 | 117 SignalHandler(handler); |
144 | 118 } |
242 | 119 |
120 } | |
121 | |
243 | 122 void EnqueueHandler(THandler handler) { |
123 if (Interlocked.CompareExchange(ref m_handler, handler, null) != null) { | |
124 if (m_extraHandlers == null) | |
125 // compare-exchange will protect from loosing already created queue | |
126 Interlocked.CompareExchange(ref m_extraHandlers, new SimpleAsyncQueue<THandler>(), null); | |
127 m_extraHandlers.Enqueue(handler); | |
128 } | |
129 } | |
130 | |
242 | 131 bool TryDequeueHandler(out THandler handler) { |
132 handler = Interlocked.Exchange(ref m_handler, null); | |
133 if (handler != null) | |
134 return true; | |
135 return m_extraHandlers != null && m_extraHandlers.TryDequeue(out handler); | |
144 | 136 } |
137 | |
138 #endregion | |
139 } | |
140 } | |
141 |