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 | 3 | <PropertyGroup> |
|
4 | 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 | 7 | <IsPackable>false</IsPackable> |
|
8 | 8 | </PropertyGroup> |
@@ -2,6 +2,7 using System; | |||
|
2 | 2 | using System.Diagnostics; |
|
3 | 3 | using System.Threading; |
|
4 | 4 | using Implab.Diagnostics; |
|
5 | using System.Linq; | |
|
5 | 6 | using Xunit; |
|
6 | 7 | |
|
7 | 8 | namespace Implab.Test { |
@@ -11,19 +12,29 namespace Implab.Test { | |||
|
11 | 12 | [Fact] |
|
12 | 13 | public async Task Test1() { |
|
13 | 14 | var listener = new SimpleTraceListener(Console.Out); |
|
15 | listener.TraceOutputOptions |= TraceOptions.ThreadId; | |
|
14 | 16 | |
|
15 | 17 | var source = TraceSource; |
|
16 | 18 | source.Switch.Level = SourceLevels.All; |
|
17 | 19 | |
|
18 | 20 | source.Listeners.Add(listener); |
|
19 | 21 | |
|
20 |
using ( |
|
|
21 | using (LogicalOperation("InnerOperation")){ | |
|
22 | using (LogicalOperation("Test1")){ | |
|
22 | 23 | await Task.Yield(); |
|
23 | Log("Inner"); | |
|
24 |
await |
|
|
25 | Log("source event"); | |
|
24 | Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast<object>().Select(x => x.ToString()))); | |
|
25 | await AsyncDummy(); | |
|
26 | Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast<object>().Select(x => x.ToString()))); | |
|
26 | 27 | } |
|
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()))); | |
|
38 | } | |
|
28 | 39 | } |
|
29 | 40 | } |
@@ -6,10 +6,10 using System.Diagnostics.CodeAnalysis; | |||
|
6 | 6 | |
|
7 | 7 | namespace Implab.Components { |
|
8 | 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 | 10 | /// </summary> |
|
11 | 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 | 13 | /// by clients. The pool maintains maximum number of unsued object, any object above this limit is disposed, |
|
14 | 14 | /// if the pool is empty it will create new objects on demand.</para> |
|
15 | 15 | /// <para>Instances of this class are thread-safe.</para> |
@@ -34,14 +34,7 namespace Implab.Components { | |||
|
34 | 34 | void Stop(CancellationToken ct); |
|
35 | 35 | |
|
36 | 36 | /// <summary> |
|
37 | /// The result of the last started operation. This property reflects | |
|
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 | |
|
37 | /// Current state of the componenet, dynamically reflects the current state. | |
|
45 | 38 | /// </summary> |
|
46 | 39 | ExecutionState State { get; } |
|
47 | 40 |
@@ -12,7 +12,7 namespace Implab.Components { | |||
|
12 | 12 | /// This class provides a basic lifecycle from the creation to the |
|
13 | 13 | /// termination of the component. |
|
14 | 14 | /// </remarks> |
|
15 | public class RunnableComponent : IRunnable, IInitializable, IDisposable { | |
|
15 | public class RunnableComponent : IAsyncComponent, IRunnable, IInitializable, IDisposable { | |
|
16 | 16 | |
|
17 | 17 | /// <summary> |
|
18 | 18 | /// This class bounds <see cref="CancellationTokenSource"/> lifetime to the task, |
@@ -80,7 +80,7 namespace Implab.Components { | |||
|
80 | 80 | } |
|
81 | 81 | |
|
82 | 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 | 84 | readonly object m_lock = new object(); |
|
85 | 85 | |
|
86 | 86 | // current operation cookie, used to check wheather a call to |
@@ -88,6 +88,7 namespace Implab.Components { | |||
|
88 | 88 | // operation, if cookies didn't match ignore completion result. |
|
89 | 89 | object m_cookie; |
|
90 | 90 | |
|
91 | // AsyncOperationDscriptor aggregates a task and it's cancellation token | |
|
91 | 92 | AsyncOperationDescriptor m_current = AsyncOperationDescriptor.None; |
|
92 | 93 | |
|
93 | 94 | ExecutionState m_state; |
@@ -152,6 +153,8 namespace Implab.Components { | |||
|
152 | 153 | var cookie = new object(); |
|
153 | 154 | if (MoveInitialize(cookie)) |
|
154 | 155 | ScheduleTask(InitializeInternalAsync, CancellationToken.None, cookie); |
|
156 | else | |
|
157 | throw new InvalidOperationException(); | |
|
155 | 158 | } |
|
156 | 159 | |
|
157 | 160 | /// <summary> |
@@ -171,6 +174,8 namespace Implab.Components { | |||
|
171 | 174 | var cookie = new object(); |
|
172 | 175 | if (MoveStart(cookie)) |
|
173 | 176 | ScheduleTask(StartInternalAsync, ct, cookie); |
|
177 | else | |
|
178 | throw new InvalidOperationException(); | |
|
174 | 179 | } |
|
175 | 180 | |
|
176 | 181 | protected virtual Task StartInternalAsync(CancellationToken ct) { |
@@ -181,6 +186,8 namespace Implab.Components { | |||
|
181 | 186 | var cookie = new object(); |
|
182 | 187 | if (MoveStop(cookie)) |
|
183 | 188 | ScheduleTask(StopAsync, ct, cookie); |
|
189 | else | |
|
190 | throw new InvalidOperationException(); | |
|
184 | 191 | } |
|
185 | 192 | |
|
186 | 193 | async Task StopAsync(CancellationToken ct) { |
@@ -196,6 +203,16 namespace Implab.Components { | |||
|
196 | 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 | 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) { | |
|
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 | } | |
|
278 | m_current = AsyncOperationDescriptor.Create(async (x) => { | |
|
279 | try { | |
|
280 | await next(x); | |
|
281 | MoveSuccess(cookie); | |
|
282 | } catch (Exception e) { | |
|
283 | MoveFailed(e, cookie); | |
|
284 | } | |
|
285 | }, ct); | |
|
269 | 286 | } |
|
270 | 287 | |
|
271 | 288 | #endregion |
|
1 | NO CONTENT: file was removed |
|
1 | 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