Auto status change to "Under Review"
@@ -0,0 +1,37 | |||||
|
1 | using System; | |||
|
2 | using System.Threading.Tasks; | |||
|
3 | ||||
|
4 | namespace Implab.Components { | |||
|
5 | /// <summary> | |||
|
6 | /// An interface for asynchronous components. | |||
|
7 | /// </summary> | |||
|
8 | /// <remarks> | |||
|
9 | /// <para> | |||
|
10 | /// Асинхронные компоненты не предназначены для одновременного использования несколькими клиентами, | |||
|
11 | /// однако существуют внутренние процессы, изменяющее состояние компонент без участия клиента. | |||
|
12 | /// Данный интерфейс определяет протокол взаимодействия с компонентой, при которм компоненте | |||
|
13 | /// посылаются сигналы от клиента, в ответ на которые компонента меняет свойство <see cref="Completion"/>, | |||
|
14 | /// данное свойство содержит в себе новую задачу, выполняемую компонентой и данное свойство | |||
|
15 | /// может измениться только при получении нового сигнала от клиента. | |||
|
16 | /// </para> | |||
|
17 | /// <para> | |||
|
18 | /// В дополнение к <see cref="Completion"/> компонента может определять другие свойства, в | |||
|
19 | /// которых будет передаваться информация о результате выполнения операции. | |||
|
20 | /// </para> | |||
|
21 | /// <para> | |||
|
22 | /// Особое внимание следует уделить реализации <see cref="IDisposable"/>, который по своей природе | |||
|
23 | /// синхронный, данное правило безусловно можно нарушить, но тогда могут возникнуть проблемы с | |||
|
24 | /// тем, что ресурсы еще не освободились, а ход программы продолжается, что приведет к ошибкам, | |||
|
25 | /// например при попытке получить ресуср другим объектом, либо при заврешении программы. | |||
|
26 | /// </para> | |||
|
27 | /// <seealso href="https://blog.stephencleary.com/2013/01/async-oop-0-introduction.html"/> | |||
|
28 | /// </remarks> | |||
|
29 | public interface IAsyncComponent { | |||
|
30 | /// <summary> | |||
|
31 | /// The result of the last started operation. This property reflects | |||
|
32 | /// only the result of the last started operation and therefore should | |||
|
33 | /// change only if a new operation is initiated. | |||
|
34 | /// </summary> | |||
|
35 | Task Completion { get; } | |||
|
36 | } | |||
|
37 | } No newline at end of file |
@@ -2,7 +2,7 | |||||
2 |
|
2 | |||
3 | <PropertyGroup> |
|
3 | <PropertyGroup> | |
4 | <TargetFramework>net46</TargetFramework> |
|
4 | <TargetFramework>net46</TargetFramework> | |
5 |
<FrameworkPathOverride Condition="'$(TargetFramework)'=='net4 |
|
5 | <FrameworkPathOverride Condition="'$(TargetFramework)'=='net46' and '$(OSTYPE)'=='linux'">/usr/lib/mono/4.5/</FrameworkPathOverride> | |
6 |
|
6 | |||
7 | <IsPackable>false</IsPackable> |
|
7 | <IsPackable>false</IsPackable> | |
8 | </PropertyGroup> |
|
8 | </PropertyGroup> |
@@ -2,6 +2,7 using System; | |||||
2 | using System.Diagnostics; |
|
2 | using System.Diagnostics; | |
3 | using System.Threading; |
|
3 | using System.Threading; | |
4 | using Implab.Diagnostics; |
|
4 | using Implab.Diagnostics; | |
|
5 | using System.Linq; | |||
5 | using Xunit; |
|
6 | using Xunit; | |
6 |
|
7 | |||
7 | namespace Implab.Test { |
|
8 | namespace Implab.Test { | |
@@ -11,19 +12,29 namespace Implab.Test { | |||||
11 | [Fact] |
|
12 | [Fact] | |
12 | public async Task Test1() { |
|
13 | public async Task Test1() { | |
13 | var listener = new SimpleTraceListener(Console.Out); |
|
14 | var listener = new SimpleTraceListener(Console.Out); | |
|
15 | listener.TraceOutputOptions |= TraceOptions.ThreadId; | |||
14 |
|
16 | |||
15 | var source = TraceSource; |
|
17 | var source = TraceSource; | |
16 | source.Switch.Level = SourceLevels.All; |
|
18 | source.Switch.Level = SourceLevels.All; | |
17 |
|
19 | |||
18 | source.Listeners.Add(listener); |
|
20 | source.Listeners.Add(listener); | |
19 |
|
21 | |||
20 |
using ( |
|
22 | using (LogicalOperation("Test1")){ | |
21 | using (LogicalOperation("InnerOperation")){ |
|
|||
22 | await Task.Yield(); |
|
23 | await Task.Yield(); | |
23 | Log("Inner"); |
|
24 | Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast<object>().Select(x => x.ToString()))); | |
24 |
await |
|
25 | await AsyncDummy(); | |
25 | Log("source event"); |
|
26 | Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast<object>().Select(x => x.ToString()))); | |
|
27 | } | |||
|
28 | } | |||
|
29 | ||||
|
30 | async Task AsyncDummy() { | |||
|
31 | using(LogicalOperation("OuterDummy")) | |||
|
32 | using(LogicalOperation("InnerDummy")) { | |||
|
33 | Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast<object>().Select(x => x.ToString()))); | |||
|
34 | await Task.Delay(1); | |||
|
35 | Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast<object>().Select(x => x.ToString()))); | |||
|
36 | } | |||
|
37 | Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast<object>().Select(x => x.ToString()))); | |||
26 |
|
|
38 | } | |
27 |
|
|
39 | } | |
28 | } |
|
40 | } | |
29 | } |
|
@@ -6,10 +6,10 using System.Diagnostics.CodeAnalysis; | |||||
6 |
|
6 | |||
7 | namespace Implab.Components { |
|
7 | namespace Implab.Components { | |
8 | /// <summary> |
|
8 | /// <summary> | |
9 | /// The base class for implementing pools of disposable objects. |
|
9 | /// The base class for implementing thread-safe pools of disposable objects. | |
10 | /// </summary> |
|
10 | /// </summary> | |
11 | /// <remarks> |
|
11 | /// <remarks> | |
12 |
/// <para>This class maintains a set of pre-created objects |
|
12 | /// <para>This class maintains a set of pre-created objects which are frequently allocated and released | |
13 | /// by clients. The pool maintains maximum number of unsued object, any object above this limit is disposed, |
|
13 | /// by clients. The pool maintains maximum number of unsued object, any object above this limit is disposed, | |
14 | /// if the pool is empty it will create new objects on demand.</para> |
|
14 | /// if the pool is empty it will create new objects on demand.</para> | |
15 | /// <para>Instances of this class are thread-safe.</para> |
|
15 | /// <para>Instances of this class are thread-safe.</para> |
@@ -34,14 +34,7 namespace Implab.Components { | |||||
34 | void Stop(CancellationToken ct); |
|
34 | void Stop(CancellationToken ct); | |
35 |
|
35 | |||
36 | /// <summary> |
|
36 | /// <summary> | |
37 | /// The result of the last started operation. This property reflects |
|
37 | /// Current state of the componenet, dynamically reflects the current state. | |
38 | /// only the result of the last started operation and therefore should |
|
|||
39 | /// change only if a new operation is initiated. |
|
|||
40 | /// </summary> |
|
|||
41 | Task Completion { get; } |
|
|||
42 |
|
||||
43 | /// <summary> |
|
|||
44 | /// Current state of the componenet |
|
|||
45 | /// </summary> |
|
38 | /// </summary> | |
46 | ExecutionState State { get; } |
|
39 | ExecutionState State { get; } | |
47 |
|
40 |
@@ -12,7 +12,7 namespace Implab.Components { | |||||
12 | /// This class provides a basic lifecycle from the creation to the |
|
12 | /// This class provides a basic lifecycle from the creation to the | |
13 | /// termination of the component. |
|
13 | /// termination of the component. | |
14 | /// </remarks> |
|
14 | /// </remarks> | |
15 | public class RunnableComponent : IRunnable, IInitializable, IDisposable { |
|
15 | public class RunnableComponent : IAsyncComponent, IRunnable, IInitializable, IDisposable { | |
16 |
|
16 | |||
17 | /// <summary> |
|
17 | /// <summary> | |
18 | /// This class bounds <see cref="CancellationTokenSource"/> lifetime to the task, |
|
18 | /// This class bounds <see cref="CancellationTokenSource"/> lifetime to the task, | |
@@ -80,7 +80,7 namespace Implab.Components { | |||||
80 | } |
|
80 | } | |
81 |
|
81 | |||
82 | // this lock is used to synchronize state flow of the component during |
|
82 | // this lock is used to synchronize state flow of the component during | |
83 | // completions or the operations. |
|
83 | // processing calls from a client and internal processes. | |
84 | readonly object m_lock = new object(); |
|
84 | readonly object m_lock = new object(); | |
85 |
|
85 | |||
86 | // current operation cookie, used to check wheather a call to |
|
86 | // current operation cookie, used to check wheather a call to | |
@@ -88,6 +88,7 namespace Implab.Components { | |||||
88 | // operation, if cookies didn't match ignore completion result. |
|
88 | // operation, if cookies didn't match ignore completion result. | |
89 | object m_cookie; |
|
89 | object m_cookie; | |
90 |
|
90 | |||
|
91 | // AsyncOperationDscriptor aggregates a task and it's cancellation token | |||
91 | AsyncOperationDescriptor m_current = AsyncOperationDescriptor.None; |
|
92 | AsyncOperationDescriptor m_current = AsyncOperationDescriptor.None; | |
92 |
|
93 | |||
93 | ExecutionState m_state; |
|
94 | ExecutionState m_state; | |
@@ -152,6 +153,8 namespace Implab.Components { | |||||
152 | var cookie = new object(); |
|
153 | var cookie = new object(); | |
153 | if (MoveInitialize(cookie)) |
|
154 | if (MoveInitialize(cookie)) | |
154 | ScheduleTask(InitializeInternalAsync, CancellationToken.None, cookie); |
|
155 | ScheduleTask(InitializeInternalAsync, CancellationToken.None, cookie); | |
|
156 | else | |||
|
157 | throw new InvalidOperationException(); | |||
155 | } |
|
158 | } | |
156 |
|
159 | |||
157 | /// <summary> |
|
160 | /// <summary> | |
@@ -171,6 +174,8 namespace Implab.Components { | |||||
171 | var cookie = new object(); |
|
174 | var cookie = new object(); | |
172 | if (MoveStart(cookie)) |
|
175 | if (MoveStart(cookie)) | |
173 | ScheduleTask(StartInternalAsync, ct, cookie); |
|
176 | ScheduleTask(StartInternalAsync, ct, cookie); | |
|
177 | else | |||
|
178 | throw new InvalidOperationException(); | |||
174 | } |
|
179 | } | |
175 |
|
180 | |||
176 | protected virtual Task StartInternalAsync(CancellationToken ct) { |
|
181 | protected virtual Task StartInternalAsync(CancellationToken ct) { | |
@@ -181,6 +186,8 namespace Implab.Components { | |||||
181 | var cookie = new object(); |
|
186 | var cookie = new object(); | |
182 | if (MoveStop(cookie)) |
|
187 | if (MoveStop(cookie)) | |
183 | ScheduleTask(StopAsync, ct, cookie); |
|
188 | ScheduleTask(StopAsync, ct, cookie); | |
|
189 | else | |||
|
190 | throw new InvalidOperationException(); | |||
184 | } |
|
191 | } | |
185 |
|
192 | |||
186 | async Task StopAsync(CancellationToken ct) { |
|
193 | async Task StopAsync(CancellationToken ct) { | |
@@ -196,6 +203,16 namespace Implab.Components { | |||||
196 | return Task.CompletedTask; |
|
203 | return Task.CompletedTask; | |
197 | } |
|
204 | } | |
198 |
|
205 | |||
|
206 | protected void Fail(Exception err) { | |||
|
207 | lock(m_lock) { | |||
|
208 | if (m_state != ExecutionState.Running) | |||
|
209 | return; | |||
|
210 | m_cookie = new object(); | |||
|
211 | LastError = err; | |||
|
212 | State = ExecutionState.Failed; | |||
|
213 | } | |||
|
214 | } | |||
|
215 | ||||
199 |
|
216 | |||
200 | #region state management |
|
217 | #region state management | |
201 |
|
218 | |||
@@ -256,16 +273,16 namespace Implab.Components { | |||||
256 | } |
|
273 | } | |
257 | } |
|
274 | } | |
258 |
|
275 | |||
259 |
|
276 | void ScheduleTask(Func<CancellationToken, Task> next, CancellationToken ct, object cookie) { | ||
260 |
|
277 | |||
261 | protected async void ScheduleTask(Func<CancellationToken, Task> next, CancellationToken ct, object cookie) { |
|
278 | m_current = AsyncOperationDescriptor.Create(async (x) => { | |
262 | try { |
|
279 | try { | |
263 | m_current = AsyncOperationDescriptor.Create(next, ct); |
|
280 | await next(x); | |
264 | await m_current.Task; |
|
|||
265 | MoveSuccess(cookie); |
|
281 | MoveSuccess(cookie); | |
266 | } catch (Exception e) { |
|
282 | } catch (Exception e) { | |
267 | MoveFailed(e, cookie); |
|
283 | MoveFailed(e, cookie); | |
268 | } |
|
284 | } | |
|
285 | }, ct); | |||
269 | } |
|
286 | } | |
270 |
|
287 | |||
271 | #endregion |
|
288 | #endregion |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 3
ok, latest stable version should be in default
You need to be logged in to leave comments.
Login now