##// END OF EJS Templates
minor fixes
cin -
r260:547a2fc0d93e v3.0.6 v3
parent child
Show More
@@ -0,0 +1,35
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Implab.Components;
5
6 namespace Implab.Test {
7 public class MockPollComponent : PollingComponent {
8
9 public Func<CancellationToken,Task> PollWorker { get; set;}
10
11 public Func<CancellationToken, Task> StartWorker { get; set; }
12
13 public Func<CancellationToken, Task> StopWorker { get; set; }
14
15 public MockPollComponent(bool initialized) : base(initialized) {
16 }
17
18 protected async override Task Poll(CancellationToken ct) {
19 if(PollWorker!= null)
20 await PollWorker.Invoke(ct);
21 }
22
23 protected async override Task StopInternalAsync(CancellationToken ct) {
24 if (StopWorker != null)
25 await StopWorker.Invoke(ct);
26 }
27
28 protected async override Task StartInternalAsync(CancellationToken ct) {
29 if (StartWorker != null)
30 await StartWorker.Invoke(ct);
31 }
32
33
34 }
35 } No newline at end of file
@@ -1,69 +1,20
1 <?xml version="1.0" encoding="utf-8"?>
2 <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
4 <PropertyGroup>
5 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
6 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
7 <ProjectGuid>{100DFEB0-75BE-436F-ADDF-1F46EF433F46}</ProjectGuid>
8 <OutputType>Exe</OutputType>
9 <AppDesignerFolder>Properties</AppDesignerFolder>
10 <RootNamespace>Implab.Playground</RootNamespace>
11 <AssemblyName>Implab.Playground</AssemblyName>
12 <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
13 <FileAlignment>512</FileAlignment>
14 <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
15 <TargetFrameworkProfile />
1 <Project Sdk="Microsoft.NET.Sdk">
2 <PropertyGroup Condition="'$(OSTYPE)'=='linux'">
3 <TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks>
4 <FrameworkPathOverride Condition="'$(TargetFramework)'=='net46'">/usr/lib/mono/4.6-api/</FrameworkPathOverride>
16 5 </PropertyGroup>
17 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
18 <PlatformTarget>AnyCPU</PlatformTarget>
19 <DebugSymbols>true</DebugSymbols>
20 <DebugType>full</DebugType>
21 <Optimize>false</Optimize>
22 <OutputPath>bin\Debug\</OutputPath>
23 <DefineConstants>DEBUG;TRACE</DefineConstants>
24 <ErrorReport>prompt</ErrorReport>
25 <WarningLevel>4</WarningLevel>
6
7 <PropertyGroup Condition="'$(OSTYPE)'=='windows'">
8 <TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks>
26 9 </PropertyGroup>
27 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
28 <PlatformTarget>AnyCPU</PlatformTarget>
29 <DebugType>pdbonly</DebugType>
30 <Optimize>true</Optimize>
31 <OutputPath>bin\Release\</OutputPath>
32 <DefineConstants>TRACE</DefineConstants>
33 <ErrorReport>prompt</ErrorReport>
34 <WarningLevel>4</WarningLevel>
35 <Prefer32Bit>true</Prefer32Bit>
36 <DebugSymbols>true</DebugSymbols>
10
11 <PropertyGroup>
12 <OutputType>Exe</OutputType>
13 <IsPackable>false</IsPackable>
37 14 </PropertyGroup>
38 <ItemGroup>
39 <Reference Include="System" />
40 <Reference Include="System.Core" />
41 <Reference Include="System.Xml.Linq" />
42 <Reference Include="System.Data.DataSetExtensions" />
43 <Reference Include="Microsoft.CSharp" />
44 <Reference Include="System.Data" />
45 <Reference Include="System.Net.Http" />
46 <Reference Include="System.Xml" />
47 </ItemGroup>
15
48 16 <ItemGroup>
49 <Compile Include="Program.cs" />
50 <Compile Include="Properties\AssemblyInfo.cs" />
51 </ItemGroup>
52 <ItemGroup>
53 <None Include="App.config" />
17 <ProjectReference Include="../Implab/Implab.csproj"/>
54 18 </ItemGroup>
55 <ItemGroup>
56 <ProjectReference Include="..\Implab\Implab.csproj">
57 <Project>{f550f1f8-8746-4ad0-9614-855f4c4b7f05}</Project>
58 <Name>Implab</Name>
59 </ProjectReference>
60 </ItemGroup>
61 <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
62 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
63 Other similar extension points exist, see Microsoft.Common.targets.
64 <Target Name="BeforeBuild">
65 </Target>
66 <Target Name="AfterBuild">
67 </Target>
68 -->
69 </Project> No newline at end of file
19
20 </Project>
@@ -1,55 +1,54
1 1 using Implab.Diagnostics;
2 2 using Implab.Formats.Json;
3 3 using Implab.Parallels;
4 4 using Implab.Xml;
5 5 using System;
6 6 using System.Collections.Concurrent;
7 7 using System.Collections.Generic;
8 8 using System.IO;
9 9 using System.Linq;
10 10 using System.Text;
11 11 using System.Threading;
12 12 using System.Threading.Tasks;
13 13 using System.Xml;
14 14 using System.Xml.Serialization;
15 15
16 16 namespace Implab.Playground {
17 17 using System.Diagnostics;
18 using System.Runtime.Remoting.Messaging;
19 18 using static Trace<Program>;
20 19
21 20 public class Program {
22 21
23 22 static void Main(string[] args) {
24 23 var listener = new SimpleTraceListener(Console.Out);
25 24
26 25 var source = Trace<Program>.TraceSource;
27 26 source.Switch.Level = SourceLevels.All;
28 27
29 28 source.Listeners.Add(listener);
30 29
31 30 var t = Environment.TickCount;
32 31
33 32 Main().Wait();
34 33
35 34 Console.WriteLine($"Done: {Environment.TickCount - t} ms");
36 35 Console.ReadKey();
37 36 }
38 37
39 38 static async Task Main() {
40 39 using (LogicalOperation(nameof(Main))) {
41 40 Log("Start");
42 41 await SomeAsync();
43 42 Log("End");
44 43 }
45 44 }
46 45
47 46 static async Task SomeAsync() {
48 47 using (LogicalOperation(nameof(SomeAsync))) {
49 48 Log("Do prepare");
50 49 await Task.Yield();
51 50 Log("Yield");
52 51 }
53 52 }
54 53 }
55 54 }
@@ -1,17 +1,23
1 1 <Project Sdk="Microsoft.NET.Sdk">
2 <PropertyGroup Condition="'$(OSTYPE)'=='linux'">
3 <TargetFrameworks>netcoreapp2.0</TargetFrameworks>
4 <FrameworkPathOverride Condition="'$(TargetFramework)'=='net46'">/usr/lib/mono/4.5/</FrameworkPathOverride>
5 </PropertyGroup>
6
7 <PropertyGroup Condition="'$(OSTYPE)'=='windows'">
8 <TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks>
9 </PropertyGroup>
2 10
3 11 <PropertyGroup>
4 <TargetFramework>netcoreapp2.1</TargetFramework>
5
6 12 <IsPackable>false</IsPackable>
7 13 </PropertyGroup>
8 14
9 15 <ItemGroup>
10 16 <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.6.0-preview-20180109-01" />
11 17 <PackageReference Include="xunit" Version="2.3.1" />
12 18 <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
13 19 <ProjectReference Include="../Implab/Implab.csproj"/>
14 20 <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
15 21 </ItemGroup>
16
22
17 23 </Project>
@@ -1,42 +1,30
1 1 using System;
2 2 using System.Threading;
3 3 using System.Threading.Tasks;
4 4 using Implab.Components;
5 5 using Xunit;
6 6
7 namespace Implab.Test
8 {
9 class TimeLog : PollingComponent {
10 public TimeLog() : base(true) {
11 }
7 namespace Implab.Test {
12 8
13 protected override Task Poll(CancellationToken ct) {
14 Console.WriteLine("Poll");
15 return Task.CompletedTask;
16 }
17 }
18
19 public class UnitTest1
20 {
9 public class RunnableComponentTests {
21 10 [Fact]
22 public async Task Test1()
23 {
11 public async Task Test1() {
24 12
25 using(var tl = new TimeLog()) {
26 tl.StateChanged += (self, args) => Console.WriteLine("{0}", args.State);
27 tl.Delay = 1000;
28 tl.Interval = 500;
13 using (var m = new MockPollComponent(true)) {
14 m.StartWorker = async (ct) => await Task.Yield();
15 m.StopWorker = async (ct) => await Task.Yield();
29 16
17 Assert.Equal(ExecutionState.Ready, m.State);
18 Assert.NotNull(m.Completion);
30 19
31 tl.Start(CancellationToken.None);
32 await tl.Completion;
20 m.Start(CancellationToken.None);
21 await m.Completion;
22 Assert.Equal(ExecutionState.Running, m.State);
33 23
34 await Task.Delay(2000);
35
36 tl.Stop(CancellationToken.None);
37 await tl.Completion;
38 await Task.Delay(3000);
24 m.Stop(CancellationToken.None);
25 await m.Completion;
26 Assert.Equal(ExecutionState.Stopped, m.State);
39 27 }
40 28 }
41 29 }
42 30 }
@@ -1,89 +1,93
1 1 using System;
2 2 using System.Threading;
3 3 using System.Threading.Tasks;
4 4
5 5 namespace Implab.Components {
6 6 public abstract class PollingComponent : RunnableComponent {
7 7
8 8 readonly Timer m_timer;
9 9
10 10 readonly CancellationTokenSource m_cancellation = new CancellationTokenSource();
11 11
12 12 Task m_pending;
13 13 Task m_poll;
14 14
15 15 /// <summary>
16 16 /// Poll interval in milliseconds.
17 17 /// </summary>
18 18 /// <returns></returns>
19 19 public int Interval { get; set; }
20 20
21 21 /// <summary>
22 22 /// Delay to the first poll after start in milliseconds
23 23 /// </summary>
24 24 /// <returns></returns>
25 25 public int Delay { get; set; }
26 26
27 27 /// <summary>
28 28 /// Indicates how to handle unhandled exceptions in <see cref="Poll()"/> method.
29 29 /// </summary>
30 30 /// <returns></returns>
31 31 public bool FailOnError { get; set; }
32 32
33 33 /// <summary>
34 34 /// Event for the unhandled exceptions in <see cref="Poll()"/> method.
35 35 /// </summary>
36 36 public event EventHandler<UnhandledExceptionEventArgs> UnhandledException;
37 37
38 38 protected PollingComponent(bool initialized) : base(initialized) {
39 39 m_timer = new Timer(OnTimer);
40 40 }
41 41
42 42 protected override void RunInternal() {
43 43 ScheduleNextPoll(Delay);
44 44 }
45 45
46 46
47 47 protected override async Task StopInternalAsync(CancellationToken ct) {
48 48 // component in Stopping state, no new polls will be scheduled
49 49 m_cancellation.Cancel();
50 50 try {
51 51 // await for pending poll
52 52 await m_poll;
53 } catch (OperationCanceledException e) {
53 } catch (OperationCanceledException) {
54 54 // OK
55 55 }
56 56 }
57 57
58 58 protected abstract Task Poll(CancellationToken ct);
59 59
60 60 void ScheduleNextPoll(int timeout) {
61 61 lock (SynchronizationObject) {
62 62 if (State == ExecutionState.Running) {
63 63 m_pending = Safe.CreateTask(m_cancellation.Token);
64 64 m_poll = m_pending.Then(() => Poll(m_cancellation.Token));
65 65 m_timer.Change(timeout, Timeout.Infinite);
66 66 }
67 67 }
68 68 }
69 69
70 70 async void OnTimer(object state) {
71 71 try {
72 72 m_pending.Start();
73 73 await m_poll;
74 ScheduleNextPoll(Interval);
74 75 } catch (Exception e) {
75 76 UnhandledException.DispatchEvent(this, new UnhandledExceptionEventArgs(e, false));
77
76 78 if (FailOnError)
77 79 Fail(e);
80 else
81 ScheduleNextPoll(Interval);
78 82 }
79 ScheduleNextPoll(Interval);
83
80 84 }
81 85
82 86 protected override void Dispose(bool disposing) {
83 87 if (disposing)
84 88 Safe.Dispose(m_timer, m_cancellation);
85 89 base.Dispose(disposing);
86 90 }
87 91
88 92 }
89 93 } No newline at end of file
@@ -1,331 +1,332
1 1 using System;
2 2 using System.Diagnostics;
3 3 using System.Threading;
4 4 using System.Threading.Tasks;
5 5
6 6 namespace Implab.Components {
7 7 /// <summary>
8 8 /// Base class for implementing components which support start and stop operations,
9 9 /// such components may represent running services.
10 10 /// </summary>
11 11 /// <remarks>
12 12 /// This class provides a basic lifecycle from the creation to the
13 13 /// termination of the component.
14 14 /// </remarks>
15 15 public abstract class RunnableComponent : IAsyncComponent, IRunnable, IInitializable, IDisposable {
16 16
17 17 /// <summary>
18 18 /// This class bounds <see cref="CancellationTokenSource"/> lifetime to the task,
19 19 /// when the task completes the associated token source will be disposed.
20 20 /// </summary>
21 21 class AsyncOperationDescriptor {
22 22
23 23 public static AsyncOperationDescriptor None { get; } = new AsyncOperationDescriptor();
24 24
25 25 readonly CancellationTokenSource m_cts;
26 26
27 27 bool m_done;
28 28
29 29 public CancellationToken Token {
30 30 get { return m_cts == null ? CancellationToken.None : m_cts.Token; }
31 31 }
32 32
33 33 public Task Task { get; private set; }
34 34
35 35 private AsyncOperationDescriptor(Task task, CancellationTokenSource cts) {
36 36 m_cts = cts;
37 37 Task = Chain(task);
38 38 }
39 39
40 40 private AsyncOperationDescriptor() {
41 41 Task = Task.CompletedTask;
42 42 }
43 43
44 44 public void Cancel() {
45 45 if (m_cts != null) {
46 46 lock (m_cts) {
47 47 if (!m_done)
48 48 m_cts.Cancel();
49 49 }
50 50 }
51 51 }
52 52
53 53 void Done() {
54 54 if (m_cts != null) {
55 55 lock (m_cts) {
56 56 m_done = true;
57 57 m_cts.Dispose();
58 58 }
59 59 } else {
60 60 m_done = true;
61 61 }
62 62 }
63 63
64 64 async Task Chain(Task other) {
65 65 try {
66 66 await other;
67 67 } finally {
68 68 Done();
69 69 }
70 70 }
71 71
72 72 public static AsyncOperationDescriptor Create(Func<CancellationToken, Task> factory, CancellationToken ct) {
73 73 var cts = ct.CanBeCanceled ?
74 74 CancellationTokenSource.CreateLinkedTokenSource(ct) :
75 75 new CancellationTokenSource();
76 76
77 77 return new AsyncOperationDescriptor(factory(cts.Token), cts);
78 78 }
79 79
80 80 }
81 81
82 82 // this lock is used to synchronize state flow of the component during
83 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
87 87 // MoveSuccess/MoveFailed method belongs to the current
88 88 // operation, if cookies didn't match ignore completion result.
89 89 object m_cookie;
90 90
91 91 // AsyncOperationDscriptor aggregates a task and it's cancellation token
92 92 AsyncOperationDescriptor m_current = AsyncOperationDescriptor.None;
93 93
94 94 ExecutionState m_state;
95 95
96 96 /// <summary>
97 97 /// Объект синхронизации используется для обеспечения совместного доступа
98 98 /// клиента компоненты и процессов, протекающих внутри компоненты, к общему
99 99 /// состоянию, т.е.true таким свойствам, как <see cref="State"/>,
100 100 /// <see cref="LastError"/>. Обработчики события <see cref="StateChanged"/>
101 101 /// вызываются уже с установленной блокировкой, поэтому дополнительная
102 102 /// синхронизация не требуется.
103 103 /// </summary>
104 104 public object SynchronizationObject { get { return m_lock; } }
105 105
106 106 protected RunnableComponent(bool initialized) {
107 107 State = initialized ? ExecutionState.Ready : ExecutionState.Created;
108 108 }
109 109
110 110 public Task Completion {
111 111 get { return m_current.Task; }
112 112 }
113 113
114 114 public ExecutionState State {
115 115 get { return m_state; }
116 116 private set {
117 117 if (m_state != value) {
118 118 m_state = value;
119 119 StateChanged.DispatchEvent(this, new StateChangeEventArgs {
120 120 State = value,
121 121 LastError = LastError
122 122 });
123 123 }
124 124 }
125 125 }
126 126
127 127 public Exception LastError { get; private set; }
128 128
129 129 /// <summary>
130 130 /// Событие изменения состояния компоненты.see Обработчики данного события
131 131 /// вызываются внутри блокировки <see cref="SynchronizationObject"/> и должны
132 132 /// выполняться максимально быстро.
133 133 /// </summary>
134 134 public event EventHandler<StateChangeEventArgs> StateChanged;
135 135
136 136 /// <summary>
137 137 /// Releases all resources used by the current component regardless of its
138 138 /// execution state.
139 139 /// </summary>
140 140 /// <remarks>
141 141 /// Calling to this method may result unexpedted results if the component
142 142 /// isn't in the stopped state. Call this method after the component is
143 143 /// stopped if needed or if the component is in the failed state.
144 144 /// </remarks>
145 145 public void Dispose() {
146 146 bool dispose = false;
147 147 lock (SynchronizationObject) {
148 148 if (m_state != ExecutionState.Disposed) {
149 149 dispose = true;
150 150 m_state = ExecutionState.Disposed;
151 151 m_cookie = new object();
152 152 }
153 153 }
154 154 if (dispose) {
155 155 Dispose(true);
156 156 GC.SuppressFinalize(this);
157 157 }
158 158 }
159 159
160 160 ~RunnableComponent() {
161 161 Dispose(false);
162 162 }
163 163
164 164 /// <summary>
165 165 /// Releases all resources used by the current component regardless of its
166 166 /// execution state.
167 167 /// </summary>
168 168 /// <param name="disposing">Indicates that the component is disposed
169 169 /// during a normal disposing or during GC.</param>
170 170 protected virtual void Dispose(bool disposing) {
171 171 }
172 172
173 173 public void Initialize() {
174 174 var cookie = new object();
175 175 if (MoveInitialize(cookie))
176 176 Safe.NoWait(ScheduleTask(InitializeInternalAsync, CancellationToken.None, cookie));
177 177 else
178 178 throw new InvalidOperationException();
179 179 }
180 180
181 181 /// <summary>
182 182 /// This method is used for initialization during a component creation.
183 183 /// </summary>
184 184 /// <param name="ct">A cancellation token for this operation</param>
185 185 /// <remarks>
186 186 /// This method should be used for short and mostly syncronous operations,
187 187 /// other operations which require time to run shoud be placed in
188 188 /// <see cref="StartInternalAsync(CancellationToken)"/> method.
189 189 /// </remarks>
190 190 protected virtual Task InitializeInternalAsync(CancellationToken ct) {
191 191 return Task.CompletedTask;
192 192 }
193 193
194 194 public void Start(CancellationToken ct) {
195 195 var cookie = new object();
196 196 if (MoveStart(cookie))
197 197 Safe.NoWait(ScheduleStartAndRun(ct, cookie));
198 198 else
199 199 throw new InvalidOperationException();
200 200 }
201 201
202 202 async Task ScheduleStartAndRun(CancellationToken ct, object cookie) {
203 203 try {
204 204 await ScheduleTask(StartInternalAsync, ct, cookie);
205 205 RunInternal();
206 206 } catch (Exception err) {
207 207 Fail(err);
208 208 }
209 209 }
210 210
211 211 protected virtual Task StartInternalAsync(CancellationToken ct) {
212 212 return Task.CompletedTask;
213 213 }
214 214
215 215 /// <summary>
216 216 /// This method is called after the component is enetered running state,
217 217 /// use this method to
218 218 /// </summary>
219 219 protected virtual void RunInternal() {
220 220
221 221 }
222 222
223 223 public void Stop(CancellationToken ct) {
224 224 var cookie = new object();
225 225 if (MoveStop(cookie))
226 226 Safe.NoWait(ScheduleTask(StopAsync, ct, cookie));
227 227 else
228 228 throw new InvalidOperationException();
229 229 }
230 230
231 231 async Task StopAsync(CancellationToken ct) {
232 232 m_current.Cancel();
233 233 await Completion;
234 234
235 235 ct.ThrowIfCancellationRequested();
236 236
237 237 await StopInternalAsync(ct);
238 238 }
239 239
240 240 protected virtual Task StopInternalAsync(CancellationToken ct) {
241 241 return Task.CompletedTask;
242 242 }
243 243
244 244 protected void Fail(Exception err) {
245 245 lock(m_lock) {
246 246 if (m_state != ExecutionState.Running)
247 247 return;
248 248 m_cookie = new object();
249 249 LastError = err;
250 250 State = ExecutionState.Failed;
251 251 }
252 252 }
253 253
254 254
255 255 #region state management
256 256
257 257 bool MoveInitialize(object cookie) {
258 258 lock (m_lock) {
259 259 if (State != ExecutionState.Created)
260 260 return false;
261 261 State = ExecutionState.Initializing;
262 262 m_cookie = cookie;
263 263 return true;
264 264 }
265 265 }
266 266
267 267 bool MoveStart(object cookie) {
268 268 lock (m_lock) {
269 269 if (State != ExecutionState.Ready)
270 270 return false;
271 271 State = ExecutionState.Starting;
272 272 m_cookie = cookie;
273 273 return true;
274 274 }
275 275 }
276 276
277 277 bool MoveStop(object cookie) {
278 278 lock (m_lock) {
279 279 if (State != ExecutionState.Starting && State != ExecutionState.Running)
280 280 return false;
281 281 State = ExecutionState.Stopping;
282 282 m_cookie = cookie;
283 283 return true;
284 284 }
285 285 }
286 286
287 287 void MoveSuccess(object cookie) {
288 288 lock (m_lock) {
289 289 if (m_cookie != cookie)
290 290 return;
291 291 switch (State) {
292 292 case ExecutionState.Initializing:
293 293 State = ExecutionState.Ready;
294 294 break;
295 295 case ExecutionState.Starting:
296 296 State = ExecutionState.Running;
297 297 break;
298 298 case ExecutionState.Stopping:
299 299 State = ExecutionState.Stopped;
300 300 break;
301 301 }
302 302 }
303 303 }
304 304
305 305 void MoveFailed(Exception err, object cookie) {
306 306 lock (m_lock) {
307 307 if (m_cookie != cookie)
308 308 return;
309 309 LastError = err;
310 310 State = ExecutionState.Failed;
311 311 }
312 312 }
313 313
314 314 Task ScheduleTask(Func<CancellationToken, Task> next, CancellationToken ct, object cookie) {
315 315
316 316 var op = AsyncOperationDescriptor.Create(async (x) => {
317 317 try {
318 318 await next(x);
319 319 MoveSuccess(cookie);
320 320 } catch (Exception e) {
321 321 MoveFailed(e, cookie);
322 throw;
322 323 }
323 324 }, ct);
324 325
325 326 m_current = op;
326 327 return op.Task;
327 328 }
328 329
329 330 #endregion
330 331 }
331 332 } No newline at end of file
@@ -1,64 +1,64
1 1 using System;
2 2 using System.Diagnostics;
3 3 using System.Threading.Tasks;
4 4
5 5 namespace Implab {
6 6 /// <summary>
7 7 /// This class is responsible for the promise resolution, dispatching and chaining
8 8 /// </summary>
9 9 public class Deferred : IResolvable {
10 10
11 11 readonly Promise m_promise;
12 12 internal Deferred() {
13 13 m_promise = new Promise();
14 14 }
15 15
16 internal Deferred(Promise promise, IDispatcher dispatcher) {
16 internal Deferred(Promise promise) {
17 17 Debug.Assert(promise != null);
18 18 m_promise = promise;
19 19 }
20 20
21 21 public IPromise Promise {
22 22 get { return m_promise; }
23 23 }
24 24
25 25 public void Cancel() {
26 26 Reject(new OperationCanceledException());
27 27 }
28 28
29 29 public virtual void Reject(Exception error) {
30 30 if (error is PromiseTransientException)
31 31 error = ((PromiseTransientException)error).InnerException;
32 32
33 33 m_promise.RejectPromise(error);
34 34 }
35 35
36 36 public virtual void Resolve() {
37 37 m_promise.ResolvePromise();
38 38 }
39 39
40 40 public virtual void Resolve(IPromise thenable) {
41 41 if (thenable == null)
42 42 Reject(new Exception("The promise or task are expected"));
43 43 if (thenable == m_promise)
44 44 Reject(new Exception("The promise cannot be resolved with oneself"));
45 45
46 46 try {
47 47 thenable.Then(this);
48 48 } catch (Exception err) {
49 49 Reject(err);
50 50 }
51 51 }
52 52
53 53 public virtual void Resolve(Task thenable) {
54 54 if (thenable == null)
55 55 Reject(new Exception("The promise or task are expected"));
56 56 try {
57 57 thenable.Then(this);
58 58 } catch(Exception err) {
59 59 Reject(err);
60 60 }
61 61 }
62 62
63 63 }
64 64 } No newline at end of file
@@ -1,19 +1,22
1 1 <Project Sdk="Microsoft.NET.Sdk">
2 2
3 3 <PropertyGroup>
4 4 <Authors>Sergey Smirnov</Authors>
5 5 <Title>Implab library</Title>
6 6 <Description>Provides some helper clesses like XML serialization helpers, JSON XML reader,
7 7 JSON pull-parser, ECMA-style promises, lightweight synchonization routines Signal
8 8 and SharedLock, Trace helpers on top of System.Diagnostics, ObjectPool etc.
9 9 </Description>
10 10 <Copyright>2012-2018 Sergey Smirnov</Copyright>
11 <LicenseUrl>https://opensource.org/licenses/BSD-2-Clause</LicenseUrl>
12 <ProjectUrl>https://implab.org</ProjectUrl>
11 <Version>3.0.6</Version>
12 <PackageLicenseUrl>https://opensource.org/licenses/BSD-2-Clause</PackageLicenseUrl>
13 <PackageProjectUrl>https://implab.org</PackageProjectUrl>
13 14 <RepositoryUrl>https://hg.implab.org/pub/ImplabNet/</RepositoryUrl>
15 <RepositoryType>mercurial</RepositoryType>
16 <PackageTags>IMPLAB;Json pull-parser;Json Xml;async;diagnostics;serialization;</PackageTags>
14 17 <TargetFrameworks>netstandard2.0;net46</TargetFrameworks>
15 18 <FrameworkPathOverride Condition="'$(TargetFramework)'=='net46' and '$(OSTYPE)'=='linux'">/usr/lib/mono/4.5/</FrameworkPathOverride>
16 19 <DefineConstants Condition="'$(TargetFramework)'=='net46'">NETFX_TRACE_BUG;$(DefineConstants)</DefineConstants>
17 20 </PropertyGroup>
18 21
19 22 </Project>
@@ -1,208 +1,222
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Diagnostics;
4 4 using System.Reflection;
5 using System.Threading;
5 6 using System.Threading.Tasks;
6 7 using Implab.Parallels;
7 8
8 9 namespace Implab {
9 10 public class Promise : AbstractEvent<IResolvable>, IPromise {
10 11 public static IDispatcher DefaultDispatcher {
11 12 get {
12 13 return ThreadPoolDispatcher.Instance;
13 14 }
14 15 }
15 16
16 17 class ResolvableSignal : IResolvable {
17 18 public Signal Signal { get; private set; }
18 19 public ResolvableSignal() {
19 20 Signal = new Signal();
20 21 }
21 22
22 23
23 24 public void Reject(Exception error) {
24 25 Signal.Set();
25 26 }
26 27
27 28 public void Resolve() {
28 29 Signal.Set();
29 30 }
30 31 }
31 32
32 33 PromiseState m_state;
33 34
34 35 Exception m_error;
35 36
36 37 public bool IsRejected {
37 38 get {
38 39 return m_state == PromiseState.Rejected;
39 40 }
40 41 }
41 42
42 43 public bool IsFulfilled {
43 44 get {
44 45 return m_state == PromiseState.Fulfilled;
45 46 }
46 47 }
47 48
48 49 public Exception RejectReason {
49 50 get {
50 51 return m_error;
51 52 }
52 53 }
53 54
54 55 internal Promise() {
55 56
56 57 }
57 58
58 59 internal void ResolvePromise() {
59 60 if (BeginTransit()) {
60 61 m_state = PromiseState.Fulfilled;
61 62 CompleteTransit();
62 63 }
63 64 }
64 65
65 66 internal void RejectPromise(Exception reason) {
66 67 if (BeginTransit()) {
67 68 m_error = reason;
68 69 m_state = PromiseState.Rejected;
69 70 CompleteTransit();
70 71 }
71 72 }
72 73
73 74
74 75 #region implemented abstract members of AbstractPromise
75 76
76 77 protected override void SignalHandler(IResolvable handler) {
77 78 switch (m_state) {
78 79 case PromiseState.Fulfilled:
79 80 handler.Resolve();
80 81 break;
81 82 case PromiseState.Rejected:
82 83 handler.Reject(RejectReason);
83 84 break;
84 85 default:
85 86 throw new InvalidOperationException(String.Format("Invalid promise signal: {0}", m_state));
86 87 }
87 88 }
88 89
89 90 protected void WaitResult(int timeout) {
90 91 if (!(IsResolved || GetFulfillSignal().Wait(timeout)))
91 92 throw new TimeoutException();
92 93 }
93 94
94 95 protected Signal GetFulfillSignal() {
95 96 var next = new ResolvableSignal();
96 97 Then(next);
97 98 return next.Signal;
98 99 }
99 100
100 101 #endregion
101 102
102 103
103 104 public Type ResultType {
104 105 get {
105 106 return typeof(void);
106 107 }
107 108 }
108 109
109 110
110 111 protected void Rethrow() {
111 112 Debug.Assert(m_error != null);
112 113 if (m_error is OperationCanceledException)
113 114 throw new OperationCanceledException("Operation cancelled", m_error);
114 115 else
115 116 throw new TargetInvocationException(m_error);
116 117 }
117 118
118 119 public void Then(IResolvable next) {
119 120 AddHandler(next);
120 121 }
121 122
122 123 public IPromise<T> Cast<T>() {
123 124 throw new InvalidCastException();
124 125 }
125 126
126 127 public void Join() {
127 128 WaitResult(-1);
128 129 if (IsRejected)
129 130 Rethrow();
130 131 }
131 132
132 133 public void Join(int timeout) {
133 134 WaitResult(timeout);
134 135 if (IsRejected)
135 136 Rethrow();
136 137 }
137 138
138 139 public static ResolvedPromise Resolve() {
139 140 return new ResolvedPromise();
140 141 }
141 142
142 143 public static ResolvedPromise<T> Resolve<T>(T result) {
143 144 return new ResolvedPromise<T>(result);
144 145 }
145 146
146 147 public static RejectedPromise Reject(Exception reason) {
147 148 return new RejectedPromise(reason);
148 149 }
149 150
150 151 public static RejectedPromise<T> Reject<T>(Exception reason) {
151 152 return new RejectedPromise<T>(reason);
152 153 }
153 154
154 155 public static IPromise Create(PromiseExecutor executor) {
155 Safe.ArgumentNotNull(executor, nameof(executor));
156 return Create(executor, CancellationToken.None);
157 }
156 158
157 var p = new Promise();
158 var d = new Deferred(p, DefaultDispatcher);
159
159 public static IPromise Create(PromiseExecutor executor, CancellationToken ct) {
160 Safe.ArgumentNotNull(executor, nameof(executor));
161 if (!ct.CanBeCanceled)
162 return Create(executor);
163
164 var d = new Deferred();
165
166 ct.Register(d.Cancel);
167
160 168 try {
161 executor(d);
162 } catch (Exception e) {
169 if (!ct.IsCancellationRequested)
170 executor(d);
171 } catch(Exception e) {
163 172 d.Reject(e);
164 173 }
165
166 174 return d.Promise;
167 175 }
168 176
169 177 public static IPromise<T> Create<T>(PromiseExecutor<T> executor) {
178 return Create(executor, CancellationToken.None);
179 }
180
181 public static IPromise<T> Create<T>(PromiseExecutor<T> executor, CancellationToken ct) {
170 182 Safe.ArgumentNotNull(executor, nameof(executor));
171 183
172 184 var d = new Deferred<T>();
173
185
186 ct.Register(d.Cancel);
187
174 188 try {
175 executor(d);
176 } catch (Exception e) {
189 if (!ct.IsCancellationRequested)
190 executor(d);
191 } catch(Exception e) {
177 192 d.Reject(e);
178 193 }
179
180 194 return d.Promise;
181 195 }
182 196
183 197 public static IPromise All(IEnumerable<IPromise> promises) {
184 198 var d = new Deferred();
185 199 var all = new PromiseAll(d);
186 200 foreach (var promise in promises) {
187 201 all.AddPromise(promise);
188 202 if (all.Done)
189 203 break;
190 204 }
191 205 all.Complete();
192 206 return all.ResultPromise;
193 207 }
194 208
195 209 public static IPromise<T[]> All<T>(IEnumerable<IPromise<T>> promises, Func<T, IPromise> cleanup = null, Action cancel = null) {
196 210 var d = new Deferred<T[]>();
197 211 var all = new PromiseAll<T>(d, cleanup, cancel);
198 212 foreach (var promise in promises) {
199 213 all.AddPromise(promise);
200 214 if (all.Done)
201 215 break;
202 216 }
203 217 all.Complete();
204 218 return all.ResultPromise;
205 219 }
206 220 }
207 221 }
208 222
1 NO CONTENT: file was removed
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

ok, latest stable version should be in default

You need to be logged in to leave comments. Login now