##// END OF EJS Templates
code cleanup...
cin -
r101:279e226dffdd v2
parent child
Show More
@@ -1,86 +1,86
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 public interface IPromise: ICancellable {
8 8 /// <summary>
9 9 /// Check whereather the promise has no more than one dependent promise.
10 10 /// </summary>
11 11 bool IsExclusive {
12 12 get;
13 13 }
14 14
15 15 /// <summary>
16 16 /// Тип результата, получаемого через данное обещание.
17 17 /// </summary>
18 18 Type PromiseType { get; }
19 19
20 20 /// <summary>
21 21 /// Обещание является выполненым, либо успешно, либо с ошибкой, либо отменено.
22 22 /// </summary>
23 23 bool IsResolved { get; }
24 24
25 25 /// <summary>
26 26 /// Обещание было отменено.
27 27 /// </summary>
28 28 bool IsCancelled { get; }
29 29
30 IPromise Then(Action success, ErrorHandler error, Action cancel);
31 IPromise Then(Action success, ErrorHandler error);
30 IPromise Then(Action success, Action<Exception> error, Action cancel);
31 IPromise Then(Action success, Action<Exception> error);
32 32 IPromise Then(Action success);
33 33
34 IPromise Chain(Func<IPromise> chained, ErrorHandler<IPromise> error, Action cancel);
35 IPromise Chain(Func<IPromise> chained, ErrorHandler<IPromise> error);
34 IPromise Chain(Func<IPromise> chained, Func<Exception, IPromise> error, Action cancel);
35 IPromise Chain(Func<IPromise> chained, Func<Exception, IPromise> error);
36 36 IPromise Chain(Func<IPromise> chained);
37 37
38 38 /// <summary>
39 39 /// Добавляет последнй обработчик в цепочку обещаний, не создает промежуточных обещаний.
40 40 /// </summary>
41 41 /// <param name="success">Success.</param>
42 42 /// <param name="error">Error.</param>
43 43 /// <param name="cancel">Cancel.</param>
44 void Last(Action success, ErrorHandler error, Action cancel);
45 void Last(Action success, ErrorHandler error);
44 void Last(Action success, Action<Exception> error, Action cancel);
45 void Last(Action success, Action<Exception> error);
46 46 void Last(Action success);
47 47
48 IPromise Error(ErrorHandler error);
48 IPromise Error(Action<Exception> error);
49 49 /// <summary>
50 /// Обрабатывает либо ошибку, либо результат. Событие отмены не обрабатывается.
50 /// Обрабатывает либо ошибку, либо результат, либо отмену.
51 51 /// </summary>
52 52 /// <param name="handler">Обработчик.</param>
53 53 /// <remarks>После обработке ошибки, она передается дальше.</remarks>
54 54 /// <summary>
55 55 /// Обрабатывает либо ошибку, либо результат, либо отмену обещания.
56 56 /// </summary>
57 57 /// <param name="handler">Обработчик.</param>
58 58 /// <remarks>После обработке ошибки, она передается дальше.</remarks>
59 59 IPromise Anyway(Action handler);
60 60 /// <summary>
61 /// Обработчик для регистрации отмены обещания, событие отмены не может быть подавлено.
61 /// Обработчик для регистрации отмены обещания.
62 62 /// </summary>
63 /// <returns>Новое обещание, связанное с текущим.</returns>
63 /// <returns>Новое обещание, связанное с текущим, выполнится после указанного обработчика.</returns>
64 64 /// <param name="handler">Обработчик события.</param>
65 65 /// <remarks>Если обработчик вызывает исключение, то оно передается обработчику ошибки, результат работы
66 66 /// которого будет передан связанному обещанию</remarks>
67 67 IPromise Cancelled(Action handler);
68 68
69 69 /// <summary>
70 70 /// Преобразует результат обещания к заданному типу и возвращает новое обещание.
71 71 /// </summary>
72 72 IPromise<T> Cast<T>();
73 73
74 74 /// <summary>
75 75 /// Синхронизирует текущий поток с обещанием.
76 76 /// </summary>
77 77 void Join();
78 78 /// <summary>
79 79 /// Синхронизирует текущий поток с обещанием.
80 80 /// </summary>
81 81 /// <param name="timeout">Время ожидания, по его истечению возникнет исключение.</param>
82 82 /// <exception cref="TimeoutException">Превышено время ожидания.</exception>
83 83 void Join(int timeout);
84 84
85 85 }
86 86 }
@@ -1,40 +1,40
1 1 using System;
2 2
3 3 namespace Implab {
4 4 public interface IPromise<T> : IPromise {
5 5
6 6 new T Join();
7 7
8 8 new T Join(int timeout);
9 9
10 void Last(ResultHandler<T> success, ErrorHandler error, Action cancel);
10 void Last(Action<T> success, Action<Exception> error, Action cancel);
11 11
12 void Last(ResultHandler<T> success, ErrorHandler error);
12 void Last(Action<T> success, Action<Exception> error);
13 13
14 void Last(ResultHandler<T> success);
14 void Last(Action<T> success);
15 15
16 IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel);
16 IPromise<T> Then(Action<T> success, Func<Exception,T> error, Action cancel);
17 17
18 IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error);
18 IPromise<T> Then(Action<T> success, Func<Exception,T> error);
19 19
20 IPromise<T> Then(ResultHandler<T> success);
20 IPromise<T> Then(Action<T> success);
21 21
22 IPromise<T2> Then<T2>(ResultMapper<T, T2> mapper, ErrorHandler<T2> error, Action cancel);
22 IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception,T2> error, Action cancel);
23 23
24 IPromise<T2> Then<T2>(ResultMapper<T, T2> mapper, ErrorHandler<T2> error);
24 IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception,T2> error);
25 25
26 IPromise<T2> Then<T2>(ResultMapper<T, T2> mapper);
26 IPromise<T2> Then<T2>(Func<T, T2> mapper);
27 27
28 IPromise<T2> Chain<T2>(ResultMapper<T, IPromise<T2>> chained, ErrorHandler<IPromise<T2>> error, Action cancel);
28 IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception,IPromise<T2>> error, Action cancel);
29 29
30 IPromise<T2> Chain<T2>(ResultMapper<T, IPromise<T2>> chained, ErrorHandler<IPromise<T2>> error);
30 IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception,IPromise<T2>> error);
31 31
32 IPromise<T2> Chain<T2>(ResultMapper<T, IPromise<T2>> chained);
32 IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained);
33 33
34 IPromise<T> Error(ErrorHandler<T> error);
34 IPromise<T> Error(Func<Exception,T> error);
35 35
36 36 new IPromise<T> Cancelled(Action handler);
37 37
38 38 new IPromise<T> Anyway(Action handler);
39 39 }
40 40 }
@@ -1,210 +1,207
1 1 using Implab.Diagnostics;
2 2 using System;
3 using System.Collections.Generic;
4 3 using System.Diagnostics;
5 using System.Linq;
6 using System.Text;
7 4 using System.Threading;
8 5
9 6 namespace Implab.Parallels {
10 7 public static class ArrayTraits {
11 8 class ArrayIterator<TSrc> : DispatchPool<int> {
12 9 readonly Action<TSrc> m_action;
13 10 readonly TSrc[] m_source;
14 11 readonly Promise<int> m_promise = new Promise<int>();
15 12 readonly LogicalOperation m_logicalOperation;
16 13
17 14 int m_pending;
18 15 int m_next;
19 16
20 17 public ArrayIterator(TSrc[] source, Action<TSrc> action, int threads)
21 18 : base(threads) {
22 19
23 20 Debug.Assert(source != null);
24 21 Debug.Assert(action != null);
25 22
26 23 m_logicalOperation = TraceContext.Instance.CurrentOperation;
27 24 m_next = 0;
28 25 m_source = source;
29 26 m_pending = source.Length;
30 27 m_action = action;
31 28
32 29 m_promise.Anyway(Dispose);
33 30
34 31 InitPool();
35 32 }
36 33
37 34 public Promise<int> Promise {
38 35 get {
39 36 return m_promise;
40 37 }
41 38 }
42 39
43 40 protected override void Worker() {
44 41 TraceContext.Instance.EnterLogicalOperation(m_logicalOperation, false);
45 42 try {
46 43 base.Worker();
47 44 } finally {
48 45 TraceContext.Instance.Leave();
49 46 }
50 47 }
51 48
52 49 protected override bool TryDequeue(out int unit) {
53 50 unit = Interlocked.Increment(ref m_next) - 1;
54 51 return unit < m_source.Length;
55 52 }
56 53
57 54 protected override void InvokeUnit(int unit) {
58 55 try {
59 56 m_action(m_source[unit]);
60 57 var pending = Interlocked.Decrement(ref m_pending);
61 58 if (pending == 0)
62 59 m_promise.Resolve(m_source.Length);
63 60 } catch (Exception e) {
64 61 m_promise.Reject(e);
65 62 }
66 63 }
67 64 }
68 65
69 66 class ArrayMapper<TSrc, TDst>: DispatchPool<int> {
70 67 readonly Func<TSrc, TDst> m_transform;
71 68 readonly TSrc[] m_source;
72 69 readonly TDst[] m_dest;
73 70 readonly Promise<TDst[]> m_promise = new Promise<TDst[]>();
74 71 readonly LogicalOperation m_logicalOperation;
75 72
76 73 int m_pending;
77 74 int m_next;
78 75
79 76 public ArrayMapper(TSrc[] source, Func<TSrc, TDst> transform, int threads)
80 77 : base(threads) {
81 78
82 79 Debug.Assert (source != null);
83 80 Debug.Assert( transform != null);
84 81
85 82 m_next = 0;
86 83 m_source = source;
87 84 m_dest = new TDst[source.Length];
88 85 m_pending = source.Length;
89 86 m_transform = transform;
90 87 m_logicalOperation = TraceContext.Instance.CurrentOperation;
91 88
92 89 m_promise.Anyway(Dispose);
93 90
94 91 InitPool();
95 92 }
96 93
97 94 public Promise<TDst[]> Promise {
98 95 get {
99 96 return m_promise;
100 97 }
101 98 }
102 99
103 100 protected override void Worker() {
104 101 TraceContext.Instance.EnterLogicalOperation(m_logicalOperation,false);
105 102 try {
106 103 base.Worker();
107 104 } finally {
108 105 TraceContext.Instance.Leave();
109 106 }
110 107 }
111 108
112 109 protected override bool TryDequeue(out int unit) {
113 110 unit = Interlocked.Increment(ref m_next) - 1;
114 111 return unit < m_source.Length;
115 112 }
116 113
117 114 protected override void InvokeUnit(int unit) {
118 115 try {
119 116 m_dest[unit] = m_transform(m_source[unit]);
120 117 var pending = Interlocked.Decrement(ref m_pending);
121 118 if (pending == 0)
122 119 m_promise.Resolve(m_dest);
123 120 } catch (Exception e) {
124 121 m_promise.Reject(e);
125 122 }
126 123 }
127 124 }
128 125
129 126 public static IPromise<TDst[]> ParallelMap<TSrc, TDst> (this TSrc[] source, Func<TSrc,TDst> transform, int threads) {
130 127 if (source == null)
131 128 throw new ArgumentNullException("source");
132 129 if (transform == null)
133 130 throw new ArgumentNullException("transform");
134 131
135 132 var mapper = new ArrayMapper<TSrc, TDst>(source, transform, threads);
136 133 return mapper.Promise;
137 134 }
138 135
139 136 public static IPromise<int> ParallelForEach<TSrc>(this TSrc[] source, Action<TSrc> action, int threads) {
140 137 if (source == null)
141 138 throw new ArgumentNullException("source");
142 139 if (action == null)
143 140 throw new ArgumentNullException("action");
144 141
145 142 var iter = new ArrayIterator<TSrc>(source, action, threads);
146 143 return iter.Promise;
147 144 }
148 145
149 public static IPromise<TDst[]> ChainedMap<TSrc, TDst>(this TSrc[] source, ResultMapper<TSrc, IPromise<TDst>> transform, int threads) {
146 public static IPromise<TDst[]> ChainedMap<TSrc, TDst>(this TSrc[] source, Func<TSrc, IPromise<TDst>> transform, int threads) {
150 147 if (source == null)
151 148 throw new ArgumentNullException("source");
152 149 if (transform == null)
153 150 throw new ArgumentNullException("transform");
154 151 if (threads <= 0)
155 throw new ArgumentOutOfRangeException("Threads number must be greater then zero");
152 throw new ArgumentOutOfRangeException("threads","Threads number must be greater then zero");
156 153
157 154 if (source.Length == 0)
158 155 return Promise<TDst[]>.ResultToPromise(new TDst[0]);
159 156
160 157 var promise = new Promise<TDst[]>();
161 158 var res = new TDst[source.Length];
162 159 var pending = source.Length;
163 160
164 161 object locker = new object();
165 162 int slots = threads;
166 163
167 164 // Analysis disable AccessToDisposedClosure
168 165 AsyncPool.InvokeNewThread(() => {
169 166 for (int i = 0; i < source.Length; i++) {
170 167 if(promise.IsResolved)
171 168 break; // stop processing in case of error or cancellation
172 169 var idx = i;
173 170
174 171 if (Interlocked.Decrement(ref slots) < 0) {
175 172 lock(locker) {
176 173 while(slots < 0)
177 174 Monitor.Wait(locker);
178 175 }
179 176 }
180 177
181 178 try {
182 179 transform(source[i])
183 180 .Anyway(() => {
184 181 Interlocked.Increment(ref slots);
185 182 lock (locker) {
186 183 Monitor.Pulse(locker);
187 184 }
188 185 })
189 186 .Last(
190 187 x => {
191 188 res[idx] = x;
192 189 var left = Interlocked.Decrement(ref pending);
193 190 if (left == 0)
194 191 promise.Resolve(res);
195 192 },
196 193 promise.Reject
197 194 );
198 195
199 196 } catch (Exception e) {
200 197 promise.Reject(e);
201 198 }
202 199 }
203 200 return 0;
204 201 });
205 202
206 203 return promise;
207 204 }
208 205
209 206 }
210 207 }
@@ -1,914 +1,908
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Reflection;
4 using System.Diagnostics;
5 4 using System.Threading;
6 5 using Implab.Parallels;
7 6
8 7 namespace Implab {
9 8
10 public delegate void ErrorHandler(Exception e);
11 public delegate T ErrorHandler<out T>(Exception e);
12 public delegate void ResultHandler<in T>(T result);
13 public delegate TNew ResultMapper<in TSrc,out TNew>(TSrc result);
14
15 9 /// <summary>
16 10 /// Класс для асинхронного получения результатов. Так называемое "обещание".
17 11 /// </summary>
18 12 /// <typeparam name="T">Тип получаемого результата</typeparam>
19 13 /// <remarks>
20 14 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции,
21 15 /// клиент получив такое обещание может установить ряд обратных вызово для получения
22 16 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para>
23 17 /// <para>
24 18 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на
25 19 /// данные события клиент должен использовать методы <c>Then</c>.
26 20 /// </para>
27 21 /// <para>
28 22 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой),
29 23 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о
30 24 /// выполнении обещания.
31 25 /// </para>
32 26 /// <para>
33 27 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался,
34 28 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном
35 29 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в
36 30 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении
37 31 /// обещания.
38 32 /// </para>
39 33 /// <para>
40 34 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать
41 35 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует
42 36 /// использовать соответствующую форму методе <c>Then</c>.
43 37 /// </para>
44 38 /// <para>
45 39 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать
46 40 /// только инициатор обещания иначе могут возникнуть противоречия.
47 41 /// </para>
48 42 /// </remarks>
49 43 public class Promise<T> : IPromise<T> {
50 44
51 45 protected struct HandlerDescriptor {
52 public ResultHandler<T> resultHandler;
53 public ErrorHandler<T> errorHandler;
46 public Action<T> resultHandler;
47 public Func<Exception,T> errorHandler;
54 48 public Action cancellHandler;
55 49 public Promise<T> medium;
56 50
57 51 public void Resolve(T result) {
58 52 if (resultHandler != null) {
59 53 try {
60 54 resultHandler(result);
61 55 } catch (Exception e) {
62 56 Reject(e);
63 57 return;
64 58 }
65 59 }
66 60 if (medium != null)
67 61 medium.Resolve(result);
68 62 }
69 63
70 64 public void Reject(Exception err) {
71 65 if (errorHandler != null) {
72 66 try {
73 67 var res = errorHandler(err);
74 68 if (medium != null)
75 69 medium.Resolve(res);
76 70 /*} catch (TransientPromiseException err2) {
77 71 if (medium != null)
78 72 medium.Reject(err2.InnerException);*/
79 73 } catch (Exception err2) {
80 74 if (medium != null)
81 75 medium.Reject(err2);
82 76 }
83 77 } else if (medium != null)
84 78 medium.Reject(err);
85 79 }
86 80
87 81 public void Cancel() {
88 82 if (cancellHandler != null) {
89 83 try {
90 84 cancellHandler();
91 85 } catch (Exception err) {
92 86 Reject(err);
93 87 return;
94 88 }
95 89 }
96 90 if (medium != null)
97 91 medium.Cancel();
98 92 }
99 93 }
100 94
101 95 const int UNRESOLVED_SATE = 0;
102 96 const int TRANSITIONAL_STATE = 1;
103 97 const int SUCCEEDED_STATE = 2;
104 98 const int REJECTED_STATE = 3;
105 99 const int CANCELLED_STATE = 4;
106 100
107 readonly bool m_cancellable;
108
109 int m_childrenCount = 0;
101 int m_childrenCount;
110 102 int m_state;
111 103 T m_result;
112 104 Exception m_error;
113 105
114 106 readonly MTQueue<HandlerDescriptor> m_handlers = new MTQueue<HandlerDescriptor>();
115 107
116 108 public Promise() {
117 m_cancellable = true;
118 109 }
119 110
120 public Promise(IPromise parent, bool cancellable) {
121 m_cancellable = cancellable;
111 public Promise(IPromise parent) {
122 112 if (parent != null)
123 113 AddHandler(
124 114 null,
125 115 null,
126 116 () => {
127 117 if (parent.IsExclusive)
128 118 parent.Cancel();
129 119 },
130 120 null
131 121 );
132 122 }
133 123
134 124 bool BeginTransit() {
135 125 return UNRESOLVED_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, UNRESOLVED_SATE);
136 126 }
137 127
138 128 void CompleteTransit(int state) {
139 129 if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, state, TRANSITIONAL_STATE))
140 130 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
141 131 }
142 132
143 133 void WaitTransition() {
144 134 while (m_state == TRANSITIONAL_STATE) {
145 135 Thread.MemoryBarrier();
146 136 }
147 137 }
148 138
149 139 public bool IsResolved {
150 140 get {
151 141 Thread.MemoryBarrier();
152 142 return m_state > 1;
153 143 }
154 144 }
155 145
156 146 public bool IsCancelled {
157 147 get {
158 148 Thread.MemoryBarrier();
159 149 return m_state == CANCELLED_STATE;
160 150 }
161 151 }
162 152
163 153 public Type PromiseType {
164 154 get { return typeof(T); }
165 155 }
166 156
167 157 /// <summary>
168 158 /// Выполняет обещание, сообщая об успешном выполнении.
169 159 /// </summary>
170 160 /// <param name="result">Результат выполнения.</param>
171 161 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
172 162 public void Resolve(T result) {
173 163 if (BeginTransit()) {
174 164 m_result = result;
175 165 CompleteTransit(SUCCEEDED_STATE);
176 166 OnStateChanged();
177 167 } else {
178 168 WaitTransition();
179 169 if (m_state != CANCELLED_STATE)
180 170 throw new InvalidOperationException("The promise is already resolved");
181 171 }
182 172 }
183 173
184 174 /// <summary>
185 175 /// Выполняет обещание, сообщая об успешном выполнении. Результатом выполнения будет пустое значения.
186 176 /// </summary>
187 177 /// <remarks>
188 178 /// Данный вариант удобен в случаях, когда интересен факт выполнения операции, нежели полученное значение.
189 179 /// </remarks>
190 180 public void Resolve() {
191 181 Resolve(default(T));
192 182 }
193 183
194 184 /// <summary>
195 185 /// Выполняет обещание, сообщая об ошибке
196 186 /// </summary>
197 187 /// <remarks>
198 188 /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков
199 189 /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные
200 190 /// будут проигнорированы.
201 191 /// </remarks>
202 192 /// <param name="error">Исключение возникшее при выполнении операции</param>
203 193 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
204 194 public void Reject(Exception error) {
205 195 if (BeginTransit()) {
206 196 m_error = error is TransientPromiseException ? error.InnerException : error;
207 197 CompleteTransit(REJECTED_STATE);
208 198 OnStateChanged();
209 199 } else {
210 200 WaitTransition();
211 201 if (m_state == SUCCEEDED_STATE)
212 202 throw new InvalidOperationException("The promise is already resolved");
213 203 }
214 204 }
215 205
216 206 /// <summary>
217 207 /// Отменяет операцию, если это возможно.
218 208 /// </summary>
219 209 /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks>
220 210 public void Cancel() {
221 if (m_cancellable && BeginTransit()) {
211 if (BeginTransit()) {
222 212 CompleteTransit(CANCELLED_STATE);
223 213 OnStateChanged();
224 214 }
225 215 }
226 216
227 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel) {
217 public IPromise<T> Then(Action<T> success, Func<Exception,T> error, Action cancel) {
228 218 if (success == null && error == null && cancel == null)
229 219 return this;
230 220
231 var medium = new Promise<T>(this, true);
221 var medium = new Promise<T>(this);
232 222
233 223 AddHandler(success, error, cancel, medium);
234 224
235 225 return medium;
236 226 }
237 227
238 228 /// <summary>
239 229 /// Adds new handlers to this promise.
240 230 /// </summary>
241 231 /// <param name="success">The handler of the successfully completed operation.
242 232 /// This handler will recieve an operation result as a parameter.</param>
243 233 /// <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>
244 234 /// <returns>The new promise chained to this one.</returns>
245 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error) {
235 public IPromise<T> Then(Action<T> success, Func<Exception,T> error) {
246 236 if (success == null && error == null)
247 237 return this;
248 238
249 var medium = new Promise<T>(this, true);
239 var medium = new Promise<T>(this);
250 240
251 241 AddHandler(success, error, null, medium);
252 242
253 243 return medium;
254 244 }
255 245
256 246
257 247
258 248
259 public IPromise<T> Then(ResultHandler<T> success) {
249 public IPromise<T> Then(Action<T> success) {
260 250 if (success == null)
261 251 return this;
262 252
263 var medium = new Promise<T>(this, true);
253 var medium = new Promise<T>(this);
264 254
265 255 AddHandler(success, null, null, medium);
266 256
267 257 return medium;
268 258 }
269 259
270 260 /// <summary>
271 261 /// Последний обработчик в цепочки обещаний.
272 262 /// </summary>
273 263 /// <param name="success"></param>
274 264 /// <param name="error"></param>
275 265 /// <param name="cancel"></param>
276 266 /// <remarks>
277 267 /// <para>
278 268 /// Данный метод не создает связанного с текущим обещания и предназначен для окончания
279 269 /// фсинхронной цепочки.
280 270 /// </para>
281 271 /// <para>
282 272 /// Если данный метод вызвать несколько раз, либо добавить другие обработчики, то цепочка
283 273 /// не будет одиночной <see cref="IsExclusive"/> и, как следствие, будет невозможна отмена
284 274 /// всей цепи обещаний снизу (с самого последнего обещания).
285 275 /// </para>
286 276 /// </remarks>
287 public void Last(ResultHandler<T> success, ErrorHandler error, Action cancel) {
277 public void Last(Action<T> success, Action<Exception> error, Action cancel) {
288 278 if (success == null && error == null && cancel == null)
289 279 return;
290 280
291 ErrorHandler<T> errorHandler = null;
281 Func<Exception,T> errorHandler = null;
292 282 if (error != null)
293 283 errorHandler = err => {
294 284 error(err);
295 285 return default(T);
296 286 };
297 287 AddHandler(success, errorHandler, cancel, null);
298 288 }
299 289
300 public void Last(ResultHandler<T> success, ErrorHandler error) {
290 public void Last(Action<T> success, Action<Exception> error) {
301 291 Last(success, error, null);
302 292 }
303 293
304 public void Last(ResultHandler<T> success) {
294 public void Last(Action<T> success) {
305 295 Last(success, null, null);
306 296 }
307 297
308 public IPromise Error(ErrorHandler error) {
298 public IPromise Error(Action<Exception> error) {
309 299 if (error == null)
310 300 return this;
311 301
312 var medium = new Promise<T>(this, true);
302 var medium = new Promise<T>(this);
313 303
314 304 AddHandler(
315 305 null,
316 306 e => {
317 307 error(e);
318 308 return default(T);
319 309 },
320 310 null,
321 311 medium
322 312 );
323 313
324 314 return medium;
325 315 }
326 316
327 317 /// <summary>
328 318 /// Handles error and allows to keep the promise.
329 319 /// </summary>
330 320 /// <remarks>
331 321 /// If the specified handler throws an exception, this exception will be used to reject the promise.
332 322 /// </remarks>
333 323 /// <param name="handler">The error handler which returns the result of the promise.</param>
334 324 /// <returns>New promise.</returns>
335 public IPromise<T> Error(ErrorHandler<T> handler) {
325 public IPromise<T> Error(Func<Exception,T> handler) {
336 326 if (handler == null)
337 327 return this;
338 328
339 var medium = new Promise<T>(this, true);
329 var medium = new Promise<T>(this);
340 330
341 331 AddHandler(null, handler, null, medium);
342 332
343 333 return medium;
344 334 }
345 335
346 336 /// <summary>
347 337 /// Позволяет преобразовать результат выполения операции к новому типу.
348 338 /// </summary>
349 339 /// <typeparam name="TNew">Новый тип результата.</typeparam>
350 340 /// <param name="mapper">Преобразование результата к новому типу.</param>
351 341 /// <param name="error">Обработчик ошибки. Данный обработчик получит
352 342 /// исключение возникшее при выполнении операции.</param>
353 343 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
354 344 /// <param name = "cancel"></param>
355 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<TNew> error, Action cancel) {
345 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper, Func<Exception,TNew> error, Action cancel) {
356 346 Safe.ArgumentNotNull(mapper, "mapper");
357 347
358 348 // создаем прицепленное обещание
359 var medium = new Promise<TNew>(this, true);
349 var medium = new Promise<TNew>(this);
360 350
361 ResultHandler<T> resultHandler = result => medium.Resolve(mapper(result));
362 ErrorHandler<T> errorHandler;
351 Action<T> resultHandler = result => medium.Resolve(mapper(result));
352 Func<Exception,T> errorHandler;
363 353 if (error != null)
364 354 errorHandler = e => {
365 355 try {
366 356 medium.Resolve(error(e));
367 357 } catch (Exception e2) {
368 358 // в случае ошибки нужно передать исключение дальше по цепочке
369 359 medium.Reject(e2);
370 360 }
371 361 return default(T);
372 362 };
373 363 else
374 364 errorHandler = e => {
375 365 medium.Reject(e);
376 366 return default(T);
377 367 };
378 368
379 369 Action cancelHandler;
380 370 if (cancel != null)
381 371 cancelHandler = () => {
382 372 cancel();
383 373 medium.Cancel();
384 374 };
385 375 else
386 376 cancelHandler = medium.Cancel;
387 377
388 378
389 379 AddHandler(
390 380 resultHandler,
391 381 errorHandler,
392 382 cancelHandler,
393 383 null
394 384 );
395 385
396 386 return medium;
397 387 }
398 388
399 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<TNew> error) {
389 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper, Func<Exception,TNew> error) {
400 390 return Then(mapper, error, null);
401 391 }
402 392
403 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper) {
393 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper) {
404 394 return Then(mapper, null, null);
405 395 }
406 396
407 397 /// <summary>
408 398 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после
409 399 /// выполнения текущей, а результат текущей операции может быть использован для инициализации
410 400 /// новой операции.
411 401 /// </summary>
412 402 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam>
413 403 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param>
414 404 /// <param name="error">Обработчик ошибки. Данный обработчик получит
415 405 /// исключение возникшее при выполнении текуещй операции.</param>
416 406 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
417 407 /// <param name = "cancel"></param>
418 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained, ErrorHandler<IPromise<TNew>> error, Action cancel) {
408 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained, Func<Exception,IPromise<TNew>> error, Action cancel) {
419 409
420 410 Safe.ArgumentNotNull(chained, "chained");
421 411
422 412 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
423 413 // создать посредника, к которому будут подвызяваться следующие обработчики.
424 414 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы
425 415 // передать через него результаты работы.
426 var medium = new Promise<TNew>(this, true);
416 var medium = new Promise<TNew>(this);
427 417
428 ResultHandler<T> resultHandler = delegate(T result) {
418 Action<T> resultHandler = delegate(T result) {
429 419 if (medium.IsCancelled)
430 420 return;
431 421
432 422 var promise = chained(result);
433 423
434 424 promise.Last(
435 425 medium.Resolve,
436 426 medium.Reject,
437 427 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
438 428 );
439 429
440 430 // notify chained operation that it's not needed anymore
441 431 // порядок вызова Then, Cancelled важен, поскольку от этого
442 432 // зависит IsExclusive
443 medium.Cancelled(() => {
433 medium.Last(
434 null,
435 null,
436 () => {
444 437 if (promise.IsExclusive)
445 438 promise.Cancel();
446 });
439 }
440 );
447 441 };
448 442
449 ErrorHandler<T> errorHandler;
443 Func<Exception,T> errorHandler;
450 444
451 445 if (error != null)
452 446 errorHandler = delegate(Exception e) {
453 447 try {
454 448 var promise = error(e);
455 449
456 450 promise.Last(
457 451 medium.Resolve,
458 452 medium.Reject,
459 453 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
460 454 );
461 455
462 456 // notify chained operation that it's not needed anymore
463 457 // порядок вызова Then, Cancelled важен, поскольку от этого
464 458 // зависит IsExclusive
465 459 medium.Cancelled(() => {
466 460 if (promise.IsExclusive)
467 461 promise.Cancel();
468 462 });
469 463 } catch (Exception e2) {
470 464 medium.Reject(e2);
471 465 }
472 466 return default(T);
473 467 };
474 468 else
475 469 errorHandler = err => {
476 470 medium.Reject(err);
477 471 return default(T);
478 472 };
479 473
480 474
481 475 Action cancelHandler;
482 476 if (cancel != null)
483 477 cancelHandler = () => {
484 478 if (cancel != null)
485 479 cancel();
486 480 medium.Cancel();
487 481 };
488 482 else
489 483 cancelHandler = medium.Cancel;
490 484
491 485 AddHandler(
492 486 resultHandler,
493 487 errorHandler,
494 488 cancelHandler,
495 489 null
496 490 );
497 491
498 492 return medium;
499 493 }
500 494
501 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained, ErrorHandler<IPromise<TNew>> error) {
495 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained, Func<Exception,IPromise<TNew>> error) {
502 496 return Chain(chained, error, null);
503 497 }
504 498
505 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained) {
499 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained) {
506 500 return Chain(chained, null, null);
507 501 }
508 502
509 503 public IPromise<T> Cancelled(Action handler) {
510 var medium = new Promise<T>(this,true);
504 var medium = new Promise<T>(this);
511 505 AddHandler(null, null, handler, medium);
512 506 return medium;
513 507 }
514 508
515 509 /// <summary>
516 510 /// Adds the specified handler for all cases (success, error, cancel)
517 511 /// </summary>
518 512 /// <param name="handler">The handler that will be called anyway</param>
519 513 /// <returns>self</returns>
520 514 public IPromise<T> Anyway(Action handler) {
521 515 Safe.ArgumentNotNull(handler, "handler");
522 516
523 517 AddHandler(
524 518 x => handler(),
525 519 e => {
526 520 handler();
527 521 throw new TransientPromiseException(e);
528 522 },
529 523 handler,
530 524 null
531 525 );
532 526 return this;
533 527 }
534 528
535 529 /// <summary>
536 530 /// Преобразует результат обещания к нужному типу
537 531 /// </summary>
538 532 /// <typeparam name="T2"></typeparam>
539 533 /// <returns></returns>
540 534 public IPromise<T2> Cast<T2>() {
541 535 return Then(x => (T2)(object)x, null);
542 536 }
543 537
544 538 /// <summary>
545 539 /// Дожидается отложенного обещания и в случае успеха, возвращает
546 540 /// его, результат, в противном случае бросает исключение.
547 541 /// </summary>
548 542 /// <remarks>
549 543 /// <para>
550 544 /// Если ожидание обещания было прервано по таймауту, это не значит,
551 545 /// что обещание было отменено или что-то в этом роде, это только
552 546 /// означает, что мы его не дождались, однако все зарегистрированные
553 547 /// обработчики, как были так остались и они будут вызваны, когда
554 548 /// обещание будет выполнено.
555 549 /// </para>
556 550 /// <para>
557 551 /// Такое поведение вполне оправдано поскольку таймаут может истечь
558 552 /// в тот момент, когда началась обработка цепочки обработчиков, и
559 553 /// к тому же текущее обещание может стоять в цепочке обещаний и его
560 554 /// отклонение может привести к непрогнозируемому результату.
561 555 /// </para>
562 556 /// </remarks>
563 557 /// <param name="timeout">Время ожидания</param>
564 558 /// <returns>Результат выполнения обещания</returns>
565 559 public T Join(int timeout) {
566 560 var evt = new ManualResetEvent(false);
567 561 Anyway(() => evt.Set());
568 562
569 563 if (!evt.WaitOne(timeout, true))
570 564 throw new TimeoutException();
571 565
572 566 switch (m_state) {
573 567 case SUCCEEDED_STATE:
574 568 return m_result;
575 569 case CANCELLED_STATE:
576 570 throw new OperationCanceledException();
577 571 case REJECTED_STATE:
578 572 throw new TargetInvocationException(m_error);
579 573 default:
580 574 throw new ApplicationException(String.Format("Invalid promise state {0}", m_state));
581 575 }
582 576 }
583 577
584 578 public T Join() {
585 579 return Join(Timeout.Infinite);
586 580 }
587 581
588 void AddHandler(ResultHandler<T> success, ErrorHandler<T> error, Action cancel, Promise<T> medium) {
582 void AddHandler(Action<T> success, Func<Exception,T> error, Action cancel, Promise<T> medium) {
589 583 if (success != null || error != null)
590 584 Interlocked.Increment(ref m_childrenCount);
591 585
592 586 var handler = new HandlerDescriptor {
593 587 resultHandler = success,
594 588 errorHandler = error,
595 589 cancellHandler = cancel,
596 590 medium = medium
597 591 };
598 592
599 593 bool queued;
600 594
601 595 if (!IsResolved) {
602 596 m_handlers.Enqueue(handler);
603 597 queued = true;
604 598 } else {
605 599 // the promise is in resolved state, just invoke the handled with minimum overhead
606 600 queued = false;
607 601 InvokeHandler(handler);
608 602 }
609 603
610 604 if (queued && IsResolved && m_handlers.TryDequeue(out handler))
611 605 // if the promise have been resolved while we was adding handler to the queue
612 606 // we can't guarantee that someone is still processing it
613 607 // therefore we will fetch a handler from the queue and execute it
614 608 // note that fetched handler may be not the one that we have added
615 609 // even we can fetch no handlers at all :)
616 610 InvokeHandler(handler);
617 611 }
618 612
619 613 protected virtual void InvokeHandler(HandlerDescriptor handler) {
620 614 switch (m_state) {
621 615 case SUCCEEDED_STATE:
622 616 handler.Resolve(m_result);
623 617 break;
624 618 case REJECTED_STATE:
625 619 handler.Reject(m_error);
626 620 break;
627 621 case CANCELLED_STATE:
628 622 handler.Cancel();
629 623 break;
630 624 default:
631 625 // do nothing
632 626 return;
633 627 }
634 628 }
635 629
636 630 void OnStateChanged() {
637 631 HandlerDescriptor handler;
638 632 while (m_handlers.TryDequeue(out handler))
639 633 InvokeHandler(handler);
640 634 }
641 635
642 636 public bool IsExclusive {
643 637 get {
644 638 return m_childrenCount <= 1;
645 639 }
646 640 }
647 641
648 642 /// <summary>
649 643 /// Объединяет несколько обещаний в одно, результатом которого является массив результатов других обещаний.
650 644 /// Если хотябы одно из переданных обещаний не будет выполнено, то новое обещение тоже не будет выполнено.
651 645 /// При отмене нового обещания, переданные обещания также будут отменены, если никто больше на них не подписан.
652 646 /// </summary>
653 647 /// <param name="promises">Список обещаний. Если список пустой, то результирующее обещание возвращается уже выполненным.</param>
654 648 /// <returns>Обещание объединяющее в себе результат переданных обещаний.</returns>
655 649 /// <exception cref="ArgumentNullException"><paramref name="promises"/> не может быть null</exception>
656 650 public static IPromise<T[]> CreateComposite(IList<IPromise<T>> promises) {
657 651 if (promises == null)
658 652 throw new ArgumentNullException();
659 653
660 654 // создаем аккумулятор для результатов и результирующее обещание
661 655 var result = new T[promises.Count];
662 656 var promise = new Promise<T[]>();
663 657
664 658 // special case
665 659 if (promises.Count == 0) {
666 660 promise.Resolve(result);
667 661 return promise;
668 662 }
669 663
670 664 int pending = promises.Count;
671 665
672 666 for (int i = 0; i < promises.Count; i++) {
673 667 var dest = i;
674 668
675 669 if (promises[i] != null) {
676 670 promises[i].Then(
677 671 x => {
678 672 result[dest] = x;
679 673 if (Interlocked.Decrement(ref pending) == 0)
680 674 promise.Resolve(result);
681 675 },
682 676 e => {
683 677 promise.Reject(e);
684 678 return default(T);
685 679 }
686 680 );
687 681 } else {
688 682 if (Interlocked.Decrement(ref pending) == 0)
689 683 promise.Resolve(result);
690 684 }
691 685 }
692 686
693 687 promise.Cancelled(
694 688 () => {
695 689 foreach (var d in promises)
696 690 if (d != null && d.IsExclusive)
697 691 d.Cancel();
698 692 }
699 693 );
700 694
701 695 return promise;
702 696 }
703 697
704 698 /// <summary>
705 699 /// Объединяет несколько обещаний в одно. Результирующее обещание будет выполнено при
706 700 /// выполнении всех указанных обещаний. При этом возвращаемые значения первичных обещаний
707 701 /// игнорируются.
708 702 /// </summary>
709 703 /// <param name="promises">Коллекция первичных обещаний, которые будут объеденены в одно.</param>
710 704 /// <returns>Новое обещание, объединяющее в себе переданные.</returns>
711 705 /// <remarks>
712 706 /// Если в коллекции встречаюься <c>null</c>, то они воспринимаются как выполненные обещания.
713 707 /// </remarks>
714 708 public static IPromise CreateComposite(ICollection<IPromise> promises) {
715 709 if (promises == null)
716 710 throw new ArgumentNullException();
717 711 if (promises.Count == 0)
718 712 return Promise<object>.ResultToPromise(null);
719 713
720 714 int countdown = promises.Count;
721 715
722 716 var result = new Promise<object>();
723 717
724 718 foreach (var d in promises) {
725 719 if (d == null) {
726 720 if (Interlocked.Decrement(ref countdown) == 0)
727 721 result.Resolve(null);
728 722 } else {
729 723 d.Then(() => {
730 724 if (Interlocked.Decrement(ref countdown) == 0)
731 725 result.Resolve(null);
732 726 });
733 727 }
734 728 }
735 729
736 730 result.Cancelled(() => {
737 731 foreach (var d in promises)
738 732 if (d != null && d.IsExclusive)
739 733 d.Cancel();
740 734 });
741 735
742 736 return result;
743 737 }
744 738
745 739 public static Promise<T> ResultToPromise(T result) {
746 740 var p = new Promise<T>();
747 741 p.Resolve(result);
748 742 return p;
749 743 }
750 744
751 745 public static Promise<T> ExceptionToPromise(Exception error) {
752 746 if (error == null)
753 747 throw new ArgumentNullException();
754 748
755 749 var p = new Promise<T>();
756 750 p.Reject(error);
757 751 return p;
758 752 }
759 753
760 754 #region IPromiseBase explicit implementation
761 755
762 IPromise IPromise.Then(Action success, ErrorHandler error, Action cancel) {
756 IPromise IPromise.Then(Action success, Action<Exception> error, Action cancel) {
763 757 return Then(
764 success != null ? new ResultHandler<T>(x => success()) : null,
765 error != null ? new ErrorHandler<T>(e => {
758 success != null ? new Action<T>(x => success()) : null,
759 error != null ? new Func<Exception,T>(e => {
766 760 error(e);
767 761 return default(T);
768 762 }) : null,
769 763 cancel
770 764 );
771 765 }
772 766
773 IPromise IPromise.Then(Action success, ErrorHandler error) {
767 IPromise IPromise.Then(Action success, Action<Exception> error) {
774 768 return Then(
775 success != null ? new ResultHandler<T>(x => success()) : null,
776 error != null ? new ErrorHandler<T>(e => {
769 success != null ? new Action<T>(x => success()) : null,
770 error != null ? new Func<Exception,T>(e => {
777 771 error(e);
778 772 return default(T);
779 773 }) : null
780 774 );
781 775 }
782 776
783 777 IPromise IPromise.Then(Action success) {
784 778 Safe.ArgumentNotNull(success, "success");
785 779 return Then(x => success());
786 780 }
787 781
788 IPromise IPromise.Chain(Func<IPromise> chained, ErrorHandler<IPromise> error, Action cancel) {
782 IPromise IPromise.Chain(Func<IPromise> chained, Func<Exception,IPromise> error, Action cancel) {
789 783 return ChainNoResult(chained, error, cancel);
790 784 }
791 785
792 IPromise ChainNoResult(Func<IPromise> chained, ErrorHandler<IPromise> error, Action cancel) {
786 IPromise ChainNoResult(Func<IPromise> chained, Func<Exception,IPromise> error, Action cancel) {
793 787 Safe.ArgumentNotNull(chained, "chained");
794 788
795 var medium = new Promise<object>(this, true);
789 var medium = new Promise<object>(this);
796 790
797 ResultHandler<T> resultHandler = delegate(T result) {
791 Action<T> resultHandler = delegate {
798 792 if (medium.IsCancelled)
799 793 return;
800 794
801 795 var promise = chained();
802 796
803 797 promise.Last(
804 798 medium.Resolve,
805 799 medium.Reject,
806 800 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
807 801 );
808 802
809 803 // notify chained operation that it's not needed anymore
810 804 // порядок вызова Then, Cancelled важен, поскольку от этого
811 805 // зависит IsExclusive
812 806 medium.Cancelled(() => {
813 807 if (promise.IsExclusive)
814 808 promise.Cancel();
815 809 });
816 810 };
817 811
818 ErrorHandler<T> errorHandler;
812 Func<Exception,T> errorHandler;
819 813
820 814 if (error != null)
821 815 errorHandler = delegate(Exception e) {
822 816 try {
823 817 var promise = error(e);
824 818
825 819 promise.Last(
826 820 medium.Resolve,
827 821 medium.Reject,
828 822 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
829 823 );
830 824
831 825 // notify chained operation that it's not needed anymore
832 826 // порядок вызова Then, Cancelled важен, поскольку от этого
833 827 // зависит IsExclusive
834 828 medium.Cancelled(() => {
835 829 if (promise.IsExclusive)
836 830 promise.Cancel();
837 831 });
838 832 } catch (Exception e2) {
839 833 medium.Reject(e2);
840 834 }
841 835 return default(T);
842 836 };
843 837 else
844 838 errorHandler = err => {
845 839 medium.Reject(err);
846 840 return default(T);
847 841 };
848 842
849 843
850 844 Action cancelHandler;
851 845 if (cancel != null)
852 846 cancelHandler = () => {
853 847 if (cancel != null)
854 848 cancel();
855 849 medium.Cancel();
856 850 };
857 851 else
858 852 cancelHandler = medium.Cancel;
859 853
860 854 AddHandler(
861 855 resultHandler,
862 856 errorHandler,
863 857 cancelHandler,
864 858 null
865 859 );
866 860
867 861 return medium;
868 862 }
869 IPromise IPromise.Chain(Func<IPromise> chained, ErrorHandler<IPromise> error) {
863 IPromise IPromise.Chain(Func<IPromise> chained, Func<Exception,IPromise> error) {
870 864 return ChainNoResult(chained, error, null);
871 865 }
872 866 IPromise IPromise.Chain(Func<IPromise> chained) {
873 867 return ChainNoResult(chained, null, null);
874 868 }
875 869
876 870
877 void IPromise.Last(Action success, ErrorHandler error, Action cancel) {
871 void IPromise.Last(Action success, Action<Exception> error, Action cancel) {
878 872 Last(x => success(), error, cancel);
879 873 }
880 874
881 void IPromise.Last(Action success, ErrorHandler error) {
875 void IPromise.Last(Action success, Action<Exception> error) {
882 876 Last(x => success(), error, null);
883 877 }
884 878
885 879 void IPromise.Last(Action success) {
886 880 Last(x => success(), null, null);
887 881 }
888 882
889 IPromise IPromise.Error(ErrorHandler error) {
883 IPromise IPromise.Error(Action<Exception> error) {
890 884 return Error(error);
891 885 }
892 886
893 887 IPromise IPromise.Anyway(Action handler) {
894 888 return Anyway(handler);
895 889 }
896 890
897 891 IPromise IPromise.Cancelled(Action handler) {
898 892 return Cancelled(handler);
899 893 }
900 894
901 895 void IPromise.Join() {
902 896 Join();
903 897 }
904 898
905 899 void IPromise.Join(int timeout) {
906 900 Join(timeout);
907 901 }
908 902
909 903 #endregion
910 904
911 905
912 906
913 907 }
914 908 }
@@ -1,65 +1,83
1 1 using System.Threading;
2 2 using System;
3 3 #if NET_4_5
4 4 using System.Threading.Tasks;
5 5 #endif
6 6
7 7 namespace Implab {
8 8 public static class PromiseExtensions {
9 9 public static IPromise<T> DispatchToCurrentContext<T>(this IPromise<T> that) {
10 10 Safe.ArgumentNotNull(that, "that");
11 11 var context = SynchronizationContext.Current;
12 12 if (context == null)
13 13 return that;
14 14
15 var p = new SyncContextPromise<T>(context, that, true);
15 var p = new SyncContextPromise<T>(context, that);
16 16
17 17 that.Last(
18 18 p.Resolve,
19 19 p.Reject,
20 20 p.Cancel
21 21 );
22 22 return p;
23 23 }
24 24
25 25 public static IPromise<T> DispatchToContext<T>(this IPromise<T> that, SynchronizationContext context) {
26 26 Safe.ArgumentNotNull(that, "that");
27 27 Safe.ArgumentNotNull(context, "context");
28 28
29 var p = new SyncContextPromise<T>(context, that, true);
29 var p = new SyncContextPromise<T>(context, that);
30 30
31 31 that.Last(
32 32 p.Resolve,
33 33 p.Reject,
34 34 p.Cancel
35 35 );
36 36 return p;
37 37 }
38 38
39 /// <summary>
40 /// Ensures the dispatched.
41 /// </summary>
42 /// <returns>The dispatched.</returns>
43 /// <param name="that">That.</param>
44 /// <param name="head">Head.</param>
45 /// <param name="cleanup">Cleanup.</param>
46 /// <typeparam name="TPromise">The 1st type parameter.</typeparam>
47 /// <typeparam name="T">The 2nd type parameter.</typeparam>
48 public static TPromise EnsureDispatched<TPromise,T>(this TPromise that, IPromise<T> head, Action<T> cleanup) where TPromise : IPromise{
49 Safe.ArgumentNotNull(that, "that");
50 Safe.ArgumentNotNull(head, "head");
51
52 that.Last(null,null,() => head.Last(cleanup));
53
54 return that;
55 }
56
39 57 public static AsyncCallback AsyncCallback<T>(this Promise<T> that, Func<IAsyncResult,T> callback) {
40 58 Safe.ArgumentNotNull(that, "that");
41 59 Safe.ArgumentNotNull(callback, "callback");
42 60 return ar => {
43 61 try {
44 62 that.Resolve(callback(ar));
45 63 } catch (Exception err) {
46 64 that.Reject(err);
47 65 }
48 66 };
49 67 }
50 68
51 69 #if NET_4_5
52 70
53 71 public static Task<T> GetTask<T>(this IPromise<T> that) {
54 72 Safe.ArgumentNotNull(that, "that");
55 73 var tcs = new TaskCompletionSource<T>();
56 74
57 75 that.Last(tcs.SetResult, tcs.SetException, tcs.SetCanceled);
58 76
59 77 return tcs.Task;
60 78 }
61 79
62 80 #endif
63 81 }
64 82 }
65 83
@@ -1,22 +1,22
1 1 using System.Threading;
2 2
3 3 namespace Implab {
4 4 public class SyncContextPromise<T> : Promise<T> {
5 5 readonly SynchronizationContext m_context;
6 6
7 7 public SyncContextPromise(SynchronizationContext context) {
8 8 Safe.ArgumentNotNull(context, "context");
9 9 m_context = context;
10 10 }
11 11
12 public SyncContextPromise(SynchronizationContext context, IPromise parent, bool cancellable)
13 : base(parent, cancellable) {
12 public SyncContextPromise(SynchronizationContext context, IPromise parent)
13 : base(parent) {
14 14 Safe.ArgumentNotNull(context, "context");
15 15 m_context = context;
16 16 }
17 17 protected override void InvokeHandler(HandlerDescriptor handler) {
18 18 m_context.Post(x => base.InvokeHandler(handler),null);
19 19 }
20 20 }
21 21 }
22 22
General Comments 0
You need to be logged in to leave comments. Login now