comparison Implab/AbstractEvent.cs @ 243:b1e0ffdf3451 v3

working on promises
author cin
date Wed, 24 Jan 2018 19:24:10 +0300
parents cbe10ac0731e
children eee3e49dd1ff
comparison
equal deleted inserted replaced
242:cbe10ac0731e 243:b1e0ffdf3451
3 using System.Threading; 3 using System.Threading;
4 using System.Reflection; 4 using System.Reflection;
5 using System.Diagnostics; 5 using System.Diagnostics;
6 6
7 namespace Implab { 7 namespace Implab {
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>
8 public abstract class AbstractEvent<THandler> where THandler : class { 37 public abstract class AbstractEvent<THandler> where THandler : class {
38 const int PENDING_SATE = 0;
9 39
10 const int PENDING_SATE = 0; 40 const int TRANSITIONAL_STATE = 1;
11 protected const int TRANSITIONAL_STATE = 1;
12 41
13 protected const int SUCCEEDED_STATE = 2; 42 const int FULFILLED_STATE = 2;
14 protected const int REJECTED_STATE = 3;
15 43
16 volatile int m_state; 44 volatile int m_state;
17 Exception m_error;
18 45
19 THandler m_handler; 46 THandler m_handler;
20 SimpleAsyncQueue<THandler> m_extraHandlers; 47 SimpleAsyncQueue<THandler> m_extraHandlers;
48
49 public bool IsFulfilled {
50 get {
51 return m_state > TRANSITIONAL_STATE;
52 }
53 }
21 54
22 #region state managment 55 #region state managment
23 protected bool BeginTransit() { 56 protected bool BeginTransit() {
24 return PENDING_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, PENDING_SATE); 57 return PENDING_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, PENDING_SATE);
25 } 58 }
26 59
27 protected void CompleteTransit(int state) { 60 protected void CompleteTransit() {
28 #if DEBUG 61 #if DEBUG
29 if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, state, TRANSITIONAL_STATE)) 62 if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, FULFILLED_STATE, TRANSITIONAL_STATE))
30 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state"); 63 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
31 #else 64 #else
32 m_state = state; 65 m_state = state;
33 #endif 66 #endif
34 Signal(); 67 Signal();
41 spin.SpinOnce(); 74 spin.SpinOnce();
42 } while (m_state == TRANSITIONAL_STATE); 75 } while (m_state == TRANSITIONAL_STATE);
43 } 76 }
44 } 77 }
45 78
46 protected bool BeginSetResult() {
47 if (!BeginTransit()) {
48 WaitTransition();
49 return false;
50 }
51 return true;
52 }
53 79
54 protected void EndSetResult() { 80 protected abstract void SignalHandler(THandler handler);
55 CompleteTransit(SUCCEEDED_STATE);
56 }
57
58
59
60 /// <summary>
61 /// Выполняет обещание, сообщая об ошибке
62 /// </summary>
63 /// <remarks>
64 /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков
65 /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные
66 /// будут проигнорированы.
67 /// </remarks>
68 /// <param name="error">Исключение возникшее при выполнении операции</param>
69 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
70 protected void SetError(Exception error) {
71 if (BeginTransit()) {
72 m_error = error;
73 CompleteTransit(REJECTED_STATE);
74 } else {
75 WaitTransition();
76 if (m_state == SUCCEEDED_STATE)
77 throw new InvalidOperationException("The promise is already resolved");
78 }
79 }
80
81 protected abstract void SignalHandler(THandler handler, int signal);
82 81
83 void Signal() { 82 void Signal() {
84 THandler handler; 83 THandler handler;
85 while (TryDequeueHandler(out handler)) 84 while (TryDequeueHandler(out handler))
86 SignalHandler(handler, m_state); 85 SignalHandler(handler);
87 } 86 }
88 87
89 #endregion 88 #endregion
90 89
91 protected abstract Signal GetFulfillSignal(); 90 protected abstract Signal GetFulfillSignal();
92 91
93 #region synchronization traits 92 #region synchronization traits
94 protected void WaitResult(int timeout) { 93 protected void WaitResult(int timeout) {
95 if (!(IsFulfilled || GetFulfillSignal().Wait(timeout))) 94 if (!(IsFulfilled || GetFulfillSignal().Wait(timeout)))
96 throw new TimeoutException(); 95 throw new TimeoutException();
97
98 if (IsRejected)
99 Rethrow();
100 } 96 }
101 97
102 protected void Rethrow() { 98
103 Debug.Assert(m_error != null);
104 if (m_error is OperationCanceledException)
105 throw new OperationCanceledException("Operation cancelled", m_error);
106 else
107 throw new TargetInvocationException(m_error);
108 }
109 #endregion 99 #endregion
110 100
111 #region handlers managment 101 #region handlers managment
112 102
113 protected void AddHandler(THandler handler) { 103 protected void AddHandler(THandler handler) {
114 104
115 if (m_state > 1) { 105 if (IsFulfilled) {
116 // the promise is in the resolved state, just invoke the handler 106 // the promise is in the resolved state, just invoke the handler
117 SignalHandler(handler, m_state); 107 SignalHandler(handler);
118 } else { 108 } else {
119 if (Interlocked.CompareExchange(ref m_handler, handler, null) != null) { 109 EnqueueHandler(handler);
120 if (m_extraHandlers == null)
121 // compare-exchange will fprotect from loosing already created queue
122 Interlocked.CompareExchange(ref m_extraHandlers, new SimpleAsyncQueue<THandler>(), null);
123 m_extraHandlers.Enqueue(handler);
124 }
125 110
126 if (m_state > 1 && TryDequeueHandler(out handler)) 111 if (IsFulfilled && TryDequeueHandler(out handler))
127 // if the promise have been resolved while we was adding the handler to the queue 112 // if the promise have been resolved while we was adding the handler to the queue
128 // we can't guarantee that someone is still processing it 113 // we can't guarantee that someone is still processing it
129 // therefore we need to fetch a handler from the queue and execute it 114 // therefore we need to fetch a handler from the queue and execute it
130 // note that fetched handler may be not the one that we have added 115 // note that fetched handler may be not the one that we have added
131 // even we can fetch no handlers at all :) 116 // even we can fetch no handlers at all :)
132 SignalHandler(handler, m_state); 117 SignalHandler(handler);
133 } 118 }
134 119
120 }
121
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 }
135 } 129 }
136 130
137 bool TryDequeueHandler(out THandler handler) { 131 bool TryDequeueHandler(out THandler handler) {
138 handler = Interlocked.Exchange(ref m_handler, null); 132 handler = Interlocked.Exchange(ref m_handler, null);
139 if (handler != null) 133 if (handler != null)
140 return true; 134 return true;
141 return m_extraHandlers != null && m_extraHandlers.TryDequeue(out handler); 135 return m_extraHandlers != null && m_extraHandlers.TryDequeue(out handler);
142 } 136 }
143 137
144 #endregion 138 #endregion
145
146 #region IPromise implementation
147
148 public bool IsFulfilled {
149 get {
150 return m_state > TRANSITIONAL_STATE;
151 }
152 }
153
154 public bool IsRejected {
155 get {
156 return m_state == REJECTED_STATE;
157 }
158 }
159
160 #endregion
161
162 public Exception RejectReason {
163 get {
164 return m_error;
165 }
166 }
167
168 } 139 }
169 } 140 }
170 141