##// END OF EJS Templates
promises refactoring
cin -
r106:d4e38929ce36 v2
parent child
Show More
@@ -0,0 +1,135
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Collections;
5
6 namespace Implab.Parallels {
7 public class MTCustomQueue<TNode> : IEnumerable<TNode> where TNode : MTCustomQueueNode<TNode> {
8 TNode m_first;
9 TNode m_last;
10
11 public void Enqueue(TNode next) {
12 Thread.MemoryBarrier();
13
14 var last = m_last;
15
16 // Interlocaked.CompareExchange implies Thread.MemoryBarrier();
17 // to ensure that the next node is completely constructed
18 while (last != Interlocked.CompareExchange(ref m_last, next, last))
19 last = m_last;
20
21 if (last != null)
22 last.next = next;
23 else
24 m_first = next;
25 }
26
27 public bool TryDequeue(out TNode node) {
28 TNode first;
29 TNode next;
30 node = null;
31
32 Thread.MemoryBarrier();
33 do {
34 first = m_first;
35 if (first == null)
36 return false;
37 next = first.next;
38 if (next == null) {
39 // this is the last element,
40 // then try to update the tail
41 if (first != Interlocked.CompareExchange(ref m_last, null, first)) {
42 // this is the race condition
43 if (m_last == null)
44 // the queue is empty
45 return false;
46 // tail has been changed, we need to restart
47 continue;
48 }
49
50 // tail succesfully updated and first.next will never be changed
51 // other readers will fail due to inconsistency m_last != m_fist && m_first.next == null
52 // however the parallel writer may update the m_first since the m_last is null
53
54 // so we need to fix inconsistency by setting m_first to null or if it has been
55 // updated by the writer already then we should just to give up
56 Interlocked.CompareExchange(ref m_first, null, first);
57 break;
58
59 }
60 if (first == Interlocked.CompareExchange(ref m_first, next, first))
61 // head succesfully updated
62 break;
63 } while (true);
64
65 node = first;
66 return true;
67 }
68
69 #region IEnumerable implementation
70
71 class Enumerator : IEnumerator<TNode> {
72 TNode m_current;
73 TNode m_first;
74
75 public Enumerator(TNode first) {
76 m_first = first;
77 }
78
79 #region IEnumerator implementation
80
81 public bool MoveNext() {
82 m_current = m_current == null ? m_first : m_current.next;
83 return m_current != null;
84 }
85
86 public void Reset() {
87 m_current = null;
88 }
89
90 object IEnumerator.Current {
91 get {
92 if (m_current == null)
93 throw new InvalidOperationException();
94 return m_current;
95 }
96 }
97
98 #endregion
99
100 #region IDisposable implementation
101
102 public void Dispose() {
103 }
104
105 #endregion
106
107 #region IEnumerator implementation
108
109 public TNode Current {
110 get {
111 if (m_current == null)
112 throw new InvalidOperationException();
113 return m_current;
114 }
115 }
116
117 #endregion
118 }
119
120 public IEnumerator<TNode> GetEnumerator() {
121 return new Enumerator(m_first);
122 }
123
124 #endregion
125
126 #region IEnumerable implementation
127
128 IEnumerator IEnumerable.GetEnumerator() {
129 return GetEnumerator();
130 }
131
132 #endregion
133 }
134 }
135
@@ -0,0 +1,6
1 namespace Implab.Parallels {
2 public class MTCustomQueueNode<TNode> where TNode : MTCustomQueueNode<TNode> {
3 public TNode next;
4 }
5 }
6
@@ -1,89 +1,95
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System.Linq;
4 using System.Text;
4 using System.Text;
5 using System.Windows.Forms;
5 using System.Windows.Forms;
6 using System.Diagnostics;
6 using System.Diagnostics;
7
7
8 namespace Implab.Fx
8 namespace Implab.Fx
9 {
9 {
10 public static class AnimationHelpers
10 public static class AnimationHelpers
11 {
11 {
12 public static Animation<TTarget> AnimateProperty<TTarget, TVal>(this Animation<TTarget> animation, Action<TTarget, TVal> setter, Func<TTarget, TVal> getter, TVal newValue, Func<TVal, TVal, int, int, TVal> fx) where TTarget : class
12 public static Animation<TTarget> AnimateProperty<TTarget, TVal>(this Animation<TTarget> animation, Action<TTarget, TVal> setter, Func<TTarget, TVal> getter, TVal newValue, Func<TVal, TVal, int, int, TVal> fx) where TTarget : class
13 {
13 {
14 if (animation == null)
14 if (animation == null)
15 throw new ArgumentNullException("animation");
15 throw new ArgumentNullException("animation");
16
16
17 TVal oldValue = getter(animation.Traget);
17 TVal oldValue = getter(animation.Traget);
18
18
19 animation.Step += (target, elaped, duration) =>
19 animation.Step += (target, elaped, duration) =>
20 {
20 {
21 var value = fx(oldValue, newValue, elaped, duration);
21 var value = fx(oldValue, newValue, elaped, duration);
22 setter(target, value);
22 setter(target, value);
23 };
23 };
24
24
25 return animation;
25 return animation;
26 }
26 }
27
27
28 public static Animation<T> AnimateTransparency<T>(this T ctl, float newValue) where T : Form
28 public static Animation<T> AnimateTransparency<T>(this T ctl, float newValue) where T : Form
29 {
29 {
30 var anim = new Animation<T>(ctl);
30 var anim = new Animation<T>(ctl);
31
31
32 anim.AnimateProperty(
32 anim.AnimateProperty(
33 (target, value) => target.Opacity = value,
33 (target, value) => target.Opacity = value,
34 target => target.Opacity,
34 target => target.Opacity,
35 newValue,
35 newValue,
36 (ov, nv, el, du) => ov + ((float)el / du) * (nv - ov)
36 (ov, nv, el, du) => ov + ((float)el / du) * (nv - ov)
37 );
37 );
38 return anim;
38 return anim;
39 }
39 }
40
40
41 public static IPromise<T> CloseFadeOut<T>(this T ctl) where T : Form
41 public static IPromise<T> CloseFadeOut<T>(this T ctl) where T : Form
42 {
42 {
43 var anim = ctl.AnimateTransparency(0);
43 var anim = ctl.AnimateTransparency(0);
44
44
45 return anim.Play().DispatchToControl(ctl).Then(frm => frm.Close());
45 return anim
46 .Play()
47 .DispatchToControl(ctl)
48 .Then(frm => {
49 frm.Close();
50 return frm;
51 });
46 }
52 }
47
53
48 public static IPromise<T> OverlayFadeIn<T>(this Form that, T overlay) where T : Form
54 public static IPromise<T> OverlayFadeIn<T>(this Form that, T overlay) where T : Form
49 {
55 {
50 if (that == null)
56 if (that == null)
51 throw new ArgumentNullException("that");
57 throw new ArgumentNullException("that");
52 if (overlay == null)
58 if (overlay == null)
53 throw new ArgumentNullException("overlay");
59 throw new ArgumentNullException("overlay");
54
60
55 // setup overlay
61 // setup overlay
56 overlay.Opacity = 0;
62 overlay.Opacity = 0;
57 overlay.FormBorderStyle = FormBorderStyle.None;
63 overlay.FormBorderStyle = FormBorderStyle.None;
58 overlay.ShowInTaskbar = false;
64 overlay.ShowInTaskbar = false;
59
65
60 that.AddOwnedForm(overlay);
66 that.AddOwnedForm(overlay);
61
67
62 EventHandler handler = (object sender, EventArgs args) =>
68 EventHandler handler = (object sender, EventArgs args) =>
63 {
69 {
64 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
70 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
65 };
71 };
66
72
67 // attach handlers
73 // attach handlers
68 that.Move += handler;
74 that.Move += handler;
69 that.Resize += handler;
75 that.Resize += handler;
70 that.Shown += handler;
76 that.Shown += handler;
71
77
72 // remove handlers to release overlay
78 // remove handlers to release overlay
73 overlay.FormClosed += (sender, args) =>
79 overlay.FormClosed += (sender, args) =>
74 {
80 {
75 that.Move -= handler;
81 that.Move -= handler;
76 that.Resize -= handler;
82 that.Resize -= handler;
77 that.Shown -= handler;
83 that.Shown -= handler;
78 };
84 };
79
85
80 overlay.Show(that);
86 overlay.Show(that);
81 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
87 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
82
88
83 return overlay
89 return overlay
84 .AnimateTransparency(1)
90 .AnimateTransparency(1)
85 .Play()
91 .Play()
86 .DispatchToControl(overlay);
92 .DispatchToControl(overlay);
87 }
93 }
88 }
94 }
89 }
95 }
@@ -1,30 +1,30
1 using System.Windows.Forms;
1 using System.Windows.Forms;
2 using System;
2 using System;
3
3
4
4
5 namespace Implab.Fx {
5 namespace Implab.Fx {
6 public class ControlBoundPromise<T> : Promise<T> {
6 public class ControlBoundPromise<T> : Promise<T> {
7 readonly Control m_target;
7 readonly Control m_target;
8
8
9 public ControlBoundPromise(Control target) {
9 public ControlBoundPromise(Control target) {
10 Safe.ArgumentNotNull(target, "target");
10 Safe.ArgumentNotNull(target, "target");
11
11
12 m_target = target;
12 m_target = target;
13 }
13 }
14
14
15 public ControlBoundPromise(Control target, IPromise parent)
15 public ControlBoundPromise(Control target, IPromise parent)
16 : base(parent) {
16 : base(parent) {
17 Safe.ArgumentNotNull(target, "target");
17 Safe.ArgumentNotNull(target, "target");
18
18
19 m_target = target;
19 m_target = target;
20 }
20 }
21
21
22 protected override void InvokeHandler(HandlerDescriptor handler) {
22 protected override void InvokeHandler(AbstractHandler handler) {
23 if (m_target.InvokeRequired)
23 if (m_target.InvokeRequired)
24 m_target.BeginInvoke(new Action<HandlerDescriptor>(base.InvokeHandler), handler);
24 m_target.BeginInvoke(new Action<AbstractHandler>(base.InvokeHandler), handler);
25 else
25 else
26 base.InvokeHandler(handler);
26 base.InvokeHandler(handler);
27 }
27 }
28 }
28 }
29 }
29 }
30
30
@@ -1,449 +1,452
1 using System;
1 using System;
2 using System.Reflection;
2 using System.Reflection;
3 using System.Threading;
3 using System.Threading;
4 using Implab.Parallels;
4 using Implab.Parallels;
5
5
6 #if MONO
6 #if MONO
7
7
8 using NUnit.Framework;
8 using NUnit.Framework;
9 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
9 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
10 using TestMethod = NUnit.Framework.TestAttribute;
10 using TestMethod = NUnit.Framework.TestAttribute;
11
11
12 #else
12 #else
13
13
14 using Microsoft.VisualStudio.TestTools.UnitTesting;
14 using Microsoft.VisualStudio.TestTools.UnitTesting;
15
15
16 #endif
16 #endif
17
17
18 namespace Implab.Test {
18 namespace Implab.Test {
19 [TestClass]
19 [TestClass]
20 public class AsyncTests {
20 public class AsyncTests {
21 [TestMethod]
21 [TestMethod]
22 public void ResolveTest() {
22 public void ResolveTest() {
23 int res = -1;
23 int res = -1;
24 var p = new Promise<int>();
24 var p = new Promise<int>();
25 p.Then(x => res = x);
25 p.Then(x => res = x);
26 p.Resolve(100);
26 p.Resolve(100);
27
27
28 Assert.AreEqual(100, res);
28 Assert.AreEqual(100, res);
29 }
29 }
30
30
31 [TestMethod]
31 [TestMethod]
32 public void RejectTest() {
32 public void RejectTest() {
33 int res = -1;
33 int res = -1;
34 Exception err = null;
34 Exception err = null;
35
35
36 var p = new Promise<int>();
36 var p = new Promise<int>();
37 p.Then(
37 p.Then(
38 x => res = x,
38 x => res = x,
39 e => {
39 e => {
40 err = e;
40 err = e;
41 return -2;
41 return -2;
42 }
42 }
43 );
43 );
44 p.Reject(new ApplicationException("error"));
44 p.Reject(new ApplicationException("error"));
45
45
46 Assert.AreEqual(res, -1);
46 Assert.AreEqual(res, -1);
47 Assert.AreEqual(err.Message, "error");
47 Assert.AreEqual(err.Message, "error");
48
48
49 }
49 }
50
50
51 [TestMethod]
51 [TestMethod]
52 public void CancelExceptionTest() {
52 public void CancelExceptionTest() {
53 var p = new Promise<bool>();
53 var p = new Promise<bool>();
54 p.Cancel();
54 p.Cancel();
55
55
56 var p2 = p.Cancelled(() => {
56 var p2 = p.Cancelled(() => {
57 throw new ApplicationException("CANCELLED");
57 throw new ApplicationException("CANCELLED");
58 });
58 });
59
59
60 try {
60 try {
61 p2.Join();
61 p2.Join();
62 Assert.Fail();
62 Assert.Fail();
63 } catch (ApplicationException err) {
63 } catch (ApplicationException err) {
64 Assert.AreEqual("CANCELLED", err.InnerException.Message);
64 Assert.AreEqual("CANCELLED", err.InnerException.Message);
65 }
65 }
66
66
67 }
67 }
68
68
69 [TestMethod]
69 [TestMethod]
70 public void ContinueOnCancelTest() {
70 public void ContinueOnCancelTest() {
71 var p = new Promise<bool>();
71 var p = new Promise<bool>();
72 p.Cancel();
72 p.Cancel();
73
73
74 var p2 = p
74 var p2 = p
75 .Cancelled(() => {
75 .Cancelled(() => {
76 throw new ApplicationException("CANCELLED");
76 throw new ApplicationException("CANCELLED");
77 })
77 })
78 .Error(e => true);
78 .Error(e => true);
79
79
80 Assert.AreEqual(true, p2.Join());
80 Assert.AreEqual(true, p2.Join());
81 }
81 }
82
82
83 [TestMethod]
83 [TestMethod]
84 public void JoinSuccessTest() {
84 public void JoinSuccessTest() {
85 var p = new Promise<int>();
85 var p = new Promise<int>();
86 p.Resolve(100);
86 p.Resolve(100);
87 Assert.AreEqual(p.Join(), 100);
87 Assert.AreEqual(p.Join(), 100);
88 }
88 }
89
89
90 [TestMethod]
90 [TestMethod]
91 public void JoinFailTest() {
91 public void JoinFailTest() {
92 var p = new Promise<int>();
92 var p = new Promise<int>();
93 p.Reject(new ApplicationException("failed"));
93 p.Reject(new ApplicationException("failed"));
94
94
95 try {
95 try {
96 p.Join();
96 p.Join();
97 throw new ApplicationException("WRONG!");
97 throw new ApplicationException("WRONG!");
98 } catch (TargetInvocationException err) {
98 } catch (TargetInvocationException err) {
99 Assert.AreEqual(err.InnerException.Message, "failed");
99 Assert.AreEqual(err.InnerException.Message, "failed");
100 } catch {
100 } catch {
101 Assert.Fail("Got wrong excaption");
101 Assert.Fail("Got wrong excaption");
102 }
102 }
103 }
103 }
104
104
105 [TestMethod]
105 [TestMethod]
106 public void MapTest() {
106 public void MapTest() {
107 var p = new Promise<int>();
107 var p = new Promise<int>();
108
108
109 var p2 = p.Then(x => x.ToString());
109 var p2 = p.Then(x => x.ToString());
110 p.Resolve(100);
110 p.Resolve(100);
111
111
112 Assert.AreEqual(p2.Join(), "100");
112 Assert.AreEqual(p2.Join(), "100");
113 }
113 }
114
114
115 [TestMethod]
115 [TestMethod]
116 public void FixErrorTest() {
116 public void FixErrorTest() {
117 var p = new Promise<int>();
117 var p = new Promise<int>();
118
118
119 var p2 = p.Error(e => 101);
119 var p2 = p.Error(e => 101);
120
120
121 p.Reject(new Exception());
121 p.Reject(new Exception());
122
122
123 Assert.AreEqual(p2.Join(), 101);
123 Assert.AreEqual(p2.Join(), 101);
124 }
124 }
125
125
126 [TestMethod]
126 [TestMethod]
127 public void ChainTest() {
127 public void ChainTest() {
128 var p1 = new Promise<int>();
128 var p1 = new Promise<int>();
129
129
130 var p3 = p1.Chain(x => {
130 var p3 = p1.Chain(x => {
131 var p2 = new Promise<string>();
131 var p2 = new Promise<string>();
132 p2.Resolve(x.ToString());
132 p2.Resolve(x.ToString());
133 return p2;
133 return p2;
134 });
134 });
135
135
136 p1.Resolve(100);
136 p1.Resolve(100);
137
137
138 Assert.AreEqual(p3.Join(), "100");
138 Assert.AreEqual(p3.Join(), "100");
139 }
139 }
140
140
141 [TestMethod]
141 [TestMethod]
142 public void ChainFailTest() {
142 public void ChainFailTest() {
143 var p1 = new Promise<int>();
143 var p1 = new Promise<int>();
144
144
145 var p3 = p1.Chain(x => {
145 var p3 = p1.Chain(x => {
146 var p2 = new Promise<string>();
146 var p2 = new Promise<string>();
147 p2.Reject(new Exception("DIE!!!"));
147 p2.Reject(new Exception("DIE!!!"));
148 return p2;
148 return p2;
149 });
149 });
150
150
151 p1.Resolve(100);
151 p1.Resolve(100);
152
152
153 Assert.IsTrue(p3.IsResolved);
153 Assert.IsTrue(p3.IsResolved);
154 }
154 }
155
155
156 [TestMethod]
156 [TestMethod]
157 public void PoolTest() {
157 public void PoolTest() {
158 var pid = Thread.CurrentThread.ManagedThreadId;
158 var pid = Thread.CurrentThread.ManagedThreadId;
159 var p = AsyncPool.Invoke(() => Thread.CurrentThread.ManagedThreadId);
159 var p = AsyncPool.Invoke(() => Thread.CurrentThread.ManagedThreadId);
160
160
161 Assert.AreNotEqual(pid, p.Join());
161 Assert.AreNotEqual(pid, p.Join());
162 }
162 }
163
163
164 [TestMethod]
164 [TestMethod]
165 public void WorkerPoolSizeTest() {
165 public void WorkerPoolSizeTest() {
166 var pool = new WorkerPool(5, 10, 1);
166 var pool = new WorkerPool(5, 10, 1);
167
167
168 Assert.AreEqual(5, pool.PoolSize);
168 Assert.AreEqual(5, pool.PoolSize);
169
169
170 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
170 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
171 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
171 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
172 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
172 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
173
173
174 Assert.AreEqual(5, pool.PoolSize);
174 Assert.AreEqual(5, pool.PoolSize);
175
175
176 for (int i = 0; i < 100; i++)
176 for (int i = 0; i < 100; i++)
177 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
177 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
178 Thread.Sleep(200);
178 Thread.Sleep(200);
179 Assert.AreEqual(10, pool.PoolSize);
179 Assert.AreEqual(10, pool.PoolSize);
180
180
181 pool.Dispose();
181 pool.Dispose();
182 }
182 }
183
183
184 [TestMethod]
184 [TestMethod]
185 public void WorkerPoolCorrectTest() {
185 public void WorkerPoolCorrectTest() {
186 var pool = new WorkerPool(0,1000,100);
186 var pool = new WorkerPool(0,1000,100);
187
187
188 const int iterations = 1000;
188 const int iterations = 1000;
189 int pending = iterations;
189 int pending = iterations;
190 var stop = new ManualResetEvent(false);
190 var stop = new ManualResetEvent(false);
191
191
192 var count = 0;
192 var count = 0;
193 for (int i = 0; i < iterations; i++) {
193 for (int i = 0; i < iterations; i++) {
194 pool
194 pool
195 .Invoke(() => 1)
195 .Invoke(() => 1)
196 .Then(x => Interlocked.Add(ref count, x))
196 .Then(x => Interlocked.Add(ref count, x))
197 .Then(x => Math.Log10(x))
197 .Then(x => Math.Log10(x))
198 .Anyway(() => {
198 .Anyway(() => {
199 Interlocked.Decrement(ref pending);
199 Interlocked.Decrement(ref pending);
200 if (pending == 0)
200 if (pending == 0)
201 stop.Set();
201 stop.Set();
202 });
202 });
203 }
203 }
204
204
205 stop.WaitOne();
205 stop.WaitOne();
206
206
207 Assert.AreEqual(iterations, count);
207 Assert.AreEqual(iterations, count);
208 Console.WriteLine("Max threads: {0}", pool.MaxRunningThreads);
208 Console.WriteLine("Max threads: {0}", pool.MaxRunningThreads);
209 pool.Dispose();
209 pool.Dispose();
210
210
211 }
211 }
212
212
213 [TestMethod]
213 [TestMethod]
214 public void WorkerPoolDisposeTest() {
214 public void WorkerPoolDisposeTest() {
215 var pool = new WorkerPool(5, 20);
215 var pool = new WorkerPool(5, 20);
216 Assert.AreEqual(5, pool.PoolSize);
216 Assert.AreEqual(5, pool.PoolSize);
217 pool.Dispose();
217 pool.Dispose();
218 Thread.Sleep(500);
218 Thread.Sleep(500);
219 Assert.AreEqual(0, pool.PoolSize);
219 Assert.AreEqual(0, pool.PoolSize);
220 pool.Dispose();
220 pool.Dispose();
221 }
221 }
222
222
223 [TestMethod]
223 [TestMethod]
224 public void MTQueueTest() {
224 public void MTQueueTest() {
225 var queue = new MTQueue<int>();
225 var queue = new MTQueue<int>();
226 int res;
226 int res;
227
227
228 queue.Enqueue(10);
228 queue.Enqueue(10);
229 Assert.IsTrue(queue.TryDequeue(out res));
229 Assert.IsTrue(queue.TryDequeue(out res));
230 Assert.AreEqual(10, res);
230 Assert.AreEqual(10, res);
231 Assert.IsFalse(queue.TryDequeue(out res));
231 Assert.IsFalse(queue.TryDequeue(out res));
232
232
233 for (int i = 0; i < 1000; i++)
233 for (int i = 0; i < 1000; i++)
234 queue.Enqueue(i);
234 queue.Enqueue(i);
235
235
236 for (int i = 0; i < 1000; i++) {
236 for (int i = 0; i < 1000; i++) {
237 queue.TryDequeue(out res);
237 queue.TryDequeue(out res);
238 Assert.AreEqual(i, res);
238 Assert.AreEqual(i, res);
239 }
239 }
240
240
241 int writers = 0;
241 int writers = 0;
242 int readers = 0;
242 int readers = 0;
243 var stop = new ManualResetEvent(false);
243 var stop = new ManualResetEvent(false);
244 int total = 0;
244 int total = 0;
245
245
246 const int itemsPerWriter = 10000;
246 const int itemsPerWriter = 10000;
247 const int writersCount = 10;
247 const int writersCount = 10;
248
248
249 for (int i = 0; i < writersCount; i++) {
249 for (int i = 0; i < writersCount; i++) {
250 Interlocked.Increment(ref writers);
250 Interlocked.Increment(ref writers);
251 AsyncPool
251 AsyncPool
252 .InvokeNewThread(() => {
252 .InvokeNewThread(() => {
253 for (int ii = 0; ii < itemsPerWriter; ii++) {
253 for (int ii = 0; ii < itemsPerWriter; ii++) {
254 queue.Enqueue(1);
254 queue.Enqueue(1);
255 }
255 }
256 return 1;
256 return 1;
257 })
257 })
258 .Anyway(() => Interlocked.Decrement(ref writers));
258 .Anyway(() => Interlocked.Decrement(ref writers));
259 }
259 }
260
260
261 for (int i = 0; i < 10; i++) {
261 for (int i = 0; i < 10; i++) {
262 Interlocked.Increment(ref readers);
262 Interlocked.Increment(ref readers);
263 AsyncPool
263 AsyncPool
264 .InvokeNewThread(() => {
264 .InvokeNewThread(() => {
265 int t;
265 int t;
266 do {
266 do {
267 while (queue.TryDequeue(out t))
267 while (queue.TryDequeue(out t))
268 Interlocked.Add(ref total, t);
268 Interlocked.Add(ref total, t);
269 } while (writers > 0);
269 } while (writers > 0);
270 return 1;
270 return 1;
271 })
271 })
272 .Anyway(() => {
272 .Anyway(() => {
273 Interlocked.Decrement(ref readers);
273 Interlocked.Decrement(ref readers);
274 if (readers == 0)
274 if (readers == 0)
275 stop.Set();
275 stop.Set();
276 });
276 });
277 }
277 }
278
278
279 stop.WaitOne();
279 stop.WaitOne();
280
280
281 Assert.AreEqual(itemsPerWriter * writersCount, total);
281 Assert.AreEqual(itemsPerWriter * writersCount, total);
282 }
282 }
283
283
284 [TestMethod]
284 [TestMethod]
285 public void ParallelMapTest() {
285 public void ParallelMapTest() {
286
286
287 const int count = 100000;
287 const int count = 100000;
288
288
289 var args = new double[count];
289 var args = new double[count];
290 var rand = new Random();
290 var rand = new Random();
291
291
292 for (int i = 0; i < count; i++)
292 for (int i = 0; i < count; i++)
293 args[i] = rand.NextDouble();
293 args[i] = rand.NextDouble();
294
294
295 var t = Environment.TickCount;
295 var t = Environment.TickCount;
296 var res = args.ParallelMap(x => Math.Sin(x*x), 4).Join();
296 var res = args.ParallelMap(x => Math.Sin(x*x), 4).Join();
297
297
298 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
298 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
299
299
300 t = Environment.TickCount;
300 t = Environment.TickCount;
301 for (int i = 0; i < count; i++)
301 for (int i = 0; i < count; i++)
302 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
302 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
303 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
303 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
304 }
304 }
305
305
306 [TestMethod]
306 [TestMethod]
307 public void ChainedMapTest() {
307 public void ChainedMapTest() {
308
308
309 using (var pool = new WorkerPool(0,10,1)) {
309 using (var pool = new WorkerPool(0,10,1)) {
310 const int count = 10000;
310 const int count = 10000;
311
311
312 var args = new double[count];
312 var args = new double[count];
313 var rand = new Random();
313 var rand = new Random();
314
314
315 for (int i = 0; i < count; i++)
315 for (int i = 0; i < count; i++)
316 args[i] = rand.NextDouble();
316 args[i] = rand.NextDouble();
317
317
318 var t = Environment.TickCount;
318 var t = Environment.TickCount;
319 var res = args
319 var res = args
320 .ChainedMap(
320 .ChainedMap(
321 // Analysis disable once AccessToDisposedClosure
321 // Analysis disable once AccessToDisposedClosure
322 x => pool.Invoke(
322 x => pool.Invoke(
323 () => Math.Sin(x * x)
323 () => Math.Sin(x * x)
324 ),
324 ),
325 4
325 4
326 )
326 )
327 .Join();
327 .Join();
328
328
329 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
329 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
330
330
331 t = Environment.TickCount;
331 t = Environment.TickCount;
332 for (int i = 0; i < count; i++)
332 for (int i = 0; i < count; i++)
333 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
333 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
334 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
334 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
335 Console.WriteLine("Max workers: {0}", pool.MaxRunningThreads);
335 Console.WriteLine("Max workers: {0}", pool.MaxRunningThreads);
336 }
336 }
337 }
337 }
338
338
339 [TestMethod]
339 [TestMethod]
340 public void ParallelForEachTest() {
340 public void ParallelForEachTest() {
341
341
342 const int count = 100000;
342 const int count = 100000;
343
343
344 var args = new int[count];
344 var args = new int[count];
345 var rand = new Random();
345 var rand = new Random();
346
346
347 for (int i = 0; i < count; i++)
347 for (int i = 0; i < count; i++)
348 args[i] = (int)(rand.NextDouble() * 100);
348 args[i] = (int)(rand.NextDouble() * 100);
349
349
350 int result = 0;
350 int result = 0;
351
351
352 var t = Environment.TickCount;
352 var t = Environment.TickCount;
353 args.ParallelForEach(x => Interlocked.Add(ref result, x), 4).Join();
353 args.ParallelForEach(x => Interlocked.Add(ref result, x), 4).Join();
354
354
355 Console.WriteLine("Iteration complete in {0} ms, result: {1}", Environment.TickCount - t, result);
355 Console.WriteLine("Iteration complete in {0} ms, result: {1}", Environment.TickCount - t, result);
356
356
357 int result2 = 0;
357 int result2 = 0;
358
358
359 t = Environment.TickCount;
359 t = Environment.TickCount;
360 for (int i = 0; i < count; i++)
360 for (int i = 0; i < count; i++)
361 result2 += args[i];
361 result2 += args[i];
362 Assert.AreEqual(result2, result);
362 Assert.AreEqual(result2, result);
363 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
363 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
364 }
364 }
365
365
366 [TestMethod]
366 [TestMethod]
367 public void ComplexCase1Test() {
367 public void ComplexCase1Test() {
368 var flags = new bool[3];
368 var flags = new bool[3];
369
369
370 // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map)
370 // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map)
371
371
372 var step1 = PromiseHelper
372 var step1 = PromiseHelper
373 .Sleep(200, "Alan")
373 .Sleep(200, "Alan")
374 .Cancelled(() => flags[0] = true);
374 .Cancelled(() => flags[0] = true);
375 var p = step1
375 var p = step1
376 .Chain(x =>
376 .Chain(x =>
377 PromiseHelper
377 PromiseHelper
378 .Sleep(200, "Hi, " + x)
378 .Sleep(200, "Hi, " + x)
379 .Then(y => y)
379 .Then(y => y)
380 .Cancelled(() => flags[1] = true)
380 .Cancelled(() => flags[1] = true)
381 )
381 )
382 .Cancelled(() => flags[2] = true);
382 .Cancelled(() => flags[2] = true);
383 step1.Join();
383 step1.Join();
384 p.Cancel();
384 p.Cancel();
385 try {
385 try {
386 Assert.AreEqual(p.Join(), "Hi, Alan");
386 Assert.AreEqual(p.Join(), "Hi, Alan");
387 Assert.Fail("Shouldn't get here");
387 Assert.Fail("Shouldn't get here");
388 } catch (OperationCanceledException) {
388 } catch (OperationCanceledException) {
389 }
389 }
390
390
391 Assert.IsFalse(flags[0]);
391 Assert.IsFalse(flags[0]);
392 Assert.IsTrue(flags[1]);
392 Assert.IsTrue(flags[1]);
393 Assert.IsTrue(flags[2]);
393 Assert.IsTrue(flags[2]);
394 }
394 }
395
395
396 [TestMethod]
396 [TestMethod]
397 public void ChainedCancel1Test() {
397 public void ChainedCancel1Test() {
398 // при отмене сцепленной асинхронной операции все обещание должно
398 // при отмене сцепленной асинхронной операции все обещание должно
399 // завершаться ошибкой OperationCanceledException
399 // завершаться ошибкой OperationCanceledException
400 var p = PromiseHelper
400 var p = PromiseHelper
401 .Sleep(1, "Hi, HAL!")
401 .Sleep(1, "Hi, HAL!")
402 .Then(x => {
402 .Then(x => {
403 // запускаем две асинхронные операции
403 // запускаем две асинхронные операции
404 var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!");
404 var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!");
405 // вторая операция отменяет первую до завершения
405 // вторая операция отменяет первую до завершения
406 PromiseHelper
406 PromiseHelper
407 .Sleep(100, "HAL, STOP!")
407 .Sleep(100, "HAL, STOP!")
408 .Then(result.Cancel);
408 .Then(result.Cancel);
409 return result;
409 return result;
410 });
410 });
411 try {
411 try {
412 p.Join();
412 p.Join();
413 } catch (TargetInvocationException err) {
413 } catch (TargetInvocationException err) {
414 Assert.IsTrue(err.InnerException is OperationCanceledException);
414 Assert.IsTrue(err.InnerException is OperationCanceledException);
415 }
415 }
416 }
416 }
417
417
418 [TestMethod]
418 [TestMethod]
419 public void ChainedCancel2Test() {
419 public void ChainedCancel2Test() {
420 // при отмене цепочки обещаний, вложенные операции также должны отменяться
420 // при отмене цепочки обещаний, вложенные операции также должны отменяться
421 var pSurvive = new Promise<bool>();
421 var pSurvive = new Promise<bool>();
422 var hemStarted = new ManualResetEvent(false);
422 var hemStarted = new ManualResetEvent(false);
423 var p = PromiseHelper
423 var p = PromiseHelper
424 .Sleep(1, "Hi, HAL!")
424 .Sleep(1, "Hi, HAL!")
425 .Chain(x => {
425 .Chain(x => {
426 hemStarted.Set();
426 hemStarted.Set();
427 // запускаем две асинхронные операции
427 // запускаем две асинхронные операции
428 var result = PromiseHelper
428 var result = PromiseHelper
429 .Sleep(10000, "HEM ENABLED!!!")
429 .Sleep(100000000, "HEM ENABLED!!!")
430 .Then(s => pSurvive.Resolve(false));
430 .Then(s => {
431 pSurvive.Resolve(false);
432 return s;
433 });
431
434
432 result
435 result
433 .Cancelled(() => pSurvive.Resolve(true));
436 .Cancelled(() => pSurvive.Resolve(true));
434
437
435 return result;
438 return result;
436 });
439 });
437
440
438 hemStarted.WaitOne();
441 hemStarted.WaitOne();
439 p.Cancel();
442 p.Cancel();
440
443
441 try {
444 try {
442 p.Join();
445 p.Join();
443 } catch (OperationCanceledException) {
446 } catch (OperationCanceledException) {
444 Assert.IsTrue(pSurvive.Join());
447 Assert.IsTrue(pSurvive.Join());
445 }
448 }
446 }
449 }
447 }
450 }
448 }
451 }
449
452
@@ -1,40 +1,34
1 using System;
1 using System;
2
2
3 namespace Implab {
3 namespace Implab {
4 public interface IPromise<T> : IPromise {
4 public interface IPromise<T> : IPromise {
5
5
6 new T Join();
6 new T Join();
7
7
8 new T Join(int timeout);
8 new T Join(int timeout);
9
9
10 void On(Action<T> success, Action<Exception> error, Action cancel);
10 void On(Action<T> success, Action<Exception> error, Action cancel);
11
11
12 void On(Action<T> success, Action<Exception> error);
12 void On(Action<T> success, Action<Exception> error);
13
13
14 void On(Action<T> success);
14 void On(Action<T> success);
15
15
16 IPromise<T> Then(Action<T> success, Func<Exception,T> error, Action cancel);
17
18 IPromise<T> Then(Action<T> success, Func<Exception,T> error);
19
20 IPromise<T> Then(Action<T> success);
21
22 IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception,T2> error, Action cancel);
16 IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception,T2> error, Action cancel);
23
17
24 IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception,T2> error);
18 IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception,T2> error);
25
19
26 IPromise<T2> Then<T2>(Func<T, T2> mapper);
20 IPromise<T2> Then<T2>(Func<T, T2> mapper);
27
21
28 IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception,IPromise<T2>> error, Action cancel);
22 IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception,IPromise<T2>> error, Action cancel);
29
23
30 IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception,IPromise<T2>> error);
24 IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception,IPromise<T2>> error);
31
25
32 IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained);
26 IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained);
33
27
34 IPromise<T> Error(Func<Exception,T> error);
28 IPromise<T> Error(Func<Exception,T> error);
35
29
36 new IPromise<T> Cancelled(Action handler);
30 new IPromise<T> Cancelled(Action handler);
37
31
38 new IPromise<T> Anyway(Action handler);
32 new IPromise<T> Anyway(Action handler);
39 }
33 }
40 }
34 }
@@ -1,223 +1,225
1 <?xml version="1.0" encoding="utf-8"?>
1 <?xml version="1.0" encoding="utf-8"?>
2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <PropertyGroup>
3 <PropertyGroup>
4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
6 <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
7 <OutputType>Library</OutputType>
7 <OutputType>Library</OutputType>
8 <RootNamespace>Implab</RootNamespace>
8 <RootNamespace>Implab</RootNamespace>
9 <AssemblyName>Implab</AssemblyName>
9 <AssemblyName>Implab</AssemblyName>
10 </PropertyGroup>
10 </PropertyGroup>
11 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
11 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
12 <DebugSymbols>true</DebugSymbols>
12 <DebugSymbols>true</DebugSymbols>
13 <DebugType>full</DebugType>
13 <DebugType>full</DebugType>
14 <Optimize>false</Optimize>
14 <Optimize>false</Optimize>
15 <OutputPath>bin\Debug</OutputPath>
15 <OutputPath>bin\Debug</OutputPath>
16 <DefineConstants>TRACE;DEBUG;</DefineConstants>
16 <DefineConstants>TRACE;DEBUG;</DefineConstants>
17 <ErrorReport>prompt</ErrorReport>
17 <ErrorReport>prompt</ErrorReport>
18 <WarningLevel>4</WarningLevel>
18 <WarningLevel>4</WarningLevel>
19 <ConsolePause>false</ConsolePause>
19 <ConsolePause>false</ConsolePause>
20 <RunCodeAnalysis>true</RunCodeAnalysis>
20 <RunCodeAnalysis>true</RunCodeAnalysis>
21 </PropertyGroup>
21 </PropertyGroup>
22 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
22 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
23 <DebugType>full</DebugType>
23 <DebugType>full</DebugType>
24 <Optimize>true</Optimize>
24 <Optimize>true</Optimize>
25 <OutputPath>bin\Release</OutputPath>
25 <OutputPath>bin\Release</OutputPath>
26 <ErrorReport>prompt</ErrorReport>
26 <ErrorReport>prompt</ErrorReport>
27 <WarningLevel>4</WarningLevel>
27 <WarningLevel>4</WarningLevel>
28 <ConsolePause>false</ConsolePause>
28 <ConsolePause>false</ConsolePause>
29 </PropertyGroup>
29 </PropertyGroup>
30 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
30 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
31 <DebugSymbols>true</DebugSymbols>
31 <DebugSymbols>true</DebugSymbols>
32 <DebugType>full</DebugType>
32 <DebugType>full</DebugType>
33 <Optimize>false</Optimize>
33 <Optimize>false</Optimize>
34 <OutputPath>bin\Debug</OutputPath>
34 <OutputPath>bin\Debug</OutputPath>
35 <DefineConstants>TRACE;DEBUG;NET_4_5</DefineConstants>
35 <DefineConstants>TRACE;DEBUG;NET_4_5</DefineConstants>
36 <ErrorReport>prompt</ErrorReport>
36 <ErrorReport>prompt</ErrorReport>
37 <WarningLevel>4</WarningLevel>
37 <WarningLevel>4</WarningLevel>
38 <RunCodeAnalysis>true</RunCodeAnalysis>
38 <RunCodeAnalysis>true</RunCodeAnalysis>
39 <ConsolePause>false</ConsolePause>
39 <ConsolePause>false</ConsolePause>
40 </PropertyGroup>
40 </PropertyGroup>
41 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
41 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
42 <Optimize>true</Optimize>
42 <Optimize>true</Optimize>
43 <OutputPath>bin\Release</OutputPath>
43 <OutputPath>bin\Release</OutputPath>
44 <ErrorReport>prompt</ErrorReport>
44 <ErrorReport>prompt</ErrorReport>
45 <WarningLevel>4</WarningLevel>
45 <WarningLevel>4</WarningLevel>
46 <ConsolePause>false</ConsolePause>
46 <ConsolePause>false</ConsolePause>
47 <DefineConstants>NET_4_5</DefineConstants>
47 <DefineConstants>NET_4_5</DefineConstants>
48 </PropertyGroup>
48 </PropertyGroup>
49 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugMono|AnyCPU' ">
49 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugMono|AnyCPU' ">
50 <DebugSymbols>true</DebugSymbols>
50 <DebugSymbols>true</DebugSymbols>
51 <DebugType>full</DebugType>
51 <DebugType>full</DebugType>
52 <Optimize>false</Optimize>
52 <Optimize>false</Optimize>
53 <OutputPath>bin\Debug</OutputPath>
53 <OutputPath>bin\Debug</OutputPath>
54 <DefineConstants>TRACE;DEBUG;NET_4_5;MONO</DefineConstants>
54 <DefineConstants>TRACE;DEBUG;NET_4_5;MONO</DefineConstants>
55 <ErrorReport>prompt</ErrorReport>
55 <ErrorReport>prompt</ErrorReport>
56 <WarningLevel>4</WarningLevel>
56 <WarningLevel>4</WarningLevel>
57 <RunCodeAnalysis>true</RunCodeAnalysis>
57 <RunCodeAnalysis>true</RunCodeAnalysis>
58 <ConsolePause>false</ConsolePause>
58 <ConsolePause>false</ConsolePause>
59 </PropertyGroup>
59 </PropertyGroup>
60 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseMono|AnyCPU' ">
60 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseMono|AnyCPU' ">
61 <Optimize>true</Optimize>
61 <Optimize>true</Optimize>
62 <OutputPath>bin\Release</OutputPath>
62 <OutputPath>bin\Release</OutputPath>
63 <DefineConstants>NET_4_5;MONO;</DefineConstants>
63 <DefineConstants>NET_4_5;MONO;</DefineConstants>
64 <ErrorReport>prompt</ErrorReport>
64 <ErrorReport>prompt</ErrorReport>
65 <WarningLevel>4</WarningLevel>
65 <WarningLevel>4</WarningLevel>
66 <ConsolePause>false</ConsolePause>
66 <ConsolePause>false</ConsolePause>
67 </PropertyGroup>
67 </PropertyGroup>
68 <ItemGroup>
68 <ItemGroup>
69 <Reference Include="System" />
69 <Reference Include="System" />
70 <Reference Include="System.Xml" />
70 <Reference Include="System.Xml" />
71 </ItemGroup>
71 </ItemGroup>
72 <ItemGroup>
72 <ItemGroup>
73 <Compile Include="Component.cs" />
73 <Compile Include="Component.cs" />
74 <Compile Include="CustomEqualityComparer.cs" />
74 <Compile Include="CustomEqualityComparer.cs" />
75 <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
75 <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
76 <Compile Include="Diagnostics\EventText.cs" />
76 <Compile Include="Diagnostics\EventText.cs" />
77 <Compile Include="Diagnostics\IEventTextFormatter.cs" />
77 <Compile Include="Diagnostics\IEventTextFormatter.cs" />
78 <Compile Include="Diagnostics\LogChannel.cs" />
78 <Compile Include="Diagnostics\LogChannel.cs" />
79 <Compile Include="Diagnostics\LogicalOperation.cs" />
79 <Compile Include="Diagnostics\LogicalOperation.cs" />
80 <Compile Include="Diagnostics\TextFileListener.cs" />
80 <Compile Include="Diagnostics\TextFileListener.cs" />
81 <Compile Include="Diagnostics\TextListenerBase.cs" />
81 <Compile Include="Diagnostics\TextListenerBase.cs" />
82 <Compile Include="Diagnostics\TraceLog.cs" />
82 <Compile Include="Diagnostics\TraceLog.cs" />
83 <Compile Include="Diagnostics\TraceEvent.cs" />
83 <Compile Include="Diagnostics\TraceEvent.cs" />
84 <Compile Include="Diagnostics\TraceEventType.cs" />
84 <Compile Include="Diagnostics\TraceEventType.cs" />
85 <Compile Include="Disposable.cs" />
85 <Compile Include="Disposable.cs" />
86 <Compile Include="ICancellable.cs" />
86 <Compile Include="ICancellable.cs" />
87 <Compile Include="IProgressHandler.cs" />
87 <Compile Include="IProgressHandler.cs" />
88 <Compile Include="IProgressNotifier.cs" />
88 <Compile Include="IProgressNotifier.cs" />
89 <Compile Include="IPromiseT.cs" />
89 <Compile Include="IPromiseT.cs" />
90 <Compile Include="IPromise.cs" />
90 <Compile Include="IPromise.cs" />
91 <Compile Include="IServiceLocator.cs" />
91 <Compile Include="IServiceLocator.cs" />
92 <Compile Include="ITaskController.cs" />
92 <Compile Include="ITaskController.cs" />
93 <Compile Include="JSON\JSONElementContext.cs" />
93 <Compile Include="JSON\JSONElementContext.cs" />
94 <Compile Include="JSON\JSONElementType.cs" />
94 <Compile Include="JSON\JSONElementType.cs" />
95 <Compile Include="JSON\JSONGrammar.cs" />
95 <Compile Include="JSON\JSONGrammar.cs" />
96 <Compile Include="JSON\JSONParser.cs" />
96 <Compile Include="JSON\JSONParser.cs" />
97 <Compile Include="JSON\JSONScanner.cs" />
97 <Compile Include="JSON\JSONScanner.cs" />
98 <Compile Include="JSON\JsonTokenType.cs" />
98 <Compile Include="JSON\JsonTokenType.cs" />
99 <Compile Include="JSON\JSONWriter.cs" />
99 <Compile Include="JSON\JSONWriter.cs" />
100 <Compile Include="JSON\JSONXmlReader.cs" />
100 <Compile Include="JSON\JSONXmlReader.cs" />
101 <Compile Include="JSON\JSONXmlReaderOptions.cs" />
101 <Compile Include="JSON\JSONXmlReaderOptions.cs" />
102 <Compile Include="JSON\StringTranslator.cs" />
102 <Compile Include="JSON\StringTranslator.cs" />
103 <Compile Include="Parallels\DispatchPool.cs" />
103 <Compile Include="Parallels\DispatchPool.cs" />
104 <Compile Include="Parallels\ArrayTraits.cs" />
104 <Compile Include="Parallels\ArrayTraits.cs" />
105 <Compile Include="Parallels\MTQueue.cs" />
105 <Compile Include="Parallels\MTQueue.cs" />
106 <Compile Include="Parallels\WorkerPool.cs" />
106 <Compile Include="Parallels\WorkerPool.cs" />
107 <Compile Include="Parsing\Alphabet.cs" />
107 <Compile Include="Parsing\Alphabet.cs" />
108 <Compile Include="Parsing\AlphabetBase.cs" />
108 <Compile Include="Parsing\AlphabetBase.cs" />
109 <Compile Include="Parsing\AltToken.cs" />
109 <Compile Include="Parsing\AltToken.cs" />
110 <Compile Include="Parsing\BinaryToken.cs" />
110 <Compile Include="Parsing\BinaryToken.cs" />
111 <Compile Include="Parsing\CatToken.cs" />
111 <Compile Include="Parsing\CatToken.cs" />
112 <Compile Include="Parsing\CDFADefinition.cs" />
112 <Compile Include="Parsing\CDFADefinition.cs" />
113 <Compile Include="Parsing\DFABuilder.cs" />
113 <Compile Include="Parsing\DFABuilder.cs" />
114 <Compile Include="Parsing\DFADefinitionBase.cs" />
114 <Compile Include="Parsing\DFADefinitionBase.cs" />
115 <Compile Include="Parsing\DFAStateDescriptor.cs" />
115 <Compile Include="Parsing\DFAStateDescriptor.cs" />
116 <Compile Include="Parsing\DFAutomaton.cs" />
116 <Compile Include="Parsing\DFAutomaton.cs" />
117 <Compile Include="Parsing\EDFADefinition.cs" />
117 <Compile Include="Parsing\EDFADefinition.cs" />
118 <Compile Include="Parsing\EmptyToken.cs" />
118 <Compile Include="Parsing\EmptyToken.cs" />
119 <Compile Include="Parsing\EndToken.cs" />
119 <Compile Include="Parsing\EndToken.cs" />
120 <Compile Include="Parsing\EnumAlphabet.cs" />
120 <Compile Include="Parsing\EnumAlphabet.cs" />
121 <Compile Include="Parsing\Grammar.cs" />
121 <Compile Include="Parsing\Grammar.cs" />
122 <Compile Include="Parsing\IAlphabet.cs" />
122 <Compile Include="Parsing\IAlphabet.cs" />
123 <Compile Include="Parsing\IDFADefinition.cs" />
123 <Compile Include="Parsing\IDFADefinition.cs" />
124 <Compile Include="Parsing\IVisitor.cs" />
124 <Compile Include="Parsing\IVisitor.cs" />
125 <Compile Include="Parsing\ParserException.cs" />
125 <Compile Include="Parsing\ParserException.cs" />
126 <Compile Include="Parsing\Scanner.cs" />
126 <Compile Include="Parsing\Scanner.cs" />
127 <Compile Include="Parsing\StarToken.cs" />
127 <Compile Include="Parsing\StarToken.cs" />
128 <Compile Include="Parsing\SymbolToken.cs" />
128 <Compile Include="Parsing\SymbolToken.cs" />
129 <Compile Include="Parsing\Token.cs" />
129 <Compile Include="Parsing\Token.cs" />
130 <Compile Include="SafePool.cs" />
130 <Compile Include="SafePool.cs" />
131 <Compile Include="ServiceLocator.cs" />
131 <Compile Include="ServiceLocator.cs" />
132 <Compile Include="TaskController.cs" />
132 <Compile Include="TaskController.cs" />
133 <Compile Include="ProgressInitEventArgs.cs" />
133 <Compile Include="ProgressInitEventArgs.cs" />
134 <Compile Include="Properties\AssemblyInfo.cs" />
134 <Compile Include="Properties\AssemblyInfo.cs" />
135 <Compile Include="Promise.cs" />
135 <Compile Include="Promise.cs" />
136 <Compile Include="Parallels\AsyncPool.cs" />
136 <Compile Include="Parallels\AsyncPool.cs" />
137 <Compile Include="Safe.cs" />
137 <Compile Include="Safe.cs" />
138 <Compile Include="ValueEventArgs.cs" />
138 <Compile Include="ValueEventArgs.cs" />
139 <Compile Include="PromiseExtensions.cs" />
139 <Compile Include="PromiseExtensions.cs" />
140 <Compile Include="TransientPromiseException.cs" />
140 <Compile Include="TransientPromiseException.cs" />
141 <Compile Include="SyncContextPromise.cs" />
141 <Compile Include="SyncContextPromise.cs" />
142 <Compile Include="ObjectPool.cs" />
142 <Compile Include="ObjectPool.cs" />
143 <Compile Include="Diagnostics\OperationContext.cs" />
143 <Compile Include="Diagnostics\OperationContext.cs" />
144 <Compile Include="Diagnostics\TraceContext.cs" />
144 <Compile Include="Diagnostics\TraceContext.cs" />
145 <Compile Include="Diagnostics\LogEventArgs.cs" />
145 <Compile Include="Diagnostics\LogEventArgs.cs" />
146 <Compile Include="Diagnostics\LogEventArgsT.cs" />
146 <Compile Include="Diagnostics\LogEventArgsT.cs" />
147 <Compile Include="Diagnostics\Extensions.cs" />
147 <Compile Include="Diagnostics\Extensions.cs" />
148 <Compile Include="IComponentContainer.cs" />
148 <Compile Include="IComponentContainer.cs" />
149 <Compile Include="MTComponentContainer.cs" />
149 <Compile Include="MTComponentContainer.cs" />
150 <Compile Include="PromiseEventType.cs" />
150 <Compile Include="PromiseEventType.cs" />
151 <Compile Include="Parallels\MTCustomQueue.cs" />
152 <Compile Include="Parallels\MTCustomQueueNode.cs" />
151 </ItemGroup>
153 </ItemGroup>
152 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
154 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
153 <ItemGroup />
155 <ItemGroup />
154 <ProjectExtensions>
156 <ProjectExtensions>
155 <MonoDevelop>
157 <MonoDevelop>
156 <Properties>
158 <Properties>
157 <Policies>
159 <Policies>
158 <CSharpFormattingPolicy IndentSwitchBody="True" NamespaceBraceStyle="EndOfLine" ClassBraceStyle="EndOfLine" InterfaceBraceStyle="EndOfLine" StructBraceStyle="EndOfLine" EnumBraceStyle="EndOfLine" MethodBraceStyle="EndOfLine" ConstructorBraceStyle="EndOfLine" DestructorBraceStyle="EndOfLine" BeforeMethodDeclarationParentheses="False" BeforeMethodCallParentheses="False" BeforeConstructorDeclarationParentheses="False" NewLineBeforeConstructorInitializerColon="NewLine" NewLineAfterConstructorInitializerColon="SameLine" BeforeIndexerDeclarationBracket="False" BeforeDelegateDeclarationParentheses="False" NewParentheses="False" SpacesBeforeBrackets="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
160 <CSharpFormattingPolicy IndentSwitchBody="True" NamespaceBraceStyle="EndOfLine" ClassBraceStyle="EndOfLine" InterfaceBraceStyle="EndOfLine" StructBraceStyle="EndOfLine" EnumBraceStyle="EndOfLine" MethodBraceStyle="EndOfLine" ConstructorBraceStyle="EndOfLine" DestructorBraceStyle="EndOfLine" BeforeMethodDeclarationParentheses="False" BeforeMethodCallParentheses="False" BeforeConstructorDeclarationParentheses="False" NewLineBeforeConstructorInitializerColon="NewLine" NewLineAfterConstructorInitializerColon="SameLine" BeforeIndexerDeclarationBracket="False" BeforeDelegateDeclarationParentheses="False" NewParentheses="False" SpacesBeforeBrackets="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
159 <TextStylePolicy FileWidth="120" EolMarker="Unix" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
161 <TextStylePolicy FileWidth="120" EolMarker="Unix" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
160 <DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" />
162 <DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" />
161 <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="application/xml" />
163 <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="application/xml" />
162 <XmlFormattingPolicy inheritsSet="Mono" inheritsScope="application/xml" scope="application/xml" />
164 <XmlFormattingPolicy inheritsSet="Mono" inheritsScope="application/xml" scope="application/xml" />
163 <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/plain" />
165 <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/plain" />
164 <NameConventionPolicy>
166 <NameConventionPolicy>
165 <Rules>
167 <Rules>
166 <NamingRule Name="Namespaces" AffectedEntity="Namespace" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
168 <NamingRule Name="Namespaces" AffectedEntity="Namespace" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
167 <NamingRule Name="Types" AffectedEntity="Class, Struct, Enum, Delegate" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
169 <NamingRule Name="Types" AffectedEntity="Class, Struct, Enum, Delegate" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
168 <NamingRule Name="Interfaces" AffectedEntity="Interface" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
170 <NamingRule Name="Interfaces" AffectedEntity="Interface" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
169 <RequiredPrefixes>
171 <RequiredPrefixes>
170 <String>I</String>
172 <String>I</String>
171 </RequiredPrefixes>
173 </RequiredPrefixes>
172 </NamingRule>
174 </NamingRule>
173 <NamingRule Name="Attributes" AffectedEntity="CustomAttributes" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
175 <NamingRule Name="Attributes" AffectedEntity="CustomAttributes" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
174 <RequiredSuffixes>
176 <RequiredSuffixes>
175 <String>Attribute</String>
177 <String>Attribute</String>
176 </RequiredSuffixes>
178 </RequiredSuffixes>
177 </NamingRule>
179 </NamingRule>
178 <NamingRule Name="Event Arguments" AffectedEntity="CustomEventArgs" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
180 <NamingRule Name="Event Arguments" AffectedEntity="CustomEventArgs" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
179 <RequiredSuffixes>
181 <RequiredSuffixes>
180 <String>EventArgs</String>
182 <String>EventArgs</String>
181 </RequiredSuffixes>
183 </RequiredSuffixes>
182 </NamingRule>
184 </NamingRule>
183 <NamingRule Name="Exceptions" AffectedEntity="CustomExceptions" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
185 <NamingRule Name="Exceptions" AffectedEntity="CustomExceptions" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
184 <RequiredSuffixes>
186 <RequiredSuffixes>
185 <String>Exception</String>
187 <String>Exception</String>
186 </RequiredSuffixes>
188 </RequiredSuffixes>
187 </NamingRule>
189 </NamingRule>
188 <NamingRule Name="Methods" AffectedEntity="Methods" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
190 <NamingRule Name="Methods" AffectedEntity="Methods" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
189 <NamingRule Name="Static Readonly Fields" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Protected, Public" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True" />
191 <NamingRule Name="Static Readonly Fields" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Protected, Public" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True" />
190 <NamingRule Name="Fields (Non Private)" AffectedEntity="Field" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
192 <NamingRule Name="Fields (Non Private)" AffectedEntity="Field" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
191 <NamingRule Name="ReadOnly Fields (Non Private)" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False" />
193 <NamingRule Name="ReadOnly Fields (Non Private)" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False" />
192 <NamingRule Name="Fields (Private)" AffectedEntity="Field, ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
194 <NamingRule Name="Fields (Private)" AffectedEntity="Field, ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
193 <RequiredPrefixes>
195 <RequiredPrefixes>
194 <String>m_</String>
196 <String>m_</String>
195 </RequiredPrefixes>
197 </RequiredPrefixes>
196 </NamingRule>
198 </NamingRule>
197 <NamingRule Name="Static Fields (Private)" AffectedEntity="Field" VisibilityMask="Private" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True">
199 <NamingRule Name="Static Fields (Private)" AffectedEntity="Field" VisibilityMask="Private" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True">
198 <RequiredPrefixes>
200 <RequiredPrefixes>
199 <String>_</String>
201 <String>_</String>
200 </RequiredPrefixes>
202 </RequiredPrefixes>
201 </NamingRule>
203 </NamingRule>
202 <NamingRule Name="ReadOnly Fields (Private)" AffectedEntity="ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
204 <NamingRule Name="ReadOnly Fields (Private)" AffectedEntity="ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
203 <RequiredPrefixes>
205 <RequiredPrefixes>
204 <String>m_</String>
206 <String>m_</String>
205 </RequiredPrefixes>
207 </RequiredPrefixes>
206 </NamingRule>
208 </NamingRule>
207 <NamingRule Name="Constant Fields" AffectedEntity="ConstantField" VisibilityMask="VisibilityMask" NamingStyle="AllUpper" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
209 <NamingRule Name="Constant Fields" AffectedEntity="ConstantField" VisibilityMask="VisibilityMask" NamingStyle="AllUpper" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
208 <NamingRule Name="Properties" AffectedEntity="Property" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
210 <NamingRule Name="Properties" AffectedEntity="Property" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
209 <NamingRule Name="Events" AffectedEntity="Event" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
211 <NamingRule Name="Events" AffectedEntity="Event" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
210 <NamingRule Name="Enum Members" AffectedEntity="EnumMember" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
212 <NamingRule Name="Enum Members" AffectedEntity="EnumMember" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
211 <NamingRule Name="Parameters" AffectedEntity="Parameter, LocalVariable" VisibilityMask="VisibilityMask" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
213 <NamingRule Name="Parameters" AffectedEntity="Parameter, LocalVariable" VisibilityMask="VisibilityMask" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
212 <NamingRule Name="Type Parameters" AffectedEntity="TypeParameter" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
214 <NamingRule Name="Type Parameters" AffectedEntity="TypeParameter" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
213 <RequiredPrefixes>
215 <RequiredPrefixes>
214 <String>T</String>
216 <String>T</String>
215 </RequiredPrefixes>
217 </RequiredPrefixes>
216 </NamingRule>
218 </NamingRule>
217 </Rules>
219 </Rules>
218 </NameConventionPolicy>
220 </NameConventionPolicy>
219 </Policies>
221 </Policies>
220 </Properties>
222 </Properties>
221 </MonoDevelop>
223 </MonoDevelop>
222 </ProjectExtensions>
224 </ProjectExtensions>
223 </Project> No newline at end of file
225 </Project>
@@ -1,932 +1,894
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Reflection;
3 using System.Reflection;
4 using System.Threading;
4 using System.Threading;
5 using Implab.Parallels;
5 using Implab.Parallels;
6
6
7 namespace Implab {
7 namespace Implab {
8
8
9 /// <summary>
9 /// <summary>
10 /// Класс для асинхронного получения результатов. Так называемое "обещание".
10 /// Класс для асинхронного получения результатов. Так называемое "обещание".
11 /// </summary>
11 /// </summary>
12 /// <typeparam name="T">Тип получаемого результата</typeparam>
12 /// <typeparam name="T">Тип получаемого результата</typeparam>
13 /// <remarks>
13 /// <remarks>
14 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции,
14 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции,
15 /// клиент получив такое обещание может установить ряд обратных вызово для получения
15 /// клиент получив такое обещание может установить ряд обратных вызово для получения
16 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para>
16 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para>
17 /// <para>
17 /// <para>
18 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на
18 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на
19 /// данные события клиент должен использовать методы <c>Then</c>.
19 /// данные события клиент должен использовать методы <c>Then</c>.
20 /// </para>
20 /// </para>
21 /// <para>
21 /// <para>
22 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой),
22 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой),
23 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о
23 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о
24 /// выполнении обещания.
24 /// выполнении обещания.
25 /// </para>
25 /// </para>
26 /// <para>
26 /// <para>
27 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался,
27 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался,
28 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном
28 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном
29 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в
29 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в
30 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении
30 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении
31 /// обещания.
31 /// обещания.
32 /// </para>
32 /// </para>
33 /// <para>
33 /// <para>
34 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать
34 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать
35 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует
35 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует
36 /// использовать соответствующую форму методе <c>Then</c>.
36 /// использовать соответствующую форму методе <c>Then</c>.
37 /// </para>
37 /// </para>
38 /// <para>
38 /// <para>
39 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать
39 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать
40 /// только инициатор обещания иначе могут возникнуть противоречия.
40 /// только инициатор обещания иначе могут возникнуть противоречия.
41 /// </para>
41 /// </para>
42 /// </remarks>
42 /// </remarks>
43 public class Promise<T> : IPromise<T> {
43 public class Promise<T> : IPromise<T> {
44
44
45 protected struct HandlerDescriptor {
45 protected abstract class AbstractHandler : MTCustomQueueNode<AbstractHandler> {
46 public Action<T> resultHandler;
46 public abstract void Resolve(T result);
47 public Func<Exception,T> errorHandler;
47 public abstract void Reject(Exception error);
48 public Action cancellHandler;
48 public abstract void Cancel();
49 public Promise<T> medium;
49 }
50
51 protected class HandlerDescriptor<T2> : AbstractHandler {
52
53 readonly Func<T,T2> m_resultHandler;
54 readonly Func<Exception,T2> m_errorHandler;
55 readonly Action m_cancellHandler;
56 readonly Promise<T2> m_medium;
50
57
51 public void Resolve(T result) {
58 public HandlerDescriptor(Func<T,T2> resultHandler, Func<Exception,T2> errorHandler, Action cancelHandler, Promise<T2> medium) {
52 if (resultHandler != null) {
59 m_resultHandler = resultHandler;
60 m_errorHandler = errorHandler;
61 m_cancellHandler = cancelHandler;
62 m_medium = medium;
63 }
64
65 public override void Resolve(T result) {
66 if (m_resultHandler != null) {
53 try {
67 try {
54 resultHandler(result);
68 if (m_medium != null)
69 m_medium.Resolve(m_resultHandler(result));
70 else
71 m_resultHandler(result);
55 } catch (Exception e) {
72 } catch (Exception e) {
56 Reject(e);
73 Reject(e);
57 return;
58 }
74 }
59 }
75 } else if(m_medium != null)
60 if (medium != null)
76 m_medium.Resolve(default(T2));
61 medium.Resolve(result);
62 }
77 }
63
78
64 public void Reject(Exception err) {
79 public override void Reject(Exception error) {
65 if (errorHandler != null) {
80 if (m_errorHandler != null) {
66 try {
81 try {
67 var res = errorHandler(err);
82 var res = m_errorHandler(error);
68 if (medium != null)
83 if (m_medium != null)
69 medium.Resolve(res);
84 m_medium.Resolve(res);
70 /*} catch (TransientPromiseException err2) {
85 /*} catch (TransientPromiseException err2) {
71 if (medium != null)
86 if (medium != null)
72 medium.Reject(err2.InnerException);*/
87 medium.Reject(err2.InnerException);*/
73 } catch (Exception err2) {
88 } catch (Exception err2) {
74 if (medium != null)
89 if (m_medium != null)
75 medium.Reject(err2);
90 m_medium.Reject(err2);
76 }
91 }
77 } else if (medium != null)
92 } else if (m_medium != null)
78 medium.Reject(err);
93 m_medium.Reject(error);
79 }
94 }
80
95
81 public void Cancel() {
96 public override void Cancel() {
82 if (cancellHandler != null) {
97 if (m_cancellHandler != null) {
83 try {
98 try {
84 cancellHandler();
99 m_cancellHandler();
85 } catch (Exception err) {
100 } catch (Exception err) {
86 Reject(err);
101 Reject(err);
87 return;
102 return;
88 }
103 }
89 }
104 }
90 if (medium != null)
105 if (m_medium != null)
91 medium.Cancel();
106 m_medium.Cancel();
92 }
107 }
93 }
108 }
94
109
95 const int UNRESOLVED_SATE = 0;
110 const int UNRESOLVED_SATE = 0;
96 const int TRANSITIONAL_STATE = 1;
111 const int TRANSITIONAL_STATE = 1;
97 const int SUCCEEDED_STATE = 2;
112 const int SUCCEEDED_STATE = 2;
98 const int REJECTED_STATE = 3;
113 const int REJECTED_STATE = 3;
99 const int CANCELLED_STATE = 4;
114 const int CANCELLED_STATE = 4;
100
115
101 int m_childrenCount;
116 int m_childrenCount;
102 int m_state;
117 int m_state;
103 T m_result;
118 T m_result;
104 Exception m_error;
119 Exception m_error;
105
120
106 readonly MTQueue<HandlerDescriptor> m_handlers = new MTQueue<HandlerDescriptor>();
121 readonly MTCustomQueue<AbstractHandler> m_handlers = new MTCustomQueue<AbstractHandler>();
122 //readonly MTQueue<AbstractHandler> m_handlers = new MTQueue<AbstractHandler>();
107
123
108 public Promise() {
124 public Promise() {
109 }
125 }
110
126
111 public Promise(IPromise parent) {
127 public Promise(IPromise parent) {
112 if (parent != null)
128 if (parent != null)
113 AddHandler(
129 AddHandler<T>(
114 null,
130 null,
115 null,
131 null,
116 () => {
132 () => {
117 if (parent.IsExclusive)
133 if (parent.IsExclusive)
118 parent.Cancel();
134 parent.Cancel();
119 },
135 },
120 null,
136 null,
121 false
137 false
122 );
138 );
123 }
139 }
124
140
125 bool BeginTransit() {
141 bool BeginTransit() {
126 return UNRESOLVED_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, UNRESOLVED_SATE);
142 return UNRESOLVED_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, UNRESOLVED_SATE);
127 }
143 }
128
144
129 void CompleteTransit(int state) {
145 void CompleteTransit(int state) {
130 if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, state, TRANSITIONAL_STATE))
146 if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, state, TRANSITIONAL_STATE))
131 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
147 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
132 }
148 }
133
149
134 void WaitTransition() {
150 void WaitTransition() {
135 while (m_state == TRANSITIONAL_STATE) {
151 while (m_state == TRANSITIONAL_STATE) {
136 Thread.MemoryBarrier();
152 Thread.MemoryBarrier();
137 }
153 }
138 }
154 }
139
155
140 public bool IsResolved {
156 public bool IsResolved {
141 get {
157 get {
142 Thread.MemoryBarrier();
158 Thread.MemoryBarrier();
143 return m_state > 1;
159 return m_state > 1;
144 }
160 }
145 }
161 }
146
162
147 public bool IsCancelled {
163 public bool IsCancelled {
148 get {
164 get {
149 Thread.MemoryBarrier();
165 Thread.MemoryBarrier();
150 return m_state == CANCELLED_STATE;
166 return m_state == CANCELLED_STATE;
151 }
167 }
152 }
168 }
153
169
154 public Type PromiseType {
170 public Type PromiseType {
155 get { return typeof(T); }
171 get { return typeof(T); }
156 }
172 }
157
173
158 /// <summary>
174 /// <summary>
159 /// Выполняет обещание, сообщая об успешном выполнении.
175 /// Выполняет обещание, сообщая об успешном выполнении.
160 /// </summary>
176 /// </summary>
161 /// <param name="result">Результат выполнения.</param>
177 /// <param name="result">Результат выполнения.</param>
162 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
178 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
163 public void Resolve(T result) {
179 public void Resolve(T result) {
164 if (BeginTransit()) {
180 if (BeginTransit()) {
165 m_result = result;
181 m_result = result;
166 CompleteTransit(SUCCEEDED_STATE);
182 CompleteTransit(SUCCEEDED_STATE);
167 OnStateChanged();
183 OnStateChanged();
168 } else {
184 } else {
169 WaitTransition();
185 WaitTransition();
170 if (m_state != CANCELLED_STATE)
186 if (m_state != CANCELLED_STATE)
171 throw new InvalidOperationException("The promise is already resolved");
187 throw new InvalidOperationException("The promise is already resolved");
172 }
188 }
173 }
189 }
174
190
175 /// <summary>
191 /// <summary>
176 /// Выполняет обещание, сообщая об успешном выполнении. Результатом выполнения будет пустое значения.
192 /// Выполняет обещание, сообщая об успешном выполнении. Результатом выполнения будет пустое значения.
177 /// </summary>
193 /// </summary>
178 /// <remarks>
194 /// <remarks>
179 /// Данный вариант удобен в случаях, когда интересен факт выполнения операции, нежели полученное значение.
195 /// Данный вариант удобен в случаях, когда интересен факт выполнения операции, нежели полученное значение.
180 /// </remarks>
196 /// </remarks>
181 public void Resolve() {
197 public void Resolve() {
182 Resolve(default(T));
198 Resolve(default(T));
183 }
199 }
184
200
185 /// <summary>
201 /// <summary>
186 /// Выполняет обещание, сообщая об ошибке
202 /// Выполняет обещание, сообщая об ошибке
187 /// </summary>
203 /// </summary>
188 /// <remarks>
204 /// <remarks>
189 /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков
205 /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков
190 /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные
206 /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные
191 /// будут проигнорированы.
207 /// будут проигнорированы.
192 /// </remarks>
208 /// </remarks>
193 /// <param name="error">Исключение возникшее при выполнении операции</param>
209 /// <param name="error">Исключение возникшее при выполнении операции</param>
194 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
210 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
195 public void Reject(Exception error) {
211 public void Reject(Exception error) {
196 if (BeginTransit()) {
212 if (BeginTransit()) {
197 m_error = error is TransientPromiseException ? error.InnerException : error;
213 m_error = error is TransientPromiseException ? error.InnerException : error;
198 CompleteTransit(REJECTED_STATE);
214 CompleteTransit(REJECTED_STATE);
199 OnStateChanged();
215 OnStateChanged();
200 } else {
216 } else {
201 WaitTransition();
217 WaitTransition();
202 if (m_state == SUCCEEDED_STATE)
218 if (m_state == SUCCEEDED_STATE)
203 throw new InvalidOperationException("The promise is already resolved");
219 throw new InvalidOperationException("The promise is already resolved");
204 }
220 }
205 }
221 }
206
222
207 /// <summary>
223 /// <summary>
208 /// Отменяет операцию, если это возможно.
224 /// Отменяет операцию, если это возможно.
209 /// </summary>
225 /// </summary>
210 /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks>
226 /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks>
211 public void Cancel() {
227 public void Cancel() {
212 if (BeginTransit()) {
228 if (BeginTransit()) {
213 CompleteTransit(CANCELLED_STATE);
229 CompleteTransit(CANCELLED_STATE);
214 OnStateChanged();
230 OnStateChanged();
215 }
231 }
216 }
232 }
217
233
218 public IPromise<T> Then(Action<T> success, Func<Exception,T> error, Action cancel) {
219 if (success == null && error == null && cancel == null)
220 return this;
221
222 var medium = new Promise<T>(this);
223
224 AddHandler(success, error, cancel, medium, true);
225
226 return medium;
227 }
228
229 /// <summary>
230 /// Adds new handlers to this promise.
231 /// </summary>
232 /// <param name="success">The handler of the successfully completed operation.
233 /// This handler will recieve an operation result as a parameter.</param>
234 /// <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>
235 /// <returns>The new promise chained to this one.</returns>
236 public IPromise<T> Then(Action<T> success, Func<Exception,T> error) {
237 if (success == null && error == null)
238 return this;
239
240 var medium = new Promise<T>(this);
241
242 AddHandler(success, error, null, medium, true);
243
244 return medium;
245 }
246
247
248
249
250 public IPromise<T> Then(Action<T> success) {
251 if (success == null)
252 return this;
253
254 var medium = new Promise<T>(this);
255
256 AddHandler(success, null, null, medium, true);
257
258 return medium;
259 }
260
261 /// <summary>
234 /// <summary>
262 /// Последний обработчик в цепочки обещаний.
235 /// Последний обработчик в цепочки обещаний.
263 /// </summary>
236 /// </summary>
264 /// <param name="success"></param>
237 /// <param name="success"></param>
265 /// <param name="error"></param>
238 /// <param name="error"></param>
266 /// <param name="cancel"></param>
239 /// <param name="cancel"></param>
267 /// <remarks>
240 /// <remarks>
268 /// <para>
241 /// <para>
269 /// Данный метод не создает связанного с текущим обещания и предназначен для окончания
242 /// Данный метод не создает связанного с текущим обещания и предназначен для окончания
270 /// фсинхронной цепочки.
243 /// фсинхронной цепочки.
271 /// </para>
244 /// </para>
272 /// <para>
245 /// <para>
273 /// Если данный метод вызвать несколько раз, либо добавить другие обработчики, то цепочка
246 /// Если данный метод вызвать несколько раз, либо добавить другие обработчики, то цепочка
274 /// не будет одиночной <see cref="IsExclusive"/> и, как следствие, будет невозможна отмена
247 /// не будет одиночной <see cref="IsExclusive"/> и, как следствие, будет невозможна отмена
275 /// всей цепи обещаний снизу (с самого последнего обещания).
248 /// всей цепи обещаний снизу (с самого последнего обещания).
276 /// </para>
249 /// </para>
277 /// </remarks>
250 /// </remarks>
278 public void On(Action<T> success, Action<Exception> error, Action cancel) {
251 public void On(Action<T> success, Action<Exception> error, Action cancel) {
279 if (success == null && error == null && cancel == null)
252 if (success == null && error == null && cancel == null)
280 return;
253 return;
281
254
282 Func<Exception,T> errorHandler = null;
255 AddHandler(
283 if (error != null)
256 success != null ? new Func<T,T>(x => {
284 errorHandler = err => {
257 success(x);
285 error(err);
258 return x;
259 }) : null,
260 error != null ? new Func<Exception,T>(e => {
261 error(e);
286 return default(T);
262 return default(T);
287 };
263 }) : null,
288 AddHandler(success, errorHandler, cancel, null, false);
264 cancel,
265 null,
266 false
267 );
289 }
268 }
290
269
291 public void On(Action<T> success, Action<Exception> error) {
270 public void On(Action<T> success, Action<Exception> error) {
292 On(success, error, null);
271 On(success, error, null);
293 }
272 }
294
273
295 public void On(Action<T> success) {
274 public void On(Action<T> success) {
296 On(success, null, null);
275 On(success, null, null);
297 }
276 }
298
277
299 public void On(Action handler, PromiseEventType events) {
278 public void On(Action handler, PromiseEventType events) {
300 Safe.ArgumentNotNull(handler, "handler");
279 Safe.ArgumentNotNull(handler, "handler");
301
280
302 Action<T> success = events.HasFlag(PromiseEventType.Success) ? new Action<T>(x => handler()) : null;
281 Func<T,T> success = events.HasFlag(PromiseEventType.Success) ? new Func<T,T>(x => {
282 handler();
283 return x;
284 }) : null;
303 Func<Exception,T> error = events.HasFlag(PromiseEventType.Error) ? new Func<Exception,T>(e => {
285 Func<Exception,T> error = events.HasFlag(PromiseEventType.Error) ? new Func<Exception,T>(e => {
304 handler();
286 handler();
305 return default(T);
287 return default(T);
306 }) : null;
288 }) : null;
307 Action cancel = events.HasFlag(PromiseEventType.Cancelled) ? handler : null;
289 Action cancel = events.HasFlag(PromiseEventType.Cancelled) ? handler : null;
308
290
309 AddHandler(success, error, cancel, null, false);
291 AddHandler(success, error, cancel, null, false);
310 }
292 }
311
293
312 public IPromise Error(Action<Exception> error) {
294 public IPromise Error(Action<Exception> error) {
313 if (error == null)
295 if (error == null)
314 return this;
296 return this;
315
297
316 var medium = new Promise<T>(this);
298 var medium = new Promise<T>(this);
317
299
318 AddHandler(
300 AddHandler(
319 null,
301 null,
320 e => {
302 e => {
321 error(e);
303 error(e);
322 return default(T);
304 return default(T);
323 },
305 },
324 null,
306 null,
325 medium,
307 medium,
326 true
308 true
327 );
309 );
328
310
329 return medium;
311 return medium;
330 }
312 }
331
313
332 /// <summary>
314 /// <summary>
333 /// Handles error and allows to keep the promise.
315 /// Handles error and allows to keep the promise.
334 /// </summary>
316 /// </summary>
335 /// <remarks>
317 /// <remarks>
336 /// If the specified handler throws an exception, this exception will be used to reject the promise.
318 /// If the specified handler throws an exception, this exception will be used to reject the promise.
337 /// </remarks>
319 /// </remarks>
338 /// <param name="handler">The error handler which returns the result of the promise.</param>
320 /// <param name="handler">The error handler which returns the result of the promise.</param>
339 /// <returns>New promise.</returns>
321 /// <returns>New promise.</returns>
340 public IPromise<T> Error(Func<Exception,T> handler) {
322 public IPromise<T> Error(Func<Exception,T> handler) {
341 if (handler == null)
323 if (handler == null)
342 return this;
324 return this;
343
325
344 var medium = new Promise<T>(this);
326 var medium = new Promise<T>(this);
345
327
346 AddHandler(null, handler, null, medium, true);
328 AddHandler(null, handler, null, medium, true);
347
329
348 return medium;
330 return medium;
349 }
331 }
350
332
351 /// <summary>
333 /// <summary>
352 /// Позволяет преобразовать результат выполения операции к новому типу.
334 /// Позволяет преобразовать результат выполения операции к новому типу.
353 /// </summary>
335 /// </summary>
354 /// <typeparam name="TNew">Новый тип результата.</typeparam>
336 /// <typeparam name="TNew">Новый тип результата.</typeparam>
355 /// <param name="mapper">Преобразование результата к новому типу.</param>
337 /// <param name="mapper">Преобразование результата к новому типу.</param>
356 /// <param name="error">Обработчик ошибки. Данный обработчик получит
338 /// <param name="error">Обработчик ошибки. Данный обработчик получит
357 /// исключение возникшее при выполнении операции.</param>
339 /// исключение возникшее при выполнении операции.</param>
358 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
340 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
359 /// <param name = "cancel"></param>
341 /// <param name = "cancel"></param>
360 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper, Func<Exception,TNew> error, Action cancel) {
342 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper, Func<Exception,TNew> error, Action cancel) {
361 Safe.ArgumentNotNull(mapper, "mapper");
343 Safe.ArgumentNotNull(mapper, "mapper");
362
344
363 // создаем прицепленное обещание
345 // создаем прицепленное обещание
364 var medium = new Promise<TNew>(this);
346 var medium = new Promise<TNew>(this);
365
347
366 Action<T> resultHandler = result => medium.Resolve(mapper(result));
367 Func<Exception,T> errorHandler;
368 if (error != null)
369 errorHandler = e => {
370 try {
371 medium.Resolve(error(e));
372 } catch (Exception e2) {
373 // в случае ошибки нужно передать исключение дальше по цепочке
374 medium.Reject(e2);
375 }
376 return default(T);
377 };
378 else
379 errorHandler = e => {
380 medium.Reject(e);
381 return default(T);
382 };
383
384 Action cancelHandler;
385 if (cancel != null)
386 cancelHandler = () => {
387 cancel();
388 medium.Cancel();
389 };
390 else
391 cancelHandler = medium.Cancel;
392
393
394 AddHandler(
348 AddHandler(
395 resultHandler,
349 mapper,
396 errorHandler,
350 error,
397 cancelHandler,
351 cancel,
398 null,
352 medium,
399 true
353 true
400 );
354 );
401
355
402 return medium;
356 return medium;
403 }
357 }
404
358
405 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper, Func<Exception,TNew> error) {
359 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper, Func<Exception,TNew> error) {
406 return Then(mapper, error, null);
360 return Then(mapper, error, null);
407 }
361 }
408
362
409 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper) {
363 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper) {
410 return Then(mapper, null, null);
364 return Then(mapper, null, null);
411 }
365 }
412
366
413 /// <summary>
367 /// <summary>
414 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после
368 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после
415 /// выполнения текущей, а результат текущей операции может быть использован для инициализации
369 /// выполнения текущей, а результат текущей операции может быть использован для инициализации
416 /// новой операции.
370 /// новой операции.
417 /// </summary>
371 /// </summary>
418 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam>
372 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam>
419 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param>
373 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param>
420 /// <param name="error">Обработчик ошибки. Данный обработчик получит
374 /// <param name="error">Обработчик ошибки. Данный обработчик получит
421 /// исключение возникшее при выполнении текуещй операции.</param>
375 /// исключение возникшее при выполнении текуещй операции.</param>
422 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
376 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
423 /// <param name = "cancel"></param>
377 /// <param name = "cancel"></param>
424 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained, Func<Exception,IPromise<TNew>> error, Action cancel) {
378 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained, Func<Exception,IPromise<TNew>> error, Action cancel) {
425
379
426 Safe.ArgumentNotNull(chained, "chained");
380 Safe.ArgumentNotNull(chained, "chained");
427
381
428 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
382 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
429 // создать посредника, к которому будут подвызяваться следующие обработчики.
383 // создать посредника, к которому будут подвызяваться следующие обработчики.
430 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы
384 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы
431 // передать через него результаты работы.
385 // передать через него результаты работы.
432 var medium = new Promise<TNew>(this);
386 var medium = new Promise<TNew>(this);
433
387
434 Action<T> resultHandler = delegate(T result) {
388 Func<T,T> resultHandler = delegate(T result) {
435 if (medium.IsCancelled)
389 if (medium.IsCancelled)
436 return;
390 return default(T);
437
391
438 var promise = chained(result);
392 var promise = chained(result);
439
393
440 promise.On(
394 promise.On(
441 medium.Resolve,
395 medium.Resolve,
442 medium.Reject,
396 medium.Reject,
443 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
397 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
444 );
398 );
445
399
446 // notify chained operation that it's not needed anymore
400 // notify chained operation that it's not needed anymore
447 // порядок вызова Then, Cancelled важен, поскольку от этого
401 // порядок вызова Then, Cancelled важен, поскольку от этого
448 // зависит IsExclusive
402 // зависит IsExclusive
449 medium.On(
403 medium.On(
450 null,
404 null,
451 null,
405 null,
452 () => {
406 () => {
453 if (promise.IsExclusive)
407 if (promise.IsExclusive)
454 promise.Cancel();
408 promise.Cancel();
455 }
409 }
456 );
410 );
411
412 return default(T);
457 };
413 };
458
414
459 Func<Exception,T> errorHandler;
415 Func<Exception,T> errorHandler;
460
416
461 if (error != null)
417 if (error != null)
462 errorHandler = delegate(Exception e) {
418 errorHandler = delegate(Exception e) {
463 try {
419 try {
464 var promise = error(e);
420 var promise = error(e);
465
421
466 promise.On(
422 promise.On(
467 medium.Resolve,
423 medium.Resolve,
468 medium.Reject,
424 medium.Reject,
469 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
425 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
470 );
426 );
471
427
472 // notify chained operation that it's not needed anymore
428 // notify chained operation that it's not needed anymore
473 // порядок вызова Then, Cancelled важен, поскольку от этого
429 // порядок вызова Then, Cancelled важен, поскольку от этого
474 // зависит IsExclusive
430 // зависит IsExclusive
475 medium.Cancelled(() => {
431 medium.Cancelled(() => {
476 if (promise.IsExclusive)
432 if (promise.IsExclusive)
477 promise.Cancel();
433 promise.Cancel();
478 });
434 });
479 } catch (Exception e2) {
435 } catch (Exception e2) {
480 medium.Reject(e2);
436 medium.Reject(e2);
481 }
437 }
482 return default(T);
438 return default(T);
483 };
439 };
484 else
440 else
485 errorHandler = err => {
441 errorHandler = err => {
486 medium.Reject(err);
442 medium.Reject(err);
487 return default(T);
443 return default(T);
488 };
444 };
489
445
490
446
491 Action cancelHandler;
447 Action cancelHandler;
492 if (cancel != null)
448 if (cancel != null)
493 cancelHandler = () => {
449 cancelHandler = () => {
494 if (cancel != null)
450 if (cancel != null)
495 cancel();
451 cancel();
496 medium.Cancel();
452 medium.Cancel();
497 };
453 };
498 else
454 else
499 cancelHandler = medium.Cancel;
455 cancelHandler = medium.Cancel;
500
456
501 AddHandler(
457 AddHandler(
502 resultHandler,
458 resultHandler,
503 errorHandler,
459 errorHandler,
504 cancelHandler,
460 cancelHandler,
505 null,
461 null,
506 true
462 true
507 );
463 );
508
464
509 return medium;
465 return medium;
510 }
466 }
511
467
512 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained, Func<Exception,IPromise<TNew>> error) {
468 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained, Func<Exception,IPromise<TNew>> error) {
513 return Chain(chained, error, null);
469 return Chain(chained, error, null);
514 }
470 }
515
471
516 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained) {
472 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained) {
517 return Chain(chained, null, null);
473 return Chain(chained, null, null);
518 }
474 }
519
475
520 public IPromise<T> Cancelled(Action handler) {
476 public IPromise<T> Cancelled(Action handler) {
521 var medium = new Promise<T>(this);
477 var medium = new Promise<T>(this);
522 AddHandler(null, null, handler, medium, false);
478 AddHandler(null, null, handler, medium, false);
523 return medium;
479 return medium;
524 }
480 }
525
481
526 /// <summary>
482 /// <summary>
527 /// Adds the specified handler for all cases (success, error, cancel)
483 /// Adds the specified handler for all cases (success, error, cancel)
528 /// </summary>
484 /// </summary>
529 /// <param name="handler">The handler that will be called anyway</param>
485 /// <param name="handler">The handler that will be called anyway</param>
530 /// <returns>self</returns>
486 /// <returns>self</returns>
531 public IPromise<T> Anyway(Action handler) {
487 public IPromise<T> Anyway(Action handler) {
532 Safe.ArgumentNotNull(handler, "handler");
488 Safe.ArgumentNotNull(handler, "handler");
533
489
534 var medium = new Promise<T>(this);
490 var medium = new Promise<T>(this);
535
491
536 AddHandler(
492 AddHandler(
537 x => handler(),
493 x => {
494 handler();
495 return x;
496 },
538 e => {
497 e => {
539 handler();
498 handler();
540 throw new TransientPromiseException(e);
499 throw new TransientPromiseException(e);
541 },
500 },
542 handler,
501 handler,
543 medium,
502 medium,
544 true
503 true
545 );
504 );
546
505
547 return medium;
506 return medium;
548 }
507 }
549
508
550 /// <summary>
509 /// <summary>
551 /// Преобразует результат обещания к нужному типу
510 /// Преобразует результат обещания к нужному типу
552 /// </summary>
511 /// </summary>
553 /// <typeparam name="T2"></typeparam>
512 /// <typeparam name="T2"></typeparam>
554 /// <returns></returns>
513 /// <returns></returns>
555 public IPromise<T2> Cast<T2>() {
514 public IPromise<T2> Cast<T2>() {
556 return Then(x => (T2)(object)x, null);
515 return Then(x => (T2)(object)x, null);
557 }
516 }
558
517
559 /// <summary>
518 /// <summary>
560 /// Дожидается отложенного обещания и в случае успеха, возвращает
519 /// Дожидается отложенного обещания и в случае успеха, возвращает
561 /// его, результат, в противном случае бросает исключение.
520 /// его, результат, в противном случае бросает исключение.
562 /// </summary>
521 /// </summary>
563 /// <remarks>
522 /// <remarks>
564 /// <para>
523 /// <para>
565 /// Если ожидание обещания было прервано по таймауту, это не значит,
524 /// Если ожидание обещания было прервано по таймауту, это не значит,
566 /// что обещание было отменено или что-то в этом роде, это только
525 /// что обещание было отменено или что-то в этом роде, это только
567 /// означает, что мы его не дождались, однако все зарегистрированные
526 /// означает, что мы его не дождались, однако все зарегистрированные
568 /// обработчики, как были так остались и они будут вызваны, когда
527 /// обработчики, как были так остались и они будут вызваны, когда
569 /// обещание будет выполнено.
528 /// обещание будет выполнено.
570 /// </para>
529 /// </para>
571 /// <para>
530 /// <para>
572 /// Такое поведение вполне оправдано поскольку таймаут может истечь
531 /// Такое поведение вполне оправдано поскольку таймаут может истечь
573 /// в тот момент, когда началась обработка цепочки обработчиков, и
532 /// в тот момент, когда началась обработка цепочки обработчиков, и
574 /// к тому же текущее обещание может стоять в цепочке обещаний и его
533 /// к тому же текущее обещание может стоять в цепочке обещаний и его
575 /// отклонение может привести к непрогнозируемому результату.
534 /// отклонение может привести к непрогнозируемому результату.
576 /// </para>
535 /// </para>
577 /// </remarks>
536 /// </remarks>
578 /// <param name="timeout">Время ожидания</param>
537 /// <param name="timeout">Время ожидания</param>
579 /// <returns>Результат выполнения обещания</returns>
538 /// <returns>Результат выполнения обещания</returns>
580 public T Join(int timeout) {
539 public T Join(int timeout) {
581 var evt = new ManualResetEvent(false);
540 var evt = new ManualResetEvent(false);
582 Anyway(() => evt.Set());
541 Anyway(() => evt.Set());
583
542
584 if (!evt.WaitOne(timeout, true))
543 if (!evt.WaitOne(timeout, true))
585 throw new TimeoutException();
544 throw new TimeoutException();
586
545
587 switch (m_state) {
546 switch (m_state) {
588 case SUCCEEDED_STATE:
547 case SUCCEEDED_STATE:
589 return m_result;
548 return m_result;
590 case CANCELLED_STATE:
549 case CANCELLED_STATE:
591 throw new OperationCanceledException();
550 throw new OperationCanceledException();
592 case REJECTED_STATE:
551 case REJECTED_STATE:
593 throw new TargetInvocationException(m_error);
552 throw new TargetInvocationException(m_error);
594 default:
553 default:
595 throw new ApplicationException(String.Format("Invalid promise state {0}", m_state));
554 throw new ApplicationException(String.Format("Invalid promise state {0}", m_state));
596 }
555 }
597 }
556 }
598
557
599 public T Join() {
558 public T Join() {
600 return Join(Timeout.Infinite);
559 return Join(Timeout.Infinite);
601 }
560 }
602
561
603 void AddHandler(Action<T> success, Func<Exception,T> error, Action cancel, Promise<T> medium, bool inc) {
562 void AddHandler<T2>(Func<T,T2> success, Func<Exception,T2> error, Action cancel, Promise<T2> medium, bool inc) {
604 if (inc)
563 if (inc)
605 Interlocked.Increment(ref m_childrenCount);
564 Interlocked.Increment(ref m_childrenCount);
606
565
607 var handler = new HandlerDescriptor {
566 AbstractHandler handler = new HandlerDescriptor<T2>(success, error, cancel, medium);
608 resultHandler = success,
609 errorHandler = error,
610 cancellHandler = cancel,
611 medium = medium
612 };
613
567
614 bool queued;
568 bool queued;
615
569
616 if (!IsResolved) {
570 if (!IsResolved) {
617 m_handlers.Enqueue(handler);
571 m_handlers.Enqueue(handler);
618 queued = true;
572 queued = true;
619 } else {
573 } else {
620 // the promise is in resolved state, just invoke the handled with minimum overhead
574 // the promise is in resolved state, just invoke the handled with minimum overhead
621 queued = false;
575 queued = false;
622 InvokeHandler(handler);
576 InvokeHandler(handler);
623 }
577 }
624
578
625 if (queued && IsResolved && m_handlers.TryDequeue(out handler))
579 if (queued && IsResolved && m_handlers.TryDequeue(out handler))
626 // if the promise have been resolved while we was adding handler to the queue
580 // if the promise have been resolved while we was adding handler to the queue
627 // we can't guarantee that someone is still processing it
581 // we can't guarantee that someone is still processing it
628 // therefore we will fetch a handler from the queue and execute it
582 // therefore we will fetch a handler from the queue and execute it
629 // note that fetched handler may be not the one that we have added
583 // note that fetched handler may be not the one that we have added
630 // even we can fetch no handlers at all :)
584 // even we can fetch no handlers at all :)
631 InvokeHandler(handler);
585 InvokeHandler(handler);
632 }
586 }
633
587
634 protected virtual void InvokeHandler(HandlerDescriptor handler) {
588 protected virtual void InvokeHandler(AbstractHandler handler) {
635 switch (m_state) {
589 switch (m_state) {
636 case SUCCEEDED_STATE:
590 case SUCCEEDED_STATE:
637 handler.Resolve(m_result);
591 handler.Resolve(m_result);
638 break;
592 break;
639 case REJECTED_STATE:
593 case REJECTED_STATE:
640 handler.Reject(m_error);
594 handler.Reject(m_error);
641 break;
595 break;
642 case CANCELLED_STATE:
596 case CANCELLED_STATE:
643 handler.Cancel();
597 handler.Cancel();
644 break;
598 break;
645 default:
599 default:
646 // do nothing
600 // do nothing
647 return;
601 return;
648 }
602 }
649 }
603 }
650
604
651 void OnStateChanged() {
605 void OnStateChanged() {
652 HandlerDescriptor handler;
606 AbstractHandler handler;
653 while (m_handlers.TryDequeue(out handler))
607 while (m_handlers.TryDequeue(out handler))
654 InvokeHandler(handler);
608 InvokeHandler(handler);
655 }
609 }
656
610
657 public bool IsExclusive {
611 public bool IsExclusive {
658 get {
612 get {
659 return m_childrenCount <= 1;
613 return m_childrenCount <= 1;
660 }
614 }
661 }
615 }
662
616
663 /// <summary>
617 /// <summary>
664 /// Объединяет несколько обещаний в одно, результатом которого является массив результатов других обещаний.
618 /// Объединяет несколько обещаний в одно, результатом которого является массив результатов других обещаний.
665 /// Если хотябы одно из переданных обещаний не будет выполнено, то новое обещение тоже не будет выполнено.
619 /// Если хотябы одно из переданных обещаний не будет выполнено, то новое обещение тоже не будет выполнено.
666 /// При отмене нового обещания, переданные обещания также будут отменены, если никто больше на них не подписан.
620 /// При отмене нового обещания, переданные обещания также будут отменены, если никто больше на них не подписан.
667 /// </summary>
621 /// </summary>
668 /// <param name="promises">Список обещаний. Если список пустой, то результирующее обещание возвращается уже выполненным.</param>
622 /// <param name="promises">Список обещаний. Если список пустой, то результирующее обещание возвращается уже выполненным.</param>
669 /// <returns>Обещание объединяющее в себе результат переданных обещаний.</returns>
623 /// <returns>Обещание объединяющее в себе результат переданных обещаний.</returns>
670 /// <exception cref="ArgumentNullException"><paramref name="promises"/> не может быть null</exception>
624 /// <exception cref="ArgumentNullException"><paramref name="promises"/> не может быть null</exception>
671 public static IPromise<T[]> CreateComposite(IList<IPromise<T>> promises) {
625 public static IPromise<T[]> CreateComposite(IList<IPromise<T>> promises) {
672 if (promises == null)
626 if (promises == null)
673 throw new ArgumentNullException();
627 throw new ArgumentNullException();
674
628
675 // создаем аккумулятор для результатов и результирующее обещание
629 // создаем аккумулятор для результатов и результирующее обещание
676 var result = new T[promises.Count];
630 var result = new T[promises.Count];
677 var promise = new Promise<T[]>();
631 var promise = new Promise<T[]>();
678
632
679 // special case
633 // special case
680 if (promises.Count == 0) {
634 if (promises.Count == 0) {
681 promise.Resolve(result);
635 promise.Resolve(result);
682 return promise;
636 return promise;
683 }
637 }
684
638
685 int pending = promises.Count;
639 int pending = promises.Count;
686
640
687 for (int i = 0; i < promises.Count; i++) {
641 for (int i = 0; i < promises.Count; i++) {
688 var dest = i;
642 var dest = i;
689
643
690 if (promises[i] != null) {
644 if (promises[i] != null) {
691 promises[i].Then(
645 promises[i].On(
692 x => {
646 x => {
693 result[dest] = x;
647 result[dest] = x;
694 if (Interlocked.Decrement(ref pending) == 0)
648 if (Interlocked.Decrement(ref pending) == 0)
695 promise.Resolve(result);
649 promise.Resolve(result);
696 },
650 },
697 e => {
651 promise.Reject
698 promise.Reject(e);
699 return default(T);
700 }
701 );
652 );
702 } else {
653 } else {
703 if (Interlocked.Decrement(ref pending) == 0)
654 if (Interlocked.Decrement(ref pending) == 0)
704 promise.Resolve(result);
655 promise.Resolve(result);
705 }
656 }
706 }
657 }
707
658
708 promise.Cancelled(
659 promise.Cancelled(
709 () => {
660 () => {
710 foreach (var d in promises)
661 foreach (var d in promises)
711 if (d != null && d.IsExclusive)
662 if (d != null && d.IsExclusive)
712 d.Cancel();
663 d.Cancel();
713 }
664 }
714 );
665 );
715
666
716 return promise;
667 return promise;
717 }
668 }
718
669
719 /// <summary>
670 /// <summary>
720 /// Объединяет несколько обещаний в одно. Результирующее обещание будет выполнено при
671 /// Объединяет несколько обещаний в одно. Результирующее обещание будет выполнено при
721 /// выполнении всех указанных обещаний. При этом возвращаемые значения первичных обещаний
672 /// выполнении всех указанных обещаний. При этом возвращаемые значения первичных обещаний
722 /// игнорируются.
673 /// игнорируются.
723 /// </summary>
674 /// </summary>
724 /// <param name="promises">Коллекция первичных обещаний, которые будут объеденены в одно.</param>
675 /// <param name="promises">Коллекция первичных обещаний, которые будут объеденены в одно.</param>
725 /// <returns>Новое обещание, объединяющее в себе переданные.</returns>
676 /// <returns>Новое обещание, объединяющее в себе переданные.</returns>
726 /// <remarks>
677 /// <remarks>
727 /// Если в коллекции встречаюься <c>null</c>, то они воспринимаются как выполненные обещания.
678 /// Если в коллекции встречаюься <c>null</c>, то они воспринимаются как выполненные обещания.
728 /// </remarks>
679 /// </remarks>
729 public static IPromise CreateComposite(ICollection<IPromise> promises) {
680 public static IPromise CreateComposite(ICollection<IPromise> promises) {
730 if (promises == null)
681 if (promises == null)
731 throw new ArgumentNullException();
682 throw new ArgumentNullException();
732 if (promises.Count == 0)
683 if (promises.Count == 0)
733 return Promise<object>.ResultToPromise(null);
684 return Promise<object>.ResultToPromise(null);
734
685
735 int countdown = promises.Count;
686 int countdown = promises.Count;
736
687
737 var result = new Promise<object>();
688 var result = new Promise<object>();
738
689
739 foreach (var d in promises) {
690 foreach (var d in promises) {
740 if (d == null) {
691 if (d == null) {
741 if (Interlocked.Decrement(ref countdown) == 0)
692 if (Interlocked.Decrement(ref countdown) == 0)
742 result.Resolve(null);
693 result.Resolve(null);
743 } else {
694 } else {
744 d.Then(() => {
695 d.Then(() => {
745 if (Interlocked.Decrement(ref countdown) == 0)
696 if (Interlocked.Decrement(ref countdown) == 0)
746 result.Resolve(null);
697 result.Resolve(null);
747 });
698 });
748 }
699 }
749 }
700 }
750
701
751 result.Cancelled(() => {
702 result.Cancelled(() => {
752 foreach (var d in promises)
703 foreach (var d in promises)
753 if (d != null && d.IsExclusive)
704 if (d != null && d.IsExclusive)
754 d.Cancel();
705 d.Cancel();
755 });
706 });
756
707
757 return result;
708 return result;
758 }
709 }
759
710
760 public static Promise<T> ResultToPromise(T result) {
711 public static Promise<T> ResultToPromise(T result) {
761 var p = new Promise<T>();
712 var p = new Promise<T>();
762 p.Resolve(result);
713 p.Resolve(result);
763 return p;
714 return p;
764 }
715 }
765
716
766 public static Promise<T> ExceptionToPromise(Exception error) {
717 public static Promise<T> ExceptionToPromise(Exception error) {
767 if (error == null)
718 if (error == null)
768 throw new ArgumentNullException();
719 throw new ArgumentNullException();
769
720
770 var p = new Promise<T>();
721 var p = new Promise<T>();
771 p.Reject(error);
722 p.Reject(error);
772 return p;
723 return p;
773 }
724 }
774
725
775 #region IPromiseBase explicit implementation
726 #region IPromiseBase explicit implementation
776
727
777 IPromise IPromise.Then(Action success, Action<Exception> error, Action cancel) {
728 IPromise IPromise.Then(Action success, Action<Exception> error, Action cancel) {
778 return Then(
729 return Then(
779 success != null ? new Action<T>(x => success()) : null,
730 success != null ? new Func<T,T>(x => {
731 success();
732 return x;
733 }) : null,
780 error != null ? new Func<Exception,T>(e => {
734 error != null ? new Func<Exception,T>(e => {
781 error(e);
735 error(e);
782 return default(T);
736 return default(T);
783 }) : null,
737 }) : null,
784 cancel
738 cancel
785 );
739 );
786 }
740 }
787
741
788 IPromise IPromise.Then(Action success, Action<Exception> error) {
742 IPromise IPromise.Then(Action success, Action<Exception> error) {
789 return Then(
743 return Then(
790 success != null ? new Action<T>(x => success()) : null,
744 success != null ? new Func<T,T>(x => {
745 success();
746 return x;
747 }) : null,
791 error != null ? new Func<Exception,T>(e => {
748 error != null ? new Func<Exception,T>(e => {
792 error(e);
749 error(e);
793 return default(T);
750 return default(T);
794 }) : null
751 }) : null
795 );
752 );
796 }
753 }
797
754
798 IPromise IPromise.Then(Action success) {
755 IPromise IPromise.Then(Action success) {
799 Safe.ArgumentNotNull(success, "success");
756 Safe.ArgumentNotNull(success, "success");
800 return Then(x => success());
757 return Then(x => {
758 success();
759 return x;
760 });
801 }
761 }
802
762
803 IPromise IPromise.Chain(Func<IPromise> chained, Func<Exception,IPromise> error, Action cancel) {
763 IPromise IPromise.Chain(Func<IPromise> chained, Func<Exception,IPromise> error, Action cancel) {
804 return ChainNoResult(chained, error, cancel);
764 return ChainNoResult(chained, error, cancel);
805 }
765 }
806
766
807 IPromise ChainNoResult(Func<IPromise> chained, Func<Exception,IPromise> error, Action cancel) {
767 IPromise ChainNoResult(Func<IPromise> chained, Func<Exception,IPromise> error, Action cancel) {
808 Safe.ArgumentNotNull(chained, "chained");
768 Safe.ArgumentNotNull(chained, "chained");
809
769
810 var medium = new Promise<object>(this);
770 var medium = new Promise<object>(this);
811
771
812 Action<T> resultHandler = delegate {
772 Func<T,T> resultHandler = delegate {
813 if (medium.IsCancelled)
773 if (medium.IsCancelled)
814 return;
774 return default(T);
815
775
816 var promise = chained();
776 var promise = chained();
817
777
818 promise.On(
778 promise.On(
819 medium.Resolve,
779 medium.Resolve,
820 medium.Reject,
780 medium.Reject,
821 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
781 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
822 );
782 );
823
783
824 // notify chained operation that it's not needed anymore
784 // notify chained operation that it's not needed anymore
825 // порядок вызова Then, Cancelled важен, поскольку от этого
785 // порядок вызова Then, Cancelled важен, поскольку от этого
826 // зависит IsExclusive
786 // зависит IsExclusive
827 medium.Cancelled(() => {
787 medium.Cancelled(() => {
828 if (promise.IsExclusive)
788 if (promise.IsExclusive)
829 promise.Cancel();
789 promise.Cancel();
830 });
790 });
791
792 return default(T);
831 };
793 };
832
794
833 Func<Exception,T> errorHandler;
795 Func<Exception,T> errorHandler;
834
796
835 if (error != null)
797 if (error != null)
836 errorHandler = delegate(Exception e) {
798 errorHandler = delegate(Exception e) {
837 try {
799 try {
838 var promise = error(e);
800 var promise = error(e);
839
801
840 promise.On(
802 promise.On(
841 medium.Resolve,
803 medium.Resolve,
842 medium.Reject,
804 medium.Reject,
843 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
805 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
844 );
806 );
845
807
846 // notify chained operation that it's not needed anymore
808 // notify chained operation that it's not needed anymore
847 // порядок вызова Then, Cancelled важен, поскольку от этого
809 // порядок вызова Then, Cancelled важен, поскольку от этого
848 // зависит IsExclusive
810 // зависит IsExclusive
849 medium.Cancelled(() => {
811 medium.Cancelled(() => {
850 if (promise.IsExclusive)
812 if (promise.IsExclusive)
851 promise.Cancel();
813 promise.Cancel();
852 });
814 });
853 } catch (Exception e2) {
815 } catch (Exception e2) {
854 medium.Reject(e2);
816 medium.Reject(e2);
855 }
817 }
856 return default(T);
818 return default(T);
857 };
819 };
858 else
820 else
859 errorHandler = err => {
821 errorHandler = err => {
860 medium.Reject(err);
822 medium.Reject(err);
861 return default(T);
823 return default(T);
862 };
824 };
863
825
864
826
865 Action cancelHandler;
827 Action cancelHandler;
866 if (cancel != null)
828 if (cancel != null)
867 cancelHandler = () => {
829 cancelHandler = () => {
868 if (cancel != null)
830 if (cancel != null)
869 cancel();
831 cancel();
870 medium.Cancel();
832 medium.Cancel();
871 };
833 };
872 else
834 else
873 cancelHandler = medium.Cancel;
835 cancelHandler = medium.Cancel;
874
836
875 AddHandler(
837 AddHandler(
876 resultHandler,
838 resultHandler,
877 errorHandler,
839 errorHandler,
878 cancelHandler,
840 cancelHandler,
879 null,
841 null,
880 true
842 true
881 );
843 );
882
844
883 return medium;
845 return medium;
884 }
846 }
885
847
886 IPromise IPromise.Chain(Func<IPromise> chained, Func<Exception,IPromise> error) {
848 IPromise IPromise.Chain(Func<IPromise> chained, Func<Exception,IPromise> error) {
887 return ChainNoResult(chained, error, null);
849 return ChainNoResult(chained, error, null);
888 }
850 }
889
851
890 IPromise IPromise.Chain(Func<IPromise> chained) {
852 IPromise IPromise.Chain(Func<IPromise> chained) {
891 return ChainNoResult(chained, null, null);
853 return ChainNoResult(chained, null, null);
892 }
854 }
893
855
894
856
895 void IPromise.On(Action success, Action<Exception> error, Action cancel) {
857 void IPromise.On(Action success, Action<Exception> error, Action cancel) {
896 On(success != null ? new Action<T>(x => success()) : null, error, cancel);
858 On(success != null ? new Action<T>(x => success()) : null, error, cancel);
897 }
859 }
898
860
899 void IPromise.On(Action success, Action<Exception> error) {
861 void IPromise.On(Action success, Action<Exception> error) {
900 On(x => success(), error, null);
862 On(x => success(), error, null);
901 }
863 }
902
864
903 void IPromise.On(Action success) {
865 void IPromise.On(Action success) {
904 On(x => success(), null, null);
866 On(x => success(), null, null);
905 }
867 }
906
868
907 IPromise IPromise.Error(Action<Exception> error) {
869 IPromise IPromise.Error(Action<Exception> error) {
908 return Error(error);
870 return Error(error);
909 }
871 }
910
872
911 IPromise IPromise.Anyway(Action handler) {
873 IPromise IPromise.Anyway(Action handler) {
912 return Anyway(handler);
874 return Anyway(handler);
913 }
875 }
914
876
915 IPromise IPromise.Cancelled(Action handler) {
877 IPromise IPromise.Cancelled(Action handler) {
916 return Cancelled(handler);
878 return Cancelled(handler);
917 }
879 }
918
880
919 void IPromise.Join() {
881 void IPromise.Join() {
920 Join();
882 Join();
921 }
883 }
922
884
923 void IPromise.Join(int timeout) {
885 void IPromise.Join(int timeout) {
924 Join(timeout);
886 Join(timeout);
925 }
887 }
926
888
927 #endregion
889 #endregion
928
890
929
891
930
892
931 }
893 }
932 }
894 }
@@ -1,22 +1,22
1 using System.Threading;
1 using System.Threading;
2
2
3 namespace Implab {
3 namespace Implab {
4 public class SyncContextPromise<T> : Promise<T> {
4 public class SyncContextPromise<T> : Promise<T> {
5 readonly SynchronizationContext m_context;
5 readonly SynchronizationContext m_context;
6
6
7 public SyncContextPromise(SynchronizationContext context) {
7 public SyncContextPromise(SynchronizationContext context) {
8 Safe.ArgumentNotNull(context, "context");
8 Safe.ArgumentNotNull(context, "context");
9 m_context = context;
9 m_context = context;
10 }
10 }
11
11
12 public SyncContextPromise(SynchronizationContext context, IPromise parent)
12 public SyncContextPromise(SynchronizationContext context, IPromise parent)
13 : base(parent) {
13 : base(parent) {
14 Safe.ArgumentNotNull(context, "context");
14 Safe.ArgumentNotNull(context, "context");
15 m_context = context;
15 m_context = context;
16 }
16 }
17 protected override void InvokeHandler(HandlerDescriptor handler) {
17 protected override void InvokeHandler(AbstractHandler handler) {
18 m_context.Post(x => base.InvokeHandler(handler),null);
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