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