##// END OF EJS Templates
refactoring
cin -
r11:6ec82bf68c8e promises
parent child
Show More
1 NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,28
1 using System;
2 using System.Threading;
3
4 namespace Implab.Parallels {
5 /// <summary>
6 /// Класс для распаралеливания задач.
7 /// </summary>
8 /// <remarks>
9 /// Используя данный класс и лямда выражения можно распараллелить
10 /// вычисления, для этого используется концепция обещаний.
11 /// </remarks>
12 public static class AsyncPool {
13
14 public static Promise<T> Invoke<T>(Func<T> func) {
15 var p = new Promise<T>();
16
17 ThreadPool.QueueUserWorkItem(param => {
18 try {
19 p.Resolve(func());
20 } catch(Exception e) {
21 p.Reject(e);
22 }
23 });
24
25 return p;
26 }
27 }
28 }
@@ -1,127 +1,139
1 1 using System;
2 2 using Microsoft.VisualStudio.TestTools.UnitTesting;
3 3 using System.Reflection;
4 4 using System.Threading;
5 using Implab.Parallels;
5 6
6 7 namespace Implab.Test
7 8 {
8 9 [TestClass]
9 10 public class AsyncTests
10 11 {
11 12 [TestMethod]
12 13 public void ResolveTest ()
13 14 {
14 15 int res = -1;
15 16 var p = new Promise<int> ();
16 17 p.Then (x => res = x);
17 18 p.Resolve (100);
18 19
19 20 Assert.AreEqual (res, 100);
20 21 }
21 22
22 23 [TestMethod]
23 24 public void RejectTest ()
24 25 {
25 26 int res = -1;
26 27 Exception err = null;
27 28
28 29 var p = new Promise<int> ();
29 30 p.Then (x => res = x, e => err = e);
30 31 p.Reject (new ApplicationException ("error"));
31 32
32 33 Assert.AreEqual (res, -1);
33 34 Assert.AreEqual (err.Message, "error");
34 35
35 36 }
36 37
37 38 [TestMethod]
38 39 public void JoinSuccessTest ()
39 40 {
40 41 var p = new Promise<int> ();
41 42 p.Resolve (100);
42 43 Assert.AreEqual (p.Join (), 100);
43 44 }
44 45
45 46 [TestMethod]
46 47 public void JoinFailTest ()
47 48 {
48 49 var p = new Promise<int> ();
49 50 p.Reject (new ApplicationException ("failed"));
50 51
51 52 try {
52 53 p.Join ();
53 54 throw new ApplicationException ("WRONG!");
54 55 } catch (TargetInvocationException err) {
55 56 Assert.AreEqual (err.InnerException.Message, "failed");
56 57 } catch {
57 58 Assert.Fail ("Got wrong excaption");
58 59 }
59 60 }
60 61
61 62 [TestMethod]
62 63 public void MapTest ()
63 64 {
64 65 var p = new Promise<int> ();
65 66
66 67 var p2 = p.Map (x => x.ToString ());
67 68 p.Resolve (100);
68 69
69 70 Assert.AreEqual (p2.Join (), "100");
70 71 }
71 72
72 73 [TestMethod]
74 public void FixErrorTest() {
75 var p = new Promise<int>();
76
77 var p2 = p.Error(e => 101);
78
79 p.Reject(new Exception());
80
81 Assert.AreEqual(p2.Join(), 101);
82 }
83
84 [TestMethod]
73 85 public void ChainTest ()
74 86 {
75 87 var p1 = new Promise<int> ();
76 88
77 89 var p3 = p1.Chain (x => {
78 90 var p2 = new Promise<string> ();
79 91 p2.Resolve (x.ToString ());
80 92 return p2;
81 93 });
82 94
83 95 p1.Resolve (100);
84 96
85 97 Assert.AreEqual (p3.Join (), "100");
86 98 }
87 99
88 100 [TestMethod]
89 101 public void PoolTest ()
90 102 {
91 103 var pid = Thread.CurrentThread.ManagedThreadId;
92 104 var p = AsyncPool.Invoke (() => Thread.CurrentThread.ManagedThreadId);
93 105
94 106 Assert.AreNotEqual (pid, p.Join ());
95 107 }
96 108
97 109 [TestMethod]
98 110 public void ComplexCase1Test() {
99 111 var flags = new bool[3];
100 112
101 113 // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map)
102 114
103 115 var p = PromiseHelper
104 116 .Sleep(200, "Alan")
105 117 .Cancelled(() => flags[0] = true)
106 118 .Chain(x =>
107 119 PromiseHelper
108 120 .Sleep(200, "Hi, " + x)
109 121 .Map( y => y )
110 122 .Cancelled(() => flags[1] = true)
111 123 )
112 124 .Cancelled(() => flags[2] = true);
113 125 Thread.Sleep(300);
114 126 p.Cancel();
115 127 try {
116 128 Assert.AreEqual(p.Join(), "Hi, Alan");
117 129 Assert.Fail("Shouldn't get here");
118 130 } catch(OperationCanceledException) {
119 131 }
120 132
121 133 Assert.IsFalse(flags[0]);
122 134 Assert.IsTrue(flags[1]);
123 135 Assert.IsTrue(flags[2]);
124 136 }
125 137 }
126 138 }
127 139
@@ -1,16 +1,17
1 using System;
1 using Implab.Parallels;
2 using System;
2 3 using System.Collections.Generic;
3 4 using System.Linq;
4 5 using System.Text;
5 6 using System.Threading;
6 7
7 8 namespace Implab.Test {
8 9 class PromiseHelper {
9 10 public static Promise<T> Sleep<T>(int timeout, T retVal) {
10 11 return AsyncPool.Invoke(() => {
11 12 Thread.Sleep(timeout);
12 13 return retVal;
13 14 });
14 15 }
15 16 }
16 17 }
@@ -1,40 +1,39
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5
6 6 namespace Implab
7 7 {
8 8 public interface IPromise
9 9 {
10 10 /// <summary>
11 11 /// Check whereather the promise has no more than one dependent promise.
12 12 /// </summary>
13 13 bool IsExclusive
14 14 {
15 15 get;
16 16 }
17 17
18 18 /// <summary>
19 19 /// The current state of the promise.
20 20 /// </summary>
21 21 PromiseState State
22 22 {
23 23 get;
24 24 }
25 25
26 26 /// <summary>
27 /// Tries to cancel the promise or the complete chain.
27 /// Tries to cancel the the complete chain of promises.
28 28 /// </summary>
29 /// <param name="dependencies">Try to cancel the whole promise chain, the parent promise will be cancelled only if it has only one promise</param>
30 /// <returns></returns>
31 bool Cancel(bool dependencies);
29 /// <returns><c>true</c> - if the promise has been cancelled, otherwise the promise will be resolved (or resolved already).</returns>
30 bool Cancel();
32 31
33 32 /// <summary>
34 33 /// Registers handler for the case when the promise is cencelled. If the promise already cancelled the
35 34 /// handler will be invoked immediatelly.
36 35 /// </summary>
37 36 /// <param name="handler">The handler</param>
38 37 void HandleCancelled(Action handler);
39 38 }
40 39 }
@@ -1,49 +1,47
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 6 <ProductVersion>10.0.0</ProductVersion>
7 7 <SchemaVersion>2.0</SchemaVersion>
8 8 <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
9 9 <OutputType>Library</OutputType>
10 10 <RootNamespace>Implab</RootNamespace>
11 11 <AssemblyName>Implab</AssemblyName>
12 12 </PropertyGroup>
13 13 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
14 14 <DebugSymbols>true</DebugSymbols>
15 15 <DebugType>full</DebugType>
16 16 <Optimize>false</Optimize>
17 17 <OutputPath>bin\Debug</OutputPath>
18 18 <DefineConstants>DEBUG;</DefineConstants>
19 19 <ErrorReport>prompt</ErrorReport>
20 20 <WarningLevel>4</WarningLevel>
21 21 <ConsolePause>false</ConsolePause>
22 22 </PropertyGroup>
23 23 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
24 24 <DebugType>full</DebugType>
25 25 <Optimize>true</Optimize>
26 26 <OutputPath>bin\Release</OutputPath>
27 27 <ErrorReport>prompt</ErrorReport>
28 28 <WarningLevel>4</WarningLevel>
29 29 <ConsolePause>false</ConsolePause>
30 30 </PropertyGroup>
31 31 <ItemGroup>
32 32 <Reference Include="System" />
33 33 </ItemGroup>
34 34 <ItemGroup>
35 35 <Compile Include="IPromise.cs" />
36 36 <Compile Include="PromiseState.cs" />
37 37 <Compile Include="TaskController.cs" />
38 38 <Compile Include="ProgressInitEventArgs.cs" />
39 39 <Compile Include="Properties\AssemblyInfo.cs" />
40 40 <Compile Include="Promise.cs" />
41 <Compile Include="AsyncPool.cs" />
41 <Compile Include="Parallels\AsyncPool.cs" />
42 42 <Compile Include="Safe.cs" />
43 43 <Compile Include="ValueEventArgs.cs" />
44 44 </ItemGroup>
45 45 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
46 <ItemGroup>
47 <Folder Include="Parallels\" />
48 </ItemGroup>
46 <ItemGroup />
49 47 </Project> No newline at end of file
@@ -1,459 +1,543
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Reflection;
4 4 using System.Diagnostics;
5 5 using System.Threading;
6 6
7 7 namespace Implab {
8 8
9 9 public delegate void ErrorHandler(Exception e);
10
10 public delegate T ErrorHandler<out T>(Exception e);
11 11 public delegate void ResultHandler<in T>(T result);
12 12 public delegate TNew ResultMapper<in TSrc, out TNew>(TSrc result);
13 13 public delegate Promise<TNew> ChainedOperation<in TSrc, TNew>(TSrc result);
14 14
15 15 /// <summary>
16 16 /// Класс для асинхронного получения результатов. Так называемое "обещание".
17 17 /// </summary>
18 18 /// <typeparam name="T">Тип получаемого результата</typeparam>
19 19 /// <remarks>
20 20 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции,
21 21 /// клиент получив такое обещание может установить ряд обратных вызово для получения
22 22 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para>
23 23 /// <para>
24 24 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на
25 25 /// данные события клиент должен использовать методы <c>Then</c>.
26 26 /// </para>
27 27 /// <para>
28 28 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой),
29 29 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о
30 30 /// выполнении обещания.
31 31 /// </para>
32 32 /// <para>
33 33 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался,
34 34 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном
35 35 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в
36 36 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении
37 37 /// обещания.
38 38 /// </para>
39 39 /// <para>
40 40 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать
41 41 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует
42 42 /// использовать соответствующую форму методе <c>Then</c>.
43 43 /// </para>
44 44 /// <para>
45 45 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать
46 46 /// только инициатор обещания иначе могут возникнуть противоречия.
47 47 /// </para>
48 48 /// </remarks>
49 49 public class Promise<T> : IPromise {
50 50
51 51 struct ResultHandlerInfo {
52 52 public ResultHandler<T> resultHandler;
53 53 public ErrorHandler errorHandler;
54 54 }
55 55
56 56 readonly IPromise m_parent;
57 57
58 58 LinkedList<ResultHandlerInfo> m_resultHandlers = new LinkedList<ResultHandlerInfo>();
59 59 LinkedList<Action> m_cancelHandlers = new LinkedList<Action>();
60 60
61 61 readonly object m_lock = new Object();
62 62 readonly bool m_cancellable;
63 63 int m_childrenCount = 0;
64 64
65 65 PromiseState m_state;
66 66 T m_result;
67 67 Exception m_error;
68 68
69 69 public Promise() {
70 70 m_cancellable = true;
71 71 }
72 72
73 73 public Promise(IPromise parent, bool cancellable) {
74 74 m_cancellable = cancellable;
75 75 m_parent = parent;
76 76 if (parent != null)
77 77 parent.HandleCancelled(InternalCancel);
78 78 }
79 79
80 80 void InternalCancel() {
81 81 // don't try to cancel parent :)
82 82 Cancel(false);
83 83 }
84 84
85 85 /// <summary>
86 86 /// Выполняет обещание, сообщая об успешном выполнении.
87 87 /// </summary>
88 88 /// <param name="result">Результат выполнения.</param>
89 89 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
90 90 public void Resolve(T result) {
91 91 lock (this) {
92 92 if (m_state == PromiseState.Cancelled)
93 93 return;
94 94 if (m_state != PromiseState.Unresolved)
95 95 throw new InvalidOperationException("The promise is already resolved");
96 96 m_result = result;
97 97 m_state = PromiseState.Resolved;
98 98 }
99 99
100 100 OnStateChanged();
101 101 }
102 102
103 103 /// <summary>
104 104 /// Выполняет обещание, сообщая об ошибке
105 105 /// </summary>
106 106 /// <param name="error">Исключение возникшее при выполнении операции</param>
107 107 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
108 108 public void Reject(Exception error) {
109 109 lock (this) {
110 110 if (m_state == PromiseState.Cancelled)
111 111 return;
112 112 if (m_state != PromiseState.Unresolved)
113 113 throw new InvalidOperationException("The promise is already resolved");
114 114 m_error = error;
115 115 m_state = PromiseState.Rejected;
116 116 }
117 117
118 118 OnStateChanged();
119 119 }
120 120
121 121 /// <summary>
122 122 /// Отменяет операцию, если это возможно.
123 123 /// </summary>
124 124 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns>
125 125 public bool Cancel() {
126 126 return Cancel(true);
127 127 }
128 128
129 protected virtual void OnStateChanged() {
130 switch (m_state) {
131 case PromiseState.Resolved:
132 foreach (var resultHandlerInfo in m_resultHandlers)
133 try {
134 if (resultHandlerInfo.resultHandler != null)
135 resultHandlerInfo.resultHandler(m_result);
136 } catch (Exception e) {
137 try {
138 if (resultHandlerInfo.errorHandler != null)
139 resultHandlerInfo.errorHandler(e);
140 } catch { }
141 }
142 break;
143 case PromiseState.Cancelled:
144 foreach (var cancelHandler in m_cancelHandlers)
145 cancelHandler();
146 break;
147 case PromiseState.Rejected:
148 foreach (var resultHandlerInfo in m_resultHandlers)
149 try {
150 if (resultHandlerInfo.errorHandler != null)
151 resultHandlerInfo.errorHandler(m_error);
152 } catch { }
153 break;
154 default:
155 throw new InvalidOperationException(String.Format("Promise entered an invalid state {0}", m_state));
156 }
157
158 m_resultHandlers = null;
159 m_cancelHandlers = null;
160 }
161
162 129 /// <summary>
163 /// Добавляет обработчики событий выполнения обещания.
130 /// Adds new handlers to this promise.
164 131 /// </summary>
165 /// <param name="success">Обработчик успешного выполнения обещания.
166 /// Данному обработчику будет передан результат выполнения операции.</param>
167 /// <param name="error">Обработчик ошибки. Данный обработчик получит
168 /// исключение возникшее при выполнении операции.</param>
169 /// <returns>Само обещание</returns>
132 /// <param name="success">The handler of the successfully completed operation.
133 /// This handler will recieve an operation result as a parameter.</param>
134 /// <param name="error">Handles an exception that may occur during the operation.</param>
135 /// <returns>The new promise chained to this one.</returns>
170 136 public Promise<T> Then(ResultHandler<T> success, ErrorHandler error) {
171 137 if (success == null && error == null)
172 138 return this;
173 139
174 var medium = new Promise<T>();
140 var medium = new Promise<T>(this, true);
175 141
176 142 var handlerInfo = new ResultHandlerInfo();
177 143
178 144 if (success != null)
179 145 handlerInfo.resultHandler = x => {
180 146 success(x);
181 147 medium.Resolve(x);
182 148 };
183 149 else
184 150 handlerInfo.resultHandler = medium.Resolve;
185 151
186 152 if (error != null)
187 153 handlerInfo.errorHandler = x => {
188 154 try {
189 155 error(x);
190 156 } catch { }
191 157 medium.Reject(x);
192 158 };
193 159 else
194 160 handlerInfo.errorHandler = medium.Reject;
195 161
196 162 AddHandler(handlerInfo);
197 163
198 164 return medium;
199 165 }
200 166
167 /// <summary>
168 /// Adds new handlers to this promise.
169 /// </summary>
170 /// <param name="success">The handler of the successfully completed operation.
171 /// This handler will recieve an operation result as a parameter.</param>
172 /// <param name="error">Handles an exception that may occur during the operation and returns the value which will be used as the result of the operation.</param>
173 /// <returns>The new promise chained to this one.</returns>
174 public Promise<T> Then(ResultHandler<T> success, ErrorHandler<T> error) {
175 if (success == null && error == null)
176 return this;
177
178 var medium = new Promise<T>(this, true);
179
180 var handlerInfo = new ResultHandlerInfo();
181
182 if (success != null)
183 handlerInfo.resultHandler = x => {
184 success(x);
185 medium.Resolve(x);
186 };
187 else
188 handlerInfo.resultHandler = medium.Resolve;
189
190 if (error != null)
191 handlerInfo.errorHandler = x => {
192 try {
193 medium.Resolve(error(x));
194 } catch { }
195 medium.Reject(x);
196 };
197 else
198 handlerInfo.errorHandler = medium.Reject;
199
200 AddHandler(handlerInfo);
201
202 return medium;
203 }
204
205
201 206 public Promise<T> Then(ResultHandler<T> success) {
202 return Then(success, null);
207 if (success == null)
208 return this;
209
210 var medium = new Promise<T>(this, true);
211
212 var handlerInfo = new ResultHandlerInfo();
213
214 if (success != null)
215 handlerInfo.resultHandler = x => {
216 success(x);
217 medium.Resolve(x);
218 };
219 else
220 handlerInfo.resultHandler = medium.Resolve;
221
222 handlerInfo.errorHandler = medium.Reject;
223
224 AddHandler(handlerInfo);
225
226 return medium;
203 227 }
204 228
205 229 public Promise<T> Error(ErrorHandler error) {
206 230 return Then(null, error);
207 231 }
208 232
233 /// <summary>
234 /// Handles error and allows to keep the promise.
235 /// </summary>
236 /// <remarks>
237 /// If the specified handler throws an exception, this exception will be used to reject the promise.
238 /// </remarks>
239 /// <param name="handler">The error handler which returns the result of the promise.</param>
240 /// <returns>New promise.</returns>
241 public Promise<T> Error(ErrorHandler<T> handler) {
242 if (handler == null)
243 return this;
244
245 var medium = new Promise<T>(this, true);
246
247 AddHandler(new ResultHandlerInfo {
248 errorHandler = e => {
249 try {
250 medium.Resolve(handler(e));
251 } catch (Exception e2) {
252 medium.Reject(e2);
253 }
254 }
255 });
256
257 return medium;
258 }
259
209 260 public Promise<T> Anyway(Action handler) {
210 261 if (handler == null)
211 262 return this;
212 263
213 264 var medium = new Promise<T>();
214 265
215 266 AddHandler(new ResultHandlerInfo {
216 267 resultHandler = x => {
217 268 // to avoid handler being called multiple times we handle exception by ourselfs
218 269 try {
219 270 handler();
220 271 medium.Resolve(x);
221 272 } catch (Exception e) {
222 273 medium.Reject(e);
223 274 }
224 275 },
225 276 errorHandler = x => {
226 277 try {
227 278 handler();
228 279 } catch { }
229 280 medium.Reject(x);
230 281 }
231 282 });
232 283
233 284 return medium;
234 285 }
235 286
236 287 /// <summary>
237 288 /// Позволяет преобразовать результат выполения операции к новому типу.
238 289 /// </summary>
239 290 /// <typeparam name="TNew">Новый тип результата.</typeparam>
240 291 /// <param name="mapper">Преобразование результата к новому типу.</param>
241 292 /// <param name="error">Обработчик ошибки. Данный обработчик получит
242 293 /// исключение возникшее при выполнении операции.</param>
243 294 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
244 295 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) {
245 296 if (mapper == null)
246 297 throw new ArgumentNullException("mapper");
247 298
248 299 // создаем прицепленное обещание
249 300 var chained = new Promise<TNew>();
250 301
251 302 AddHandler(new ResultHandlerInfo() {
252 303 resultHandler = result => chained.Resolve(mapper(result)),
253 304 errorHandler = delegate(Exception e) {
254 305 if (error != null)
255 306 try {
256 307 error(e);
257 308 } catch { }
258 309 // в случае ошибки нужно передать исключение дальше по цепочке
259 310 chained.Reject(e);
260 311 }
261 312 });
262 313
263 314 return chained;
264 315 }
265 316
266 317 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper) {
267 318 return Map(mapper, null);
268 319 }
269 320
270 321 /// <summary>
271 322 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после
272 323 /// выполнения текущей, а результат текущей операции может быть использован для инициализации
273 324 /// новой операции.
274 325 /// </summary>
275 326 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam>
276 327 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param>
277 328 /// <param name="error">Обработчик ошибки. Данный обработчик получит
278 329 /// исключение возникшее при выполнении текуещй операции.</param>
279 330 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
280 331 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) {
281 332
282 333 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
283 334 // создать посредника, к которому будут подвызяваться следующие обработчики.
284 335 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы
285 336 // передать через него результаты работы.
286 337 var medium = new Promise<TNew>(this, true);
287 338
288 339 AddHandler(new ResultHandlerInfo {
289 340 resultHandler = delegate(T result) {
290 341 if (medium.State == PromiseState.Cancelled)
291 342 return;
292 343
293 344 var promise = chained(result);
294 345
295 346 // notify chained operation that it's not needed
296 347 medium.Cancelled(() => promise.Cancel());
297 348 promise.Then(
298 medium.Resolve,
299 medium.Reject
349 x => medium.Resolve(x),
350 e => medium.Reject(e)
300 351 );
301 352 },
302 353 errorHandler = delegate(Exception e) {
303 354 if (error != null)
304 355 error(e);
305 356 // в случае ошибки нужно передать исключение дальше по цепочке
306 357 medium.Reject(e);
307 358 }
308 359 });
309 360
310 361 return medium;
311 362 }
312 363
313 364 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) {
314 365 return Chain(chained, null);
315 366 }
316 367
317 368 public Promise<T> Cancelled(Action handler) {
318 369 if (handler == null)
319 370 return this;
320 371 lock (m_lock) {
321 372 if (m_state == PromiseState.Unresolved)
322 373 m_cancelHandlers.AddLast(handler);
323 374 else if (m_state == PromiseState.Cancelled)
324 375 handler();
325 376 }
326 377 return this;
327 378 }
328 379
329 380 public void HandleCancelled(Action handler) {
330 381 Cancelled(handler);
331 382 }
332 383
333 384 /// <summary>
334 385 /// Дожидается отложенного обещания и в случае успеха, возвращает
335 386 /// его, результат, в противном случае бросает исключение.
336 387 /// </summary>
337 388 /// <remarks>
338 389 /// <para>
339 390 /// Если ожидание обещания было прервано по таймауту, это не значит,
340 391 /// что обещание было отменено или что-то в этом роде, это только
341 392 /// означает, что мы его не дождались, однако все зарегистрированные
342 393 /// обработчики, как были так остались и они будут вызваны, когда
343 394 /// обещание будет выполнено.
344 395 /// </para>
345 396 /// <para>
346 397 /// Такое поведение вполне оправдано поскольку таймаут может истечь
347 398 /// в тот момент, когда началась обработка цепочки обработчиков, и
348 399 /// к тому же текущее обещание может стоять в цепочке обещаний и его
349 400 /// отклонение может привести к непрогнозируемому результату.
350 401 /// </para>
351 402 /// </remarks>
352 403 /// <param name="timeout">Время ожидания</param>
353 404 /// <returns>Результат выполнения обещания</returns>
354 405 public T Join(int timeout) {
355 406 var evt = new ManualResetEvent(false);
356 407 Anyway(() => evt.Set());
357 408 Cancelled(() => evt.Set());
358 409
359 410 if (!evt.WaitOne(timeout, true))
360 411 throw new TimeoutException();
361 412
362 413 switch (State) {
363 414 case PromiseState.Resolved:
364 415 return m_result;
365 416 case PromiseState.Cancelled:
366 417 throw new OperationCanceledException();
367 418 case PromiseState.Rejected:
368 419 throw new TargetInvocationException(m_error);
369 420 default:
370 421 throw new ApplicationException(String.Format("Invalid promise state {0}", State));
371 422 }
372 423 }
373 424
374 425 public T Join() {
375 426 return Join(Timeout.Infinite);
376 427 }
377 428
378 429 void AddHandler(ResultHandlerInfo handler) {
379 430 bool invokeRequired = false;
380 431
381 432 lock (m_lock) {
382 433 m_childrenCount++;
383 434 if (m_state == PromiseState.Unresolved) {
384 435 m_resultHandlers.AddLast(handler);
385 436 } else
386 437 invokeRequired = true;
387 438 }
388 439
389 440 // обработчики не должны блокировать сам объект
390 441 if (invokeRequired)
391 442 InvokeHandler(handler);
392 443 }
393 444
394 445 void InvokeHandler(ResultHandlerInfo handler) {
395 446 switch (m_state) {
396 447 case PromiseState.Resolved:
397 448 try {
398 449 if (handler.resultHandler != null)
399 450 handler.resultHandler(m_result);
400 451 } catch (Exception e) {
401 452 try {
402 453 if (handler.errorHandler != null)
403 454 handler.errorHandler(e);
404 455 } catch { }
405 456 }
406 457 break;
407 458 case PromiseState.Rejected:
408 459 try {
409 460 if (handler.errorHandler != null)
410 461 handler.errorHandler(m_error);
411 462 } catch { }
412 463 break;
413 464 default:
414 465 // do nothing
415 466 return;
416 467 }
417 468 }
418 469
470 protected virtual void OnStateChanged() {
471 switch (m_state) {
472 case PromiseState.Resolved:
473 foreach (var resultHandlerInfo in m_resultHandlers)
474 try {
475 if (resultHandlerInfo.resultHandler != null)
476 resultHandlerInfo.resultHandler(m_result);
477 } catch (Exception e) {
478 try {
479 if (resultHandlerInfo.errorHandler != null)
480 resultHandlerInfo.errorHandler(e);
481 } catch { }
482 }
483 break;
484 case PromiseState.Cancelled:
485 foreach (var cancelHandler in m_cancelHandlers)
486 cancelHandler();
487 break;
488 case PromiseState.Rejected:
489 foreach (var resultHandlerInfo in m_resultHandlers)
490 try {
491 if (resultHandlerInfo.errorHandler != null)
492 resultHandlerInfo.errorHandler(m_error);
493 } catch { }
494 break;
495 default:
496 throw new InvalidOperationException(String.Format("Promise entered an invalid state {0}", m_state));
497 }
498
499 m_resultHandlers = null;
500 m_cancelHandlers = null;
501 }
502
419 503
420 504
421 505 public bool IsExclusive {
422 506 get {
423 507 lock (m_lock) {
424 508 return m_childrenCount <= 1;
425 509 }
426 510 }
427 511 }
428 512
429 513 public PromiseState State {
430 514 get {
431 515 lock (m_lock) {
432 516 return m_state;
433 517 }
434 518 }
435 519 }
436 520
437 public bool Cancel(bool dependencies) {
521 protected bool Cancel(bool dependencies) {
438 522 bool result;
439 523
440 524 lock (m_lock) {
441 525 if (m_state == PromiseState.Unresolved) {
442 526 m_state = PromiseState.Cancelled;
443 527 result = true;
444 528 } else {
445 529 result = false;
446 530 }
447 531 }
448 532
449 533 if (result)
450 534 OnStateChanged();
451 535
452 536 if (dependencies && m_parent != null && m_parent.IsExclusive) {
453 m_parent.Cancel(true);
537 m_parent.Cancel();
454 538 }
455 539
456 540 return result;
457 541 }
458 542 }
459 543 }
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now