comparison Implab/Components/RunnableComponent.cs @ 251:7c7e9ad6fe4a v3

Prerelease version of RunnableComponent Added draft messaging interfaces Added more more helpers to Xml/SerializationHelpers
author cin
date Sun, 11 Feb 2018 00:49:51 +0300
parents 9f63dade3a40
children 6f4630d0bcd9
comparison
equal deleted inserted replaced
250:9f63dade3a40 251:7c7e9ad6fe4a
1 using System; 1 using System;
2 using System.Diagnostics;
2 using System.Threading; 3 using System.Threading;
3 using System.Threading.Tasks; 4 using System.Threading.Tasks;
4 5
5 namespace Implab.Components 6 namespace Implab.Components {
6 { 7 /// <summary>
7 public class RunnableComponent : IRunnable { 8 /// Base class for implementing components which support start and stop operations,
8 9 /// such components may represent running services.
10 /// </summary>
11 /// <remarks>
12 /// This class provides a basic lifecycle from the creation to the
13 /// termination of the component.
14 /// </remarks>
15 public class RunnableComponent : IRunnable, IInitializable, IDisposable {
16
17 /// <summary>
18 /// This class bound <see cref="CancellationTokenSource"/> lifetime to the task,
19 /// when the task completes the associated token source will be disposed.
20 /// </summary>
21 class AsyncOperationDescriptor {
22
23 public static AsyncOperationDescriptor None { get; } = new AsyncOperationDescriptor();
24
25 readonly CancellationTokenSource m_cts;
26
27 bool m_done;
28
29 public CancellationToken Token {
30 get { return m_cts == null ? CancellationToken.None : m_cts.Token; }
31 }
32
33 public Task Task { get; private set; }
34
35 private AsyncOperationDescriptor(Task task, CancellationTokenSource cts) {
36 m_cts = cts;
37 Task = Chain(task);
38 }
39
40 private AsyncOperationDescriptor() {
41 Task = Task.CompletedTask;
42 }
43
44 public void Cancel() {
45 if (m_cts != null) {
46 lock (m_cts) {
47 if (!m_done)
48 m_cts.Cancel();
49 }
50 }
51 }
52
53 void Done() {
54 if (m_cts != null) {
55 lock (m_cts) {
56 m_done = true;
57 m_cts.Dispose();
58 }
59 } else {
60 m_done = true;
61 }
62 }
63
64 async Task Chain(Task other) {
65 try {
66 await other;
67 } finally {
68 Done();
69 }
70 }
71
72 public static AsyncOperationDescriptor Create(Func<CancellationToken, Task> factory, CancellationToken ct) {
73 var cts = ct.CanBeCanceled ?
74 CancellationTokenSource.CreateLinkedTokenSource(ct) :
75 new CancellationTokenSource();
76
77 return new AsyncOperationDescriptor(factory(cts.Token), cts);
78 }
79
80 }
81
82 // this lock is used to synchronize state flow of the component during
83 // completions or the operations.
9 readonly object m_lock = new object(); 84 readonly object m_lock = new object();
10 85
11 CancellationTokenSource m_cts; 86 // current operation cookie, used to check wheather a call to
12 87 // MoveSuccess/MoveFailed method belongs to the current
13 public Task<ExecutionState> Completion { 88 // operation, if cookies didn't match ignore completion result.
14 get; 89 object m_cookie;
15 private set; 90
16 } 91 AsyncOperationDescriptor m_current = AsyncOperationDescriptor.None;
17 92
18 public ExecutionState State => throw new NotImplementedException(); 93 ExecutionState m_state;
19 94
20 public Exception LastError => throw new NotImplementedException(); 95
96 protected RunnableComponent(bool initialized) {
97 State = initialized ? ExecutionState.Ready : ExecutionState.Created;
98 }
99
100 public Task Completion {
101 get { return m_current.Task; }
102 }
103
104 public ExecutionState State {
105 get { return m_state; }
106 private set {
107 if (m_state != value) {
108 m_state = value;
109 StateChanged.DispatchEvent(this, new StateChangeEventArgs {
110 State = value,
111 LastError = LastError
112 });
113 }
114 }
115 }
116
117 public Exception LastError { get; private set; }
21 118
22 public event EventHandler<StateChangeEventArgs> StateChanged; 119 public event EventHandler<StateChangeEventArgs> StateChanged;
23 120
121 /// <summary>
122 /// Releases all resources used by the current component regardless of its
123 /// execution state.
124 /// </summary>
125 /// <remarks>
126 /// Calling to this method may result unexpedted results if the component
127 /// isn't in the stopped state. Call this method after the component is
128 /// stopped if needed or if the component is in the failed state.
129 /// </remarks>
24 public void Dispose() { 130 public void Dispose() {
25 lock(m_lock) { 131 bool dispose = false;
132 if (dispose) {
26 Dispose(true); 133 Dispose(true);
27 GC.SuppressFinalize(this); 134 GC.SuppressFinalize(this);
28 } 135 }
29 } 136 }
30 137
138 ~RunnableComponent() {
139 Dispose(false);
140 }
141
142 /// <summary>
143 /// Releases all resources used by the current component regardless of its
144 /// execution state.
145 /// </summary>
146 /// <param name="disposing">Indicates that the component is disposed
147 /// during a normal disposing or during GC.</param>
31 protected virtual void Dispose(bool disposing) { 148 protected virtual void Dispose(bool disposing) {
32 if (disposing) { 149 }
33 Safe.Dispose(m_cts); 150
34 } 151 public void Initialize() {
152 var cookie = new object();
153 if (MoveInitialize(cookie))
154 ScheduleTask(InitializeInternalAsync, CancellationToken.None, cookie);
155 }
156
157 /// <summary>
158 /// This method is used for initialization during a component creation.
159 /// </summary>
160 /// <param name="ct">A cancellation token for this operation</param>
161 /// <remarks>
162 /// This method should be used for short and mostly syncronous operations,
163 /// other operations which require time to run shoud be placed in
164 /// <see cref="StartInternal(CancellationToken)"/> method.
165 /// </remarks>
166 protected virtual Task InitializeInternalAsync(CancellationToken ct) {
167 return Task.CompletedTask;
35 } 168 }
36 169
37 public void Start(CancellationToken ct) { 170 public void Start(CancellationToken ct) {
38 lock(m_lock) { 171 var cookie = new object();
39 switch (State) 172 if (MoveStart(cookie))
40 { 173 ScheduleTask(StartInternal, ct, cookie);
41 174 }
42 default: 175
43 throw new InvalidOperationException(); 176 protected virtual Task StartInternal(CancellationToken ct) {
44 } 177 return Task.CompletedTask;
45 }
46 } 178 }
47 179
48 public void Stop(CancellationToken ct) { 180 public void Stop(CancellationToken ct) {
49 throw new NotImplementedException(); 181 var cookie = new object();
50 } 182 if (MoveStop(cookie))
51 183 ScheduleTask(StopAsync, ct, cookie);
52 protected virtual Task StartImpl(CancellationToken ct) { 184 }
53 185
186 async Task StopAsync(CancellationToken ct) {
187 m_current.Cancel();
188 await Completion;
189
190 ct.ThrowIfCancellationRequested();
191
192 await StopInternalAsync(ct);
193 }
194
195 protected virtual Task StopInternalAsync(CancellationToken ct) {
54 return Task.CompletedTask; 196 return Task.CompletedTask;
55 } 197 }
198
199
200 #region state management
201
202 bool MoveInitialize(object cookie) {
203 lock (m_lock) {
204 if (State != ExecutionState.Created)
205 return false;
206 State = ExecutionState.Initializing;
207 m_cookie = cookie;
208 return true;
209 }
210 }
211
212 bool MoveStart(object cookie) {
213 lock (m_lock) {
214 if (State != ExecutionState.Ready)
215 return false;
216 State = ExecutionState.Starting;
217 m_cookie = cookie;
218 return true;
219 }
220 }
221
222 bool MoveStop(object cookie) {
223 lock (m_lock) {
224 if (State != ExecutionState.Starting && State != ExecutionState.Running)
225 return false;
226 State = ExecutionState.Stopping;
227 m_cookie = cookie;
228 return true;
229 }
230 }
231
232 void MoveSuccess(object cookie) {
233 lock (m_lock) {
234 if (m_cookie != cookie)
235 return;
236 switch (State) {
237 case ExecutionState.Initializing:
238 State = ExecutionState.Ready;
239 break;
240 case ExecutionState.Starting:
241 State = ExecutionState.Running;
242 break;
243 case ExecutionState.Stopping:
244 State = ExecutionState.Stopped;
245 break;
246 }
247 }
248 }
249
250 void MoveFailed(Exception err, object cookie) {
251 lock (m_lock) {
252 if (m_cookie != cookie)
253 return;
254 LastError = err;
255 State = ExecutionState.Failed;
256 }
257 }
258
259
260
261 protected async void ScheduleTask(Func<CancellationToken, Task> next, CancellationToken ct, object cookie) {
262 try {
263 m_current = AsyncOperationDescriptor.Create(next, ct);
264 await m_current.Task;
265 MoveSuccess(cookie);
266 } catch (Exception e) {
267 MoveFailed(e, cookie);
268 }
269 }
270
271 #endregion
56 } 272 }
57 } 273 }