##// END OF EJS Templates
Refactoring
cin -
r66:790e8a997d30 default
parent child
Show More
@@ -0,0 +1,31
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Implab
7 {
8 public interface IPromise<T>: IPromise
9 {
10
11 new T Join();
12 new T Join(int timeout);
13
14 IPromise<T> Then(ResultHandler<T> success, ErrorHandler error);
15 IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error);
16 IPromise<T> Then(ResultHandler<T> success);
17 new IPromise<T> Error(ErrorHandler error);
18 IPromise<T> Error(ErrorHandler<T> error);
19
20 IPromise<T2> Map<T2>(ResultMapper<T,T2> mapper, ErrorHandler error);
21 IPromise<T2> Map<T2>(ResultMapper<T, T2> mapper);
22
23 IPromise<T2> Chain<T2>(ChainedOperation<T, T2> chained, ErrorHandler error);
24 IPromise<T2> Chain<T2>(ChainedOperation<T, T2> chained);
25
26 new IPromise<T> Cancelled(Action handler);
27 new IPromise<T> Finally(Action handler);
28 new IPromise<T> Anyway(Action handler);
29
30 }
31 }
@@ -1,122 +1,122
1 using Implab.Parallels;
1 using Implab.Parallels;
2 using System;
2 using System;
3 using System.Collections.Generic;
3 using System.Collections.Generic;
4 using System.Linq;
4 using System.Linq;
5 using System.Text;
5 using System.Text;
6 using System.Threading;
6 using System.Threading;
7 using System.Threading.Tasks;
7 using System.Threading.Tasks;
8 using System.Windows.Forms;
8 using System.Windows.Forms;
9
9
10 namespace Implab.Diagnostics.Interactive
10 namespace Implab.Diagnostics.Interactive
11 {
11 {
12 public class InteractiveListener: TextListenerBase
12 public class InteractiveListener: TextListenerBase
13 {
13 {
14 TraceForm m_form;
14 TraceForm m_form;
15
15
16 SynchronizationContext m_syncGuiThread;
16 SynchronizationContext m_syncGuiThread;
17 readonly Promise<object> m_guiStarted = new Promise<object>();
17 readonly Promise<object> m_guiStarted = new Promise<object>();
18
18
19 readonly IPromiseBase m_guiFinished;
19 readonly IPromise m_guiFinished;
20 readonly IPromiseBase m_workerFinished = new Promise<object>();
20 readonly IPromise m_workerFinished = new Promise<object>();
21
21
22 readonly MTQueue<TraceViewItem> m_queue = new MTQueue<TraceViewItem>();
22 readonly MTQueue<TraceViewItem> m_queue = new MTQueue<TraceViewItem>();
23 readonly AutoResetEvent m_queueEvent = new AutoResetEvent(false);
23 readonly AutoResetEvent m_queueEvent = new AutoResetEvent(false);
24
24
25 int m_queueLength;
25 int m_queueLength;
26 bool m_exitPending;
26 bool m_exitPending;
27
27
28 readonly object m_pauseLock = new object();
28 readonly object m_pauseLock = new object();
29 bool m_paused;
29 bool m_paused;
30 readonly ManualResetEvent m_pauseEvent = new ManualResetEvent(true);
30 readonly ManualResetEvent m_pauseEvent = new ManualResetEvent(true);
31
31
32 public InteractiveListener(bool global) : base(global) {
32 public InteractiveListener(bool global) : base(global) {
33 m_guiFinished = AsyncPool.InvokeNewThread(GuiThread);
33 m_guiFinished = AsyncPool.InvokeNewThread(GuiThread);
34 m_workerFinished = AsyncPool.InvokeNewThread(QueueThread);
34 m_workerFinished = AsyncPool.InvokeNewThread(QueueThread);
35
35
36 m_guiStarted.Join();
36 m_guiStarted.Join();
37 }
37 }
38
38
39 void GuiThread() {
39 void GuiThread() {
40 m_form = new TraceForm(); // will create SynchronizationContext
40 m_form = new TraceForm(); // will create SynchronizationContext
41
41
42 m_form.PauseEvents += (s,a) => Pause();
42 m_form.PauseEvents += (s,a) => Pause();
43 m_form.ResumeEvents += (s, a) => Resume();
43 m_form.ResumeEvents += (s, a) => Resume();
44
44
45 m_syncGuiThread = SynchronizationContext.Current;
45 m_syncGuiThread = SynchronizationContext.Current;
46 m_guiStarted.Resolve();
46 m_guiStarted.Resolve();
47 Application.Run();
47 Application.Run();
48 }
48 }
49
49
50 void QueueThread() {
50 void QueueThread() {
51 while (!m_exitPending) {
51 while (!m_exitPending) {
52 if (m_paused)
52 if (m_paused)
53 m_pauseEvent.WaitOne();
53 m_pauseEvent.WaitOne();
54
54
55 TraceViewItem item;
55 TraceViewItem item;
56 if (m_queue.TryDequeue(out item)) {
56 if (m_queue.TryDequeue(out item)) {
57 Interlocked.Decrement(ref m_queueLength);
57 Interlocked.Decrement(ref m_queueLength);
58
58
59 m_syncGuiThread.Send(x => m_form.AddTraceEvent(item),null);
59 m_syncGuiThread.Send(x => m_form.AddTraceEvent(item),null);
60 } else {
60 } else {
61 m_queueEvent.WaitOne();
61 m_queueEvent.WaitOne();
62 }
62 }
63 }
63 }
64 }
64 }
65
65
66 public void Pause() {
66 public void Pause() {
67 // for consistency we need to set this properties atomically
67 // for consistency we need to set this properties atomically
68 lock (m_pauseLock) {
68 lock (m_pauseLock) {
69 m_pauseEvent.Reset();
69 m_pauseEvent.Reset();
70 m_paused = true;
70 m_paused = true;
71 }
71 }
72 }
72 }
73
73
74 public void Resume() {
74 public void Resume() {
75 // for consistency we need to set this properties atomically
75 // for consistency we need to set this properties atomically
76 lock (m_pauseLock) {
76 lock (m_pauseLock) {
77 m_paused = false;
77 m_paused = false;
78 m_pauseEvent.Set();
78 m_pauseEvent.Set();
79 }
79 }
80 }
80 }
81
81
82 void Enqueue(TraceViewItem item) {
82 void Enqueue(TraceViewItem item) {
83 m_queue.Enqueue(item);
83 m_queue.Enqueue(item);
84 if (Interlocked.Increment(ref m_queueLength) == 1)
84 if (Interlocked.Increment(ref m_queueLength) == 1)
85 m_queueEvent.Set();
85 m_queueEvent.Set();
86 }
86 }
87
87
88 public void ShowForm() {
88 public void ShowForm() {
89 m_syncGuiThread.Post(x => m_form.Show(), null);
89 m_syncGuiThread.Post(x => m_form.Show(), null);
90 }
90 }
91
91
92 public void HideForm() {
92 public void HideForm() {
93 m_syncGuiThread.Post(x => m_form.Hide(), null);
93 m_syncGuiThread.Post(x => m_form.Hide(), null);
94 }
94 }
95
95
96 void Terminate() {
96 void Terminate() {
97 m_exitPending = true;
97 m_exitPending = true;
98 Resume();
98 Resume();
99 m_syncGuiThread.Post(x => Application.ExitThread(), null);
99 m_syncGuiThread.Post(x => Application.ExitThread(), null);
100 }
100 }
101
101
102 protected override void Dispose(bool disposing) {
102 protected override void Dispose(bool disposing) {
103 if (disposing) {
103 if (disposing) {
104 Terminate();
104 Terminate();
105 m_guiFinished.Join();
105 m_guiFinished.Join();
106 }
106 }
107 base.Dispose(disposing);
107 base.Dispose(disposing);
108 }
108 }
109
109
110 protected override void WriteEntry(TraceContext context, EventText text, string channel) {
110 protected override void WriteEntry(TraceContext context, EventText text, string channel) {
111 var item = new TraceViewItem {
111 var item = new TraceViewItem {
112 Indent = text.indent,
112 Indent = text.indent,
113 Message = text.content,
113 Message = text.content,
114 Thread = context.ThreadId,
114 Thread = context.ThreadId,
115 Channel = channel,
115 Channel = channel,
116 Timestamp = Environment.TickCount
116 Timestamp = Environment.TickCount
117 };
117 };
118
118
119 Enqueue(item);
119 Enqueue(item);
120 }
120 }
121 }
121 }
122 }
122 }
@@ -1,238 +1,238
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.Threading;
5 using System.Threading;
6 using System.Threading.Tasks;
6 using System.Threading.Tasks;
7
7
8 namespace Implab.Diagnostics {
8 namespace Implab.Diagnostics {
9 /// <summary>
9 /// <summary>
10 /// Контекст трассировки, привязывается к потоку и содержит в себе информацию о стеке логических операций.
10 /// Контекст трассировки, привязывается к потоку и содержит в себе информацию о стеке логических операций.
11 /// </summary>
11 /// </summary>
12 /// <remarks>
12 /// <remarks>
13 /// Контекст трассировки передается слушателям событий для определения места, где возникло событие.
13 /// Контекст трассировки передается слушателям событий для определения места, где возникло событие.
14 /// </remarks>
14 /// </remarks>
15 public class TraceContext {
15 public class TraceContext {
16 LogicalOperation m_currentOperation;
16 LogicalOperation m_currentOperation;
17 readonly LogicalOperation m_bound;
17 readonly LogicalOperation m_bound;
18 readonly int m_threadId;
18 readonly int m_threadId;
19
19
20 [ThreadStatic]
20 [ThreadStatic]
21 static TraceContext _current;
21 static TraceContext _current;
22
22
23 /// <summary>
23 /// <summary>
24 /// Текущий контекст трассировки для потока, создается астоматически при первом обращении.
24 /// Текущий контекст трассировки для потока, создается астоматически при первом обращении.
25 /// </summary>
25 /// </summary>
26 public static TraceContext Current {
26 public static TraceContext Current {
27 get {
27 get {
28 if (_current == null) {
28 if (_current == null) {
29 _current = new TraceContext();
29 _current = new TraceContext();
30 _current.LogEvent(TraceEventType.Created,"[{0}]", _current.ThreadId);
30 _current.LogEvent(TraceEventType.Created,"[{0}]", _current.ThreadId);
31 }
31 }
32 return _current;
32 return _current;
33 }
33 }
34 }
34 }
35
35
36 TraceContext(TraceContext context)
36 TraceContext(TraceContext context)
37 : this(context, false) {
37 : this(context, false) {
38 }
38 }
39
39
40 TraceContext(TraceContext context, bool attach) {
40 TraceContext(TraceContext context, bool attach) {
41 if (context == null)
41 if (context == null)
42 throw new ArgumentNullException("context");
42 throw new ArgumentNullException("context");
43
43
44 m_currentOperation = context.CurrentOperation;
44 m_currentOperation = context.CurrentOperation;
45 m_bound = attach ? context.BoundOperation : context.CurrentOperation;
45 m_bound = attach ? context.BoundOperation : context.CurrentOperation;
46 m_threadId = Thread.CurrentThread.ManagedThreadId;
46 m_threadId = Thread.CurrentThread.ManagedThreadId;
47 }
47 }
48
48
49 TraceContext() {
49 TraceContext() {
50 m_currentOperation = new LogicalOperation();
50 m_currentOperation = new LogicalOperation();
51 m_bound = m_currentOperation;
51 m_bound = m_currentOperation;
52 m_threadId = Thread.CurrentThread.ManagedThreadId;
52 m_threadId = Thread.CurrentThread.ManagedThreadId;
53 }
53 }
54
54
55 /// <summary>
55 /// <summary>
56 /// При необходимости копирует состояние контекста трассивровки в текущий поток.
56 /// При необходимости копирует состояние контекста трассивровки в текущий поток.
57 /// </summary>
57 /// </summary>
58 /// <param name="from">Исходный контекст трассировки, который передается.</param>
58 /// <param name="from">Исходный контекст трассировки, который передается.</param>
59 /// <remarks>
59 /// <remarks>
60 /// <para>
60 /// <para>
61 /// Копирование происходит за счет создания нового контекста трассировки и заполнением его
61 /// Копирование происходит за счет создания нового контекста трассировки и заполнением его
62 /// состояния из переданного контекста. При этом копируется стек операций, однако в новом
62 /// состояния из переданного контекста. При этом копируется стек операций, однако в новом
63 /// контексте ранее начатые логические операции не могут быть завершены.
63 /// контексте ранее начатые логические операции не могут быть завершены.
64 /// </para>
64 /// </para>
65 /// <para>
65 /// <para>
66 /// Если передача состояния состоялась, то вызывается событие трассировки <see cref="TraceEventType.Fork"/>.
66 /// Если передача состояния состоялась, то вызывается событие трассировки <see cref="TraceEventType.Fork"/>.
67 /// </para>
67 /// </para>
68 /// </remarks>
68 /// </remarks>
69 public static void Fork(TraceContext from) {
69 public static void Fork(TraceContext from) {
70 if (_current == from)
70 if (_current == from)
71 return;
71 return;
72 if (from != null) {
72 if (from != null) {
73 var context = new TraceContext(from);
73 var context = new TraceContext(from);
74 context.LogEvent(TraceEventType.Fork, "[{0}]-->[{1}]",from.ThreadId, context.ThreadId);
74 context.LogEvent(TraceEventType.Fork, "[{0}]-->[{1}]",from.ThreadId, context.ThreadId);
75 _current = context;
75 _current = context;
76 } else {
76 } else {
77 _current = new TraceContext();
77 _current = new TraceContext();
78 }
78 }
79 }
79 }
80
80
81 /// <summary>
81 /// <summary>
82 /// Задает текущему потоку указанный контекст, текущей поток может заканчивать ранее начатые
82 /// Задает текущему потоку указанный контекст, текущей поток может заканчивать ранее начатые
83 /// логические операции в указанном контексте.
83 /// логические операции в указанном контексте.
84 /// </summary>
84 /// </summary>
85 /// <param name="source"></param>
85 /// <param name="source"></param>
86 public static void Attach(TraceContext source) {
86 public static void Attach(TraceContext source) {
87 if (_current == source)
87 if (_current == source)
88 return;
88 return;
89 if (source != null) {
89 if (source != null) {
90 var context = new TraceContext(source, true);
90 var context = new TraceContext(source, true);
91 context.LogEvent(TraceEventType.Attach, "[{0}]-->[{1}]", source.ThreadId, context.ThreadId);
91 context.LogEvent(TraceEventType.Attach, "[{0}]-->[{1}]", source.ThreadId, context.ThreadId);
92 _current = context;
92 _current = context;
93 } else {
93 } else {
94 _current = new TraceContext();
94 _current = new TraceContext();
95 }
95 }
96 }
96 }
97
97
98 /// <summary>
98 /// <summary>
99 /// Отсоединяет текущий контекст трассировки от потока, для дальнейшей его передачи другому потоку
99 /// Отсоединяет текущий контекст трассировки от потока, для дальнейшей его передачи другому потоку
100 /// <see cref="Attach(TraceContext)"/>.
100 /// <see cref="Attach(TraceContext)"/>.
101 /// </summary>
101 /// </summary>
102 /// <returns>Контекст трассировки потока</returns>
102 /// <returns>Контекст трассировки потока</returns>
103 /// <remarks>
103 /// <remarks>
104 /// После отсоединения контекста трассировки от потока, при первом обращении к трассировке в этом
104 /// После отсоединения контекста трассировки от потока, при первом обращении к трассировке в этом
105 /// потоке будет создан новый контекст.
105 /// потоке будет создан новый контекст.
106 /// </remarks>
106 /// </remarks>
107 public static TraceContext Detach() {
107 public static TraceContext Detach() {
108 var context = Current;
108 var context = Current;
109 context.LogEvent(TraceEventType.Detach, null);
109 context.LogEvent(TraceEventType.Detach, null);
110 _current = null;
110 _current = null;
111 return context;
111 return context;
112 }
112 }
113
113
114 /// <summary>
114 /// <summary>
115 /// Создает постоянную копию текущего контекста, данную копию можно хранить и использовать для передачи через <see cref="Fork(TraceContext)"/>
115 /// Создает постоянную копию текущего контекста, данную копию можно хранить и использовать для передачи через <see cref="Fork(TraceContext)"/>
116 /// </summary>
116 /// </summary>
117 /// <returns>Копия текущего контекста трассировки.</returns>
117 /// <returns>Копия текущего контекста трассировки.</returns>
118 public static TraceContext Snapshot() {
118 public static TraceContext Snapshot() {
119 return _current == null ? new TraceContext() : new TraceContext(_current,false);
119 return _current == null ? new TraceContext() : new TraceContext(_current,false);
120 }
120 }
121
121
122 /// <summary>
122 /// <summary>
123 /// Выполняет переданное действие в указанном контексте трассировки, по окончании восстанавливает предыдущий контекст трассировки потока.
123 /// Выполняет переданное действие в указанном контексте трассировки, по окончании восстанавливает предыдущий контекст трассировки потока.
124 /// </summary>
124 /// </summary>
125 /// <param name="action"></param>
125 /// <param name="action"></param>
126 public void Invoke(Action action) {
126 public void Invoke(Action action) {
127 if (action == null)
127 if (action == null)
128 throw new ArgumentNullException("action");
128 throw new ArgumentNullException("action");
129 var old = _current;
129 var old = _current;
130 Fork(this);
130 Fork(this);
131 try {
131 try {
132 action();
132 action();
133 } finally {
133 } finally {
134 if(_current != null)
134 if(_current != null)
135 _current.EndAllOperations();
135 _current.EndAllOperations();
136 _current = old;
136 _current = old;
137 }
137 }
138 }
138 }
139
139
140 /// <summary>
140 /// <summary>
141 /// Текущая логическая операция.
141 /// Текущая логическая операция.
142 /// </summary>
142 /// </summary>
143 public LogicalOperation CurrentOperation {
143 public LogicalOperation CurrentOperation {
144 get {
144 get {
145 return m_currentOperation;
145 return m_currentOperation;
146 }
146 }
147 }
147 }
148
148
149 /// <summary>
149 /// <summary>
150 /// Операция ниже которой нельзя опускаться в стеке логических операций, т.е. она не может быть завершена в текущем контексте.
150 /// Операция ниже которой нельзя опускаться в стеке логических операций, т.е. она не может быть завершена в текущем контексте.
151 /// </summary>
151 /// </summary>
152 public LogicalOperation BoundOperation {
152 public LogicalOperation BoundOperation {
153 get {
153 get {
154 return m_bound;
154 return m_bound;
155 }
155 }
156 }
156 }
157
157
158 /// <summary>
158 /// <summary>
159 /// Поток, в котором создан контекст трассировки.
159 /// Поток, в котором создан контекст трассировки.
160 /// </summary>
160 /// </summary>
161 public int ThreadId {
161 public int ThreadId {
162 get {
162 get {
163 return m_threadId;
163 return m_threadId;
164 }
164 }
165 }
165 }
166
166
167 /// <summary>
167 /// <summary>
168 /// Начинает безымянную логическую операцию.
168 /// Начинает безымянную логическую операцию.
169 /// </summary>
169 /// </summary>
170 public void StartLogicalOperation() {
170 public void StartLogicalOperation() {
171 StartLogicalOperation(null);
171 StartLogicalOperation(null);
172 }
172 }
173
173
174 /// <summary>
174 /// <summary>
175 /// Начинает логическую операцию с указанным именем. Созданная операция будет добвалена в стек логических операций контекста, затем будет создано соответсвующее событие.
175 /// Начинает логическую операцию с указанным именем. Созданная операция будет добвалена в стек логических операций контекста, затем будет создано соответсвующее событие.
176 /// </summary>
176 /// </summary>
177 /// <param name="name">Имя начинаемой операции.</param>
177 /// <param name="name">Имя начинаемой операции.</param>
178 public void StartLogicalOperation(string name) {
178 public void StartLogicalOperation(string name) {
179 m_currentOperation = new LogicalOperation(name, m_currentOperation);
179 m_currentOperation = new LogicalOperation(name, m_currentOperation);
180 LogEvent(TraceEventType.OperationStarted, name);
180 LogEvent(TraceEventType.OperationStarted, name);
181 }
181 }
182
182
183 /// <summary>
183 /// <summary>
184 /// Заканчивает логическую операцию начатую в текущем контексте. Операции, начатые в других контекстах не могут быть закончены в текущем контексте.
184 /// Заканчивает логическую операцию начатую в текущем контексте. Операции, начатые в других контекстах не могут быть закончены в текущем контексте.
185 /// </summary>
185 /// </summary>
186 /// <remarks>
186 /// <remarks>
187 /// При вызове данного метода создается событие журнала трассировки, либо о завершении операции, либо об ошибки, поскольку данная операция
187 /// При вызове данного метода создается событие журнала трассировки, либо о завершении операции, либо об ошибки, поскольку данная операция
188 /// начата в другом контексте.
188 /// начата в другом контексте.
189 /// </remarks>
189 /// </remarks>
190 public void EndLogicalOperation() {
190 public void EndLogicalOperation() {
191 if (m_bound == m_currentOperation) {
191 if (m_bound == m_currentOperation) {
192 LogEvent(TraceEventType.Error, "Trying to end the operation which isn't belongs to current trace");
192 LogEvent(TraceEventType.Error, "Trying to end the operation which isn't belongs to current trace");
193 } else {
193 } else {
194 var op = m_currentOperation;
194 var op = m_currentOperation;
195 LogEvent(TraceEventType.OperationCompleted, "{0} {1} ms", op.Name, op.Duration);
195 LogEvent(TraceEventType.OperationCompleted, "{0} {1} ms", op.Name, op.Duration);
196 m_currentOperation = m_currentOperation.Parent;
196 m_currentOperation = m_currentOperation.Parent;
197 }
197 }
198 }
198 }
199
199
200 /// <summary>
200 /// <summary>
201 /// Создает копию контекста и возвращается на предыдущую операцию в текущем контексте, это позволяет начать операцию в одном потоке, а завершить - в другом.
201 /// Создает копию контекста и возвращается на предыдущую операцию в текущем контексте, это позволяет начать операцию в одном потоке, а завершить - в другом.
202 /// </summary>
202 /// </summary>
203 /// <returns>Контекст трассировки, который можно присоединить к другому потоку.</returns>
203 /// <returns>Контекст трассировки, который можно присоединить к другому потоку.</returns>
204 public TraceContext DetachLogicalOperation() {
204 public TraceContext DetachLogicalOperation() {
205 if (m_bound == m_currentOperation) {
205 if (m_bound == m_currentOperation) {
206 return new TraceContext();
206 return new TraceContext();
207 } else {
207 } else {
208 var detached = new TraceContext(this, true);
208 var detached = new TraceContext(this, true);
209 m_currentOperation = m_currentOperation.Parent;
209 m_currentOperation = m_currentOperation.Parent;
210 return detached;
210 return detached;
211 }
211 }
212 }
212 }
213
213
214 public void BindLogicalOperationToPromise(IPromiseBase promise) {
214 public void BindLogicalOperationToPromise(IPromise promise) {
215 Safe.ArgumentNotNull(promise, "promise");
215 Safe.ArgumentNotNull(promise, "promise");
216
216
217 var ctx = DetachLogicalOperation();
217 var ctx = DetachLogicalOperation();
218 promise.Finally(() => {
218 promise.Finally(() => {
219 var old = _current;
219 var old = _current;
220 TraceContext.Attach(ctx);
220 TraceContext.Attach(ctx);
221 TraceContext.Current.EndLogicalOperation();
221 TraceContext.Current.EndLogicalOperation();
222 _current = old;
222 _current = old;
223 });
223 });
224 }
224 }
225
225
226 /// <summary>
226 /// <summary>
227 /// Заврешает все начатые в этом контексте операции
227 /// Заврешает все начатые в этом контексте операции
228 /// </summary>
228 /// </summary>
229 public void EndAllOperations() {
229 public void EndAllOperations() {
230 while (m_bound != m_currentOperation)
230 while (m_bound != m_currentOperation)
231 EndLogicalOperation();
231 EndLogicalOperation();
232 }
232 }
233
233
234 void LogEvent(TraceEventType type, string format, params object[] args) {
234 void LogEvent(TraceEventType type, string format, params object[] args) {
235 LogChannel<TraceEvent>.Default.LogEvent(this, TraceEvent.Create(type, format, args));
235 LogChannel<TraceEvent>.Default.LogEvent(this, TraceEvent.Create(type, format, args));
236 }
236 }
237 }
237 }
238 }
238 }
@@ -1,55 +1,55
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Diagnostics;
3 using System.Diagnostics;
4 using System.Linq;
4 using System.Linq;
5 using System.Text;
5 using System.Text;
6 using System.Threading.Tasks;
6 using System.Threading.Tasks;
7
7
8 namespace Implab.Diagnostics {
8 namespace Implab.Diagnostics {
9 /// <summary>
9 /// <summary>
10 /// Класс для публикации событий выполнения программы, события публикуются через <see cref="LogChannel{TraceEvent}"/>.
10 /// Класс для публикации событий выполнения программы, события публикуются через <see cref="LogChannel{TraceEvent}"/>.
11 /// Журнал трассировки отражает логический ход выполнения программы и существует всегда, поскольку тесно связан с
11 /// Журнал трассировки отражает логический ход выполнения программы и существует всегда, поскольку тесно связан с
12 /// контекстом трассировки.
12 /// контекстом трассировки.
13 /// </summary>
13 /// </summary>
14 public static class TraceLog {
14 public static class TraceLog {
15 [Conditional("TRACE")]
15 [Conditional("TRACE")]
16 public static void StartLogicalOperation() {
16 public static void StartLogicalOperation() {
17 TraceContext.Current.StartLogicalOperation();
17 TraceContext.Current.StartLogicalOperation();
18 }
18 }
19
19
20 [Conditional("TRACE")]
20 [Conditional("TRACE")]
21 public static void StartLogicalOperation(string name) {
21 public static void StartLogicalOperation(string name) {
22 TraceContext.Current.StartLogicalOperation(name);
22 TraceContext.Current.StartLogicalOperation(name);
23 }
23 }
24
24
25 [Conditional("TRACE")]
25 [Conditional("TRACE")]
26 public static void EndLogicalOperation() {
26 public static void EndLogicalOperation() {
27 TraceContext.Current.EndLogicalOperation();
27 TraceContext.Current.EndLogicalOperation();
28 }
28 }
29
29
30 [Conditional("TRACE")]
30 [Conditional("TRACE")]
31 public static void BindLogicalOperationToPromise(IPromiseBase promise) {
31 public static void BindLogicalOperationToPromise(IPromise promise) {
32 TraceContext.Current.BindLogicalOperationToPromise(promise);
32 TraceContext.Current.BindLogicalOperationToPromise(promise);
33 }
33 }
34
34
35 [Conditional("TRACE")]
35 [Conditional("TRACE")]
36 public static void TraceInformation(string format, params object[] arguments) {
36 public static void TraceInformation(string format, params object[] arguments) {
37 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Information, format, arguments));
37 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Information, format, arguments));
38 }
38 }
39
39
40 [Conditional("TRACE")]
40 [Conditional("TRACE")]
41 public static void TraceWarning(string format, params object[] arguments) {
41 public static void TraceWarning(string format, params object[] arguments) {
42 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Warning, format, arguments));
42 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Warning, format, arguments));
43 }
43 }
44
44
45 [Conditional("TRACE")]
45 [Conditional("TRACE")]
46 public static void TraceError(string format, params object[] arguments) {
46 public static void TraceError(string format, params object[] arguments) {
47 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Error, format, arguments));
47 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Error, format, arguments));
48 }
48 }
49
49
50 [Conditional("TRACE")]
50 [Conditional("TRACE")]
51 public static void TraceError(Exception err) {
51 public static void TraceError(Exception err) {
52 TraceError("{0}", err);
52 TraceError("{0}", err);
53 }
53 }
54 }
54 }
55 }
55 }
@@ -1,31 +1,37
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
5
6 namespace Implab
6 namespace Implab {
7 {
7 public interface IPromise: ICancellable {
8 public interface IPromise<T>: IPromiseBase
8 /// <summary>
9 {
9 /// Check whereather the promise has no more than one dependent promise.
10 /// </summary>
11 bool IsExclusive {
12 get;
13 }
10
14
11 new T Join();
15 /// <summary>
12 new T Join(int timeout);
16 /// Тип результата, получаемого через данное обещание.
17 /// </summary>
18 Type PromiseType { get; }
13
19
14 IPromise<T> Then(ResultHandler<T> success, ErrorHandler error);
20 bool IsResolved { get; }
15 IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error);
21
16 IPromise<T> Then(ResultHandler<T> success);
22 bool IsCancelled { get; }
17 new IPromise<T> Error(ErrorHandler error);
18 IPromise<T> Error(ErrorHandler<T> error);
19
23
20 IPromise<T2> Map<T2>(ResultMapper<T,T2> mapper, ErrorHandler error);
24 IPromise Then(Action success,ErrorHandler error);
21 IPromise<T2> Map<T2>(ResultMapper<T, T2> mapper);
25 IPromise Then(Action success);
26 IPromise Error(ErrorHandler error);
27 IPromise Anyway(Action handler);
28 IPromise Finally(Action handler);
29 IPromise Cancelled(Action handler);
22
30
23 IPromise<T2> Chain<T2>(ChainedOperation<T, T2> chained, ErrorHandler error);
31 IPromise<T> Cast<T>();
24 IPromise<T2> Chain<T2>(ChainedOperation<T, T2> chained);
25
32
26 new IPromise<T> Cancelled(Action handler);
33 void Join();
27 new IPromise<T> Finally(Action handler);
34 void Join(int timeout);
28 new IPromise<T> Anyway(Action handler);
29
35
30 }
36 }
31 }
37 }
@@ -1,107 +1,107
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 <ProductVersion>10.0.0</ProductVersion>
6 <ProductVersion>10.0.0</ProductVersion>
7 <SchemaVersion>2.0</SchemaVersion>
7 <SchemaVersion>2.0</SchemaVersion>
8 <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
8 <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
9 <OutputType>Library</OutputType>
9 <OutputType>Library</OutputType>
10 <RootNamespace>Implab</RootNamespace>
10 <RootNamespace>Implab</RootNamespace>
11 <AssemblyName>Implab</AssemblyName>
11 <AssemblyName>Implab</AssemblyName>
12 </PropertyGroup>
12 </PropertyGroup>
13 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
13 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
14 <DebugSymbols>true</DebugSymbols>
14 <DebugSymbols>true</DebugSymbols>
15 <DebugType>full</DebugType>
15 <DebugType>full</DebugType>
16 <Optimize>false</Optimize>
16 <Optimize>false</Optimize>
17 <OutputPath>bin\Debug</OutputPath>
17 <OutputPath>bin\Debug</OutputPath>
18 <DefineConstants>TRACE;DEBUG;</DefineConstants>
18 <DefineConstants>TRACE;DEBUG;</DefineConstants>
19 <ErrorReport>prompt</ErrorReport>
19 <ErrorReport>prompt</ErrorReport>
20 <WarningLevel>4</WarningLevel>
20 <WarningLevel>4</WarningLevel>
21 <ConsolePause>false</ConsolePause>
21 <ConsolePause>false</ConsolePause>
22 <RunCodeAnalysis>true</RunCodeAnalysis>
22 <RunCodeAnalysis>true</RunCodeAnalysis>
23 </PropertyGroup>
23 </PropertyGroup>
24 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
24 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
25 <DebugType>full</DebugType>
25 <DebugType>full</DebugType>
26 <Optimize>true</Optimize>
26 <Optimize>true</Optimize>
27 <OutputPath>bin\Release</OutputPath>
27 <OutputPath>bin\Release</OutputPath>
28 <ErrorReport>prompt</ErrorReport>
28 <ErrorReport>prompt</ErrorReport>
29 <WarningLevel>4</WarningLevel>
29 <WarningLevel>4</WarningLevel>
30 <ConsolePause>false</ConsolePause>
30 <ConsolePause>false</ConsolePause>
31 </PropertyGroup>
31 </PropertyGroup>
32 <ItemGroup>
32 <ItemGroup>
33 <Reference Include="System" />
33 <Reference Include="System" />
34 <Reference Include="System.XML" />
34 <Reference Include="System.XML" />
35 </ItemGroup>
35 </ItemGroup>
36 <ItemGroup>
36 <ItemGroup>
37 <Compile Include="Component.cs" />
37 <Compile Include="Component.cs" />
38 <Compile Include="CustomEqualityComparer.cs" />
38 <Compile Include="CustomEqualityComparer.cs" />
39 <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
39 <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
40 <Compile Include="Diagnostics\EventText.cs" />
40 <Compile Include="Diagnostics\EventText.cs" />
41 <Compile Include="Diagnostics\IEventTextFormatter.cs" />
41 <Compile Include="Diagnostics\IEventTextFormatter.cs" />
42 <Compile Include="Diagnostics\LogChannel.cs" />
42 <Compile Include="Diagnostics\LogChannel.cs" />
43 <Compile Include="Diagnostics\LogicalOperation.cs" />
43 <Compile Include="Diagnostics\LogicalOperation.cs" />
44 <Compile Include="Diagnostics\TextFileListener.cs" />
44 <Compile Include="Diagnostics\TextFileListener.cs" />
45 <Compile Include="Diagnostics\TextListenerBase.cs" />
45 <Compile Include="Diagnostics\TextListenerBase.cs" />
46 <Compile Include="Diagnostics\TraceLog.cs" />
46 <Compile Include="Diagnostics\TraceLog.cs" />
47 <Compile Include="Diagnostics\TraceContext.cs" />
47 <Compile Include="Diagnostics\TraceContext.cs" />
48 <Compile Include="Diagnostics\TraceEvent.cs" />
48 <Compile Include="Diagnostics\TraceEvent.cs" />
49 <Compile Include="Diagnostics\TraceEventType.cs" />
49 <Compile Include="Diagnostics\TraceEventType.cs" />
50 <Compile Include="Disposable.cs" />
50 <Compile Include="Disposable.cs" />
51 <Compile Include="ICancellable.cs" />
51 <Compile Include="ICancellable.cs" />
52 <Compile Include="IProgressHandler.cs" />
52 <Compile Include="IProgressHandler.cs" />
53 <Compile Include="IProgressNotifier.cs" />
53 <Compile Include="IProgressNotifier.cs" />
54 <Compile Include="IPromiseT.cs" />
54 <Compile Include="IPromise.cs" />
55 <Compile Include="IPromise.cs" />
55 <Compile Include="IPromiseBase.cs" />
56 <Compile Include="IServiceLocator.cs" />
56 <Compile Include="IServiceLocator.cs" />
57 <Compile Include="ITaskController.cs" />
57 <Compile Include="ITaskController.cs" />
58 <Compile Include="JSON\JSONElementContext.cs" />
58 <Compile Include="JSON\JSONElementContext.cs" />
59 <Compile Include="JSON\JSONElementType.cs" />
59 <Compile Include="JSON\JSONElementType.cs" />
60 <Compile Include="JSON\JSONGrammar.cs" />
60 <Compile Include="JSON\JSONGrammar.cs" />
61 <Compile Include="JSON\JSONParser.cs" />
61 <Compile Include="JSON\JSONParser.cs" />
62 <Compile Include="JSON\JSONScanner.cs" />
62 <Compile Include="JSON\JSONScanner.cs" />
63 <Compile Include="JSON\JsonTokenType.cs" />
63 <Compile Include="JSON\JsonTokenType.cs" />
64 <Compile Include="JSON\JSONWriter.cs" />
64 <Compile Include="JSON\JSONWriter.cs" />
65 <Compile Include="JSON\JSONXmlReader.cs" />
65 <Compile Include="JSON\JSONXmlReader.cs" />
66 <Compile Include="JSON\JSONXmlReaderOptions.cs" />
66 <Compile Include="JSON\JSONXmlReaderOptions.cs" />
67 <Compile Include="JSON\StringTranslator.cs" />
67 <Compile Include="JSON\StringTranslator.cs" />
68 <Compile Include="Parallels\DispatchPool.cs" />
68 <Compile Include="Parallels\DispatchPool.cs" />
69 <Compile Include="Parallels\ArrayTraits.cs" />
69 <Compile Include="Parallels\ArrayTraits.cs" />
70 <Compile Include="Parallels\MTQueue.cs" />
70 <Compile Include="Parallels\MTQueue.cs" />
71 <Compile Include="Parallels\WorkerPool.cs" />
71 <Compile Include="Parallels\WorkerPool.cs" />
72 <Compile Include="Parsing\Alphabet.cs" />
72 <Compile Include="Parsing\Alphabet.cs" />
73 <Compile Include="Parsing\AlphabetBase.cs" />
73 <Compile Include="Parsing\AlphabetBase.cs" />
74 <Compile Include="Parsing\AltToken.cs" />
74 <Compile Include="Parsing\AltToken.cs" />
75 <Compile Include="Parsing\BinaryToken.cs" />
75 <Compile Include="Parsing\BinaryToken.cs" />
76 <Compile Include="Parsing\CatToken.cs" />
76 <Compile Include="Parsing\CatToken.cs" />
77 <Compile Include="Parsing\CDFADefinition.cs" />
77 <Compile Include="Parsing\CDFADefinition.cs" />
78 <Compile Include="Parsing\DFABuilder.cs" />
78 <Compile Include="Parsing\DFABuilder.cs" />
79 <Compile Include="Parsing\DFADefinitionBase.cs" />
79 <Compile Include="Parsing\DFADefinitionBase.cs" />
80 <Compile Include="Parsing\DFAStateDescriptor.cs" />
80 <Compile Include="Parsing\DFAStateDescriptor.cs" />
81 <Compile Include="Parsing\DFAutomaton.cs" />
81 <Compile Include="Parsing\DFAutomaton.cs" />
82 <Compile Include="Parsing\EDFADefinition.cs" />
82 <Compile Include="Parsing\EDFADefinition.cs" />
83 <Compile Include="Parsing\EmptyToken.cs" />
83 <Compile Include="Parsing\EmptyToken.cs" />
84 <Compile Include="Parsing\EndToken.cs" />
84 <Compile Include="Parsing\EndToken.cs" />
85 <Compile Include="Parsing\EnumAlphabet.cs" />
85 <Compile Include="Parsing\EnumAlphabet.cs" />
86 <Compile Include="Parsing\Grammar.cs" />
86 <Compile Include="Parsing\Grammar.cs" />
87 <Compile Include="Parsing\IAlphabet.cs" />
87 <Compile Include="Parsing\IAlphabet.cs" />
88 <Compile Include="Parsing\IDFADefinition.cs" />
88 <Compile Include="Parsing\IDFADefinition.cs" />
89 <Compile Include="Parsing\IVisitor.cs" />
89 <Compile Include="Parsing\IVisitor.cs" />
90 <Compile Include="Parsing\ParserException.cs" />
90 <Compile Include="Parsing\ParserException.cs" />
91 <Compile Include="Parsing\Scanner.cs" />
91 <Compile Include="Parsing\Scanner.cs" />
92 <Compile Include="Parsing\StarToken.cs" />
92 <Compile Include="Parsing\StarToken.cs" />
93 <Compile Include="Parsing\SymbolToken.cs" />
93 <Compile Include="Parsing\SymbolToken.cs" />
94 <Compile Include="Parsing\Token.cs" />
94 <Compile Include="Parsing\Token.cs" />
95 <Compile Include="SafePool.cs" />
95 <Compile Include="SafePool.cs" />
96 <Compile Include="ServiceLocator.cs" />
96 <Compile Include="ServiceLocator.cs" />
97 <Compile Include="TaskController.cs" />
97 <Compile Include="TaskController.cs" />
98 <Compile Include="ProgressInitEventArgs.cs" />
98 <Compile Include="ProgressInitEventArgs.cs" />
99 <Compile Include="Properties\AssemblyInfo.cs" />
99 <Compile Include="Properties\AssemblyInfo.cs" />
100 <Compile Include="Promise.cs" />
100 <Compile Include="Promise.cs" />
101 <Compile Include="Parallels\AsyncPool.cs" />
101 <Compile Include="Parallels\AsyncPool.cs" />
102 <Compile Include="Safe.cs" />
102 <Compile Include="Safe.cs" />
103 <Compile Include="ValueEventArgs.cs" />
103 <Compile Include="ValueEventArgs.cs" />
104 </ItemGroup>
104 </ItemGroup>
105 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
105 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
106 <ItemGroup />
106 <ItemGroup />
107 </Project> No newline at end of file
107 </Project>
@@ -1,71 +1,71
1 using Implab.Diagnostics;
1 using Implab.Diagnostics;
2 using System;
2 using System;
3 using System.Threading;
3 using System.Threading;
4
4
5 namespace Implab.Parallels {
5 namespace Implab.Parallels {
6 /// <summary>
6 /// <summary>
7 /// Класс для распаралеливания задач.
7 /// Класс для распаралеливания задач.
8 /// </summary>
8 /// </summary>
9 /// <remarks>
9 /// <remarks>
10 /// Используя данный класс и лямда выражения можно распараллелить
10 /// Используя данный класс и лямда выражения можно распараллелить
11 /// вычисления, для этого используется концепция обещаний.
11 /// вычисления, для этого используется концепция обещаний.
12 /// </remarks>
12 /// </remarks>
13 public static class AsyncPool {
13 public static class AsyncPool {
14
14
15 public static IPromise<T> Invoke<T>(Func<T> func) {
15 public static IPromise<T> Invoke<T>(Func<T> func) {
16 var p = new Promise<T>();
16 var p = new Promise<T>();
17 var caller = TraceContext.Snapshot();
17 var caller = TraceContext.Snapshot();
18
18
19 ThreadPool.QueueUserWorkItem(param => {
19 ThreadPool.QueueUserWorkItem(param => {
20 TraceContext.Fork(caller);
20 TraceContext.Fork(caller);
21 try {
21 try {
22 p.Resolve(func());
22 p.Resolve(func());
23 } catch(Exception e) {
23 } catch(Exception e) {
24 p.Reject(e);
24 p.Reject(e);
25 }
25 }
26 });
26 });
27
27
28 return p;
28 return p;
29 }
29 }
30
30
31 public static IPromise<T> InvokeNewThread<T>(Func<T> func) {
31 public static IPromise<T> InvokeNewThread<T>(Func<T> func) {
32 var p = new Promise<T>();
32 var p = new Promise<T>();
33
33
34 var caller = TraceContext.Snapshot();
34 var caller = TraceContext.Snapshot();
35
35
36 var worker = new Thread(() => {
36 var worker = new Thread(() => {
37 TraceContext.Fork(caller);
37 TraceContext.Fork(caller);
38 try {
38 try {
39 p.Resolve(func());
39 p.Resolve(func());
40 } catch (Exception e) {
40 } catch (Exception e) {
41 p.Reject(e);
41 p.Reject(e);
42 }
42 }
43 });
43 });
44 worker.IsBackground = true;
44 worker.IsBackground = true;
45 worker.Start();
45 worker.Start();
46
46
47 return p;
47 return p;
48 }
48 }
49
49
50
50
51 public static IPromiseBase InvokeNewThread(Action func) {
51 public static IPromise InvokeNewThread(Action func) {
52 var p = new Promise<object>();
52 var p = new Promise<object>();
53
53
54 var caller = TraceContext.Snapshot();
54 var caller = TraceContext.Snapshot();
55
55
56 var worker = new Thread(() => {
56 var worker = new Thread(() => {
57 TraceContext.Fork(caller);
57 TraceContext.Fork(caller);
58 try {
58 try {
59 func();
59 func();
60 p.Resolve();
60 p.Resolve();
61 } catch (Exception e) {
61 } catch (Exception e) {
62 p.Reject(e);
62 p.Reject(e);
63 }
63 }
64 });
64 });
65 worker.IsBackground = true;
65 worker.IsBackground = true;
66 worker.Start();
66 worker.Start();
67
67
68 return p;
68 return p;
69 }
69 }
70 }
70 }
71 }
71 }
@@ -1,762 +1,762
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.Diagnostics;
4 using System.Diagnostics;
5 using System.Threading;
5 using System.Threading;
6 using Implab.Parallels;
6 using Implab.Parallels;
7
7
8 namespace Implab {
8 namespace Implab {
9
9
10 public delegate void ErrorHandler(Exception e);
10 public delegate void ErrorHandler(Exception e);
11 public delegate T ErrorHandler<out T>(Exception e);
11 public delegate T ErrorHandler<out T>(Exception e);
12 public delegate void ResultHandler<in T>(T result);
12 public delegate void ResultHandler<in T>(T result);
13 public delegate TNew ResultMapper<in TSrc, out TNew>(TSrc result);
13 public delegate TNew ResultMapper<in TSrc, out TNew>(TSrc result);
14 public delegate IPromise<TNew> ChainedOperation<in TSrc, TNew>(TSrc result);
14 public delegate IPromise<TNew> ChainedOperation<in TSrc, TNew>(TSrc result);
15
15
16 /// <summary>
16 /// <summary>
17 /// Класс для асинхронного получения результатов. Так называемое "обещание".
17 /// Класс для асинхронного получения результатов. Так называемое "обещание".
18 /// </summary>
18 /// </summary>
19 /// <typeparam name="T">Тип получаемого результата</typeparam>
19 /// <typeparam name="T">Тип получаемого результата</typeparam>
20 /// <remarks>
20 /// <remarks>
21 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции,
21 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции,
22 /// клиент получив такое обещание может установить ряд обратных вызово для получения
22 /// клиент получив такое обещание может установить ряд обратных вызово для получения
23 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para>
23 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para>
24 /// <para>
24 /// <para>
25 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на
25 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на
26 /// данные события клиент должен использовать методы <c>Then</c>.
26 /// данные события клиент должен использовать методы <c>Then</c>.
27 /// </para>
27 /// </para>
28 /// <para>
28 /// <para>
29 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой),
29 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой),
30 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о
30 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о
31 /// выполнении обещания.
31 /// выполнении обещания.
32 /// </para>
32 /// </para>
33 /// <para>
33 /// <para>
34 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался,
34 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался,
35 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном
35 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном
36 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в
36 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в
37 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении
37 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении
38 /// обещания.
38 /// обещания.
39 /// </para>
39 /// </para>
40 /// <para>
40 /// <para>
41 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать
41 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать
42 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует
42 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует
43 /// использовать соответствующую форму методе <c>Then</c>.
43 /// использовать соответствующую форму методе <c>Then</c>.
44 /// </para>
44 /// </para>
45 /// <para>
45 /// <para>
46 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать
46 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать
47 /// только инициатор обещания иначе могут возникнуть противоречия.
47 /// только инициатор обещания иначе могут возникнуть противоречия.
48 /// </para>
48 /// </para>
49 /// </remarks>
49 /// </remarks>
50 public class Promise<T> : IPromise<T> {
50 public class Promise<T> : IPromise<T> {
51
51
52 protected struct HandlerDescriptor {
52 protected struct HandlerDescriptor {
53 public ResultHandler<T> resultHandler;
53 public ResultHandler<T> resultHandler;
54 public ErrorHandler errorHandler;
54 public ErrorHandler errorHandler;
55 public Action cancellHandler;
55 public Action cancellHandler;
56
56
57 public void Resolve(T result) {
57 public void Resolve(T result) {
58 if (resultHandler != null)
58 if (resultHandler != null)
59 try {
59 try {
60 resultHandler(result);
60 resultHandler(result);
61 } catch (Exception e) {
61 } catch (Exception e) {
62 Reject(e);
62 Reject(e);
63 }
63 }
64 }
64 }
65
65
66 public void Reject(Exception err) {
66 public void Reject(Exception err) {
67 if (errorHandler != null)
67 if (errorHandler != null)
68 try {
68 try {
69 errorHandler(err);
69 errorHandler(err);
70 } catch {
70 } catch {
71 }
71 }
72 }
72 }
73
73
74 public void Cancel() {
74 public void Cancel() {
75 if (cancellHandler != null)
75 if (cancellHandler != null)
76 try {
76 try {
77 cancellHandler();
77 cancellHandler();
78 } catch {
78 } catch {
79 }
79 }
80 }
80 }
81 }
81 }
82
82
83 const int UnresolvedSate = 0;
83 const int UnresolvedSate = 0;
84 const int TransitionalState = 1;
84 const int TransitionalState = 1;
85 const int SucceededState = 2;
85 const int SucceededState = 2;
86 const int RejectedState = 3;
86 const int RejectedState = 3;
87 const int CancelledState = 4;
87 const int CancelledState = 4;
88
88
89 readonly bool m_cancellable;
89 readonly bool m_cancellable;
90
90
91 int m_childrenCount = 0;
91 int m_childrenCount = 0;
92 int m_state;
92 int m_state;
93 T m_result;
93 T m_result;
94 Exception m_error;
94 Exception m_error;
95
95
96 readonly MTQueue<HandlerDescriptor> m_handlers = new MTQueue<HandlerDescriptor>();
96 readonly MTQueue<HandlerDescriptor> m_handlers = new MTQueue<HandlerDescriptor>();
97
97
98 public Promise() {
98 public Promise() {
99 m_cancellable = true;
99 m_cancellable = true;
100 }
100 }
101
101
102 public Promise(IPromiseBase parent, bool cancellable) {
102 public Promise(IPromise parent, bool cancellable) {
103 m_cancellable = cancellable;
103 m_cancellable = cancellable;
104 if (parent != null)
104 if (parent != null)
105 AddHandler(
105 AddHandler(
106 null,
106 null,
107 null,
107 null,
108 () => {
108 () => {
109 if (parent.IsExclusive)
109 if (parent.IsExclusive)
110 parent.Cancel();
110 parent.Cancel();
111 }
111 }
112 );
112 );
113 }
113 }
114
114
115 bool BeginTransit() {
115 bool BeginTransit() {
116 return UnresolvedSate == Interlocked.CompareExchange(ref m_state, TransitionalState, UnresolvedSate);
116 return UnresolvedSate == Interlocked.CompareExchange(ref m_state, TransitionalState, UnresolvedSate);
117 }
117 }
118
118
119 void CompleteTransit(int state) {
119 void CompleteTransit(int state) {
120 if (TransitionalState != Interlocked.CompareExchange(ref m_state, state, TransitionalState))
120 if (TransitionalState != Interlocked.CompareExchange(ref m_state, state, TransitionalState))
121 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
121 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
122 }
122 }
123
123
124 void WaitTransition() {
124 void WaitTransition() {
125 while (m_state == TransitionalState) {
125 while (m_state == TransitionalState) {
126 /* noop */
126 /* noop */
127 }
127 }
128 }
128 }
129
129
130 public bool IsResolved {
130 public bool IsResolved {
131 get {
131 get {
132 return m_state > 1;
132 return m_state > 1;
133 }
133 }
134 }
134 }
135
135
136 public bool IsCancelled {
136 public bool IsCancelled {
137 get {
137 get {
138 return m_state == CancelledState;
138 return m_state == CancelledState;
139 }
139 }
140 }
140 }
141
141
142 public Type PromiseType {
142 public Type PromiseType {
143 get { return typeof(T); }
143 get { return typeof(T); }
144 }
144 }
145
145
146 /// <summary>
146 /// <summary>
147 /// Выполняет обещание, сообщая об успешном выполнении.
147 /// Выполняет обещание, сообщая об успешном выполнении.
148 /// </summary>
148 /// </summary>
149 /// <param name="result">Результат выполнения.</param>
149 /// <param name="result">Результат выполнения.</param>
150 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
150 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
151 public void Resolve(T result) {
151 public void Resolve(T result) {
152 if (BeginTransit()) {
152 if (BeginTransit()) {
153 m_result = result;
153 m_result = result;
154 CompleteTransit(SucceededState);
154 CompleteTransit(SucceededState);
155 OnStateChanged();
155 OnStateChanged();
156 } else {
156 } else {
157 WaitTransition();
157 WaitTransition();
158 if (m_state != CancelledState)
158 if (m_state != CancelledState)
159 throw new InvalidOperationException("The promise is already resolved");
159 throw new InvalidOperationException("The promise is already resolved");
160 }
160 }
161 }
161 }
162
162
163 /// <summary>
163 /// <summary>
164 /// Выполняет обещание, сообщая об успешном выполнении. Результатом выполнения будет пустое значения.
164 /// Выполняет обещание, сообщая об успешном выполнении. Результатом выполнения будет пустое значения.
165 /// </summary>
165 /// </summary>
166 /// <remarks>
166 /// <remarks>
167 /// Данный вариант удобен в случаях, когда интересен факт выполнения операции, нежели полученное значение.
167 /// Данный вариант удобен в случаях, когда интересен факт выполнения операции, нежели полученное значение.
168 /// </remarks>
168 /// </remarks>
169 public void Resolve() {
169 public void Resolve() {
170 Resolve(default(T));
170 Resolve(default(T));
171 }
171 }
172
172
173 /// <summary>
173 /// <summary>
174 /// Выполняет обещание, сообщая об ошибке
174 /// Выполняет обещание, сообщая об ошибке
175 /// </summary>
175 /// </summary>
176 /// <remarks>
176 /// <remarks>
177 /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков
177 /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков
178 /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные
178 /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные
179 /// будут проигнорированы.
179 /// будут проигнорированы.
180 /// </remarks>
180 /// </remarks>
181 /// <param name="error">Исключение возникшее при выполнении операции</param>
181 /// <param name="error">Исключение возникшее при выполнении операции</param>
182 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
182 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
183 public void Reject(Exception error) {
183 public void Reject(Exception error) {
184 if (BeginTransit()) {
184 if (BeginTransit()) {
185 m_error = error;
185 m_error = error;
186 CompleteTransit(RejectedState);
186 CompleteTransit(RejectedState);
187 OnStateChanged();
187 OnStateChanged();
188 } else {
188 } else {
189 WaitTransition();
189 WaitTransition();
190 if (m_state == SucceededState)
190 if (m_state == SucceededState)
191 throw new InvalidOperationException("The promise is already resolved");
191 throw new InvalidOperationException("The promise is already resolved");
192 }
192 }
193 }
193 }
194
194
195 /// <summary>
195 /// <summary>
196 /// Отменяет операцию, если это возможно.
196 /// Отменяет операцию, если это возможно.
197 /// </summary>
197 /// </summary>
198 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns>
198 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns>
199 public bool Cancel() {
199 public bool Cancel() {
200 if (BeginTransit()) {
200 if (BeginTransit()) {
201 CompleteTransit(CancelledState);
201 CompleteTransit(CancelledState);
202 OnStateChanged();
202 OnStateChanged();
203 return true;
203 return true;
204 } else {
204 } else {
205 return false;
205 return false;
206 }
206 }
207 }
207 }
208
208
209 // сделано для возвращаемого типа void
209 // сделано для возвращаемого типа void
210 protected void InternalCancel() {
210 protected void InternalCancel() {
211 Cancel();
211 Cancel();
212 }
212 }
213
213
214 /// <summary>
214 /// <summary>
215 /// Adds new handlers to this promise.
215 /// Adds new handlers to this promise.
216 /// </summary>
216 /// </summary>
217 /// <param name="success">The handler of the successfully completed operation.
217 /// <param name="success">The handler of the successfully completed operation.
218 /// This handler will recieve an operation result as a parameter.</param>
218 /// This handler will recieve an operation result as a parameter.</param>
219 /// <param name="error">Handles an exception that may occur during the operation.</param>
219 /// <param name="error">Handles an exception that may occur during the operation.</param>
220 /// <returns>The new promise chained to this one.</returns>
220 /// <returns>The new promise chained to this one.</returns>
221 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler error) {
221 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler error) {
222 if (success == null && error == null)
222 if (success == null && error == null)
223 return this;
223 return this;
224
224
225 var medium = new Promise<T>(this, true);
225 var medium = new Promise<T>(this, true);
226
226
227 ResultHandler<T> resultHandler;
227 ResultHandler<T> resultHandler;
228 if (success != null)
228 if (success != null)
229 resultHandler = x => {
229 resultHandler = x => {
230 success(x);
230 success(x);
231 medium.Resolve(x);
231 medium.Resolve(x);
232 };
232 };
233 else
233 else
234 resultHandler = medium.Resolve;
234 resultHandler = medium.Resolve;
235
235
236 ErrorHandler errorHandler;
236 ErrorHandler errorHandler;
237 if (error != null)
237 if (error != null)
238 errorHandler = x => {
238 errorHandler = x => {
239 // несмотря на то, что обработчик ошибки вызывается безопасно,
239 // несмотря на то, что обработчик ошибки вызывается безопасно,
240 // т.е. возникшие в нем ошибки будут подавлены, нам нужно
240 // т.е. возникшие в нем ошибки будут подавлены, нам нужно
241 // гарантировать, что ошибка будет передана дальше по цепочке обещаний
241 // гарантировать, что ошибка будет передана дальше по цепочке обещаний
242 try {
242 try {
243 error(x);
243 error(x);
244 } catch { }
244 } catch { }
245 medium.Reject(x);
245 medium.Reject(x);
246 };
246 };
247 else
247 else
248 errorHandler = medium.Reject;
248 errorHandler = medium.Reject;
249
249
250 AddHandler(resultHandler, errorHandler, medium.InternalCancel);
250 AddHandler(resultHandler, errorHandler, medium.InternalCancel);
251
251
252 return medium;
252 return medium;
253 }
253 }
254
254
255 public IPromiseBase Then(Action success, ErrorHandler error) {
255 public IPromise Then(Action success, ErrorHandler error) {
256 return Then(x => success(), error);
256 return Then(x => success(), error);
257 }
257 }
258
258
259 public IPromiseBase Then(Action success) {
259 public IPromise Then(Action success) {
260 return Then(x => success());
260 return Then(x => success());
261 }
261 }
262
262
263 /// <summary>
263 /// <summary>
264 /// Adds new handlers to this promise.
264 /// Adds new handlers to this promise.
265 /// </summary>
265 /// </summary>
266 /// <param name="success">The handler of the successfully completed operation.
266 /// <param name="success">The handler of the successfully completed operation.
267 /// This handler will recieve an operation result as a parameter.</param>
267 /// This handler will recieve an operation result as a parameter.</param>
268 /// <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>
268 /// <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>
269 /// <returns>The new promise chained to this one.</returns>
269 /// <returns>The new promise chained to this one.</returns>
270 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error) {
270 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error) {
271 if (success == null && error == null)
271 if (success == null && error == null)
272 return this;
272 return this;
273
273
274 var medium = new Promise<T>(this, true);
274 var medium = new Promise<T>(this, true);
275
275
276 ResultHandler<T> resultHandler;
276 ResultHandler<T> resultHandler;
277 ErrorHandler errorHandler;
277 ErrorHandler errorHandler;
278
278
279 if (success != null)
279 if (success != null)
280 resultHandler = x => {
280 resultHandler = x => {
281 success(x);
281 success(x);
282 medium.Resolve(x);
282 medium.Resolve(x);
283 };
283 };
284 else
284 else
285 resultHandler = medium.Resolve;
285 resultHandler = medium.Resolve;
286
286
287 if (error != null)
287 if (error != null)
288 errorHandler = x => {
288 errorHandler = x => {
289 try {
289 try {
290 medium.Resolve(error(x));
290 medium.Resolve(error(x));
291 } catch (Exception e) {
291 } catch (Exception e) {
292 medium.Reject(e);
292 medium.Reject(e);
293 }
293 }
294 };
294 };
295 else
295 else
296 errorHandler = medium.Reject;
296 errorHandler = medium.Reject;
297
297
298 AddHandler(resultHandler, errorHandler, medium.InternalCancel);
298 AddHandler(resultHandler, errorHandler, medium.InternalCancel);
299
299
300 return medium;
300 return medium;
301 }
301 }
302
302
303
303
304 public IPromise<T> Then(ResultHandler<T> success) {
304 public IPromise<T> Then(ResultHandler<T> success) {
305 if (success == null)
305 if (success == null)
306 return this;
306 return this;
307
307
308 var medium = new Promise<T>(this, true);
308 var medium = new Promise<T>(this, true);
309
309
310 ResultHandler<T> resultHandler;
310 ResultHandler<T> resultHandler;
311
311
312 if (success != null)
312 if (success != null)
313 resultHandler = x => {
313 resultHandler = x => {
314 success(x);
314 success(x);
315 medium.Resolve(x);
315 medium.Resolve(x);
316 };
316 };
317 else
317 else
318 resultHandler = medium.Resolve;
318 resultHandler = medium.Resolve;
319
319
320 AddHandler(resultHandler, medium.Reject, medium.InternalCancel);
320 AddHandler(resultHandler, medium.Reject, medium.InternalCancel);
321
321
322 return medium;
322 return medium;
323 }
323 }
324
324
325 public IPromise<T> Error(ErrorHandler error) {
325 public IPromise<T> Error(ErrorHandler error) {
326 return Then((ResultHandler<T>)null, error);
326 return Then((ResultHandler<T>)null, error);
327 }
327 }
328
328
329 /// <summary>
329 /// <summary>
330 /// Handles error and allows to keep the promise.
330 /// Handles error and allows to keep the promise.
331 /// </summary>
331 /// </summary>
332 /// <remarks>
332 /// <remarks>
333 /// If the specified handler throws an exception, this exception will be used to reject the promise.
333 /// If the specified handler throws an exception, this exception will be used to reject the promise.
334 /// </remarks>
334 /// </remarks>
335 /// <param name="handler">The error handler which returns the result of the promise.</param>
335 /// <param name="handler">The error handler which returns the result of the promise.</param>
336 /// <returns>New promise.</returns>
336 /// <returns>New promise.</returns>
337 public IPromise<T> Error(ErrorHandler<T> handler) {
337 public IPromise<T> Error(ErrorHandler<T> handler) {
338 if (handler == null)
338 if (handler == null)
339 return this;
339 return this;
340
340
341 var medium = new Promise<T>(this, true);
341 var medium = new Promise<T>(this, true);
342
342
343 AddHandler(
343 AddHandler(
344 x => medium.Resolve(x),
344 x => medium.Resolve(x),
345 e => {
345 e => {
346 try {
346 try {
347 medium.Resolve(handler(e));
347 medium.Resolve(handler(e));
348 } catch (Exception e2) {
348 } catch (Exception e2) {
349 medium.Reject(e2);
349 medium.Reject(e2);
350 }
350 }
351 },
351 },
352 medium.InternalCancel
352 medium.InternalCancel
353 );
353 );
354
354
355 return medium;
355 return medium;
356 }
356 }
357
357
358 public IPromise<T> Anyway(Action handler) {
358 public IPromise<T> Anyway(Action handler) {
359 if (handler == null)
359 if (handler == null)
360 return this;
360 return this;
361
361
362 var medium = new Promise<T>(this,true);
362 var medium = new Promise<T>(this,true);
363
363
364 AddHandler(
364 AddHandler(
365 x => {
365 x => {
366 // to avoid handler being called multiple times we handle exception by ourselfs
366 // to avoid handler being called multiple times we handle exception by ourselfs
367 try {
367 try {
368 handler();
368 handler();
369 medium.Resolve(x);
369 medium.Resolve(x);
370 } catch (Exception e) {
370 } catch (Exception e) {
371 medium.Reject(e);
371 medium.Reject(e);
372 }
372 }
373 },
373 },
374
374
375 e => {
375 e => {
376 try {
376 try {
377 handler();
377 handler();
378 } catch { }
378 } catch { }
379 medium.Reject(e);
379 medium.Reject(e);
380 },
380 },
381
381
382 medium.InternalCancel
382 medium.InternalCancel
383 );
383 );
384
384
385 return medium;
385 return medium;
386 }
386 }
387
387
388 /// <summary>
388 /// <summary>
389 /// Позволяет преобразовать результат выполения операции к новому типу.
389 /// Позволяет преобразовать результат выполения операции к новому типу.
390 /// </summary>
390 /// </summary>
391 /// <typeparam name="TNew">Новый тип результата.</typeparam>
391 /// <typeparam name="TNew">Новый тип результата.</typeparam>
392 /// <param name="mapper">Преобразование результата к новому типу.</param>
392 /// <param name="mapper">Преобразование результата к новому типу.</param>
393 /// <param name="error">Обработчик ошибки. Данный обработчик получит
393 /// <param name="error">Обработчик ошибки. Данный обработчик получит
394 /// исключение возникшее при выполнении операции.</param>
394 /// исключение возникшее при выполнении операции.</param>
395 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
395 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
396 public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) {
396 public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) {
397 if (mapper == null)
397 if (mapper == null)
398 throw new ArgumentNullException("mapper");
398 throw new ArgumentNullException("mapper");
399
399
400 // создаем прицепленное обещание
400 // создаем прицепленное обещание
401 var chained = new Promise<TNew>(this,true);
401 var chained = new Promise<TNew>(this,true);
402
402
403 ResultHandler<T> resultHandler = result => chained.Resolve(mapper(result));
403 ResultHandler<T> resultHandler = result => chained.Resolve(mapper(result));
404 ErrorHandler errorHandler = delegate(Exception e) {
404 ErrorHandler errorHandler = delegate(Exception e) {
405 if (error != null)
405 if (error != null)
406 try {
406 try {
407 error(e);
407 error(e);
408 } catch { }
408 } catch { }
409 // в случае ошибки нужно передать исключение дальше по цепочке
409 // в случае ошибки нужно передать исключение дальше по цепочке
410 chained.Reject(e);
410 chained.Reject(e);
411 };
411 };
412
412
413
413
414 AddHandler(
414 AddHandler(
415 resultHandler,
415 resultHandler,
416 errorHandler,
416 errorHandler,
417 chained.InternalCancel
417 chained.InternalCancel
418 );
418 );
419
419
420 return chained;
420 return chained;
421 }
421 }
422
422
423 public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper) {
423 public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper) {
424 return Map(mapper, null);
424 return Map(mapper, null);
425 }
425 }
426
426
427 /// <summary>
427 /// <summary>
428 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после
428 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после
429 /// выполнения текущей, а результат текущей операции может быть использован для инициализации
429 /// выполнения текущей, а результат текущей операции может быть использован для инициализации
430 /// новой операции.
430 /// новой операции.
431 /// </summary>
431 /// </summary>
432 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam>
432 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam>
433 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param>
433 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param>
434 /// <param name="error">Обработчик ошибки. Данный обработчик получит
434 /// <param name="error">Обработчик ошибки. Данный обработчик получит
435 /// исключение возникшее при выполнении текуещй операции.</param>
435 /// исключение возникшее при выполнении текуещй операции.</param>
436 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
436 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
437 public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) {
437 public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) {
438
438
439 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
439 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
440 // создать посредника, к которому будут подвызяваться следующие обработчики.
440 // создать посредника, к которому будут подвызяваться следующие обработчики.
441 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы
441 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы
442 // передать через него результаты работы.
442 // передать через него результаты работы.
443 var medium = new Promise<TNew>(this, true);
443 var medium = new Promise<TNew>(this, true);
444
444
445 ResultHandler<T> resultHandler = delegate(T result) {
445 ResultHandler<T> resultHandler = delegate(T result) {
446 if (medium.IsCancelled)
446 if (medium.IsCancelled)
447 return;
447 return;
448
448
449 var promise = chained(result);
449 var promise = chained(result);
450
450
451 promise.Then(
451 promise.Then(
452 x => medium.Resolve(x),
452 x => medium.Resolve(x),
453 e => medium.Reject(e)
453 e => medium.Reject(e)
454 );
454 );
455
455
456 // notify chained operation that it's not needed anymore
456 // notify chained operation that it's not needed anymore
457 // порядок вызова Then, Cancelled важен, поскольку от этого
457 // порядок вызова Then, Cancelled важен, поскольку от этого
458 // зависит IsExclusive
458 // зависит IsExclusive
459 medium.Cancelled(() => {
459 medium.Cancelled(() => {
460 if(promise.IsExclusive)
460 if(promise.IsExclusive)
461 promise.Cancel();
461 promise.Cancel();
462 });
462 });
463
463
464 // внешняя отмена связанной операции рассматривается как ошибка
464 // внешняя отмена связанной операции рассматривается как ошибка
465 promise.Cancelled(() => medium.Reject(new OperationCanceledException()));
465 promise.Cancelled(() => medium.Reject(new OperationCanceledException()));
466 };
466 };
467
467
468 ErrorHandler errorHandler = delegate(Exception e) {
468 ErrorHandler errorHandler = delegate(Exception e) {
469 if (error != null)
469 if (error != null)
470 error(e);
470 error(e);
471 // в случае ошибки нужно передать исключение дальше по цепочке
471 // в случае ошибки нужно передать исключение дальше по цепочке
472 medium.Reject(e);
472 medium.Reject(e);
473 };
473 };
474
474
475 AddHandler(
475 AddHandler(
476 resultHandler,
476 resultHandler,
477 errorHandler,
477 errorHandler,
478 medium.InternalCancel
478 medium.InternalCancel
479 );
479 );
480
480
481 return medium;
481 return medium;
482 }
482 }
483
483
484 public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) {
484 public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) {
485 return Chain(chained, null);
485 return Chain(chained, null);
486 }
486 }
487
487
488 public IPromise<T> Cancelled(Action handler) {
488 public IPromise<T> Cancelled(Action handler) {
489 AddHandler(null, null, handler);
489 AddHandler(null, null, handler);
490 return this;
490 return this;
491 }
491 }
492
492
493 /// <summary>
493 /// <summary>
494 /// Adds the specified handler for all cases (success, error, cancel)
494 /// Adds the specified handler for all cases (success, error, cancel)
495 /// </summary>
495 /// </summary>
496 /// <param name="handler">The handler that will be called anyway</param>
496 /// <param name="handler">The handler that will be called anyway</param>
497 /// <returns>self</returns>
497 /// <returns>self</returns>
498 public IPromise<T> Finally(Action handler) {
498 public IPromise<T> Finally(Action handler) {
499 if (handler == null)
499 if (handler == null)
500 throw new ArgumentNullException("handler");
500 throw new ArgumentNullException("handler");
501 AddHandler(
501 AddHandler(
502 x => handler(),
502 x => handler(),
503 e => handler(),
503 e => handler(),
504 handler
504 handler
505 );
505 );
506 return this;
506 return this;
507 }
507 }
508
508
509 /// <summary>
509 /// <summary>
510 /// Преобразует результат обещания к нужному типу
510 /// Преобразует результат обещания к нужному типу
511 /// </summary>
511 /// </summary>
512 /// <typeparam name="T2"></typeparam>
512 /// <typeparam name="T2"></typeparam>
513 /// <returns></returns>
513 /// <returns></returns>
514 public IPromise<T2> Cast<T2>() {
514 public IPromise<T2> Cast<T2>() {
515 return Map(x => (T2)(object)x, null);
515 return Map(x => (T2)(object)x, null);
516 }
516 }
517
517
518 /// <summary>
518 /// <summary>
519 /// Дожидается отложенного обещания и в случае успеха, возвращает
519 /// Дожидается отложенного обещания и в случае успеха, возвращает
520 /// его, результат, в противном случае бросает исключение.
520 /// его, результат, в противном случае бросает исключение.
521 /// </summary>
521 /// </summary>
522 /// <remarks>
522 /// <remarks>
523 /// <para>
523 /// <para>
524 /// Если ожидание обещания было прервано по таймауту, это не значит,
524 /// Если ожидание обещания было прервано по таймауту, это не значит,
525 /// что обещание было отменено или что-то в этом роде, это только
525 /// что обещание было отменено или что-то в этом роде, это только
526 /// означает, что мы его не дождались, однако все зарегистрированные
526 /// означает, что мы его не дождались, однако все зарегистрированные
527 /// обработчики, как были так остались и они будут вызваны, когда
527 /// обработчики, как были так остались и они будут вызваны, когда
528 /// обещание будет выполнено.
528 /// обещание будет выполнено.
529 /// </para>
529 /// </para>
530 /// <para>
530 /// <para>
531 /// Такое поведение вполне оправдано поскольку таймаут может истечь
531 /// Такое поведение вполне оправдано поскольку таймаут может истечь
532 /// в тот момент, когда началась обработка цепочки обработчиков, и
532 /// в тот момент, когда началась обработка цепочки обработчиков, и
533 /// к тому же текущее обещание может стоять в цепочке обещаний и его
533 /// к тому же текущее обещание может стоять в цепочке обещаний и его
534 /// отклонение может привести к непрогнозируемому результату.
534 /// отклонение может привести к непрогнозируемому результату.
535 /// </para>
535 /// </para>
536 /// </remarks>
536 /// </remarks>
537 /// <param name="timeout">Время ожидания</param>
537 /// <param name="timeout">Время ожидания</param>
538 /// <returns>Результат выполнения обещания</returns>
538 /// <returns>Результат выполнения обещания</returns>
539 public T Join(int timeout) {
539 public T Join(int timeout) {
540 var evt = new ManualResetEvent(false);
540 var evt = new ManualResetEvent(false);
541 Anyway(() => evt.Set());
541 Anyway(() => evt.Set());
542 Cancelled(() => evt.Set());
542 Cancelled(() => evt.Set());
543
543
544 if (!evt.WaitOne(timeout, true))
544 if (!evt.WaitOne(timeout, true))
545 throw new TimeoutException();
545 throw new TimeoutException();
546
546
547 switch (m_state) {
547 switch (m_state) {
548 case SucceededState:
548 case SucceededState:
549 return m_result;
549 return m_result;
550 case CancelledState:
550 case CancelledState:
551 throw new OperationCanceledException();
551 throw new OperationCanceledException();
552 case RejectedState:
552 case RejectedState:
553 throw new TargetInvocationException(m_error);
553 throw new TargetInvocationException(m_error);
554 default:
554 default:
555 throw new ApplicationException(String.Format("Invalid promise state {0}", m_state));
555 throw new ApplicationException(String.Format("Invalid promise state {0}", m_state));
556 }
556 }
557 }
557 }
558
558
559 public T Join() {
559 public T Join() {
560 return Join(Timeout.Infinite);
560 return Join(Timeout.Infinite);
561 }
561 }
562
562
563 void AddHandler(ResultHandler<T> success, ErrorHandler error, Action cancel) {
563 void AddHandler(ResultHandler<T> success, ErrorHandler error, Action cancel) {
564 if (success != null || error != null)
564 if (success != null || error != null)
565 Interlocked.Increment(ref m_childrenCount);
565 Interlocked.Increment(ref m_childrenCount);
566
566
567 HandlerDescriptor handler = new HandlerDescriptor {
567 HandlerDescriptor handler = new HandlerDescriptor {
568 resultHandler = success,
568 resultHandler = success,
569 errorHandler = error,
569 errorHandler = error,
570 cancellHandler = cancel
570 cancellHandler = cancel
571 };
571 };
572
572
573 bool queued;
573 bool queued;
574
574
575 if (!IsResolved) {
575 if (!IsResolved) {
576 m_handlers.Enqueue(handler);
576 m_handlers.Enqueue(handler);
577 queued = true;
577 queued = true;
578 } else {
578 } else {
579 // the promise is in resolved state, just invoke the handled with minimum overhead
579 // the promise is in resolved state, just invoke the handled with minimum overhead
580 queued = false;
580 queued = false;
581 InvokeHandler(handler);
581 InvokeHandler(handler);
582 }
582 }
583
583
584 if (queued && IsResolved && m_handlers.TryDequeue(out handler))
584 if (queued && IsResolved && m_handlers.TryDequeue(out handler))
585 // if the promise have been resolved while we was adding handler to the queue
585 // if the promise have been resolved while we was adding handler to the queue
586 // we can't guarantee that someone is still processing it
586 // we can't guarantee that someone is still processing it
587 // therefore we will fetch a handler from the queue and execute it
587 // therefore we will fetch a handler from the queue and execute it
588 // note that fetched handler may be not the one that we have added
588 // note that fetched handler may be not the one that we have added
589 // even we can fetch no handlers at all :)
589 // even we can fetch no handlers at all :)
590 InvokeHandler(handler);
590 InvokeHandler(handler);
591 }
591 }
592
592
593 protected virtual void InvokeHandler(HandlerDescriptor handler) {
593 protected virtual void InvokeHandler(HandlerDescriptor handler) {
594 switch (m_state) {
594 switch (m_state) {
595 case SucceededState:
595 case SucceededState:
596 handler.Resolve(m_result);
596 handler.Resolve(m_result);
597 break;
597 break;
598 case RejectedState:
598 case RejectedState:
599 handler.Reject(m_error);
599 handler.Reject(m_error);
600 break;
600 break;
601 case CancelledState:
601 case CancelledState:
602 handler.Cancel();
602 handler.Cancel();
603 break;
603 break;
604 default:
604 default:
605 // do nothing
605 // do nothing
606 return;
606 return;
607 }
607 }
608 }
608 }
609
609
610 void OnStateChanged() {
610 void OnStateChanged() {
611 HandlerDescriptor handler;
611 HandlerDescriptor handler;
612 while (m_handlers.TryDequeue(out handler))
612 while (m_handlers.TryDequeue(out handler))
613 InvokeHandler(handler);
613 InvokeHandler(handler);
614 }
614 }
615
615
616 public bool IsExclusive {
616 public bool IsExclusive {
617 get {
617 get {
618 return m_childrenCount <= 1;
618 return m_childrenCount <= 1;
619 }
619 }
620 }
620 }
621
621
622 /// <summary>
622 /// <summary>
623 /// Объединяет несколько обещаний в одно, результатом которого является массив результатов других обещаний.
623 /// Объединяет несколько обещаний в одно, результатом которого является массив результатов других обещаний.
624 /// Если хотябы одно из переданных обещаний не будет выполнено, то новое обещение тоже не будет выполнено.
624 /// Если хотябы одно из переданных обещаний не будет выполнено, то новое обещение тоже не будет выполнено.
625 /// При отмене нового обещания, переданные обещания также будут отменены, если никто больше на них не подписан.
625 /// При отмене нового обещания, переданные обещания также будут отменены, если никто больше на них не подписан.
626 /// </summary>
626 /// </summary>
627 /// <param name="promises">Список обещаний. Если список пустой, то результирующее обещание возвращается уже выполненным.</param>
627 /// <param name="promises">Список обещаний. Если список пустой, то результирующее обещание возвращается уже выполненным.</param>
628 /// <returns>Обещание объединяющее в себе результат переданных обещаний.</returns>
628 /// <returns>Обещание объединяющее в себе результат переданных обещаний.</returns>
629 /// <exception cref="ArgumentNullException"><paramref name="promises"/> не может быть null</exception>
629 /// <exception cref="ArgumentNullException"><paramref name="promises"/> не может быть null</exception>
630 public static IPromise<T[]> CreateComposite(IList<IPromise<T>> promises) {
630 public static IPromise<T[]> CreateComposite(IList<IPromise<T>> promises) {
631 if (promises == null)
631 if (promises == null)
632 throw new ArgumentNullException();
632 throw new ArgumentNullException();
633
633
634 // создаем аккумулятор для результатов и результирующее обещание
634 // создаем аккумулятор для результатов и результирующее обещание
635 var result = new T[promises.Count];
635 var result = new T[promises.Count];
636 var promise = new Promise<T[]>();
636 var promise = new Promise<T[]>();
637
637
638 // special case
638 // special case
639 if (promises.Count == 0) {
639 if (promises.Count == 0) {
640 promise.Resolve(result);
640 promise.Resolve(result);
641 return promise;
641 return promise;
642 }
642 }
643
643
644 int pending = promises.Count;
644 int pending = promises.Count;
645
645
646 for (int i = 0; i < promises.Count; i++) {
646 for (int i = 0; i < promises.Count; i++) {
647 var dest = i;
647 var dest = i;
648
648
649 if (promises[i] != null) {
649 if (promises[i] != null) {
650 promises[i].Then(
650 promises[i].Then(
651 x => {
651 x => {
652 result[dest] = x;
652 result[dest] = x;
653 if (Interlocked.Decrement(ref pending) == 0)
653 if (Interlocked.Decrement(ref pending) == 0)
654 promise.Resolve(result);
654 promise.Resolve(result);
655 },
655 },
656 e => promise.Reject(e)
656 e => promise.Reject(e)
657 );
657 );
658 } else {
658 } else {
659 if (Interlocked.Decrement(ref pending) == 0)
659 if (Interlocked.Decrement(ref pending) == 0)
660 promise.Resolve(result);
660 promise.Resolve(result);
661 }
661 }
662 }
662 }
663
663
664 promise.Cancelled(
664 promise.Cancelled(
665 () => {
665 () => {
666 foreach (var d in promises)
666 foreach (var d in promises)
667 if (d != null && d.IsExclusive)
667 if (d != null && d.IsExclusive)
668 d.Cancel();
668 d.Cancel();
669 }
669 }
670 );
670 );
671
671
672 return promise;
672 return promise;
673 }
673 }
674
674
675 /// <summary>
675 /// <summary>
676 /// Объединяет несколько обещаний в одно. Результирующее обещание будет выполнено при
676 /// Объединяет несколько обещаний в одно. Результирующее обещание будет выполнено при
677 /// выполнении всех указанных обещаний. При этом возвращаемые значения первичных обещаний
677 /// выполнении всех указанных обещаний. При этом возвращаемые значения первичных обещаний
678 /// игнорируются.
678 /// игнорируются.
679 /// </summary>
679 /// </summary>
680 /// <param name="promises">Коллекция первичных обещаний, которые будут объеденены в одно.</param>
680 /// <param name="promises">Коллекция первичных обещаний, которые будут объеденены в одно.</param>
681 /// <returns>Новое обещание, объединяющее в себе переданные.</returns>
681 /// <returns>Новое обещание, объединяющее в себе переданные.</returns>
682 /// <remarks>
682 /// <remarks>
683 /// Если в коллекции встречаюься <c>null</c>, то они воспринимаются как выполненные обещания.
683 /// Если в коллекции встречаюься <c>null</c>, то они воспринимаются как выполненные обещания.
684 /// </remarks>
684 /// </remarks>
685 public static IPromiseBase CreateComposite(ICollection<IPromiseBase> promises) {
685 public static IPromise CreateComposite(ICollection<IPromise> promises) {
686 if (promises == null)
686 if (promises == null)
687 throw new ArgumentNullException();
687 throw new ArgumentNullException();
688 if (promises.Count == 0)
688 if (promises.Count == 0)
689 return Promise<object>.ResultToPromise(null);
689 return Promise<object>.ResultToPromise(null);
690
690
691 int countdown = promises.Count;
691 int countdown = promises.Count;
692
692
693 var result = new Promise<object>();
693 var result = new Promise<object>();
694
694
695 foreach (var d in promises) {
695 foreach (var d in promises) {
696 if (d == null) {
696 if (d == null) {
697 if (Interlocked.Decrement(ref countdown) == 0)
697 if (Interlocked.Decrement(ref countdown) == 0)
698 result.Resolve(null);
698 result.Resolve(null);
699 } else {
699 } else {
700 d.Then(() => {
700 d.Then(() => {
701 if (Interlocked.Decrement(ref countdown) == 0)
701 if (Interlocked.Decrement(ref countdown) == 0)
702 result.Resolve(null);
702 result.Resolve(null);
703 });
703 });
704 }
704 }
705 }
705 }
706
706
707 result.Cancelled(() => {
707 result.Cancelled(() => {
708 foreach (var d in promises)
708 foreach (var d in promises)
709 if (d != null && d.IsExclusive)
709 if (d != null && d.IsExclusive)
710 d.Cancel();
710 d.Cancel();
711 });
711 });
712
712
713 return result;
713 return result;
714 }
714 }
715
715
716 public static Promise<T> ResultToPromise(T result) {
716 public static Promise<T> ResultToPromise(T result) {
717 var p = new Promise<T>();
717 var p = new Promise<T>();
718 p.Resolve(result);
718 p.Resolve(result);
719 return p;
719 return p;
720 }
720 }
721
721
722 public static Promise<T> ExceptionToPromise(Exception error) {
722 public static Promise<T> ExceptionToPromise(Exception error) {
723 if (error == null)
723 if (error == null)
724 throw new ArgumentNullException();
724 throw new ArgumentNullException();
725
725
726 var p = new Promise<T>();
726 var p = new Promise<T>();
727 p.Reject(error);
727 p.Reject(error);
728 return p;
728 return p;
729 }
729 }
730
730
731 #region IPromiseBase explicit implementation
731 #region IPromiseBase explicit implementation
732
732
733 IPromiseBase IPromiseBase.Error(ErrorHandler error) {
733 IPromise IPromise.Error(ErrorHandler error) {
734 return Error(error);
734 return Error(error);
735 }
735 }
736
736
737 IPromiseBase IPromiseBase.Anyway(Action handler) {
737 IPromise IPromise.Anyway(Action handler) {
738 return Anyway(handler);
738 return Anyway(handler);
739 }
739 }
740
740
741 IPromiseBase IPromiseBase.Finally(Action handler) {
741 IPromise IPromise.Finally(Action handler) {
742 return Finally(handler);
742 return Finally(handler);
743 }
743 }
744
744
745 IPromiseBase IPromiseBase.Cancelled(Action handler) {
745 IPromise IPromise.Cancelled(Action handler) {
746 return Cancelled(handler);
746 return Cancelled(handler);
747 }
747 }
748
748
749 void IPromiseBase.Join() {
749 void IPromise.Join() {
750 Join();
750 Join();
751 }
751 }
752
752
753 void IPromiseBase.Join(int timeout) {
753 void IPromise.Join(int timeout) {
754 Join(timeout);
754 Join(timeout);
755 }
755 }
756
756
757 #endregion
757 #endregion
758
758
759
759
760
760
761 }
761 }
762 }
762 }
@@ -1,40 +1,66
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.Text.RegularExpressions;
5 using System.Text.RegularExpressions;
6 using System.Diagnostics;
6
7
7 namespace Implab
8 namespace Implab
8 {
9 {
9 public static class Safe
10 public static class Safe
10 {
11 {
11 public static void ArgumentMatch(string param, string name, Regex rx) {
12 public static void ArgumentMatch(string param, string name, Regex rx) {
12 if (rx == null)
13 if (rx == null)
13 throw new ArgumentNullException("rx");
14 throw new ArgumentNullException("rx");
14 if (!rx.IsMatch(param))
15 if (!rx.IsMatch(param))
15 throw new ArgumentException(String.Format("A prameter value must match {0}", rx), name);
16 throw new ArgumentException(String.Format("A prameter value must match {0}", rx), name);
16 }
17 }
17
18
18 public static void ArgumentNotEmpty(string param, string name) {
19 public static void ArgumentNotEmpty(string param, string name) {
19 if (String.IsNullOrEmpty(param))
20 if (String.IsNullOrEmpty(param))
20 throw new ArgumentException("A parameter can't be empty", name);
21 throw new ArgumentException("A parameter can't be empty", name);
21 }
22 }
22
23
23 public static void ArgumentNotNull(object param, string name) {
24 public static void ArgumentNotNull(object param, string name) {
24 if (param == null)
25 if (param == null)
25 throw new ArgumentNullException(name);
26 throw new ArgumentNullException(name);
26 }
27 }
27
28
28 public static void ArgumentInRange(int arg, int min, int max, string name) {
29 public static void ArgumentInRange(int arg, int min, int max, string name) {
29 if (arg < min || arg > max)
30 if (arg < min || arg > max)
30 throw new ArgumentOutOfRangeException(name);
31 throw new ArgumentOutOfRangeException(name);
31 }
32 }
32
33
33 public static void Dispose<T>(T obj) where T : class
34 public static void Dispose<T>(T obj) where T : class
34 {
35 {
35 var disp = obj as IDisposable;
36 var disp = obj as IDisposable;
36 if (disp != null)
37 if (disp != null)
37 disp.Dispose();
38 disp.Dispose();
38 }
39 }
40
41 [DebuggerStepThrough]
42 public static IPromise<T> GuargPromise<T>(Func<T> action) {
43 ArgumentNotNull(action, "action");
44
45 var p = new Promise<T>();
46 try {
47 p.Resolve(action());
48 } catch (Exception err) {
49 p.Reject(err);
50 }
51
52 return p;
53 }
54
55 [DebuggerStepThrough]
56 public static IPromise<T> GuardPromise<T>(Func<IPromise<T>> action) {
57 ArgumentNotNull(action, "action");
58
59 try {
60 return action();
61 } catch (Exception err) {
62 return Promise<T>.ExceptionToPromise(err);
39 }
63 }
40 }
64 }
65 }
66 }
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now