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