##// END OF EJS Templates
implemeted new cancellable promises concept
cin -
r10:aa33d0bb8c0c promises
parent child
Show More
@@ -0,0 +1,16
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading;
6
7 namespace Implab.Test {
8 class PromiseHelper {
9 public static Promise<T> Sleep<T>(int timeout, T retVal) {
10 return AsyncPool.Invoke(() => {
11 Thread.Sleep(timeout);
12 return retVal;
13 });
14 }
15 }
16 }
@@ -1,12 +1,13
1 syntax: glob
1 syntax: glob
2 Implab.Test/bin/
2 Implab.Test/bin/
3 *.user
3 *.user
4 Implab.Test/obj/
4 Implab.Test/obj/
5 *.userprefs
5 *.userprefs
6 Implab/bin/
6 Implab/bin/
7 Implab/obj/
7 Implab/obj/
8 TestResults/
8 TestResults/
9 Implab.Fx/obj/
9 Implab.Fx/obj/
10 Implab.Fx/bin/
10 Implab.Fx/bin/
11 Implab.Fx.Test/bin/
11 Implab.Fx.Test/bin/
12 Implab.Fx.Test/obj/
12 Implab.Fx.Test/obj/
13 _ReSharper.Implab/
@@ -1,101 +1,127
1 using System;
1 using System;
2 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using Microsoft.VisualStudio.TestTools.UnitTesting;
3 using Implab;
4 using System.Reflection;
3 using System.Reflection;
5 using System.Threading;
4 using System.Threading;
6
5
7 namespace Implab.Tests
6 namespace Implab.Test
8 {
7 {
9 [TestClass]
8 [TestClass]
10 public class AsyncTests
9 public class AsyncTests
11 {
10 {
12 [TestMethod]
11 [TestMethod]
13 public void ResolveTest ()
12 public void ResolveTest ()
14 {
13 {
15 int res = -1;
14 int res = -1;
16 var p = new Promise<int> ();
15 var p = new Promise<int> ();
17 p.Then (x => res = x);
16 p.Then (x => res = x);
18 p.Resolve (100);
17 p.Resolve (100);
19
18
20 Assert.AreEqual (res, 100);
19 Assert.AreEqual (res, 100);
21 }
20 }
22
21
23 [TestMethod]
22 [TestMethod]
24 public void RejectTest ()
23 public void RejectTest ()
25 {
24 {
26 int res = -1;
25 int res = -1;
27 Exception err = null;
26 Exception err = null;
28
27
29 var p = new Promise<int> ();
28 var p = new Promise<int> ();
30 p.Then (x => res = x, e => err = e);
29 p.Then (x => res = x, e => err = e);
31 p.Reject (new ApplicationException ("error"));
30 p.Reject (new ApplicationException ("error"));
32
31
33 Assert.AreEqual (res, -1);
32 Assert.AreEqual (res, -1);
34 Assert.AreEqual (err.Message, "error");
33 Assert.AreEqual (err.Message, "error");
35
34
36 }
35 }
37
36
38 [TestMethod]
37 [TestMethod]
39 public void JoinSuccessTest ()
38 public void JoinSuccessTest ()
40 {
39 {
41 var p = new Promise<int> ();
40 var p = new Promise<int> ();
42 p.Resolve (100);
41 p.Resolve (100);
43 Assert.AreEqual (p.Join (), 100);
42 Assert.AreEqual (p.Join (), 100);
44 }
43 }
45
44
46 [TestMethod]
45 [TestMethod]
47 public void JoinFailTest ()
46 public void JoinFailTest ()
48 {
47 {
49 var p = new Promise<int> ();
48 var p = new Promise<int> ();
50 p.Reject (new ApplicationException ("failed"));
49 p.Reject (new ApplicationException ("failed"));
51
50
52 try {
51 try {
53 p.Join ();
52 p.Join ();
54 throw new ApplicationException ("WRONG!");
53 throw new ApplicationException ("WRONG!");
55 } catch (TargetInvocationException err) {
54 } catch (TargetInvocationException err) {
56 Assert.AreEqual (err.InnerException.Message, "failed");
55 Assert.AreEqual (err.InnerException.Message, "failed");
57 } catch {
56 } catch {
58 Assert.Fail ("Got wrong excaption");
57 Assert.Fail ("Got wrong excaption");
59 }
58 }
60 }
59 }
61
60
62 [TestMethod]
61 [TestMethod]
63 public void MapTest ()
62 public void MapTest ()
64 {
63 {
65 var p = new Promise<int> ();
64 var p = new Promise<int> ();
66
65
67 var p2 = p.Map (x => x.ToString ());
66 var p2 = p.Map (x => x.ToString ());
68 p.Resolve (100);
67 p.Resolve (100);
69
68
70 Assert.AreEqual (p2.Join (), "100");
69 Assert.AreEqual (p2.Join (), "100");
71 }
70 }
72
71
73 [TestMethod]
72 [TestMethod]
74 public void ChainTest ()
73 public void ChainTest ()
75 {
74 {
76 var p1 = new Promise<int> ();
75 var p1 = new Promise<int> ();
77
76
78 var p3 = p1.Chain (x => {
77 var p3 = p1.Chain (x => {
79 var p2 = new Promise<string> ();
78 var p2 = new Promise<string> ();
80 p2.Resolve (x.ToString ());
79 p2.Resolve (x.ToString ());
81 return p2;
80 return p2;
82 });
81 });
83
82
84 p1.Resolve (100);
83 p1.Resolve (100);
85
84
86 Assert.AreEqual (p3.Join (), "100");
85 Assert.AreEqual (p3.Join (), "100");
87 }
86 }
88
87
89 [TestMethod]
88 [TestMethod]
90 public void PoolTest ()
89 public void PoolTest ()
91 {
90 {
92 var pid = Thread.CurrentThread.ManagedThreadId;
91 var pid = Thread.CurrentThread.ManagedThreadId;
93 var p = AsyncPool.Invoke (() => {
92 var p = AsyncPool.Invoke (() => Thread.CurrentThread.ManagedThreadId);
94 return Thread.CurrentThread.ManagedThreadId;
95 });
96
93
97 Assert.AreNotEqual (pid, p.Join ());
94 Assert.AreNotEqual (pid, p.Join ());
98 }
95 }
96
97 [TestMethod]
98 public void ComplexCase1Test() {
99 var flags = new bool[3];
100
101 // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map)
102
103 var p = PromiseHelper
104 .Sleep(200, "Alan")
105 .Cancelled(() => flags[0] = true)
106 .Chain(x =>
107 PromiseHelper
108 .Sleep(200, "Hi, " + x)
109 .Map( y => y )
110 .Cancelled(() => flags[1] = true)
111 )
112 .Cancelled(() => flags[2] = true);
113 Thread.Sleep(300);
114 p.Cancel();
115 try {
116 Assert.AreEqual(p.Join(), "Hi, Alan");
117 Assert.Fail("Shouldn't get here");
118 } catch(OperationCanceledException) {
119 }
120
121 Assert.IsFalse(flags[0]);
122 Assert.IsTrue(flags[1]);
123 Assert.IsTrue(flags[2]);
124 }
99 }
125 }
100 }
126 }
101
127
@@ -1,65 +1,66
1 <?xml version="1.0" encoding="utf-8"?>
1 <?xml version="1.0" encoding="utf-8"?>
2 <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2 <Project ToolsVersion="4.0" DefaultTargets="Build" 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>
6 <ProductVersion>
7 </ProductVersion>
7 </ProductVersion>
8 <SchemaVersion>2.0</SchemaVersion>
8 <SchemaVersion>2.0</SchemaVersion>
9 <ProjectGuid>{63F92C0C-61BF-48C0-A377-8D67C3C661D0}</ProjectGuid>
9 <ProjectGuid>{63F92C0C-61BF-48C0-A377-8D67C3C661D0}</ProjectGuid>
10 <OutputType>Library</OutputType>
10 <OutputType>Library</OutputType>
11 <AppDesignerFolder>Properties</AppDesignerFolder>
11 <AppDesignerFolder>Properties</AppDesignerFolder>
12 <RootNamespace>Implab.Test</RootNamespace>
12 <RootNamespace>Implab.Test</RootNamespace>
13 <AssemblyName>Implab.Test</AssemblyName>
13 <AssemblyName>Implab.Test</AssemblyName>
14 <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
14 <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
15 <FileAlignment>512</FileAlignment>
15 <FileAlignment>512</FileAlignment>
16 <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
16 <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
17 </PropertyGroup>
17 </PropertyGroup>
18 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
18 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
19 <DebugSymbols>true</DebugSymbols>
19 <DebugSymbols>true</DebugSymbols>
20 <DebugType>full</DebugType>
20 <DebugType>full</DebugType>
21 <Optimize>false</Optimize>
21 <Optimize>false</Optimize>
22 <OutputPath>bin\Debug\</OutputPath>
22 <OutputPath>bin\Debug\</OutputPath>
23 <DefineConstants>DEBUG;TRACE</DefineConstants>
23 <DefineConstants>DEBUG;TRACE</DefineConstants>
24 <ErrorReport>prompt</ErrorReport>
24 <ErrorReport>prompt</ErrorReport>
25 <WarningLevel>4</WarningLevel>
25 <WarningLevel>4</WarningLevel>
26 </PropertyGroup>
26 </PropertyGroup>
27 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
27 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
28 <DebugType>pdbonly</DebugType>
28 <DebugType>pdbonly</DebugType>
29 <Optimize>true</Optimize>
29 <Optimize>true</Optimize>
30 <OutputPath>bin\Release\</OutputPath>
30 <OutputPath>bin\Release\</OutputPath>
31 <DefineConstants>TRACE</DefineConstants>
31 <DefineConstants>TRACE</DefineConstants>
32 <ErrorReport>prompt</ErrorReport>
32 <ErrorReport>prompt</ErrorReport>
33 <WarningLevel>4</WarningLevel>
33 <WarningLevel>4</WarningLevel>
34 </PropertyGroup>
34 </PropertyGroup>
35 <ItemGroup>
35 <ItemGroup>
36 <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
36 <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
37 <Reference Include="System" />
37 <Reference Include="System" />
38 <Reference Include="System.Core">
38 <Reference Include="System.Core">
39 <RequiredTargetFramework>3.5</RequiredTargetFramework>
39 <RequiredTargetFramework>3.5</RequiredTargetFramework>
40 </Reference>
40 </Reference>
41 </ItemGroup>
41 </ItemGroup>
42 <ItemGroup>
42 <ItemGroup>
43 <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
43 <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
44 <Visible>False</Visible>
44 <Visible>False</Visible>
45 </CodeAnalysisDependentAssemblyPaths>
45 </CodeAnalysisDependentAssemblyPaths>
46 </ItemGroup>
46 </ItemGroup>
47 <ItemGroup>
47 <ItemGroup>
48 <Compile Include="AsyncTests.cs" />
48 <Compile Include="AsyncTests.cs" />
49 <Compile Include="PromiseHelper.cs" />
49 <Compile Include="Properties\AssemblyInfo.cs" />
50 <Compile Include="Properties\AssemblyInfo.cs" />
50 </ItemGroup>
51 </ItemGroup>
51 <ItemGroup>
52 <ItemGroup>
52 <ProjectReference Include="..\Implab\Implab.csproj">
53 <ProjectReference Include="..\Implab\Implab.csproj">
53 <Project>{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}</Project>
54 <Project>{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}</Project>
54 <Name>Implab</Name>
55 <Name>Implab</Name>
55 </ProjectReference>
56 </ProjectReference>
56 </ItemGroup>
57 </ItemGroup>
57 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
58 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
58 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
59 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
59 Other similar extension points exist, see Microsoft.Common.targets.
60 Other similar extension points exist, see Microsoft.Common.targets.
60 <Target Name="BeforeBuild">
61 <Target Name="BeforeBuild">
61 </Target>
62 </Target>
62 <Target Name="AfterBuild">
63 <Target Name="AfterBuild">
63 </Target>
64 </Target>
64 -->
65 -->
65 </Project> No newline at end of file
66 </Project>
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,33 +1,40
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 promise or the complete chain.
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 /// <param name="dependencies">Try to cancel the whole promise chain, the parent promise will be cancelled only if it has only one promise</param>
30 /// <returns></returns>
30 /// <returns></returns>
31 bool Cancel(bool dependencies);
31 bool Cancel(bool dependencies);
32
33 /// <summary>
34 /// Registers handler for the case when the promise is cencelled. If the promise already cancelled the
35 /// handler will be invoked immediatelly.
36 /// </summary>
37 /// <param name="handler">The handler</param>
38 void HandleCancelled(Action handler);
32 }
39 }
33 }
40 }
@@ -1,443 +1,459
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Reflection;
3 using System.Reflection;
5 using System.Text;
6 using System.Diagnostics;
4 using System.Diagnostics;
7 using System.Threading;
5 using System.Threading;
8
6
9 namespace Implab {
7 namespace Implab {
10
8
11 public delegate void ErrorHandler(Exception e);
9 public delegate void ErrorHandler(Exception e);
12
10
13 public delegate void ResultHandler<T>(T result);
11 public delegate void ResultHandler<in T>(T result);
14 public delegate TNew ResultMapper<TSrc, TNew>(TSrc result);
12 public delegate TNew ResultMapper<in TSrc, out TNew>(TSrc result);
15 public delegate Promise<TNew> ChainedOperation<TSrc, TNew>(TSrc result);
13 public delegate Promise<TNew> ChainedOperation<in TSrc, TNew>(TSrc result);
16
14
17 /// <summary>
15 /// <summary>
18 /// Класс для асинхронного получения результатов. Так называемое "обещание".
16 /// Класс для асинхронного получения результатов. Так называемое "обещание".
19 /// </summary>
17 /// </summary>
20 /// <typeparam name="T">Тип получаемого результата</typeparam>
18 /// <typeparam name="T">Тип получаемого результата</typeparam>
21 /// <remarks>
19 /// <remarks>
22 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции,
20 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции,
23 /// клиент получив такое обещание может установить ряд обратных вызово для получения
21 /// клиент получив такое обещание может установить ряд обратных вызово для получения
24 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para>
22 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para>
25 /// <para>
23 /// <para>
26 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на
24 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на
27 /// данные события клиент должен использовать методы <c>Then</c>.
25 /// данные события клиент должен использовать методы <c>Then</c>.
28 /// </para>
26 /// </para>
29 /// <para>
27 /// <para>
30 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой),
28 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой),
31 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о
29 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о
32 /// выполнении обещания.
30 /// выполнении обещания.
33 /// </para>
31 /// </para>
34 /// <para>
32 /// <para>
35 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался,
33 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался,
36 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном
34 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном
37 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в
35 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в
38 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении
36 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении
39 /// обещания.
37 /// обещания.
40 /// </para>
38 /// </para>
41 /// <para>
39 /// <para>
42 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать
40 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать
43 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует
41 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует
44 /// использовать соответствующую форму методе <c>Then</c>.
42 /// использовать соответствующую форму методе <c>Then</c>.
45 /// </para>
43 /// </para>
46 /// <para>
44 /// <para>
47 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать
45 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать
48 /// только инициатор обещания иначе могут возникнуть противоречия.
46 /// только инициатор обещания иначе могут возникнуть противоречия.
49 /// </para>
47 /// </para>
50 /// </remarks>
48 /// </remarks>
51 public class Promise<T> : IPromise {
49 public class Promise<T> : IPromise {
52
50
53 struct ResultHandlerInfo {
51 struct ResultHandlerInfo {
54 public ResultHandler<T> resultHandler;
52 public ResultHandler<T> resultHandler;
55 public ErrorHandler errorHandler;
53 public ErrorHandler errorHandler;
56 }
54 }
57
55
58 IPromise m_parent;
56 readonly IPromise m_parent;
59
57
60 LinkedList<ResultHandlerInfo> m_resultHandlers = new LinkedList<ResultHandlerInfo>();
58 LinkedList<ResultHandlerInfo> m_resultHandlers = new LinkedList<ResultHandlerInfo>();
61 LinkedList<Action> m_cancelHandlers = new LinkedList<Action>();
59 LinkedList<Action> m_cancelHandlers = new LinkedList<Action>();
62
60
63 object m_lock = new Object();
61 readonly object m_lock = new Object();
64 bool m_cancellable;
62 readonly bool m_cancellable;
63 int m_childrenCount = 0;
65
64
66 PromiseState m_state;
65 PromiseState m_state;
67 T m_result;
66 T m_result;
68 Exception m_error;
67 Exception m_error;
69
68
70 int m_childrenCount;
71
72 public Promise() {
69 public Promise() {
73 m_cancellable = true;
70 m_cancellable = true;
74 }
71 }
75
72
76 public Promise(IPromise parent, bool cancellable) {
73 public Promise(IPromise parent, bool cancellable) {
77 m_cancellable = cancellable;
74 m_cancellable = cancellable;
78 m_parent = parent;
75 m_parent = parent;
76 if (parent != null)
77 parent.HandleCancelled(InternalCancel);
79 }
78 }
80
79
81 /// <summary>
80 void InternalCancel() {
82 /// Событие, возникающее при отмене асинхронной операции.
81 // don't try to cancel parent :)
83 /// </summary>
82 Cancel(false);
84 /// <description>
83 }
85 /// Как правило используется для оповещения объекта, выполняющего асинхронную операцию, о том, что ее следует отменить.
86 /// </description>
87 public event EventHandler Cancelled;
88
84
89 /// <summary>
85 /// <summary>
90 /// Выполняет обещание, сообщая об успешном выполнении.
86 /// Выполняет обещание, сообщая об успешном выполнении.
91 /// </summary>
87 /// </summary>
92 /// <param name="result">Результат выполнения.</param>
88 /// <param name="result">Результат выполнения.</param>
93 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
89 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
94 public void Resolve(T result) {
90 public void Resolve(T result) {
95 lock (this) {
91 lock (this) {
96 if (m_state == PromiseState.Cancelled)
92 if (m_state == PromiseState.Cancelled)
97 return;
93 return;
98 if (m_state != PromiseState.Unresolved)
94 if (m_state != PromiseState.Unresolved)
99 throw new InvalidOperationException("The promise is already resolved");
95 throw new InvalidOperationException("The promise is already resolved");
100 m_result = result;
96 m_result = result;
101 m_state = PromiseState.Resolved;
97 m_state = PromiseState.Resolved;
102 }
98 }
103
99
104 // state has been changed to rejected new handlers can't be added
100 OnStateChanged();
105
106 foreach (var handler in m_resultHandlers)
107 InvokeHandler(handler);
108
109 /* ResultHandlerInfo handler;
110 while (FetchNextHandler(out handler))
111 InvokeHandler(handler); */
112 }
101 }
113
102
114 /// <summary>
103 /// <summary>
115 /// Выполняет обещание, сообщая об ошибке
104 /// Выполняет обещание, сообщая об ошибке
116 /// </summary>
105 /// </summary>
117 /// <param name="error">Исключение возникшее при выполнении операции</param>
106 /// <param name="error">Исключение возникшее при выполнении операции</param>
118 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
107 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
119 public void Reject(Exception error) {
108 public void Reject(Exception error) {
120 lock (this) {
109 lock (this) {
121 if (m_state == PromiseState.Cancelled)
110 if (m_state == PromiseState.Cancelled)
122 return;
111 return;
123 if (m_state != PromiseState.Unresolved)
112 if (m_state != PromiseState.Unresolved)
124 throw new InvalidOperationException("The promise is already resolved");
113 throw new InvalidOperationException("The promise is already resolved");
125 m_error = error;
114 m_error = error;
126 m_state = PromiseState.Rejected;
115 m_state = PromiseState.Rejected;
127 }
116 }
128
117
129 // state has been changed to rejected new handlers can't be added
118 OnStateChanged();
130
131 foreach (var handler in m_resultHandlers)
132 InvokeHandler(handler);
133
134 /*ResultHandlerInfo handler;
135 while (FetchNextHandler(out handler))
136 InvokeHandler(handler);*/
137 }
119 }
138
120
139 /// <summary>
121 /// <summary>
140 /// Отменяет операцию, если это возможно.
122 /// Отменяет операцию, если это возможно.
141 /// </summary>
123 /// </summary>
142 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns>
124 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns>
143 public bool Cancel() {
125 public bool Cancel() {
144 return Cancel(true);
126 return Cancel(true);
145 }
127 }
146
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
147 /// <summary>
162 /// <summary>
148 /// Добавляет обработчики событий выполнения обещания.
163 /// Добавляет обработчики событий выполнения обещания.
149 /// </summary>
164 /// </summary>
150 /// <param name="success">Обработчик успешного выполнения обещания.
165 /// <param name="success">Обработчик успешного выполнения обещания.
151 /// Данному обработчику будет передан результат выполнения операции.</param>
166 /// Данному обработчику будет передан результат выполнения операции.</param>
152 /// <param name="error">Обработчик ошибки. Данный обработчик получит
167 /// <param name="error">Обработчик ошибки. Данный обработчик получит
153 /// исключение возникшее при выполнении операции.</param>
168 /// исключение возникшее при выполнении операции.</param>
154 /// <returns>Само обещание</returns>
169 /// <returns>Само обещание</returns>
155 public Promise<T> Then(ResultHandler<T> success, ErrorHandler error) {
170 public Promise<T> Then(ResultHandler<T> success, ErrorHandler error) {
156 if (success == null && error == null)
171 if (success == null && error == null)
157 return this;
172 return this;
158
173
159 var medium = new Promise<T>();
174 var medium = new Promise<T>();
160
175
161 var handlerInfo = new ResultHandlerInfo();
176 var handlerInfo = new ResultHandlerInfo();
162
177
163 if (success != null)
178 if (success != null)
164 handlerInfo.resultHandler = x => {
179 handlerInfo.resultHandler = x => {
165 try {
166 success(x);
180 success(x);
167 medium.Resolve(x);
181 medium.Resolve(x);
168 } catch (Exception e) {
169 medium.Reject(e);
170 }
171 };
182 };
172 else
183 else
173 handlerInfo.resultHandler = x => medium.Resolve(x);
184 handlerInfo.resultHandler = medium.Resolve;
174
185
175 if (error != null)
186 if (error != null)
176 handlerInfo.errorHandler = x => {
187 handlerInfo.errorHandler = x => {
177 try {
188 try {
178 error(x);
189 error(x);
179 } catch { }
190 } catch { }
180 medium.Reject(x);
191 medium.Reject(x);
181 };
192 };
182 else
193 else
183 handlerInfo.errorHandler = x => medium.Reject(x);
194 handlerInfo.errorHandler = medium.Reject;
184
195
185 AddHandler(handlerInfo);
196 AddHandler(handlerInfo);
186
197
187 return medium;
198 return medium;
188 }
199 }
189
200
190 public Promise<T> Then(ResultHandler<T> success) {
201 public Promise<T> Then(ResultHandler<T> success) {
191 return Then(success, null);
202 return Then(success, null);
192 }
203 }
193
204
194 public Promise<T> Error(ErrorHandler error) {
205 public Promise<T> Error(ErrorHandler error) {
195 return Then(null, error);
206 return Then(null, error);
196 }
207 }
197
208
198 public Promise<T> Anyway(Action handler) {
209 public Promise<T> Anyway(Action handler) {
199 if (handler == null)
210 if (handler == null)
200 return this;
211 return this;
201
212
202 var medium = new Promise<T>();
213 var medium = new Promise<T>();
203
214
204 AddHandler(new ResultHandlerInfo {
215 AddHandler(new ResultHandlerInfo {
205 resultHandler = x => {
216 resultHandler = x => {
217 // to avoid handler being called multiple times we handle exception by ourselfs
206 try {
218 try {
207 handler();
219 handler();
208 medium.Resolve(x);
220 medium.Resolve(x);
209 } catch (Exception e) {
221 } catch (Exception e) {
210 medium.Reject(e);
222 medium.Reject(e);
211 }
223 }
212 },
224 },
213 errorHandler = x => {
225 errorHandler = x => {
214 try {
226 try {
215 handler();
227 handler();
216 } catch { }
228 } catch { }
217 medium.Reject(x);
229 medium.Reject(x);
218 }
230 }
219 });
231 });
220
232
221 return medium;
233 return medium;
222 }
234 }
223
235
224 /// <summary>
236 /// <summary>
225 /// Позволяет преобразовать результат выполения операции к новому типу.
237 /// Позволяет преобразовать результат выполения операции к новому типу.
226 /// </summary>
238 /// </summary>
227 /// <typeparam name="TNew">Новый тип результата.</typeparam>
239 /// <typeparam name="TNew">Новый тип результата.</typeparam>
228 /// <param name="mapper">Преобразование результата к новому типу.</param>
240 /// <param name="mapper">Преобразование результата к новому типу.</param>
229 /// <param name="error">Обработчик ошибки. Данный обработчик получит
241 /// <param name="error">Обработчик ошибки. Данный обработчик получит
230 /// исключение возникшее при выполнении операции.</param>
242 /// исключение возникшее при выполнении операции.</param>
231 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
243 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
232 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) {
244 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) {
233 if (mapper == null)
245 if (mapper == null)
234 throw new ArgumentNullException("mapper");
246 throw new ArgumentNullException("mapper");
235
247
236 // создаем прицепленное обещание
248 // создаем прицепленное обещание
237 Promise<TNew> chained = new Promise<TNew>();
249 var chained = new Promise<TNew>();
238
250
239 AddHandler(new ResultHandlerInfo() {
251 AddHandler(new ResultHandlerInfo() {
240 resultHandler = delegate(T result) {
252 resultHandler = result => chained.Resolve(mapper(result)),
241 try {
242 // если преобразование выдаст исключение, то сработает reject сцепленного deferred
243 chained.Resolve(mapper(result));
244 } catch (Exception e) {
245 chained.Reject(e);
246 }
247 },
248 errorHandler = delegate(Exception e) {
253 errorHandler = delegate(Exception e) {
249 if (error != null)
254 if (error != null)
255 try {
250 error(e);
256 error(e);
257 } catch { }
251 // в случае ошибки нужно передать исключение дальше по цепочке
258 // в случае ошибки нужно передать исключение дальше по цепочке
252 chained.Reject(e);
259 chained.Reject(e);
253 }
260 }
254 });
261 });
255
262
256 return chained;
263 return chained;
257 }
264 }
258
265
259 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper) {
266 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper) {
260 return Map(mapper, null);
267 return Map(mapper, null);
261 }
268 }
262
269
263 /// <summary>
270 /// <summary>
264 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после
271 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после
265 /// выполнения текущей, а результат текущей операции может быть использован для инициализации
272 /// выполнения текущей, а результат текущей операции может быть использован для инициализации
266 /// новой операции.
273 /// новой операции.
267 /// </summary>
274 /// </summary>
268 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam>
275 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam>
269 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param>
276 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param>
270 /// <param name="error">Обработчик ошибки. Данный обработчик получит
277 /// <param name="error">Обработчик ошибки. Данный обработчик получит
271 /// исключение возникшее при выполнении текуещй операции.</param>
278 /// исключение возникшее при выполнении текуещй операции.</param>
272 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
279 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
273 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) {
280 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) {
274
281
275 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
282 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
276 // создать посредника, к которому будут подвызяваться следующие обработчики.
283 // создать посредника, к которому будут подвызяваться следующие обработчики.
277 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы
284 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы
278 // передать через него результаты работы.
285 // передать через него результаты работы.
279 Promise<TNew> medium = new Promise<TNew>();
286 var medium = new Promise<TNew>(this, true);
280
287
281 AddHandler(new ResultHandlerInfo() {
288 AddHandler(new ResultHandlerInfo {
282 resultHandler = delegate(T result) {
289 resultHandler = delegate(T result) {
283 try {
290 if (medium.State == PromiseState.Cancelled)
284 chained(result).Then(
291 return;
285 x => medium.Resolve(x),
292
286 e => medium.Reject(e)
293 var promise = chained(result);
294
295 // notify chained operation that it's not needed
296 medium.Cancelled(() => promise.Cancel());
297 promise.Then(
298 medium.Resolve,
299 medium.Reject
287 );
300 );
288 } catch (Exception e) {
289 // если сцепленное действие выдало исключение вместо обещания, то передаем ошибку по цепочке
290 medium.Reject(e);
291 }
292 },
301 },
293 errorHandler = delegate(Exception e) {
302 errorHandler = delegate(Exception e) {
294 if (error != null)
303 if (error != null)
295 error(e);
304 error(e);
296 // в случае ошибки нужно передать исключение дальше по цепочке
305 // в случае ошибки нужно передать исключение дальше по цепочке
297 medium.Reject(e);
306 medium.Reject(e);
298 }
307 }
299 });
308 });
300
309
301 return medium;
310 return medium;
302 }
311 }
303
312
304 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) {
313 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) {
305 return Chain(chained, null);
314 return Chain(chained, null);
306 }
315 }
307
316
317 public Promise<T> Cancelled(Action handler) {
318 if (handler == null)
319 return this;
320 lock (m_lock) {
321 if (m_state == PromiseState.Unresolved)
322 m_cancelHandlers.AddLast(handler);
323 else if (m_state == PromiseState.Cancelled)
324 handler();
325 }
326 return this;
327 }
328
329 public void HandleCancelled(Action handler) {
330 Cancelled(handler);
331 }
332
308 /// <summary>
333 /// <summary>
309 /// Дожидается отложенного обещания и в случае успеха, возвращает
334 /// Дожидается отложенного обещания и в случае успеха, возвращает
310 /// его, результат, в противном случае бросает исключение.
335 /// его, результат, в противном случае бросает исключение.
311 /// </summary>
336 /// </summary>
312 /// <remarks>
337 /// <remarks>
313 /// <para>
338 /// <para>
314 /// Если ожидание обещания было прервано по таймауту, это не значит,
339 /// Если ожидание обещания было прервано по таймауту, это не значит,
315 /// что обещание было отменено или что-то в этом роде, это только
340 /// что обещание было отменено или что-то в этом роде, это только
316 /// означает, что мы его не дождались, однако все зарегистрированные
341 /// означает, что мы его не дождались, однако все зарегистрированные
317 /// обработчики, как были так остались и они будут вызваны, когда
342 /// обработчики, как были так остались и они будут вызваны, когда
318 /// обещание будет выполнено.
343 /// обещание будет выполнено.
319 /// </para>
344 /// </para>
320 /// <para>
345 /// <para>
321 /// Такое поведение вполне оправдано поскольку таймаут может истечь
346 /// Такое поведение вполне оправдано поскольку таймаут может истечь
322 /// в тот момент, когда началась обработка цепочки обработчиков, и
347 /// в тот момент, когда началась обработка цепочки обработчиков, и
323 /// к тому же текущее обещание может стоять в цепочке обещаний и его
348 /// к тому же текущее обещание может стоять в цепочке обещаний и его
324 /// отклонение может привести к непрогнозируемому результату.
349 /// отклонение может привести к непрогнозируемому результату.
325 /// </para>
350 /// </para>
326 /// </remarks>
351 /// </remarks>
327 /// <param name="timeout">Время ожидания</param>
352 /// <param name="timeout">Время ожидания</param>
328 /// <returns>Результат выполнения обещания</returns>
353 /// <returns>Результат выполнения обещания</returns>
329 public T Join(int timeout) {
354 public T Join(int timeout) {
330 ManualResetEvent evt = new ManualResetEvent(false);
355 var evt = new ManualResetEvent(false);
331 Anyway(() => evt.Set());
356 Anyway(() => evt.Set());
357 Cancelled(() => evt.Set());
332
358
333 if (!evt.WaitOne(timeout, true))
359 if (!evt.WaitOne(timeout, true))
334 throw new TimeoutException();
360 throw new TimeoutException();
335
361
336 if (m_error != null)
362 switch (State) {
363 case PromiseState.Resolved:
364 return m_result;
365 case PromiseState.Cancelled:
366 throw new OperationCanceledException();
367 case PromiseState.Rejected:
337 throw new TargetInvocationException(m_error);
368 throw new TargetInvocationException(m_error);
338 else
369 default:
339 return m_result;
370 throw new ApplicationException(String.Format("Invalid promise state {0}", State));
371 }
340 }
372 }
341
373
342 public T Join() {
374 public T Join() {
343 return Join(Timeout.Infinite);
375 return Join(Timeout.Infinite);
344 }
376 }
345
377
346 /// <summary>
347 /// Данный метод последовательно извлекает обработчики обещания и когда
348 /// их больше не осталось - ставит состояние "разрешено".
349 /// </summary>
350 /// <param name="handler">Информация об обработчике</param>
351 /// <returns>Признак того, что еще остались обработчики в очереди</returns>
352 bool FetchNextHandler(out ResultHandlerInfo handler) {
353 handler = default(ResultHandlerInfo);
354
355 lock (this) {
356 Debug.Assert(m_state != PromiseState.Unresolved);
357
358 if (m_resultHandlers.Count > 0) {
359 handler = m_resultHandlers.First.Value;
360 m_resultHandlers.RemoveFirst();
361 return true;
362 } else {
363 return false;
364 }
365 }
366 }
367
368 void AddHandler(ResultHandlerInfo handler) {
378 void AddHandler(ResultHandlerInfo handler) {
369 bool invokeRequired = false;
379 bool invokeRequired = false;
370
380
371 lock (this) {
381 lock (m_lock) {
372 if (m_state == PromiseState.Unresolved)
382 m_childrenCount++;
383 if (m_state == PromiseState.Unresolved) {
373 m_resultHandlers.AddLast(handler);
384 m_resultHandlers.AddLast(handler);
374 else
385 } else
375 invokeRequired = true;
386 invokeRequired = true;
376 }
387 }
377
388
378 // обработчики не должны блокировать сам объект
389 // обработчики не должны блокировать сам объект
379 if (invokeRequired)
390 if (invokeRequired)
380 InvokeHandler(handler);
391 InvokeHandler(handler);
381 }
392 }
382
393
383 void InvokeHandler(ResultHandlerInfo handler) {
394 void InvokeHandler(ResultHandlerInfo handler) {
384 if (m_error == null) {
395 switch (m_state) {
396 case PromiseState.Resolved:
385 try {
397 try {
386 if (handler.resultHandler != null)
398 if (handler.resultHandler != null)
387 handler.resultHandler(m_result);
399 handler.resultHandler(m_result);
400 } catch (Exception e) {
401 try {
402 if (handler.errorHandler != null)
403 handler.errorHandler(e);
388 } catch { }
404 } catch { }
389 }
405 }
390
406 break;
391 if (m_error != null) {
407 case PromiseState.Rejected:
392 try {
408 try {
393 if (handler.errorHandler != null)
409 if (handler.errorHandler != null)
394 handler.errorHandler(m_error);
410 handler.errorHandler(m_error);
395 } catch { }
411 } catch { }
412 break;
413 default:
414 // do nothing
415 return;
396 }
416 }
397 }
417 }
398
418
399
419
400
420
401 public bool IsExclusive {
421 public bool IsExclusive {
402 get {
422 get {
403 lock (m_lock) {
423 lock (m_lock) {
404 return m_childrenCount <= 1;
424 return m_childrenCount <= 1;
405 }
425 }
406 }
426 }
407 }
427 }
408
428
409 public PromiseState State {
429 public PromiseState State {
410 get {
430 get {
411 lock (m_lock) {
431 lock (m_lock) {
412 return m_state;
432 return m_state;
413 }
433 }
414 }
434 }
415 }
435 }
416
436
417 public bool Cancel(bool dependencies) {
437 public bool Cancel(bool dependencies) {
418 bool result;
438 bool result;
419
439
420 lock (m_lock) {
440 lock (m_lock) {
421 if (m_state == PromiseState.Unresolved) {
441 if (m_state == PromiseState.Unresolved) {
422 m_state = PromiseState.Cancelled;
442 m_state = PromiseState.Cancelled;
423 result = true;
443 result = true;
424 } else {
444 } else {
425 result = false;
445 result = false;
426 }
446 }
427 }
447 }
428
448
429 if (dependencies && m_parent != null && m_parent.IsExclusive) {
449 if (result)
430 // TODO syncronize IsExclusive, AddHandler, Cancel (maybe CancelExclusive)
450 OnStateChanged();
431 m_parent.Cancel(true);
432 }
433
451
434 if (result) {
452 if (dependencies && m_parent != null && m_parent.IsExclusive) {
435 // state has been changed to cancelled, new handlers can't be added
453 m_parent.Cancel(true);
436 foreach (var handler in m_cancelHandlers)
437 handler();
438 }
454 }
439
455
440 return result;
456 return result;
441 }
457 }
442 }
458 }
443 }
459 }
@@ -1,113 +1,112
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System.Linq;
4 using System.Text;
4 using System.Text;
5 using System.Threading;
5 using System.Threading;
6
6
7 namespace Implab
7 namespace Implab
8 {
8 {
9 /// <summary>
9 /// <summary>
10 /// This class allows to interact with asyncronuos task.
10 /// This class allows to interact with asyncronuos task.
11 /// </summary>
11 /// </summary>
12 /// <remarks>
12 /// <remarks>
13 /// Members of this object are thread safe.
13 /// Members of this object are thread safe.
14 /// </remarks>
14 /// </remarks>
15 class TaskController
15 class TaskController
16 {
16 {
17 object m_lock;
17 readonly object m_lock;
18 string m_message;
18 string m_message;
19 bool m_cancelled;
20
19
21 float m_current;
20 float m_current;
22 float m_max;
21 float m_max;
23
22
24 public event EventHandler<ValueEventArgs<string>> MessageUpdated;
23 public event EventHandler<ValueEventArgs<string>> MessageUpdated;
25 public event EventHandler<ValueEventArgs<float>> ProgressUpdated;
24 public event EventHandler<ValueEventArgs<float>> ProgressUpdated;
26 public event EventHandler<ProgressInitEventArgs> ProgressInit;
25 public event EventHandler<ProgressInitEventArgs> ProgressInit;
27
26
28 public TaskController()
27 public TaskController()
29 {
28 {
30 m_lock = new Object();
29 m_lock = new Object();
31 }
30 }
32
31
33 public string Message
32 public string Message
34 {
33 {
35 get
34 get
36 {
35 {
37 lock (m_lock)
36 lock (m_lock)
38 return m_message;
37 return m_message;
39 }
38 }
40 set
39 set
41 {
40 {
42 lock (m_lock)
41 lock (m_lock)
43 {
42 {
44 m_message = value;
43 m_message = value;
45 OnMessageUpdated();
44 OnMessageUpdated();
46 }
45 }
47 }
46 }
48 }
47 }
49
48
50 public float CurrentProgress
49 public float CurrentProgress
51 {
50 {
52 get
51 get
53 {
52 {
54 lock (m_lock)
53 lock (m_lock)
55 return m_current;
54 return m_current;
56 }
55 }
57 set
56 set
58 {
57 {
59 lock (m_lock)
58 lock (m_lock)
60 {
59 {
61 var prev = m_current;
60 var prev = m_current;
62 m_current = value;
61 m_current = value;
63 if (m_current >= m_max)
62 if (m_current >= m_max)
64 m_current = m_max;
63 m_current = m_max;
65 if (m_current != prev)
64 if (m_current != prev)
66 OnProgressUpdated();
65 OnProgressUpdated();
67 }
66 }
68 }
67 }
69 }
68 }
70
69
71 public void InitProgress(float current, float max, string message)
70 public void InitProgress(float current, float max, string message)
72 {
71 {
73 if (max < 0)
72 if (max < 0)
74 throw new ArgumentOutOfRangeException("max");
73 throw new ArgumentOutOfRangeException("max");
75 if (current < 0 || current > max)
74 if (current < 0 || current > max)
76 throw new ArgumentOutOfRangeException("current");
75 throw new ArgumentOutOfRangeException("current");
77
76
78 lock(m_lock) {
77 lock(m_lock) {
79 m_current = current;
78 m_current = current;
80 m_max = max;
79 m_max = max;
81 m_message = message;
80 m_message = message;
82 OnProgressInit();
81 OnProgressInit();
83 }
82 }
84 }
83 }
85
84
86 protected virtual void OnMessageUpdated()
85 protected virtual void OnMessageUpdated()
87 {
86 {
88 var temp = MessageUpdated;
87 var temp = MessageUpdated;
89 if (temp != null)
88 if (temp != null)
90 {
89 {
91 temp(this, new ValueEventArgs<string>(m_message));
90 temp(this, new ValueEventArgs<string>(m_message));
92 }
91 }
93 }
92 }
94
93
95 protected virtual void OnProgressUpdated()
94 protected virtual void OnProgressUpdated()
96 {
95 {
97 var temp = ProgressUpdated;
96 var temp = ProgressUpdated;
98 if (temp != null)
97 if (temp != null)
99 {
98 {
100 temp(this,new ValueEventArgs<float>(m_current));
99 temp(this,new ValueEventArgs<float>(m_current));
101 }
100 }
102 }
101 }
103
102
104 protected virtual void OnProgressInit()
103 protected virtual void OnProgressInit()
105 {
104 {
106 var temp = ProgressInit;
105 var temp = ProgressInit;
107 if (temp != null)
106 if (temp != null)
108 {
107 {
109 temp(this, new ProgressInitEventArgs(m_current,m_max, m_message));
108 temp(this, new ProgressInitEventArgs(m_current,m_max, m_message));
110 }
109 }
111 }
110 }
112 }
111 }
113 }
112 }
General Comments 0
You need to be logged in to leave comments. Login now