##// END OF EJS Templates
Слияние с v2
cin -
r192:f1da3afc3521 merge release v2.1 default
parent child
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,80
1 <?xml version="1.0" encoding="utf-8"?>
2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <PropertyGroup>
4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 <ProductVersion>8.0.30703</ProductVersion>
7 <SchemaVersion>2.0</SchemaVersion>
8 <ProjectGuid>{2BD05F84-E067-4B87-9477-FDC2676A21C6}</ProjectGuid>
9 <OutputType>Library</OutputType>
10 <RootNamespace>Implab.Fx.Test</RootNamespace>
11 <AssemblyName>Implab.Fx.Test</AssemblyName>
12 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
13 </PropertyGroup>
14 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
15 <DebugSymbols>true</DebugSymbols>
16 <DebugType>full</DebugType>
17 <Optimize>false</Optimize>
18 <OutputPath>bin\Debug</OutputPath>
19 <DefineConstants>DEBUG;MONO</DefineConstants>
20 <ErrorReport>prompt</ErrorReport>
21 <WarningLevel>4</WarningLevel>
22 <ConsolePause>false</ConsolePause>
23 </PropertyGroup>
24 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
25 <Optimize>true</Optimize>
26 <OutputPath>bin\Release</OutputPath>
27 <ErrorReport>prompt</ErrorReport>
28 <WarningLevel>4</WarningLevel>
29 <ConsolePause>false</ConsolePause>
30 <DefineConstants>MONO</DefineConstants>
31 </PropertyGroup>
32 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
33 <DebugSymbols>true</DebugSymbols>
34 <DebugType>full</DebugType>
35 <Optimize>false</Optimize>
36 <OutputPath>bin\Debug</OutputPath>
37 <DefineConstants>DEBUG;TRACE;NET_4_5;MONO</DefineConstants>
38 <ErrorReport>prompt</ErrorReport>
39 <WarningLevel>4</WarningLevel>
40 <ConsolePause>false</ConsolePause>
41 </PropertyGroup>
42 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
43 <Optimize>true</Optimize>
44 <OutputPath>bin\Release</OutputPath>
45 <DefineConstants>NET_4_5;MONO</DefineConstants>
46 <ErrorReport>prompt</ErrorReport>
47 <WarningLevel>4</WarningLevel>
48 <ConsolePause>false</ConsolePause>
49 </PropertyGroup>
50 <ItemGroup>
51 <Reference Include="System" />
52 <Reference Include="nunit.framework" />
53 <Reference Include="System.Windows.Forms" />
54 <Reference Include="System.Drawing" />
55 <Reference Include="System.Data" />
56 </ItemGroup>
57 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
58 <ItemGroup>
59 <ProjectReference Include="..\Implab.Fx\Implab.Fx.csproj">
60 <Project>{06E706F8-6881-43EB-927E-FFC503AF6ABC}</Project>
61 <Name>Implab.Fx</Name>
62 </ProjectReference>
63 <ProjectReference Include="..\Implab\Implab.csproj">
64 <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
65 <Name>Implab</Name>
66 </ProjectReference>
67 </ItemGroup>
68 <ItemGroup>
69 <Compile Include="OverlayTest.cs" />
70 <Compile Include="Properties\AssemblyInfo.cs" />
71 <Compile Include="Sample\MainForm.Designer.cs" />
72 <Compile Include="Sample\MainForm.cs" />
73 <Compile Include="Sample\OverlayForm.Designer.cs" />
74 <Compile Include="Sample\OverlayForm.cs" />
75 </ItemGroup>
76 <ItemGroup>
77 <None Include="Sample\MainForm.resx" />
78 <None Include="Sample\OverlayForm.resx" />
79 </ItemGroup>
80 </Project> No newline at end of file
@@ -0,0 +1,23
1 using System.Windows.Forms;
2 using System;
3
4
5 namespace Implab.Fx {
6 public class ControlBoundPromise<T> : Promise<T> {
7 readonly Control m_target;
8
9 public ControlBoundPromise(Control target) {
10 Safe.ArgumentNotNull(target, "target");
11
12 m_target = target;
13 }
14
15 protected override void SignalHandler(HandlerDescriptor handler, int signal) {
16 if (m_target.InvokeRequired)
17 m_target.BeginInvoke(new Action<Promise<T>.HandlerDescriptor, int>(base.SignalHandler), handler, signal);
18 else
19 base.SignalHandler(handler, signal);
20 }
21 }
22 }
23
@@ -0,0 +1,148
1 using System;
2 using Implab.Parallels;
3
4 #if MONO
5
6 using NUnit.Framework;
7 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
8 using TestMethodAttribute = NUnit.Framework.TestAttribute;
9
10 #else
11
12 using Microsoft.VisualStudio.TestTools.UnitTesting;
13
14 #endif
15
16 namespace Implab.Test {
17 [TestClass]
18 public class CancelationTests {
19
20 [TestMethod]
21 public void PromiseCancelTest() {
22 var p = new Promise();
23 bool requested = false;
24 var reason = new Exception("Test");
25
26 // request cancelation
27 p.Cancel(reason);
28
29 Assert.IsTrue(p.IsCancellationRequested);
30 Assert.AreSame(reason, p.CancellationReason);
31 Assert.IsFalse(p.IsCancelled);
32
33 p.CancellationRequested(r => {
34 Assert.AreSame(reason, r);
35 requested = true;
36 });
37
38 Assert.IsTrue(requested);
39
40 // cancel the promise
41 Assert.IsTrue(p.CancelOperationIfRequested());
42 Assert.IsTrue(p.IsCancelled);
43 Assert.AreSame(reason, p.Error);
44 }
45
46 [TestMethod]
47 public void CancelActionBeforeStartTask() {
48 bool run = false;
49 var task = new ActionTask(() => {
50 run = true;
51 }, null, null, true);
52
53 // request cancelation
54 task.Cancel();
55 Assert.IsTrue(task.IsCancelled);
56 task.Resolve();
57 Assert.IsFalse(run);
58 }
59
60 [TestMethod]
61 public void CancelActionAfterTaskStarted() {
62 var finish = new Signal();
63 var started = new Signal();
64
65 var task = new ActionTask(() => {
66 started.Set();
67 finish.Wait();
68 }, null, null, true);
69
70 AsyncPool.RunThread(() => {
71 task.Resolve();
72 });
73
74 started.Wait(1000);
75
76 task.Cancel();
77 Assert.IsTrue(task.IsCancellationRequested);
78 Assert.IsFalse(task.IsCancelled);
79 Assert.IsFalse(task.IsResolved);
80
81 finish.Set();
82 task.Join(1000);
83
84 }
85
86 [TestMethod]
87 public void CancelTaskChainFromBottom() {
88 var started = new Signal();
89 var check1 = new Signal();
90 var requested = false;
91 var p1 = AsyncPool.RunThread(token => {
92 token.CancellationRequested(reason => requested = true);
93 started.Set();
94 check1.Wait();
95 token.CancelOperationIfRequested();
96 });
97
98 started.Wait();
99
100 var p2 = p1.Then(() => {
101 });
102
103 Assert.IsFalse(p1.IsResolved);
104 Assert.IsFalse(p2.IsResolved);
105
106 p2.Cancel();
107
108 Assert.IsFalse(p2.IsCancelled);
109 Assert.IsFalse(p1.IsCancelled);
110 Assert.IsTrue(requested);
111
112 check1.Set();
113
114 try {
115 p2.Join(1000);
116 Assert.Fail("The chain isn't cancelled");
117 } catch(OperationCanceledException){
118 }
119
120 Assert.IsTrue(p1.IsCancelled);
121 Assert.IsTrue(p2.IsCancelled);
122 }
123
124
125
126 [TestMethod]
127 public void CancellableAsyncTask() {
128 var finish = new Signal();
129 var started = new Signal();
130
131 var p = AsyncPool.RunThread(token => {
132 token.CancellationRequested(r => finish.Set());
133 started.Set();
134 finish.Wait();
135 Assert.IsTrue(token.CancelOperationIfRequested());
136 });
137
138 started.Wait(1000);
139 Assert.IsFalse(p.IsResolved);
140 p.Cancel();
141 try {
142 p.Join(1000);
143 } catch (OperationCanceledException) {
144 }
145 }
146 }
147 }
148
@@ -0,0 +1,52
1 <?xml version="1.0" encoding="utf-8"?>
2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <PropertyGroup>
4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 <ProductVersion>8.0.30703</ProductVersion>
7 <SchemaVersion>2.0</SchemaVersion>
8 <ProjectGuid>{4D364996-7ECD-4193-8F90-F223FFEA49DA}</ProjectGuid>
9 <OutputType>Library</OutputType>
10 <RootNamespace>Implab.Format.Test</RootNamespace>
11 <AssemblyName>Implab.Format.Test</AssemblyName>
12 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
13 <ReleaseVersion>0.2</ReleaseVersion>
14 </PropertyGroup>
15 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
16 <DebugSymbols>true</DebugSymbols>
17 <DebugType>full</DebugType>
18 <Optimize>false</Optimize>
19 <OutputPath>bin\Debug</OutputPath>
20 <DefineConstants>DEBUG;</DefineConstants>
21 <ErrorReport>prompt</ErrorReport>
22 <WarningLevel>4</WarningLevel>
23 <ConsolePause>false</ConsolePause>
24 </PropertyGroup>
25 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
26 <DebugType>full</DebugType>
27 <Optimize>true</Optimize>
28 <OutputPath>bin\Release</OutputPath>
29 <ErrorReport>prompt</ErrorReport>
30 <WarningLevel>4</WarningLevel>
31 <ConsolePause>false</ConsolePause>
32 </PropertyGroup>
33 <ItemGroup>
34 <Reference Include="System" />
35 <Reference Include="nunit.framework">
36 <HintPath>..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
37 </Reference>
38 </ItemGroup>
39 <ItemGroup>
40 <Compile Include="JsonTests.cs" />
41 </ItemGroup>
42 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
43 <ItemGroup>
44 <ProjectReference Include="..\..\Implab\Implab.csproj">
45 <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
46 <Name>Implab</Name>
47 </ProjectReference>
48 </ItemGroup>
49 <ItemGroup>
50 <None Include="packages.config" />
51 </ItemGroup>
52 </Project> No newline at end of file
@@ -0,0 +1,88
1 using NUnit.Framework;
2 using System;
3 using Implab.Formats.JSON;
4 using Implab.Automaton;
5
6 namespace Implab.Format.Test {
7 [TestFixture]
8 public class JsonTests {
9 [Test]
10 public void TestScannerValidTokens() {
11 using (var scanner = new JSONScanner(@"9123, -123, 0, 0.1, -0.2, -0.1e3, 1.3E-3, ""some \t\n\u0020 text"", literal []{}:")) {
12
13 Tuple<JsonTokenType,object>[] expexted = {
14 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 9123d),
15 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
16 new Tuple<JsonTokenType,object>(JsonTokenType.Number, -123d),
17 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
18 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 0d),
19 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
20 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 0.1d),
21 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
22 new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.2d),
23 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
24 new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.1e3d),
25 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
26 new Tuple<JsonTokenType,object>(JsonTokenType.Number, 1.3E-3d),
27 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
28 new Tuple<JsonTokenType,object>(JsonTokenType.String, "some \t\n text"),
29 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
30 new Tuple<JsonTokenType,object>(JsonTokenType.Literal, "literal"),
31 new Tuple<JsonTokenType,object>(JsonTokenType.BeginArray, " ["),
32 new Tuple<JsonTokenType,object>(JsonTokenType.EndArray, "]"),
33 new Tuple<JsonTokenType,object>(JsonTokenType.BeginObject, "{"),
34 new Tuple<JsonTokenType,object>(JsonTokenType.EndObject, "}"),
35 new Tuple<JsonTokenType,object>(JsonTokenType.NameSeparator, ":")
36 };
37
38 object value;
39 JsonTokenType tokenType;
40 for (var i = 0; i < expexted.Length; i++) {
41
42 Assert.IsTrue(scanner.ReadToken(out value, out tokenType));
43 Assert.AreEqual(expexted[i].Item1, tokenType);
44 Assert.AreEqual(expexted[i].Item2, value);
45 }
46
47 Assert.IsFalse(scanner.ReadToken(out value, out tokenType));
48 }
49 }
50
51 [Test]
52 public void TestScannerBadTokens() {
53 var bad = new [] {
54 " 1",
55 " literal",
56 " \"",
57 "\"unclosed string",
58 "1.bad",
59 "001", // should be read as three numbers
60 "--10",
61 "+10",
62 "1.0.0",
63 "1e1.0",
64 "l1teral0",
65 ".123",
66 "-.123"
67 };
68
69 foreach (var json in bad)
70 using (var scanner = new JSONScanner(json)) {
71 try {
72 object value;
73 JsonTokenType token;
74 scanner.ReadToken(out value, out token);
75 if (!Object.Equals(value,json)) {
76 Console.WriteLine("'{0}' is read as {1}", json, value is String ? String.Format("'{0}'", value) : value );
77 continue;
78 }
79 Assert.Fail("Token '{0}' shouldn't pass", json);
80 } catch (ParserException e) {
81 Console.WriteLine(e.Message);
82 }
83 }
84
85 }
86 }
87 }
88
@@ -0,0 +1,4
1 <?xml version="1.0" encoding="utf-8"?>
2 <packages>
3 <package id="NUnit" version="2.6.4" targetFramework="net45" />
4 </packages> No newline at end of file
@@ -0,0 +1,69
1 <?xml version="1.0" encoding="utf-8"?>
2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <PropertyGroup>
4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 <ProductVersion>8.0.30703</ProductVersion>
7 <SchemaVersion>2.0</SchemaVersion>
8 <ProjectGuid>{2BD05F84-E067-4B87-9477-FDC2676A21C6}</ProjectGuid>
9 <OutputType>Library</OutputType>
10 <RootNamespace>Implab.Test</RootNamespace>
11 <AssemblyName>Implab.Test</AssemblyName>
12 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
13 <ReleaseVersion>0.2</ReleaseVersion>
14 </PropertyGroup>
15 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
16 <DebugSymbols>true</DebugSymbols>
17 <DebugType>full</DebugType>
18 <Optimize>false</Optimize>
19 <OutputPath>bin\Debug</OutputPath>
20 <DefineConstants>DEBUG;MONO</DefineConstants>
21 <ErrorReport>prompt</ErrorReport>
22 <WarningLevel>4</WarningLevel>
23 <ConsolePause>false</ConsolePause>
24 </PropertyGroup>
25 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
26 <Optimize>true</Optimize>
27 <OutputPath>bin\Release</OutputPath>
28 <ErrorReport>prompt</ErrorReport>
29 <WarningLevel>4</WarningLevel>
30 <ConsolePause>false</ConsolePause>
31 <DefineConstants>MONO</DefineConstants>
32 </PropertyGroup>
33 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
34 <DebugSymbols>true</DebugSymbols>
35 <DebugType>full</DebugType>
36 <Optimize>false</Optimize>
37 <OutputPath>bin\Debug</OutputPath>
38 <DefineConstants>DEBUG;TRACE;NET_4_5;MONO</DefineConstants>
39 <ErrorReport>prompt</ErrorReport>
40 <WarningLevel>4</WarningLevel>
41 <ConsolePause>false</ConsolePause>
42 </PropertyGroup>
43 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
44 <Optimize>true</Optimize>
45 <OutputPath>bin\Release</OutputPath>
46 <DefineConstants>NET_4_5;MONO</DefineConstants>
47 <ErrorReport>prompt</ErrorReport>
48 <WarningLevel>4</WarningLevel>
49 <ConsolePause>false</ConsolePause>
50 </PropertyGroup>
51 <ItemGroup>
52 <Reference Include="System" />
53 <Reference Include="nunit.framework" />
54 </ItemGroup>
55 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
56 <ItemGroup>
57 <Compile Include="AsyncTests.cs" />
58 <Compile Include="PromiseHelper.cs" />
59 <Compile Include="Properties\AssemblyInfo.cs" />
60 <Compile Include="CancelationTests.cs" />
61 <Compile Include="RunnableComponentTests.cs" />
62 </ItemGroup>
63 <ItemGroup>
64 <ProjectReference Include="..\Implab\Implab.csproj">
65 <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
66 <Name>Implab</Name>
67 </ProjectReference>
68 </ItemGroup>
69 </Project> No newline at end of file
@@ -0,0 +1,195
1 using System;
2 using System.Reflection;
3 using System.Threading;
4 using Implab.Parallels;
5 using Implab.Components;
6
7 #if MONO
8
9 using NUnit.Framework;
10 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
11 using TestMethodAttribute = NUnit.Framework.TestAttribute;
12 using AssertFailedException = NUnit.Framework.AssertionException;
13 #else
14
15 using Microsoft.VisualStudio.TestTools.UnitTesting;
16
17 #endif
18
19 namespace Implab.Test {
20 [TestClass]
21 public class RunnableComponentTests {
22
23 static void ShouldThrow(Action action) {
24 try {
25 action();
26 Assert.Fail();
27 } catch (AssertFailedException) {
28 throw;
29 } catch {
30 }
31 }
32
33 class Runnable : RunnableComponent {
34 public Runnable(bool initialized) : base(initialized) {
35 }
36
37 public Action MockInit {
38 get;
39 set;
40 }
41
42 public Func<IPromise> MockStart {
43 get;
44 set;
45 }
46
47 public Func<IPromise> MockStop {
48 get;
49 set;
50 }
51
52 protected override IPromise OnStart() {
53 return MockStart != null ? MockStart() : base.OnStart();
54 }
55
56 protected override IPromise OnStop() {
57 return MockStop != null ? MockStop() : base.OnStart();
58 }
59
60 protected override void OnInitialize() {
61 if (MockInit != null)
62 MockInit();
63 }
64 }
65
66 [TestMethod]
67 public void NormalFlowTest() {
68 var comp = new Runnable(false);
69
70 Assert.AreEqual(ExecutionState.Created, comp.State);
71
72 comp.Init();
73
74 Assert.AreEqual(ExecutionState.Ready, comp.State);
75
76 comp.Start().Join(1000);
77
78 Assert.AreEqual(ExecutionState.Running, comp.State);
79
80 comp.Stop().Join(1000);
81
82 Assert.AreEqual(ExecutionState.Disposed, comp.State);
83
84 }
85
86 [TestMethod]
87 public void InitFailTest() {
88 var comp = new Runnable(false) {
89 MockInit = () => {
90 throw new Exception("BAD");
91 }
92 };
93
94 ShouldThrow(() => comp.Start());
95 ShouldThrow(() => comp.Stop());
96 Assert.AreEqual(ExecutionState.Created, comp.State);
97
98 ShouldThrow(comp.Init);
99
100 Assert.AreEqual(ExecutionState.Failed, comp.State);
101
102 ShouldThrow(() => comp.Start());
103 ShouldThrow(() => comp.Stop());
104 Assert.AreEqual(ExecutionState.Failed, comp.State);
105
106 comp.Dispose();
107 Assert.AreEqual(ExecutionState.Disposed, comp.State);
108 }
109
110 [TestMethod]
111 public void DisposedTest() {
112
113 var comp = new Runnable(false);
114 comp.Dispose();
115
116 ShouldThrow(() => comp.Start());
117 ShouldThrow(() => comp.Stop());
118 ShouldThrow(comp.Init);
119
120 Assert.AreEqual(ExecutionState.Disposed, comp.State);
121 }
122
123 [TestMethod]
124 public void StartCancelTest() {
125 var comp = new Runnable(true) {
126 MockStart = () => PromiseHelper.Sleep(100000, 0)
127 };
128
129 var p = comp.Start();
130 Assert.AreEqual(ExecutionState.Starting, comp.State);
131 p.Cancel();
132 ShouldThrow(() => p.Join(1000));
133 Assert.AreEqual(ExecutionState.Failed, comp.State);
134
135 Assert.IsInstanceOfType(comp.LastError, typeof(OperationCanceledException));
136
137 comp.Dispose();
138 }
139
140 [TestMethod]
141 public void StartStopTest() {
142 var stop = new Signal();
143 var comp = new Runnable(true) {
144 MockStart = () => PromiseHelper.Sleep(100000, 0),
145 MockStop = () => AsyncPool.RunThread(stop.Wait)
146 };
147
148 var p1 = comp.Start();
149 var p2 = comp.Stop();
150 // should enter stopping state
151
152 ShouldThrow(p1.Join);
153 Assert.IsTrue(p1.IsCancelled);
154 Assert.AreEqual(ExecutionState.Stopping, comp.State);
155
156 stop.Set();
157 p2.Join(1000);
158 Assert.AreEqual(ExecutionState.Disposed, comp.State);
159 }
160
161 [TestMethod]
162 public void StartStopFailTest() {
163 var comp = new Runnable(true) {
164 MockStart = () => PromiseHelper.Sleep(100000, 0).Then(null,null,x => { throw new Exception("I'm dead"); })
165 };
166
167 comp.Start();
168 var p = comp.Stop();
169 // if Start fails to cancel, should fail to stop
170 ShouldThrow(() => p.Join(1000));
171 Assert.AreEqual(ExecutionState.Failed, comp.State);
172 Assert.IsNotNull(comp.LastError);
173 Assert.AreEqual("I'm dead", comp.LastError.Message);
174 }
175
176 [TestMethod]
177 public void StopCancelTest() {
178 var comp = new Runnable(true) {
179 MockStop = () => PromiseHelper.Sleep(100000, 0)
180 };
181
182 comp.Start();
183 var p = comp.Stop();
184 Assert.AreEqual(ExecutionState.Stopping, comp.State);
185 p.Cancel();
186 ShouldThrow(() => p.Join(1000));
187 Assert.AreEqual(ExecutionState.Failed, comp.State);
188 Assert.IsInstanceOfType(comp.LastError, typeof(OperationCanceledException));
189
190 comp.Dispose();
191 }
192
193 }
194 }
195
@@ -0,0 +1,301
1 
2 Microsoft Visual Studio Solution File, Format Version 11.00
3 # Visual Studio 2010
4 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab", "Implab\Implab.csproj", "{F550F1F8-8746-4AD0-9614-855F4C4B7F05}"
5 EndProject
6 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CE8D8D18-437A-445C-B662-4C2CE79A76F6}"
7 ProjectSection(SolutionItems) = preProject
8 Implab.vsmdi = Implab.vsmdi
9 Local.testsettings = Local.testsettings
10 TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings
11 EndProjectSection
12 EndProject
13 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx", "Implab.Fx\Implab.Fx.csproj", "{06E706F8-6881-43EB-927E-FFC503AF6ABC}"
14 EndProject
15 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BCA337C3-BFDC-4825-BBDB-E6D467E4E452}"
16 EndProject
17 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Test.mono", "Implab.Test\Implab.Test.mono.csproj", "{2BD05F84-E067-4B87-9477-FDC2676A21C6}"
18 EndProject
19 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Format.Test", "Implab.Test\Implab.Format.Test\Implab.Format.Test.csproj", "{4D364996-7ECD-4193-8F90-F223FFEA49DA}"
20 EndProject
21 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoPlay", "MonoPlay\MonoPlay.csproj", "{15DD7123-D504-4627-8B4F-D00C7F04D033}"
22 EndProject
23 Global
24 GlobalSection(SolutionConfigurationPlatforms) = preSolution
25 Debug|Any CPU = Debug|Any CPU
26 Release|Any CPU = Release|Any CPU
27 Debug 4.5|Any CPU = Debug 4.5|Any CPU
28 Release 4.5|Any CPU = Release 4.5|Any CPU
29 EndGlobalSection
30 GlobalSection(ProjectConfigurationPlatforms) = postSolution
31 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
32 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
33 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU
35 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
36 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
37 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
38 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.Build.0 = Release|Any CPU
39 {15DD7123-D504-4627-8B4F-D00C7F04D033}.Debug 4.5|Any CPU.ActiveCfg = Debug|Any CPU
40 {15DD7123-D504-4627-8B4F-D00C7F04D033}.Debug 4.5|Any CPU.Build.0 = Debug|Any CPU
41 {15DD7123-D504-4627-8B4F-D00C7F04D033}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42 {15DD7123-D504-4627-8B4F-D00C7F04D033}.Debug|Any CPU.Build.0 = Debug|Any CPU
43 {15DD7123-D504-4627-8B4F-D00C7F04D033}.Release 4.5|Any CPU.ActiveCfg = Release|Any CPU
44 {15DD7123-D504-4627-8B4F-D00C7F04D033}.Release 4.5|Any CPU.Build.0 = Release|Any CPU
45 {15DD7123-D504-4627-8B4F-D00C7F04D033}.Release|Any CPU.ActiveCfg = Release|Any CPU
46 {15DD7123-D504-4627-8B4F-D00C7F04D033}.Release|Any CPU.Build.0 = Release|Any CPU
47 {2BD05F84-E067-4B87-9477-FDC2676A21C6}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
48 {2BD05F84-E067-4B87-9477-FDC2676A21C6}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
49 {2BD05F84-E067-4B87-9477-FDC2676A21C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 {2BD05F84-E067-4B87-9477-FDC2676A21C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
51 {2BD05F84-E067-4B87-9477-FDC2676A21C6}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
52 {2BD05F84-E067-4B87-9477-FDC2676A21C6}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
53 {2BD05F84-E067-4B87-9477-FDC2676A21C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
54 {2BD05F84-E067-4B87-9477-FDC2676A21C6}.Release|Any CPU.Build.0 = Release|Any CPU
55 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug 4.5|Any CPU.ActiveCfg = Debug|Any CPU
56 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug 4.5|Any CPU.Build.0 = Debug|Any CPU
57 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
58 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
59 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release 4.5|Any CPU.ActiveCfg = Release|Any CPU
60 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release 4.5|Any CPU.Build.0 = Release|Any CPU
61 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
62 {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release|Any CPU.Build.0 = Release|Any CPU
63 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
64 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
65 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
66 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
67 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
68 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
69 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU
70 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU
71 {2F31E405-E267-4195-A05D-574093C21209}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
72 {2F31E405-E267-4195-A05D-574093C21209}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
73 {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
74 {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.Build.0 = Debug|Any CPU
75 {2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
76 {2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
77 {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.ActiveCfg = Release|Any CPU
78 {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.Build.0 = Release|Any CPU
79 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
80 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
81 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
82 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
83 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
84 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
85 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
86 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.Build.0 = Release|Any CPU
87 EndGlobalSection
88 GlobalSection(NestedProjects) = preSolution
89 {2BD05F84-E067-4B87-9477-FDC2676A21C6} = {BCA337C3-BFDC-4825-BBDB-E6D467E4E452}
90 {4D364996-7ECD-4193-8F90-F223FFEA49DA} = {BCA337C3-BFDC-4825-BBDB-E6D467E4E452}
91 EndGlobalSection
92 GlobalSection(MonoDevelopProperties) = preSolution
93 Policies = $0
94 $0.CSharpFormattingPolicy = $1
95 $1.IndentSwitchBody = True
96 $1.NamespaceBraceStyle = EndOfLine
97 $1.ClassBraceStyle = EndOfLine
98 $1.InterfaceBraceStyle = EndOfLine
99 $1.StructBraceStyle = EndOfLine
100 $1.EnumBraceStyle = EndOfLine
101 $1.MethodBraceStyle = EndOfLine
102 $1.ConstructorBraceStyle = EndOfLine
103 $1.DestructorBraceStyle = EndOfLine
104 $1.BeforeMethodDeclarationParentheses = False
105 $1.BeforeMethodCallParentheses = False
106 $1.BeforeConstructorDeclarationParentheses = False
107 $1.NewLineBeforeConstructorInitializerColon = NewLine
108 $1.NewLineAfterConstructorInitializerColon = SameLine
109 $1.BeforeIndexerDeclarationBracket = False
110 $1.BeforeDelegateDeclarationParentheses = False
111 $1.NewParentheses = False
112 $1.SpacesBeforeBrackets = False
113 $1.inheritsSet = Mono
114 $1.inheritsScope = text/x-csharp
115 $1.scope = text/x-csharp
116 $0.TextStylePolicy = $2
117 $2.FileWidth = 120
118 $2.EolMarker = Unix
119 $2.inheritsSet = VisualStudio
120 $2.inheritsScope = text/plain
121 $2.scope = text/x-csharp
122 $0.DotNetNamingPolicy = $3
123 $3.DirectoryNamespaceAssociation = PrefixedHierarchical
124 $3.ResourceNamePolicy = MSBuild
125 $0.TextStylePolicy = $4
126 $4.FileWidth = 120
127 $4.TabsToSpaces = False
128 $4.inheritsSet = VisualStudio
129 $4.inheritsScope = text/plain
130 $4.scope = application/xml
131 $0.XmlFormattingPolicy = $5
132 $5.inheritsSet = Mono
133 $5.inheritsScope = application/xml
134 $5.scope = application/xml
135 $0.TextStylePolicy = $6
136 $6.FileWidth = 120
137 $6.TabsToSpaces = False
138 $6.inheritsSet = VisualStudio
139 $6.inheritsScope = text/plain
140 $6.scope = text/plain
141 $0.NameConventionPolicy = $7
142 $7.Rules = $8
143 $8.NamingRule = $9
144 $9.Name = Namespaces
145 $9.AffectedEntity = Namespace
146 $9.VisibilityMask = VisibilityMask
147 $9.NamingStyle = PascalCase
148 $9.IncludeInstanceMembers = True
149 $9.IncludeStaticEntities = True
150 $8.NamingRule = $10
151 $10.Name = Types
152 $10.AffectedEntity = Class, Struct, Enum, Delegate
153 $10.VisibilityMask = VisibilityMask
154 $10.NamingStyle = PascalCase
155 $10.IncludeInstanceMembers = True
156 $10.IncludeStaticEntities = True
157 $8.NamingRule = $11
158 $11.Name = Interfaces
159 $11.RequiredPrefixes = $12
160 $12.String = I
161 $11.AffectedEntity = Interface
162 $11.VisibilityMask = VisibilityMask
163 $11.NamingStyle = PascalCase
164 $11.IncludeInstanceMembers = True
165 $11.IncludeStaticEntities = True
166 $8.NamingRule = $13
167 $13.Name = Attributes
168 $13.RequiredSuffixes = $14
169 $14.String = Attribute
170 $13.AffectedEntity = CustomAttributes
171 $13.VisibilityMask = VisibilityMask
172 $13.NamingStyle = PascalCase
173 $13.IncludeInstanceMembers = True
174 $13.IncludeStaticEntities = True
175 $8.NamingRule = $15
176 $15.Name = Event Arguments
177 $15.RequiredSuffixes = $16
178 $16.String = EventArgs
179 $15.AffectedEntity = CustomEventArgs
180 $15.VisibilityMask = VisibilityMask
181 $15.NamingStyle = PascalCase
182 $15.IncludeInstanceMembers = True
183 $15.IncludeStaticEntities = True
184 $8.NamingRule = $17
185 $17.Name = Exceptions
186 $17.RequiredSuffixes = $18
187 $18.String = Exception
188 $17.AffectedEntity = CustomExceptions
189 $17.VisibilityMask = VisibilityMask
190 $17.NamingStyle = PascalCase
191 $17.IncludeInstanceMembers = True
192 $17.IncludeStaticEntities = True
193 $8.NamingRule = $19
194 $19.Name = Methods
195 $19.AffectedEntity = Methods
196 $19.VisibilityMask = VisibilityMask
197 $19.NamingStyle = PascalCase
198 $19.IncludeInstanceMembers = True
199 $19.IncludeStaticEntities = True
200 $8.NamingRule = $20
201 $20.Name = Static Readonly Fields
202 $20.AffectedEntity = ReadonlyField
203 $20.VisibilityMask = Internal, Protected, Public
204 $20.NamingStyle = PascalCase
205 $20.IncludeInstanceMembers = False
206 $20.IncludeStaticEntities = True
207 $8.NamingRule = $21
208 $21.Name = Fields (Non Private)
209 $21.AffectedEntity = Field
210 $21.VisibilityMask = Internal, Public
211 $21.NamingStyle = CamelCase
212 $21.IncludeInstanceMembers = True
213 $21.IncludeStaticEntities = True
214 $8.NamingRule = $22
215 $22.Name = ReadOnly Fields (Non Private)
216 $22.AffectedEntity = ReadonlyField
217 $22.VisibilityMask = Internal, Public
218 $22.NamingStyle = CamelCase
219 $22.IncludeInstanceMembers = True
220 $22.IncludeStaticEntities = False
221 $8.NamingRule = $23
222 $23.Name = Fields (Private)
223 $23.RequiredPrefixes = $24
224 $24.String = m_
225 $23.AffectedEntity = Field, ReadonlyField
226 $23.VisibilityMask = Private, Protected
227 $23.NamingStyle = CamelCase
228 $23.IncludeInstanceMembers = True
229 $23.IncludeStaticEntities = False
230 $8.NamingRule = $25
231 $25.Name = Static Fields (Private)
232 $25.RequiredPrefixes = $26
233 $26.String = _
234 $25.AffectedEntity = Field
235 $25.VisibilityMask = Private
236 $25.NamingStyle = CamelCase
237 $25.IncludeInstanceMembers = False
238 $25.IncludeStaticEntities = True
239 $8.NamingRule = $27
240 $27.Name = ReadOnly Fields (Private)
241 $27.RequiredPrefixes = $28
242 $28.String = m_
243 $27.AffectedEntity = ReadonlyField
244 $27.VisibilityMask = Private, Protected
245 $27.NamingStyle = CamelCase
246 $27.IncludeInstanceMembers = True
247 $27.IncludeStaticEntities = False
248 $8.NamingRule = $29
249 $29.Name = Constant Fields
250 $29.AffectedEntity = ConstantField
251 $29.VisibilityMask = VisibilityMask
252 $29.NamingStyle = AllUpper
253 $29.IncludeInstanceMembers = True
254 $29.IncludeStaticEntities = True
255 $8.NamingRule = $30
256 $30.Name = Properties
257 $30.AffectedEntity = Property
258 $30.VisibilityMask = VisibilityMask
259 $30.NamingStyle = PascalCase
260 $30.IncludeInstanceMembers = True
261 $30.IncludeStaticEntities = True
262 $8.NamingRule = $31
263 $31.Name = Events
264 $31.AffectedEntity = Event
265 $31.VisibilityMask = VisibilityMask
266 $31.NamingStyle = PascalCase
267 $31.IncludeInstanceMembers = True
268 $31.IncludeStaticEntities = True
269 $8.NamingRule = $32
270 $32.Name = Enum Members
271 $32.AffectedEntity = EnumMember
272 $32.VisibilityMask = VisibilityMask
273 $32.NamingStyle = PascalCase
274 $32.IncludeInstanceMembers = True
275 $32.IncludeStaticEntities = True
276 $8.NamingRule = $33
277 $33.Name = Parameters
278 $33.AffectedEntity = Parameter, LocalVariable
279 $33.VisibilityMask = VisibilityMask
280 $33.NamingStyle = CamelCase
281 $33.IncludeInstanceMembers = True
282 $33.IncludeStaticEntities = True
283 $8.NamingRule = $34
284 $34.Name = Type Parameters
285 $34.RequiredPrefixes = $35
286 $35.String = T
287 $34.AffectedEntity = TypeParameter
288 $34.VisibilityMask = VisibilityMask
289 $34.NamingStyle = PascalCase
290 $34.IncludeInstanceMembers = True
291 $34.IncludeStaticEntities = True
292 version = 0.2
293 StartupItem = MonoPlay\MonoPlay.csproj
294 EndGlobalSection
295 GlobalSection(TestCaseManagementSettings) = postSolution
296 CategoryFile = Implab.vsmdi
297 EndGlobalSection
298 GlobalSection(SolutionProperties) = preSolution
299 HideSolutionNode = FALSE
300 EndGlobalSection
301 EndGlobal
@@ -0,0 +1,300
1 using System;
2 using Implab.Parallels;
3 using System.Threading;
4 using System.Reflection;
5
6 namespace Implab {
7 public abstract class AbstractEvent<THandler> : ICancellationToken, ICancellable {
8
9 const int UNRESOLVED_SATE = 0;
10 const int TRANSITIONAL_STATE = 1;
11 protected const int SUCCEEDED_STATE = 2;
12 protected const int REJECTED_STATE = 3;
13 protected const int CANCELLED_STATE = 4;
14
15 const int CANCEL_NOT_REQUESTED = 0;
16 const int CANCEL_REQUESTING = 1;
17 const int CANCEL_REQUESTED = 2;
18
19 const int RESERVED_HANDLERS_COUNT = 4;
20
21 int m_state;
22 Exception m_error;
23 int m_handlersCount;
24
25 //readonly THandler[] m_handlers = new THandler[RESERVED_HANDLERS_COUNT];
26 THandler[] m_handlers;
27 MTQueue<THandler> m_extraHandlers;
28 int m_handlerPointer = -1;
29 int m_handlersCommited;
30
31 int m_cancelRequest;
32 Exception m_cancelationReason;
33 MTQueue<Action<Exception>> m_cancelationHandlers;
34
35
36 #region state managment
37 bool BeginTransit() {
38 return UNRESOLVED_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, UNRESOLVED_SATE);
39 }
40
41 void CompleteTransit(int state) {
42 if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, state, TRANSITIONAL_STATE))
43 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
44 }
45
46 void WaitTransition() {
47 while (m_state == TRANSITIONAL_STATE) {
48 Thread.MemoryBarrier();
49 }
50 }
51
52 protected bool BeginSetResult() {
53 if (!BeginTransit()) {
54 WaitTransition();
55 if (m_state != CANCELLED_STATE)
56 throw new InvalidOperationException("The promise is already resolved");
57 return false;
58 }
59 return true;
60 }
61
62 protected void EndSetResult() {
63 CompleteTransit(SUCCEEDED_STATE);
64 Signal();
65 }
66
67
68
69 /// <summary>
70 /// Выполняет обещание, сообщая об ошибке
71 /// </summary>
72 /// <remarks>
73 /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков
74 /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные
75 /// будут проигнорированы.
76 /// </remarks>
77 /// <param name="error">Исключение возникшее при выполнении операции</param>
78 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
79 protected void SetError(Exception error) {
80 if (BeginTransit()) {
81 m_error = error;
82 CompleteTransit(REJECTED_STATE);
83
84 Signal();
85 } else {
86 WaitTransition();
87 if (m_state == SUCCEEDED_STATE)
88 throw new InvalidOperationException("The promise is already resolved");
89 }
90 }
91
92 /// <summary>
93 /// Отменяет операцию, если это возможно.
94 /// </summary>
95 /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks>
96 protected void SetCancelled(Exception reason) {
97 if (BeginTransit()) {
98 m_error = reason;
99 CompleteTransit(CANCELLED_STATE);
100 Signal();
101 }
102 }
103
104 protected abstract void SignalHandler(THandler handler, int signal);
105
106 void Signal() {
107 var hp = m_handlerPointer;
108 var slot = hp +1 ;
109 while (slot < m_handlersCommited) {
110 if (Interlocked.CompareExchange(ref m_handlerPointer, slot, hp) == hp) {
111 SignalHandler(m_handlers[slot], m_state);
112 }
113 hp = m_handlerPointer;
114 slot = hp +1 ;
115 }
116
117
118 if (m_extraHandlers != null) {
119 THandler handler;
120 while (m_extraHandlers.TryDequeue(out handler))
121 SignalHandler(handler, m_state);
122 }
123 }
124
125 #endregion
126
127 protected abstract Signal GetResolveSignal();
128
129 #region synchronization traits
130 protected void WaitResult(int timeout) {
131 if (!(IsResolved || GetResolveSignal().Wait(timeout)))
132 throw new TimeoutException();
133
134 switch (m_state) {
135 case SUCCEEDED_STATE:
136 return;
137 case CANCELLED_STATE:
138 throw new OperationCanceledException("The operation has been cancelled", m_error);
139 case REJECTED_STATE:
140 throw new TargetInvocationException(m_error);
141 default:
142 throw new ApplicationException(String.Format("The promise state {0} is invalid", m_state));
143 }
144 }
145 #endregion
146
147 #region handlers managment
148
149 protected void AddHandler(THandler handler) {
150
151 if (m_state > 1) {
152 // the promise is in the resolved state, just invoke the handler
153 SignalHandler(handler, m_state);
154 } else {
155 var slot = Interlocked.Increment(ref m_handlersCount) - 1;
156
157 if (slot < RESERVED_HANDLERS_COUNT) {
158
159 if (slot == 0) {
160 m_handlers = new THandler[RESERVED_HANDLERS_COUNT];
161 } else {
162 while (m_handlers == null)
163 Thread.MemoryBarrier();
164 }
165
166 m_handlers[slot] = handler;
167
168 while (slot != Interlocked.CompareExchange(ref m_handlersCommited, slot + 1, slot)) {
169 }
170
171 if (m_state > 1) {
172 do {
173 var hp = m_handlerPointer;
174 slot = hp + 1;
175 if (slot < m_handlersCommited) {
176 if (Interlocked.CompareExchange(ref m_handlerPointer, slot, hp) != hp)
177 continue;
178 SignalHandler(m_handlers[slot], m_state);
179 }
180 break;
181 } while(true);
182 }
183 } else {
184 if (slot == RESERVED_HANDLERS_COUNT) {
185 m_extraHandlers = new MTQueue<THandler>();
186 } else {
187 while (m_extraHandlers == null)
188 Thread.MemoryBarrier();
189 }
190
191 m_extraHandlers.Enqueue(handler);
192
193 if (m_state > 1 && m_extraHandlers.TryDequeue(out handler))
194 // if the promise have been resolved while we was adding the handler to the queue
195 // we can't guarantee that someone is still processing it
196 // therefore we need to fetch a handler from the queue and execute it
197 // note that fetched handler may be not the one that we have added
198 // even we can fetch no handlers at all :)
199 SignalHandler(handler, m_state);
200 }
201 }
202 }
203
204 #endregion
205
206 #region IPromise implementation
207
208 public bool IsResolved {
209 get {
210 Thread.MemoryBarrier();
211 return m_state > 1;
212 }
213 }
214
215 public bool IsCancelled {
216 get {
217 Thread.MemoryBarrier();
218 return m_state == CANCELLED_STATE;
219 }
220 }
221
222 #endregion
223
224 public Exception Error {
225 get {
226 return m_error;
227 }
228 }
229
230 public bool CancelOperationIfRequested() {
231 if (IsCancellationRequested) {
232 CancelOperation(CancellationReason);
233 return true;
234 }
235 return false;
236 }
237
238 public virtual void CancelOperation(Exception reason) {
239 SetCancelled(reason);
240 }
241
242 public void CancellationRequested(Action<Exception> handler) {
243 Safe.ArgumentNotNull(handler, "handler");
244 if (IsCancellationRequested)
245 handler(CancellationReason);
246
247 if (m_cancelationHandlers == null)
248 Interlocked.CompareExchange(ref m_cancelationHandlers, new MTQueue<Action<Exception>>(), null);
249
250 m_cancelationHandlers.Enqueue(handler);
251
252 if (IsCancellationRequested && m_cancelationHandlers.TryDequeue(out handler))
253 // TryDeque implies MemoryBarrier()
254 handler(m_cancelationReason);
255 }
256
257 public bool IsCancellationRequested {
258 get {
259 do {
260 if (m_cancelRequest == CANCEL_NOT_REQUESTED)
261 return false;
262 if (m_cancelRequest == CANCEL_REQUESTED)
263 return true;
264 Thread.MemoryBarrier();
265 } while(true);
266 }
267 }
268
269 public Exception CancellationReason {
270 get {
271 do {
272 Thread.MemoryBarrier();
273 } while(m_cancelRequest == CANCEL_REQUESTING);
274
275 return m_cancelationReason;
276 }
277 }
278
279 #region ICancellable implementation
280
281 public void Cancel() {
282 Cancel(null);
283 }
284
285 public void Cancel(Exception reason) {
286 if (CANCEL_NOT_REQUESTED == Interlocked.CompareExchange(ref m_cancelRequest, CANCEL_REQUESTING, CANCEL_NOT_REQUESTED)) {
287 m_cancelationReason = reason;
288 m_cancelRequest = CANCEL_REQUESTED;
289 if (m_cancelationHandlers != null) {
290 Action<Exception> handler;
291 while (m_cancelationHandlers.TryDequeue(out handler))
292 handler(m_cancelationReason);
293 }
294 }
295 }
296
297 #endregion
298 }
299 }
300
@@ -0,0 +1,142
1 using System;
2 using Implab.Parallels;
3
4 namespace Implab {
5 public abstract class AbstractPromise : AbstractEvent<AbstractPromise.HandlerDescriptor>, IPromise {
6 public struct HandlerDescriptor {
7 readonly Action m_handler;
8 readonly Action<Exception> m_error;
9 readonly Action<Exception> m_cancel;
10 readonly PromiseEventType m_mask;
11
12 public HandlerDescriptor(Action success, Action<Exception> error, Action<Exception> cancel) {
13 m_handler = success;
14 m_error = error;
15 m_cancel = cancel;
16 m_mask = PromiseEventType.Success;
17 }
18
19 public HandlerDescriptor(Action handler, PromiseEventType mask) {
20 m_handler = handler;
21 m_error = null;
22 m_cancel = null;
23 m_mask = mask;
24 }
25
26 public void SignalSuccess() {
27 if ((m_mask & PromiseEventType.Success) != 0 && m_handler != null) {
28 try {
29 m_handler();
30 } catch (Exception err) {
31 // avoid calling handler twice in case of error
32 if (m_error != null)
33 SignalError(err);
34 }
35 }
36 }
37
38 public void SignalError(Exception err) {
39 if (m_error != null) {
40 try {
41 m_error(err);
42 // Analysis disable once EmptyGeneralCatchClause
43 } catch {
44 }
45 } else if ((m_mask & PromiseEventType.Error ) != 0 && m_handler != null) {
46 try {
47 m_handler();
48 // Analysis disable once EmptyGeneralCatchClause
49 } catch {
50 }
51 }
52 }
53
54 public void SignalCancel(Exception reason) {
55 if (m_cancel != null) {
56 try {
57 m_cancel(reason);
58 } catch (Exception err) {
59 SignalError(err);
60 }
61 } else if ( (m_mask & PromiseEventType.Cancelled) != 0 && m_handler != null) {
62 try {
63 m_handler();
64 // Analysis disable once EmptyGeneralCatchClause
65 } catch {
66 }
67 }
68 }
69 }
70
71
72 #region implemented abstract members of AbstractPromise
73
74 protected override void SignalHandler(HandlerDescriptor handler, int signal) {
75 switch (signal) {
76 case SUCCEEDED_STATE:
77 handler.SignalSuccess();
78 break;
79 case REJECTED_STATE:
80 handler.SignalError(Error);
81 break;
82 case CANCELLED_STATE:
83 handler.SignalCancel(CancellationReason);
84 break;
85 default:
86 throw new InvalidOperationException(String.Format("Invalid promise signal: {0}", signal));
87 }
88 }
89
90 protected override Signal GetResolveSignal() {
91 var signal = new Signal();
92 On(signal.Set, PromiseEventType.All);
93 return signal;
94 }
95
96 #endregion
97
98 public Type PromiseType {
99 get {
100 return typeof(void);
101 }
102 }
103
104 public IPromise On(Action success, Action<Exception> error, Action<Exception> cancel) {
105 AddHandler(new HandlerDescriptor(success, error, cancel));
106 return this;
107 }
108
109 public IPromise On(Action success, Action<Exception> error) {
110 AddHandler(new HandlerDescriptor(success, error, null));
111 return this;
112 }
113
114 public IPromise On(Action success) {
115 AddHandler(new HandlerDescriptor(success, null, null));
116 return this;
117 }
118
119 public IPromise On(Action handler, PromiseEventType events) {
120 AddHandler(new HandlerDescriptor(handler,events));
121 return this;
122 }
123
124 public IPromise<T> Cast<T>() {
125 throw new InvalidCastException();
126 }
127
128 public void Join() {
129 WaitResult(-1);
130 }
131
132 public void Join(int timeout) {
133 WaitResult(timeout);
134 }
135
136 protected void SetResult() {
137 if(BeginSetResult())
138 EndSetResult();
139 }
140 }
141 }
142
@@ -0,0 +1,206
1 using System;
2 using Implab.Parallels;
3
4 namespace Implab {
5 public abstract class AbstractPromise<T> : AbstractEvent<AbstractPromise<T>.HandlerDescriptor>, IPromise<T> {
6 public struct HandlerDescriptor {
7 readonly Action m_handler;
8 readonly Action<T> m_success;
9 readonly Action<Exception> m_error;
10 readonly Action<Exception> m_cancel;
11 readonly PromiseEventType m_mask;
12
13 public HandlerDescriptor(Action<T> success, Action<Exception> error, Action<Exception> cancel) {
14 m_success = success;
15 m_error = error;
16 m_cancel = cancel;
17
18 m_handler = null;
19 m_mask = 0;
20 }
21
22 public HandlerDescriptor(Action success, Action<Exception> error, Action<Exception> cancel) {
23 m_handler = success;
24 m_success = null;
25 m_error = error;
26 m_cancel = cancel;
27 m_mask = PromiseEventType.Success;
28 }
29
30 public HandlerDescriptor(Action handler, PromiseEventType mask) {
31 m_handler = handler;
32 m_mask = mask;
33 m_success = null;
34 m_error = null;
35 m_cancel = null;
36 }
37
38 public void SignalSuccess(T result) {
39 if (m_success != null) {
40 try {
41 m_success(result);
42 } catch(Exception err) {
43 SignalError(err);
44 }
45 } else if ((m_mask & PromiseEventType.Success) != 0 && m_handler != null) {
46 try {
47 m_handler();
48 } catch(Exception err) {
49 // avoid calling handler twice in case of error
50 if (m_error != null)
51 SignalError(err);
52 }
53 }
54 }
55
56 public void SignalError(Exception err) {
57 if (m_error != null) {
58 try {
59 m_error(err);
60 // Analysis disable once EmptyGeneralCatchClause
61 } catch {
62 }
63 } else if ((m_mask & PromiseEventType.Error) != 0 && m_handler != null) {
64 try {
65 m_handler();
66 // Analysis disable once EmptyGeneralCatchClause
67 } catch {
68 }
69 }
70 }
71
72 public void SignalCancel(Exception reason) {
73 if (m_cancel != null) {
74 try {
75 m_cancel(reason);
76 } catch (Exception err) {
77 SignalError(err);
78 }
79 } else if ((m_mask & PromiseEventType.Cancelled) != 0 && m_handler != null) {
80 try {
81 m_handler();
82 // Analysis disable once EmptyGeneralCatchClause
83 } catch {
84 }
85 }
86 }
87 }
88
89 public Type PromiseType {
90 get {
91 return typeof(T);
92 }
93 }
94
95 public T Join() {
96 WaitResult(-1);
97 return m_result;
98 }
99 public T Join(int timeout) {
100 WaitResult(timeout);
101 return m_result;
102 }
103
104 void IPromise.Join() {
105 WaitResult(-1);
106 }
107 void IPromise.Join(int timeout) {
108 WaitResult(timeout);
109 }
110
111 public IPromise<T> On(Action<T> success, Action<Exception> error, Action<Exception> cancel) {
112 AddHandler(new HandlerDescriptor(success, error, cancel));
113 return this;
114 }
115
116 public IPromise<T> On(Action<T> success, Action<Exception> error) {
117 AddHandler(new HandlerDescriptor(success, error, null));
118 return this;
119 }
120
121 public IPromise<T> On(Action<T> success) {
122 AddHandler(new HandlerDescriptor(success, null, null));
123 return this;
124 }
125
126 public IPromise<T> On(Action handler, PromiseEventType events) {
127 AddHandler(new HandlerDescriptor(handler, events));
128 return this;
129 }
130
131 public IPromise<T> On(Action success, Action<Exception> error, Action<Exception> cancel) {
132 AddHandler(new HandlerDescriptor(success, error, cancel));
133 return this;
134 }
135
136 public IPromise<T> On(Action success, Action<Exception> error) {
137 AddHandler(new HandlerDescriptor(success, error, null));
138 return this;
139 }
140
141 public IPromise<T> On(Action success) {
142 AddHandler(new HandlerDescriptor(success, null, null));
143 return this;
144 }
145
146 IPromise IPromise.On(Action success, Action<Exception> error, Action<Exception> cancel) {
147 AddHandler(new HandlerDescriptor(success, error, cancel));
148 return this;
149 }
150
151 IPromise IPromise.On(Action success, Action<Exception> error) {
152 AddHandler(new HandlerDescriptor(success, error, null));
153 return this;
154 }
155
156 IPromise IPromise.On(Action success) {
157 AddHandler(new HandlerDescriptor(success, null, null));
158 return this;
159 }
160
161 IPromise IPromise.On(Action handler, PromiseEventType events) {
162 AddHandler(new HandlerDescriptor(handler, events));
163 return this;
164 }
165
166 public IPromise<T2> Cast<T2>() {
167 return (IPromise<T2>)this;
168 }
169
170 #region implemented abstract members of AbstractPromise
171
172 protected override Signal GetResolveSignal() {
173 var signal = new Signal();
174 AddHandler(new HandlerDescriptor(signal.Set, PromiseEventType.All));
175 return signal;
176 }
177
178 protected override void SignalHandler(HandlerDescriptor handler, int signal) {
179 switch (signal) {
180 case SUCCEEDED_STATE:
181 handler.SignalSuccess(m_result);
182 break;
183 case REJECTED_STATE:
184 handler.SignalError(Error);
185 break;
186 case CANCELLED_STATE:
187 handler.SignalCancel(CancellationReason);
188 break;
189 default:
190 throw new InvalidOperationException(String.Format("Invalid promise signal: {0}", signal));
191 }
192 }
193
194 #endregion
195
196 T m_result;
197
198 protected void SetResult(T value) {
199 if (BeginSetResult()) {
200 m_result = value;
201 EndSetResult();
202 }
203 }
204 }
205 }
206
@@ -0,0 +1,45
1 using System;
2 using System.Threading;
3
4 namespace Implab {
5 /// <summary>
6 /// Базовый класс для реализации задачь. Задача представляет собой некторое
7 /// действие, которое можно иницировать и обработать результат его выполнения
8 /// в виде обещания, для этого оно реализует интерфейс <see cref="IPromise"/>.
9 /// </summary>
10 /// <remarks>
11 /// Данный класс определяет стандартное поведение при обработки результатов, в частности
12 /// обработку <see cref="System.OperationCanceledException"/> и <see cref="PromiseTransientException"/>
13 /// </remarks>
14 public abstract class AbstractTask : AbstractPromise {
15 int m_cancelationLock;
16
17 /// <summary>
18 /// Получает эксклюзивное право отмены задания, используется для отмены задания до начала его выполнения.
19 /// </summary>
20 /// <returns><c>true</c>, if cancelation was locked, <c>false</c> otherwise.</returns>
21 protected bool LockCancelation() {
22 return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
23 }
24
25
26
27 protected void SetErrorInternal(Exception error) {
28 // unwrap
29 while (error is PromiseTransientException && error.InnerException != null)
30 error = error.InnerException;
31
32 if (error is OperationCanceledException)
33 SetCancelled(error);
34 else
35 SetError(error);
36 }
37
38 protected void SetCancelledInternal(Exception reason) {
39 SetCancelled(
40 reason == null ? new OperationCanceledException() : reason is OperationCanceledException ? reason : new OperationCanceledException(null, reason)
41 );
42 }
43 }
44 }
45
@@ -0,0 +1,36
1 using System;
2 using System.Threading;
3
4 namespace Implab {
5 public abstract class AbstractTask<T> : AbstractPromise<T> {
6 int m_cancelationLock;
7
8 /// <summary>
9 /// Получает эксклюзивное право отмены задания, используется для отмены задания до начала его выполнения.
10 /// </summary>
11 /// <returns><c>true</c>, if cancelation was locked, <c>false</c> otherwise.</returns>
12 protected bool LockCancelation() {
13 return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
14 }
15
16
17
18 protected void SetErrorInternal(Exception error) {
19 // unwrap
20 while (error is PromiseTransientException && error.InnerException != null)
21 error = error.InnerException;
22
23 if (error is OperationCanceledException)
24 SetCancelled(error);
25 else
26 SetError(error);
27 }
28
29 protected void SetCancelledInternal(Exception reason) {
30 SetCancelled(
31 reason == null ? new OperationCanceledException() : reason is OperationCanceledException ? reason : new OperationCanceledException(null, reason)
32 );
33 }
34 }
35 }
36
@@ -0,0 +1,36
1 using System;
2
3 namespace Implab {
4 public class ActionChainTask : ActionChainTaskBase, IDeferred {
5 readonly Func<IPromise> m_task;
6
7 /// <summary>
8 /// Initializes a new instance of the <see cref="Implab.ActionChainTask"/> class.
9 /// </summary>
10 /// <param name="task">The operation which will be performed when the <see cref="Resolve()"/> is called.</param>
11 /// <param name="error">The error handler which will invoke when the <see cref="Reject(Exception)"/> is called or when the task fails with an error.</param>
12 /// <param name="cancel">The cancellation handler.</param>
13 /// <param name="autoCancellable">If set to <c>true</c> will automatically accept
14 /// all cancel requests before the task is started with <see cref="Resolve()"/>,
15 /// after that all requests are directed to the task.</param>
16 public ActionChainTask(Func<IPromise> task, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel, bool autoCancellable) : base(error,cancel, autoCancellable) {
17 m_task = task;
18 }
19
20 public void Resolve() {
21 if (m_task != null && LockCancelation()) {
22 try {
23 var p = m_task();
24 p.On(SetResult, HandleErrorInternal, HandleCancelInternal);
25 CancellationRequested(p.Cancel);
26 } catch (OperationCanceledException reason){
27 HandleCancelInternal(reason);
28 } catch(Exception err) {
29 HandleErrorInternal(err);
30 }
31 }
32 }
33
34 }
35 }
36
@@ -0,0 +1,62
1 using System;
2 using System.Threading;
3
4 namespace Implab {
5 public class ActionChainTaskBase : AbstractTask {
6 readonly Func<Exception, IPromise> m_error;
7 readonly Func<Exception, IPromise> m_cancel;
8
9 protected ActionChainTaskBase(Func<Exception, IPromise> error, Func<Exception, IPromise> cancel, bool autoCancellable) {
10 m_error = error;
11 m_cancel = cancel;
12 if (autoCancellable)
13 CancellationRequested(CancelOperation);
14 }
15
16 public void Reject(Exception error) {
17 if (LockCancelation())
18 HandleErrorInternal(error);
19 }
20
21 public override void CancelOperation(Exception reason) {
22 if (LockCancelation())
23 // отмена вызвана до начала выполнения задачи
24 HandleCancelInternal(reason);
25 }
26
27 protected void HandleCancelInternal(Exception reason) {
28 if (m_cancel != null) {
29 try {
30 // вызываем обработчик отмены
31 var p = m_cancel(reason);
32 p.On(SetResult, HandleErrorInternal, SetCancelledInternal);
33 // сообщаем асинхронной операции, что клиент уже не хочет получать результат
34 // т.е. если он инициировал отмену, задача отменилась, вызвался обрабочик отмены
35 // отбработчику сообщили, что результат уже не нужен и уже сам обработчик решает
36 // отдавать ли результат или подтвердить отмену (или вернуть ошибку).
37 CancellationRequested(p.Cancel);
38 } catch (Exception err) {
39 HandleErrorInternal(err);
40 }
41 } else {
42 HandleErrorInternal(reason ?? new OperationCanceledException());
43 }
44 }
45
46 protected void HandleErrorInternal(Exception error) {
47 if (m_error != null) {
48 try {
49 var p = m_error(error);
50 p.On(SetResult, SetErrorInternal, SetCancelledInternal);
51 CancellationRequested(p.Cancel);
52 } catch (Exception err) {
53 SetErrorInternal(error);
54 }
55 } else {
56 SetErrorInternal(error);
57 }
58 }
59
60 }
61 }
62
@@ -0,0 +1,27
1 using System;
2
3 namespace Implab {
4 public class ActionChainTask<T> : ActionChainTaskBase, IDeferred<T> {
5 readonly Func<T, IPromise> m_task;
6
7 public ActionChainTask(Func<T, IPromise> task, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel, bool autoCancellable) : base(error,cancel, autoCancellable) {
8 m_task = task;
9 }
10
11 public void Resolve(T value) {
12 if (m_task != null && LockCancelation()) {
13 try {
14 var p = m_task(value);
15 p.On(SetResult, HandleErrorInternal, HandleCancelInternal);
16 CancellationRequested(p.Cancel);
17 } catch (OperationCanceledException reason) {
18 HandleCancelInternal(reason);
19 } catch(Exception err) {
20 HandleErrorInternal(err);
21 }
22 }
23 }
24
25 }
26 }
27
@@ -0,0 +1,24
1 using System;
2
3 namespace Implab {
4 public class ActionTask : ActionTaskBase, IDeferred {
5 readonly Action m_task;
6 public ActionTask(Action task, Action<Exception> error, Action<Exception> cancel, bool autoCancellable) : base(error,cancel, autoCancellable) {
7 m_task = task;
8 }
9
10 public void Resolve() {
11 if (m_task != null && LockCancelation()) {
12 try {
13 m_task();
14 SetResult();
15 } catch(OperationCanceledException reason) {
16 HandleCancelInternal(reason);
17 } catch(Exception err) {
18 HandleErrorInternal(err);
19 }
20 }
21 }
22 }
23 }
24
@@ -0,0 +1,53
1 using System;
2
3 namespace Implab {
4 public class ActionTaskBase : AbstractTask {
5 readonly Action<Exception> m_cancel;
6 readonly Action<Exception> m_error;
7
8 protected ActionTaskBase( Action<Exception> error, Action<Exception> cancel, bool autoCancellable) {
9 m_error = error;
10 m_cancel = cancel;
11 if (autoCancellable)
12 CancellationRequested(CancelOperation);
13 }
14
15 public void Reject(Exception error) {
16 Safe.ArgumentNotNull(error, "error");
17 if (LockCancelation())
18 HandleErrorInternal(error);
19 }
20
21 public override void CancelOperation(Exception reason) {
22 if (LockCancelation())
23 HandleCancelInternal(reason);
24 }
25
26 protected void HandleErrorInternal(Exception error) {
27 if (m_error != null) {
28 try {
29 m_error(error);
30 SetResult();
31 } catch(Exception err) {
32 SetErrorInternal(err);
33 }
34 } else {
35 SetErrorInternal(error);
36 }
37 }
38
39 protected void HandleCancelInternal(Exception error) {
40 if (m_cancel != null) {
41 try {
42 m_cancel(error);
43 SetResult();
44 } catch(Exception err) {
45 HandleErrorInternal(err);
46 }
47 } else {
48 HandleErrorInternal(error ?? new OperationCanceledException());
49 }
50 }
51 }
52 }
53
@@ -0,0 +1,24
1 using System;
2
3 namespace Implab {
4 public class ActionTask<T> : ActionTaskBase, IDeferred<T> {
5 readonly Action<T> m_task;
6 public ActionTask(Action<T> task, Action<Exception> error, Action<Exception> cancel, bool autoCancellable) : base(error,cancel, autoCancellable) {
7 m_task = task;
8 }
9
10 public void Resolve(T value) {
11 if (m_task != null && LockCancelation()) {
12 try {
13 m_task(value);
14 SetResult();
15 } catch(OperationCanceledException reason) {
16 HandleCancelInternal(reason);
17 } catch(Exception err) {
18 HandleErrorInternal(err);
19 }
20 }
21 }
22 }
23 }
24
@@ -0,0 +1,9
1
2 namespace Implab.Automaton {
3 public static class AutomatonConst {
4 public const int UNREACHABLE_STATE = -1;
5
6 public const int UNCLASSIFIED_INPUT = 0;
7 }
8 }
9
@@ -0,0 +1,33
1 using System;
2
3 namespace Implab.Automaton {
4 public struct AutomatonTransition : IEquatable<AutomatonTransition> {
5 public readonly int s1;
6 public readonly int s2;
7 public readonly int edge;
8
9 public AutomatonTransition(int s1, int s2, int edge) {
10 this.s1 = s1;
11 this.s2 = s2;
12 this.edge = edge;
13 }
14
15
16 #region IEquatable implementation
17 public bool Equals(AutomatonTransition other) {
18 return other.s1 == s1 && other.s2 == s2 && other.edge == edge ;
19 }
20 #endregion
21
22 public override bool Equals(object obj) {
23 if (obj is AutomatonTransition)
24 return Equals((AutomatonTransition)obj);
25 return base.Equals(obj);
26 }
27
28 public override int GetHashCode() {
29 return s1 + s2 + edge;
30 }
31 }
32 }
33
@@ -0,0 +1,348
1 using Implab;
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Diagnostics;
6 using System.IO;
7 using System.CodeDom.Compiler;
8 using System.CodeDom;
9
10 namespace Implab.Automaton {
11 public class DFATable : IDFATableBuilder {
12 int m_stateCount;
13 int m_symbolCount;
14 int m_initialState;
15
16 readonly HashSet<int> m_finalStates = new HashSet<int>();
17 readonly HashSet<AutomatonTransition> m_transitions = new HashSet<AutomatonTransition>();
18
19
20 #region IDFADefinition implementation
21
22 public bool IsFinalState(int s) {
23 Safe.ArgumentInRange(s, 0, m_stateCount, "s");
24
25 return m_finalStates.Contains(s);
26 }
27
28 public IEnumerable<int> FinalStates {
29 get {
30 return m_finalStates;
31 }
32 }
33
34 public int StateCount {
35 get { return m_stateCount; }
36 }
37
38 public int AlphabetSize {
39 get { return m_symbolCount; }
40 }
41
42 public int InitialState {
43 get { return m_initialState; }
44 }
45
46 #endregion
47
48 public void SetInitialState(int s) {
49 Safe.ArgumentAssert(s >= 0, "s");
50 m_stateCount = Math.Max(m_stateCount, s + 1);
51 m_initialState = s;
52 }
53
54 public void MarkFinalState(int state) {
55 m_stateCount = Math.Max(m_stateCount, state + 1);
56 m_finalStates.Add(state);
57 }
58
59 public void Add(AutomatonTransition item) {
60 Safe.ArgumentAssert(item.s1 >= 0, "item");
61 Safe.ArgumentAssert(item.s2 >= 0, "item");
62 Safe.ArgumentAssert(item.edge >= 0, "item");
63
64 m_stateCount = Math.Max(m_stateCount, Math.Max(item.s1, item.s2) + 1);
65 m_symbolCount = Math.Max(m_symbolCount, item.edge + 1);
66
67 m_transitions.Add(item);
68 }
69
70 public void Clear() {
71 m_stateCount = 0;
72 m_symbolCount = 0;
73 m_finalStates.Clear();
74 m_transitions.Clear();
75 }
76
77 public bool Contains(AutomatonTransition item) {
78 return m_transitions.Contains(item);
79 }
80
81 public void CopyTo(AutomatonTransition[] array, int arrayIndex) {
82 m_transitions.CopyTo(array, arrayIndex);
83 }
84
85 public bool Remove(AutomatonTransition item) {
86 return m_transitions.Remove(item);
87 }
88
89 public int Count {
90 get {
91 return m_transitions.Count;
92 }
93 }
94
95 public bool IsReadOnly {
96 get {
97 return false;
98 }
99 }
100
101 public IEnumerator<AutomatonTransition> GetEnumerator() {
102 return m_transitions.GetEnumerator();
103 }
104
105 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
106 return GetEnumerator();
107 }
108
109 public void AddSymbol(int symbol) {
110 Safe.ArgumentAssert(symbol >= 0, "symbol");
111 m_symbolCount = Math.Max(symbol + 1, m_symbolCount);
112 }
113
114 public int[,] CreateTransitionTable() {
115 var table = new int[StateCount,AlphabetSize];
116
117 for (int i = 0; i < StateCount; i++)
118 for (int j = 0; j < AlphabetSize; j++)
119 table[i, j] = AutomatonConst.UNREACHABLE_STATE;
120
121 foreach (var t in this)
122 table[t.s1,t.edge] = t.s2;
123
124 return table;
125 }
126
127 public bool[] CreateFinalStateTable() {
128 var table = new bool[StateCount];
129
130 foreach (var s in FinalStates)
131 table[s] = true;
132
133 return table;
134 }
135
136 /// <summary>Формирует множества конечных состояний перед началом работы алгоритма минимизации.</summary>
137 /// <remarks>
138 /// В процессе построения минимального автомата требуется разделить множество состояний,
139 /// на два подмножества - конечные состояния и все остальные, после чего эти подмножества
140 /// будут резделены на более мелкие. Иногда требуется гарантировать различия конечных сосотяний,
141 /// для этого необходимо переопределить даннцю фукнцию, для получения множеств конечных состояний.
142 /// </remarks>
143 /// <returns>The final states.</returns>
144 protected virtual IEnumerable<HashSet<int>> SplitFinalStates(IEnumerable<int> states) {
145 return new [] { new HashSet<int>(states) };
146 }
147
148 protected void Optimize(
149 IDFATableBuilder optimalDFA,
150 IDictionary<int,int> alphabetMap,
151 IDictionary<int,int> stateMap
152 ) {
153 Safe.ArgumentNotNull(optimalDFA, "dfa");
154 Safe.ArgumentNotNull(alphabetMap, "alphabetMap");
155 Safe.ArgumentNotNull(stateMap, "stateMap");
156
157
158 var setComparer = new CustomEqualityComparer<HashSet<int>>(
159 (x, y) => x.SetEquals(y),
160 s => s.Sum(x => x.GetHashCode())
161 );
162
163 var optimalStates = new HashSet<HashSet<int>>(setComparer);
164 var queue = new HashSet<HashSet<int>>(setComparer);
165
166 optimalStates.Add(new HashSet<int>(FinalStates));
167
168 var state = new HashSet<int>(
169 Enumerable
170 .Range(0, m_stateCount)
171 .Where(i => !m_finalStates.Contains(i))
172 );
173
174 optimalStates.Add(state);
175 queue.Add(state);
176
177 var rmap = m_transitions
178 .GroupBy(t => t.s2)
179 .ToDictionary(
180 g => g.Key, // s2
181 g => g.ToLookup(t => t.edge, t => t.s1)//.ToDictionary(p => p.Key)
182 );
183
184 while (queue.Count > 0) {
185 var stateA = queue.First();
186 queue.Remove(stateA);
187
188 for (int c = 0; c < m_symbolCount; c++) {
189 var stateX = new HashSet<int>();
190 foreach(var a in stateA.Where(rmap.ContainsKey))
191 stateX.UnionWith(rmap[a][c]); // all states from wich the symbol 'c' leads to the state 'a'
192
193 var tmp = optimalStates.ToArray();
194 foreach (var stateY in tmp) {
195 var stateR1 = new HashSet<int>(stateY);
196 var stateR2 = new HashSet<int>(stateY);
197
198 stateR1.IntersectWith(stateX);
199 stateR2.ExceptWith(stateX);
200
201 if (stateR1.Count > 0 && stateR2.Count > 0) {
202
203
204 optimalStates.Remove(stateY);
205 optimalStates.Add(stateR1);
206 optimalStates.Add(stateR2);
207
208 if (queue.Contains(stateY)) {
209 queue.Remove(stateY);
210 queue.Add(stateR1);
211 queue.Add(stateR2);
212 } else {
213 queue.Add(stateR1.Count <= stateR2.Count ? stateR1 : stateR2);
214 }
215 }
216 }
217 }
218 }
219
220 // дополнительно разбиваем конечные состояния
221 foreach (var final in optimalStates.Where(s => s.Overlaps(m_finalStates)).ToArray()) {
222 optimalStates.Remove(final);
223 foreach (var split in SplitFinalStates(final))
224 optimalStates.Add(split);
225 }
226
227
228 // карта получения оптимального состояния по соотвествующему ему простому состоянию
229 var nextState = 0;
230 foreach (var item in optimalStates) {
231 var id = nextState++;
232 foreach (var s in item)
233 stateMap[s] = id;
234 }
235
236 // получаем минимальный алфавит
237 // входные символы не различимы, если Move(s,a1) == Move(s,a2), для любого s
238 // для этого используем алгоритм кластеризации, сначала
239 // считаем, что все символы не различимы
240
241 var minClasses = new HashSet<HashSet<int>>(setComparer);
242 var alphaQueue = new Queue<HashSet<int>>();
243 alphaQueue.Enqueue(new HashSet<int>(Enumerable.Range(0,AlphabetSize)));
244
245 // для всех состояний, будем проверять каждый класс на различимость,
246 // т.е. символы различимы, если они приводят к разным состояниям
247 for (int s = 0 ; s < optimalStates.Count; s++) {
248 var newQueue = new Queue<HashSet<int>>();
249
250 foreach (var A in alphaQueue) {
251 // классы из одного символа делить бесполезно, переводим их сразу в
252 // результирующий алфавит
253 if (A.Count == 1) {
254 minClasses.Add(A);
255 continue;
256 }
257
258 // различаем классы символов, которые переводят в различные оптимальные состояния
259 // optimalState -> alphaClass
260 var classes = new Dictionary<int, HashSet<int>>();
261
262 foreach (var term in A) {
263 // ищем все переходы класса по символу term
264 var s2 = m_transitions.Where(t => stateMap[t.s1] == s && t.edge == term).Select(t => stateMap[t.s2]).DefaultIfEmpty(-1).First();
265
266 HashSet<int> a2;
267 if (!classes.TryGetValue(s2, out a2)) {
268 a2 = new HashSet<int>();
269 newQueue.Enqueue(a2);
270 classes[s2] = a2;
271 }
272 a2.Add(term);
273 }
274 }
275
276 if (newQueue.Count == 0)
277 break;
278 alphaQueue = newQueue;
279 }
280
281 // после окончания работы алгоритма в очереди останутся минимальные различимые классы
282 // входных символов
283 foreach (var A in alphaQueue)
284 minClasses.Add(A);
285
286 // построение отображения алфавитов входных символов.
287 // поскольку символ DFAConst.UNCLASSIFIED_INPUT может иметь
288 // специальное значение, тогда сохраним минимальный класс,
289 // содержащий этот символ на томже месте.
290
291 var nextCls = 0;
292 foreach (var item in minClasses) {
293 if (nextCls == AutomatonConst.UNCLASSIFIED_INPUT)
294 nextCls++;
295
296 // сохраняем DFAConst.UNCLASSIFIED_INPUT
297 var cls = item.Contains(AutomatonConst.UNCLASSIFIED_INPUT) ? AutomatonConst.UNCLASSIFIED_INPUT : nextCls++;
298 optimalDFA.AddSymbol(cls);
299
300 foreach (var a in item)
301 alphabetMap[a] = cls;
302 }
303
304 // построение автомата
305 optimalDFA.SetInitialState(stateMap[m_initialState]);
306
307 foreach (var sf in m_finalStates.Select(s => stateMap[s]).Distinct())
308 optimalDFA.MarkFinalState(sf);
309
310 foreach (var t in m_transitions.Select(t => new AutomatonTransition(stateMap[t.s1],stateMap[t.s2],alphabetMap[t.edge])).Distinct())
311 optimalDFA.Add(t);
312 }
313
314 protected string PrintDFA<TInput, TState>(IAlphabet<TInput> inputAlphabet, IAlphabet<TState> stateAlphabet) {
315 Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet");
316 Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet");
317
318 var data = new List<string>();
319
320 data.Add("digraph dfa {");
321
322 foreach (var final in m_finalStates)
323 data.Add(String.Format("{0} [shape=box];",String.Join("", stateAlphabet.GetSymbols(final))));
324
325 foreach (var t in m_transitions)
326 data.Add(String.Format(
327 "{0} -> {2} [label={1}];",
328 String.Join("", stateAlphabet.GetSymbols(t.s1)),
329 ToLiteral(ToLiteral(String.Join("", t.edge == AutomatonConst.UNCLASSIFIED_INPUT ? new [] { "@" } : inputAlphabet.GetSymbols(t.edge).Select(x => x.ToString())))),
330 String.Join("", stateAlphabet.GetSymbols(t.s2))
331 ));
332 data.Add("}");
333 return String.Join("\n", data);
334 }
335
336 static string ToLiteral(string input)
337 {
338 using (var writer = new StringWriter())
339 {
340 using (var provider = CodeDomProvider.CreateProvider("CSharp"))
341 {
342 provider.GenerateCodeFromExpression(new CodePrimitiveExpression(input), writer, null);
343 return writer.ToString();
344 }
345 }
346 }
347 }
348 }
@@ -0,0 +1,66
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.Globalization;
5 using System.Linq;
6 using System.Diagnostics.CodeAnalysis;
7
8 namespace Implab.Automaton {
9 /// <summary>
10 /// Алфавит символами которого являются элементы перечислений.
11 /// </summary>
12 /// <typeparam name="T">Тип перечислений</typeparam>
13 public class EnumAlphabet<T> : IndexedAlphabetBase<T> where T : struct, IConvertible {
14 [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
15 static readonly Lazy<T[]> _symbols = new Lazy<T[]>(GetSymbols);
16
17 [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
18 static readonly Lazy<EnumAlphabet<T>> _fullAlphabet = new Lazy<EnumAlphabet<T>>(CreateEnumAlphabet);
19
20 static EnumAlphabet<T> CreateEnumAlphabet() {
21 var symbols = _symbols.Value;
22
23 if (
24 symbols[symbols.Length - 1].ToInt32(CultureInfo.InvariantCulture) >= symbols.Length
25 || symbols[0].ToInt32(CultureInfo.InvariantCulture) != 0
26 )
27 throw new InvalidOperationException("The specified enumeration must be zero-based and continuously numbered");
28
29 return new EnumAlphabet<T>(symbols.Select(x => x.ToInt32(CultureInfo.InvariantCulture)).ToArray());
30 }
31
32 static T[] GetSymbols() {
33 if (!typeof(T).IsEnum)
34 throw new InvalidOperationException("Invalid generic parameter, enumeration is required");
35
36 if (Enum.GetUnderlyingType(typeof(T)) != typeof(Int32))
37 throw new InvalidOperationException("Only enums based on Int32 are supported");
38
39 return ((T[])Enum.GetValues(typeof(T)))
40 .OrderBy(x => x.ToInt32(CultureInfo.InvariantCulture))
41 .ToArray();
42 }
43
44 public static EnumAlphabet<T> FullAlphabet {
45 get {
46 return _fullAlphabet.Value;
47 }
48 }
49
50
51 public EnumAlphabet()
52 : base(_symbols.Value.Length) {
53 }
54
55 public EnumAlphabet(int[] map)
56 : base(map) {
57 Debug.Assert(map.Length == _symbols.Value.Length);
58 }
59
60
61 public override int GetSymbolIndex(T symbol) {
62 return symbol.ToInt32(CultureInfo.InvariantCulture);
63 }
64
65 }
66 }
@@ -0,0 +1,34
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
7 namespace Implab.Automaton {
8 /// <summary>
9 /// Алфавит. Множество символов, которые разбиты на классы, при этом классы имеют непрерывную нумерацию,
10 /// что позволяет использовать их в качестве индексов массивов.
11 /// </summary>
12 /// <remarks>
13 /// <para>Алфавит является сюрьективным отображением множества символов в множество индексов, это позволяет сократить размер таблицы переходов автомата
14 /// для входных символов, которые для него не различимы.</para>
15 /// </remarks>
16 /// <typeparam name="TSymbol">Тип символов.</typeparam>
17 public interface IAlphabet<TSymbol> {
18 /// <summary>
19 /// Количество классов символов в алфавите.
20 /// </summary>
21 int Count { get; }
22
23 /// <summary>
24 /// Преобразует входной символ в индекс символа из алфавита.
25 /// </summary>
26 /// <param name="symobl">Исходный символ</param>
27 /// <returns>Индекс в алфавите</returns>
28 int Translate(TSymbol symobl);
29
30 bool Contains(TSymbol symbol);
31
32 IEnumerable<TSymbol> GetSymbols(int cls);
33 }
34 }
@@ -0,0 +1,26
1
2 using System.Collections.Generic;
3
4 namespace Implab.Automaton {
5 public interface IAlphabetBuilder<TSymbol> : IAlphabet<TSymbol> {
6 /// <summary>
7 /// Добавляет новый символ в алфавит, если символ уже был добавлен, то
8 /// возвращается ранее сопоставленный с символом класс.
9 /// </summary>
10 /// <param name="symbol">Символ для добавления.</param>
11 /// <returns>Индекс класса, который попоставлен с символом.</returns>
12 int DefineSymbol(TSymbol symbol);
13
14 int DefineSymbol(TSymbol symbol, int cls);
15 /// <summary>
16 /// Доабвляем класс символов. Множеству указанных исходных символов
17 /// будет сопоставлен символ в алфавите.
18 /// </summary>
19 /// <param name="symbols">Множестов исходных символов</param>
20 /// <returns>Идентификатор символа алфавита.</returns>
21 int DefineClass(IEnumerable<TSymbol> symbols);
22
23 int DefineClass(IEnumerable<TSymbol> symbols, int cls);
24 }
25 }
26
@@ -0,0 +1,53
1 using System.Collections.Generic;
2
3
4 namespace Implab.Automaton {
5 /// <summary>
6 /// Полностью описывает DFA автомат, его поведение, состояние и входные символы.
7 /// </summary>
8 /// <example>
9 /// class MyAutomaton {
10 /// int m_current;
11 /// readonly DFAStateDescriptor<string>[] m_automaton;
12 /// readonly IAlphabet<MyCommands> m_commands;
13 ///
14 /// public MyAutomaton(IDFADefinition&lt;MyCommands,MyStates,string&gt; definition) {
15 /// m_current = definition.StateAlphabet.Translate(MyStates.Initial);
16 /// m_automaton = definition.GetTransitionTable();
17 /// m_commands = definition.InputAlphabet;
18 /// }
19 ///
20 /// // defined a method which will move the automaton to the next state
21 /// public void Move(MyCommands cmd) {
22 /// // use transition map to determine the next state
23 /// var next = m_automaton[m_current].transitions[m_commands.Translate(cmd)];
24 ///
25 /// // validate that we aren't in the unreachable state
26 /// if (next == DFAConst.UNREACHABLE_STATE)
27 /// throw new InvalidOperationException("The specified command is invalid");
28 ///
29 /// // if everything is ok
30 /// m_current = next;
31 /// }
32 /// }
33 /// </example>
34 public interface IDFATable : IEnumerable<AutomatonTransition> {
35 int StateCount {
36 get;
37 }
38
39 int AlphabetSize {
40 get;
41 }
42
43 int InitialState {
44 get;
45 }
46
47 bool IsFinalState(int s);
48
49 IEnumerable<int> FinalStates {
50 get;
51 }
52 }
53 }
@@ -0,0 +1,26
1 using System;
2 using System.Collections.Generic;
3
4 namespace Implab.Automaton {
5 public interface IDFATableBuilder : IDFATable, ICollection<AutomatonTransition> {
6 /// <summary>
7 /// Marks the state as final.
8 /// </summary>
9 /// <param name="state">State.</param>
10 void MarkFinalState(int state);
11
12 void SetInitialState(int s);
13
14 /// <summary>
15 /// Increases if needed the input alphabet size to hold the specified symbol.
16 /// </summary>
17 /// <remarks>
18 /// <code>
19 /// AlphabetSize = Math.Max(AlphabetSize, symbol + 1)
20 /// </code>
21 /// </remarks>
22 /// <param name="symbol">Symbol.</param>
23 void AddSymbol(int symbol);
24 }
25 }
26
@@ -0,0 +1,50
1 using Implab;
2 using System;
3 using System.Collections.Generic;
4 using System.Diagnostics;
5 using System.Linq;
6
7 namespace Implab.Automaton {
8 /// <summary>
9 /// Indexed alphabet is the finite set of symbols where each symbol has a zero-based unique index.
10 /// </summary>
11 /// <remarks>
12 /// Indexed alphabets are usefull in bulting efficient translations from source alphabet
13 /// to the input alphabet of the automaton. It's assumed that the index to the symbol match
14 /// is well known and documented.
15 /// </remarks>
16 public abstract class IndexedAlphabetBase<T> : MapAlphabet<T> {
17
18 protected IndexedAlphabetBase() :base(true, null) {
19 }
20
21 public abstract int GetSymbolIndex(T symbol);
22
23 /// <summary>
24 /// Gets the translation map from the index of the symbol to it's class this is usefull for the optimized input symbols transtaion.
25 /// </summary>
26 /// <remarks>
27 /// The map is continous and start from the symbol with zero code. The last symbol
28 /// in the map is the last classified symbol in the alphabet, i.e. the map can be
29 /// shorter then the whole alphabet.
30 /// </remarks>
31 /// <returns>The translation map.</returns>
32 public int[] GetTranslationMap() {
33 var map = new Dictionary<int, int>();
34
35 int max = 0;
36 foreach (var p in Mappings) {
37 var index = GetSymbolIndex(p.Key);
38 max = Math.Max(max, index);
39 map[index] = p.Value;
40 }
41
42 var result = new int[max + 1];
43
44 for (int i = 0; i < result.Length; i++)
45 map.TryGetValue(i, out result[i]);
46
47 return result;
48 }
49 }
50 }
@@ -0,0 +1,84
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4
5 namespace Implab.Automaton {
6 public class MapAlphabet<T> : IAlphabetBuilder<T> {
7 readonly Dictionary<T,int> m_map;
8 int m_nextCls;
9 readonly bool m_supportUnclassified;
10
11 public MapAlphabet(bool supportUnclassified, IEqualityComparer<T> comparer) {
12 m_map = comparer != null ? new Dictionary<T, int>(comparer) : new Dictionary<T,int>();
13 m_supportUnclassified = supportUnclassified;
14 m_nextCls = supportUnclassified ? 1 : 0;
15 }
16
17 #region IAlphabetBuilder implementation
18
19 public int DefineSymbol(T symbol) {
20 int cls;
21 return m_map.TryGetValue(symbol, out cls) ? cls : DefineSymbol(symbol, m_nextCls);
22 }
23
24 public int DefineSymbol(T symbol, int cls) {
25 Safe.ArgumentAssert(cls >= 0, "cls");
26
27 m_nextCls = Math.Max(cls + 1, m_nextCls);
28 m_map.Add(symbol, cls);
29 return cls;
30 }
31
32 public int DefineClass(IEnumerable<T> symbols) {
33 return DefineClass(symbols, m_nextCls);
34 }
35
36 public int DefineClass(IEnumerable<T> symbols, int cls) {
37 Safe.ArgumentAssert(cls >= 0, "cls");
38 Safe.ArgumentNotNull(symbols, "symbols");
39
40 m_nextCls = Math.Max(cls + 1, m_nextCls);
41
42 foreach (var symbol in symbols)
43 m_map[symbol] = cls;
44 return cls;
45 }
46
47 #endregion
48
49 #region IAlphabet implementation
50
51 public int Translate(T symbol) {
52 int cls;
53 if (m_map.TryGetValue(symbol, out cls))
54 return cls;
55 if (!m_supportUnclassified)
56 throw new ArgumentOutOfRangeException("symbol", "The specified symbol isn't in the alphabet");
57 return AutomatonConst.UNCLASSIFIED_INPUT;
58 }
59
60 public int Count {
61 get {
62 return m_nextCls;
63 }
64 }
65
66 public bool Contains(T symbol) {
67 return m_supportUnclassified || m_map.ContainsKey(symbol);
68 }
69
70
71 public IEnumerable<T> GetSymbols(int cls) {
72 Safe.ArgumentAssert(!m_supportUnclassified || cls > 0, "cls");
73 return m_map.Where(p => p.Value == cls).Select(p => p.Key);
74 }
75 #endregion
76
77 public IEnumerable<KeyValuePair<T,int>> Mappings {
78 get {
79 return m_map;
80 }
81 }
82 }
83 }
84
@@ -0,0 +1,17
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Implab.Automaton {
7 [Serializable]
8 public class ParserException : Exception {
9 public ParserException() { }
10 public ParserException(string message) : base(message) { }
11 public ParserException(string message, Exception inner) : base(message, inner) { }
12 protected ParserException(
13 System.Runtime.Serialization.SerializationInfo info,
14 System.Runtime.Serialization.StreamingContext context)
15 : base(info, context) { }
16 }
17 }
@@ -0,0 +1,17
1 using System;
2
3 namespace Implab.Automaton.RegularExpressions {
4 public class AltToken: BinaryToken {
5 public AltToken(Token left, Token right)
6 : base(left, right) {
7 }
8
9 public override void Accept(IVisitor visitor) {
10 Safe.ArgumentNotNull(visitor, "visitor");
11 visitor.Visit(this);
12 }
13 public override string ToString() {
14 return String.Format(Right is BinaryToken ? "{0}|({1})" : "{0}|{1}", Left, Right);
15 }
16 }
17 }
@@ -0,0 +1,21
1 using Implab;
2
3 namespace Implab.Automaton.RegularExpressions {
4 public abstract class BinaryToken: Token {
5 readonly Token m_left;
6 readonly Token m_right;
7
8 public Token Left {
9 get { return m_left; }
10 }
11
12 public Token Right {
13 get { return m_right; }
14 }
15
16 protected BinaryToken(Token left, Token right) {
17 Safe.ArgumentNotNull(m_left = left, "left");
18 Safe.ArgumentNotNull(m_right = right, "right");
19 }
20 }
21 }
@@ -0,0 +1,22
1 using System;
2
3 namespace Implab.Automaton.RegularExpressions {
4 public class CatToken : BinaryToken {
5 public CatToken(Token left, Token right)
6 : base(left, right) {
7 }
8
9 public override void Accept(IVisitor visitor) {
10 Safe.ArgumentNotNull(visitor, "visitor");
11 visitor.Visit(this);
12 }
13
14 public override string ToString() {
15 return String.Format("{0}{1}", FormatToken(Left), FormatToken(Right));
16 }
17
18 static string FormatToken(Token token) {
19 return String.Format(token is AltToken ? "({0})" : "{0}", token);
20 }
21 }
22 }
@@ -0,0 +1,13
1 using Implab;
2
3 namespace Implab.Automaton.RegularExpressions {
4 public class EmptyToken: Token {
5 public override void Accept(IVisitor visitor) {
6 Safe.ArgumentNotNull(visitor, "visitor");
7 visitor.Visit(this);
8 }
9 public override string ToString() {
10 return "$";
11 }
12 }
13 }
@@ -0,0 +1,18
1 using Implab;
2
3 namespace Implab.Automaton.RegularExpressions {
4 /// <summary>
5 /// Конечный символ расширенного регулярного выражения, при построении ДКА
6 /// используется для определения конечных состояний.
7 /// </summary>
8 public class EndToken: Token {
9
10 public override void Accept(IVisitor visitor) {
11 Safe.ArgumentNotNull(visitor, "visitor");
12 visitor.Visit(this);
13 }
14 public override string ToString() {
15 return "#";
16 }
17 }
18 }
@@ -0,0 +1,23
1 namespace Implab.Automaton.RegularExpressions {
2 /// <summary>
3 /// Конечный символ расширенного регулярного выражения, при построении ДКА
4 /// используется для определения конечных состояний.
5 /// </summary>
6 public class EndToken<TTag>: EndToken {
7
8 readonly TTag m_tag;
9
10 public EndToken(TTag tag) {
11 m_tag = tag;
12 }
13
14 public EndToken()
15 : this(default(TTag)) {
16 }
17
18 public TTag Tag {
19 get { return m_tag; }
20 }
21
22 }
23 }
@@ -0,0 +1,7
1
2 namespace Implab.Automaton.RegularExpressions {
3 public interface ITaggedDFABuilder<TTag> : IDFATableBuilder {
4 void SetStateTag(int s, TTag[] tags);
5 }
6 }
7
@@ -0,0 +1,13
1 namespace Implab.Automaton.RegularExpressions {
2 /// <summary>
3 /// Интерфейс обходчика синтаксического дерева регулярного выражения
4 /// </summary>
5 public interface IVisitor {
6 void Visit(AltToken token);
7 void Visit(StarToken token);
8 void Visit(CatToken token);
9 void Visit(EmptyToken token);
10 void Visit(EndToken token);
11 void Visit(SymbolToken token);
12 }
13 }
@@ -0,0 +1,91
1 using System.Collections.Generic;
2 using System.Linq;
3
4 namespace Implab.Automaton.RegularExpressions {
5 public class RegularDFA<TInput, TTag> : DFATable, ITaggedDFABuilder<TTag> {
6
7 readonly Dictionary<int,TTag[]> m_tags = new Dictionary<int, TTag[]>();
8 readonly IAlphabet<TInput> m_alphabet;
9
10 public RegularDFA(IAlphabet<TInput> alphabet) {
11 Safe.ArgumentNotNull(alphabet, "aplhabet");
12
13 m_alphabet = alphabet;
14 }
15
16
17 public IAlphabet<TInput> InputAlphabet {
18 get {
19 return m_alphabet;
20 }
21 }
22
23 public void MarkFinalState(int s, TTag[] tags) {
24 MarkFinalState(s);
25 SetStateTag(s, tags);
26 }
27
28 public void SetStateTag(int s, TTag[] tags) {
29 Safe.ArgumentNotNull(tags, "tags");
30 m_tags[s] = tags;
31 }
32
33 public TTag[] GetStateTag(int s) {
34 TTag[] tags;
35 return m_tags.TryGetValue(s, out tags) ? tags : new TTag[0];
36 }
37
38 public TTag[][] CreateTagTable() {
39 var table = new TTag[StateCount][];
40
41 foreach (var pair in m_tags)
42 table[pair.Key] = pair.Value;
43
44 return table;
45 }
46
47 /// <summary>
48 /// Optimize the specified alphabet.
49 /// </summary>
50 /// <param name="alphabet">Пустой алфавит, который будет зполнен в процессе оптимизации.</param>
51 public RegularDFA<TInput,TTag> Optimize(IAlphabetBuilder<TInput> alphabet) {
52 Safe.ArgumentNotNull(alphabet, "alphabet");
53
54 var dfa = new RegularDFA<TInput, TTag>(alphabet);
55
56 var alphaMap = new Dictionary<int,int>();
57 var stateMap = new Dictionary<int,int>();
58
59 Optimize(dfa, alphaMap, stateMap);
60
61 // mark tags in the new DFA
62 foreach (var g in m_tags.Where(x => x.Key < StateCount).GroupBy(x => stateMap[x.Key], x => x.Value ))
63 dfa.SetStateTag(g.Key, g.SelectMany(x => x).ToArray());
64
65 // make the alphabet for the new DFA
66 // skip all unclassified symbols
67 foreach (var pair in alphaMap.Where(x => x.Value != 0))
68 alphabet.DefineClass(m_alphabet.GetSymbols(pair.Key), pair.Value);
69 return dfa;
70 }
71
72 protected override IEnumerable<HashSet<int>> SplitFinalStates(IEnumerable<int> states) {
73 var arrayComparer = new CustomEqualityComparer<TTag[]>(
74 (x,y) => x.Length == y.Length && x.All(it => y.Contains(it)),
75 x => x.Sum(it => x.GetHashCode())
76 );
77 return states.GroupBy(x => m_tags[x] ?? new TTag[0], arrayComparer).Select(g => new HashSet<int>(g));
78 }
79
80 public override string ToString() {
81 var states = new MapAlphabet<string>(false, null);
82
83 for (int i = 0; i < StateCount; i++)
84 states.DefineSymbol(string.Format("s{0}", i), i);
85
86 return string.Format("//[RegularDFA {1} x {2}]\n{0}", PrintDFA(InputAlphabet, states),StateCount, AlphabetSize);
87 }
88
89 }
90 }
91
@@ -0,0 +1,212
1 using Implab;
2 using System;
3 using System.Collections.Generic;
4 using System.Diagnostics;
5 using System.Linq;
6
7 namespace Implab.Automaton.RegularExpressions {
8 /// <summary>
9 /// Используется для построения ДКА по регулярному выражению, сначала обходит
10 /// регулярное выражение и вычисляет followpos, затем используется метод
11 /// <see cref="BuildDFA(IDFADefinition)"/> для построения автомата.
12 /// </summary>
13 public class RegularExpressionVisitor : IVisitor {
14 int m_idx;
15 Token m_root;
16 HashSet<int> m_firstpos;
17 HashSet<int> m_lastpos;
18
19 readonly Dictionary<int, HashSet<int>> m_followpos = new Dictionary<int, HashSet<int>>();
20 readonly Dictionary<int, int> m_indexes = new Dictionary<int, int>();
21 readonly HashSet<int> m_ends = new HashSet<int>();
22
23 readonly IDFATableBuilder m_builder;
24 readonly IAlphabetBuilder<HashSet<int>> m_states = new MapAlphabet<HashSet<int>>(
25 false,
26 new CustomEqualityComparer<HashSet<int>>(
27 (x, y) => x.SetEquals(y),
28 x => x.Sum(n => n.GetHashCode())
29 )
30 );
31
32 public RegularExpressionVisitor(IDFATableBuilder builder) {
33 Safe.ArgumentNotNull(builder, "builder");
34
35 m_builder = builder;
36 }
37
38 HashSet<int> Followpos(int pos) {
39 HashSet<int> set;
40 return m_followpos.TryGetValue(pos, out set) ? set : m_followpos[pos] = new HashSet<int>();
41 }
42
43 bool Nullable(object n) {
44 if (n is EmptyToken || n is StarToken)
45 return true;
46 var altToken = n as AltToken;
47 if (altToken != null)
48 return Nullable(altToken.Left) || Nullable(altToken.Right);
49 var catToken = n as CatToken;
50 if (catToken != null)
51 return Nullable(catToken.Left) && Nullable(catToken.Right);
52 return false;
53 }
54
55 protected int Index {
56 get { return m_idx; }
57 }
58
59 public void Visit(AltToken token) {
60 if (m_root == null)
61 m_root = token;
62 var firtspos = new HashSet<int>();
63 var lastpos = new HashSet<int>();
64
65 token.Left.Accept(this);
66 firtspos.UnionWith(m_firstpos);
67 lastpos.UnionWith(m_lastpos);
68
69 token.Right.Accept(this);
70 firtspos.UnionWith(m_firstpos);
71 lastpos.UnionWith(m_lastpos);
72
73 m_firstpos = firtspos;
74 m_lastpos = lastpos;
75 }
76
77 public void Visit(StarToken token) {
78 if (m_root == null)
79 m_root = token;
80 token.Token.Accept(this);
81
82 foreach (var i in m_lastpos)
83 Followpos(i).UnionWith(m_firstpos);
84 }
85
86 public void Visit(CatToken token) {
87 if (m_root == null)
88 m_root = token;
89
90 var firtspos = new HashSet<int>();
91 var lastpos = new HashSet<int>();
92 token.Left.Accept(this);
93 firtspos.UnionWith(m_firstpos);
94 var leftLastpos = m_lastpos;
95
96 token.Right.Accept(this);
97 lastpos.UnionWith(m_lastpos);
98 var rightFirstpos = m_firstpos;
99
100 if (Nullable(token.Left))
101 firtspos.UnionWith(rightFirstpos);
102
103 if (Nullable(token.Right))
104 lastpos.UnionWith(leftLastpos);
105
106 m_firstpos = firtspos;
107 m_lastpos = lastpos;
108
109 foreach (var i in leftLastpos)
110 Followpos(i).UnionWith(rightFirstpos);
111
112 }
113
114 public void Visit(EmptyToken token) {
115 if (m_root == null)
116 m_root = token;
117 }
118
119 public void Visit(SymbolToken token) {
120 if (m_root == null)
121 m_root = token;
122 m_idx++;
123 m_indexes[m_idx] = token.Value;
124 m_firstpos = new HashSet<int>(new[] { m_idx });
125 m_lastpos = new HashSet<int>(new[] { m_idx });
126 }
127
128 public virtual void Visit(EndToken token) {
129 if (m_root == null)
130 m_root = token;
131 m_idx++;
132 m_indexes[m_idx] = AutomatonConst.UNCLASSIFIED_INPUT;
133 m_firstpos = new HashSet<int>(new[] { m_idx });
134 m_lastpos = new HashSet<int>(new[] { m_idx });
135 Followpos(m_idx);
136 m_ends.Add(m_idx);
137 }
138
139 public void BuildDFA() {
140 AddState(m_firstpos);
141 SetInitialState(m_firstpos);
142
143 if(IsFinal(m_firstpos))
144 MarkFinalState(m_firstpos);
145
146 var inputMax = m_indexes.Values.Max();
147 var queue = new Queue<HashSet<int>>();
148
149 queue.Enqueue(m_firstpos);
150
151 while (queue.Count > 0) {
152 var s1 = queue.Dequeue();
153
154 for (int a = 0; a <= inputMax; a++) {
155 var s2 = new HashSet<int>();
156 foreach (var p in s1) {
157 if (m_indexes[p] == a) {
158 s2.UnionWith(Followpos(p));
159 }
160 }
161 if (s2.Count > 0) {
162 if (!HasState(s2)) {
163 AddState(s2);
164 if (IsFinal(s2))
165 MarkFinalState(s2);
166
167 queue.Enqueue(s2);
168 }
169
170 DefineTransition(s1, s2, a);
171 }
172
173 }
174 }
175 }
176
177 protected bool HasState(HashSet<int> state) {
178 return m_states.Contains(state);
179 }
180
181 protected void AddState(HashSet<int> state) {
182 Debug.Assert(!HasState(state));
183
184 m_states.DefineSymbol(state);
185 }
186
187 protected int Translate(HashSet<int> state) {
188 Debug.Assert(HasState(state));
189
190 return m_states.Translate(state);
191 }
192
193 protected virtual void SetInitialState(HashSet<int> state) {
194 m_builder.SetInitialState(Translate(state));
195 }
196
197 protected virtual void MarkFinalState(HashSet<int> state) {
198 m_builder.MarkFinalState(Translate(state));
199 }
200
201 protected virtual void DefineTransition(HashSet<int> s1, HashSet<int> s2, int ch) {
202
203 m_builder.Add(new AutomatonTransition(Translate(s1), Translate(s2), ch));
204 }
205
206 bool IsFinal(IEnumerable<int> state) {
207 Debug.Assert(state != null);
208 return state.Any(m_ends.Contains);
209 }
210
211 }
212 }
@@ -0,0 +1,37
1 using Implab;
2 using System;
3 using System.Collections.Generic;
4 using System.Diagnostics;
5 using System.Linq;
6
7 namespace Implab.Automaton.RegularExpressions {
8 /// <summary>
9 /// </summary>
10 public class RegularExpressionVisitor<TTag> : RegularExpressionVisitor {
11 readonly Dictionary<int, TTag> m_tags = new Dictionary<int, TTag>();
12
13 readonly ITaggedDFABuilder<TTag> m_builder;
14
15 public RegularExpressionVisitor(ITaggedDFABuilder<TTag> builder) : base(builder) {
16 m_builder = builder;
17 }
18
19 public override void Visit(EndToken token) {
20 base.Visit(token);
21 var tagged = token as EndToken<TTag>;
22 if (tagged != null)
23 m_tags.Add(Index, tagged.Tag);
24 }
25
26 protected override void MarkFinalState(HashSet<int> state) {
27 base.MarkFinalState(state);
28 m_builder.SetStateTag(Translate(state), GetStateTags(state));
29 }
30
31 TTag[] GetStateTags(IEnumerable<int> state) {
32 Debug.Assert(state != null);
33 return state.Where(m_tags.ContainsKey).Select(pos => m_tags[pos]).ToArray();
34 }
35
36 }
37 }
@@ -0,0 +1,31
1 using Implab;
2 using System;
3
4
5 namespace Implab.Automaton.RegularExpressions {
6 /// <summary>
7 /// Замыкание выражения с 0 и более повторов.
8 /// </summary>
9 public class StarToken: Token {
10
11 Token m_token;
12
13 public Token Token {
14 get { return m_token; }
15 }
16
17 public StarToken(Token token) {
18 Safe.ArgumentNotNull(token, "token");
19 m_token = token;
20 }
21
22 public override void Accept(IVisitor visitor) {
23 Safe.ArgumentNotNull(visitor, "visitor");
24 visitor.Visit(this);
25 }
26
27 public override string ToString() {
28 return String.Format("({0})*", Token);
29 }
30 }
31 }
@@ -0,0 +1,27
1 using Implab;
2
3 namespace Implab.Automaton.RegularExpressions {
4 /// <summary>
5 /// Выражение, соответсвующее одному символу.
6 /// </summary>
7 public class SymbolToken: Token {
8 int m_value;
9
10 public int Value {
11 get { return m_value; }
12 }
13
14 public SymbolToken(int value) {
15 m_value = value;
16 }
17 public override void Accept(IVisitor visitor) {
18 Safe.ArgumentNotNull(visitor, "visitor");
19
20 visitor.Visit(this);
21 }
22
23 public override string ToString() {
24 return Value.ToString();
25 }
26 }
27 }
@@ -0,0 +1,63
1 using Implab;
2 using System;
3 using System.Linq;
4
5 namespace Implab.Automaton.RegularExpressions {
6 public abstract class Token {
7 public abstract void Accept(IVisitor visitor);
8
9 public Token End() {
10 return Cat(new EndToken());
11 }
12
13 public Token Tag<TTag>(TTag tag) {
14 return Cat(new EndToken<TTag>(tag));
15 }
16
17 public Token Cat(Token right) {
18 return new CatToken(this, right);
19 }
20
21 public Token Or(Token right) {
22 return new AltToken(this, right);
23 }
24
25 public Token Optional() {
26 return Or(new EmptyToken());
27 }
28
29 public Token EClosure() {
30 return new StarToken(this);
31 }
32
33 public Token Closure() {
34 return Cat(new StarToken(this));
35 }
36
37 public Token Repeat(int count) {
38 Token token = null;
39
40 for (int i = 0; i < count; i++)
41 token = token != null ? token.Cat(this) : this;
42 return token ?? new EmptyToken();
43 }
44
45 public Token Repeat(int min, int max) {
46 if (min > max || min < 1)
47 throw new ArgumentOutOfRangeException();
48 var token = Repeat(min);
49
50 for (int i = min; i < max; i++)
51 token = token.Cat( Optional() );
52 return token;
53 }
54
55 public static Token New(params int[] set) {
56 Safe.ArgumentNotNull(set, "set");
57 Token token = null;
58 foreach(var c in set.Distinct())
59 token = token == null ? new SymbolToken(c) : token.Or(new SymbolToken(c));
60 return token;
61 }
62 }
63 }
@@ -0,0 +1,25
1 using System;
2 using System.Collections.Generic;
3
4 namespace Implab.Components {
5 /// <summary>
6 /// Global application components and services.
7 /// </summary>
8 public static class App {
9 readonly static ComponentContainer<object> _root = new ComponentContainer<object>();
10
11 /// <summary>
12 /// The container for application level components.
13 /// </summary>
14 /// <remarks>Pools of disposable objects can be placed here and they will be automatically
15 /// disposed when application domain is unloaded.</remarks>
16 public static ICollection<object> RootContainer {
17 get { return _root; }
18 }
19
20 static App() {
21 AppDomain.CurrentDomain.DomainUnload += (sender, e) => _root.Dispose();
22 }
23 }
24 }
25
@@ -0,0 +1,127
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4
5 namespace Implab.Components {
6 /// <summary>
7 /// Component container, used to store track components in multi-threaded environmment.
8 /// </summary>
9 /// <remarks>Instanses of this class are thread safe.</remarks>
10 public class ComponentContainer<T> : Disposable, ICollection<T> {
11 readonly HashSet<T> m_components = new HashSet<T>();
12
13 /// <summary>
14 /// Removes currently stored compoenents from the container and disposes them if possible.
15 /// </summary>
16 /// <remarks>
17 /// A new components may be added before this method completes.
18 /// </remarks>
19 public void Clear() {
20 T[] removed;
21
22 lock (m_components) {
23 removed = new T[m_components.Count];
24 m_components.CopyTo(removed);
25 m_components.Clear();
26 }
27
28 foreach (var item in removed.OfType<IDisposable>())
29 item.Dispose();
30 }
31
32 /// <summary>
33 /// Checks whether the specified item in the collection.
34 /// </summary>
35 /// <param name="item">The item to check.</param>
36 public bool Contains(T item) {
37 lock (m_components)
38 return m_components.Contains(item);
39 }
40
41 /// <summary>
42 /// Copies currently stored components to the specified array.
43 /// </summary>
44 /// <param name="array">A destination array for components.</param>
45 /// <param name="arrayIndex">A starting index in the destination array.</param>
46 public void CopyTo(T[] array, int arrayIndex) {
47 lock (m_components)
48 m_components.CopyTo(array, arrayIndex);
49 }
50
51 /// <summary>
52 /// Remove the specified item from the collection.
53 /// </summary>
54 /// <param name="item">The item to remove.</param>
55 public bool Remove(T item) {
56 lock (m_components)
57 return m_components.Remove(item);
58 }
59
60 /// <summary>
61 /// Gets the count of components in the collection.
62 /// </summary>
63 public int Count {
64 get {
65 lock (m_components)
66 return m_components.Count;
67 }
68 }
69
70 /// <summary>
71 /// Gets a value indicating whether this instance is read only.
72 /// </summary>
73 /// <remarks>
74 /// Always false.
75 /// </remarks>
76 public bool IsReadOnly {
77 get {
78 return false;
79 }
80 }
81
82 /// <summary>
83 /// Gets the enumerator for components in the collection.
84 /// </summary>
85 /// <returns>The enumerator.</returns>
86 public IEnumerator<T> GetEnumerator() {
87 T[] items;
88 lock (m_components) {
89 items = new T[m_components.Count];
90 m_components.CopyTo(items);
91 return (IEnumerator<T>)items.GetEnumerator();
92 }
93 }
94
95 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
96 return GetEnumerator();
97 }
98
99 /// <summary>
100 /// Add the specified item to the collection.
101 /// </summary>
102 /// <param name="item">The item to add.</param>
103 /// <remarks>
104 /// If the collection is alredy disposed, the item isn't added to the collection and disposed if possible.
105 /// </remarks>
106 public void Add(T item) {
107 Safe.ArgumentNotNull(item, "item");
108
109 lock (m_components) {
110 if (IsDisposed)
111 Safe.Dispose(item);
112 else
113 m_components.Add(item);
114 }
115 }
116
117 /// <summary>
118 /// Disposes the components stored in the collection.
119 /// </summary>
120 /// <param name="disposing">If set to <c>true</c> the collection is disposing.</param>
121 protected override void Dispose(bool disposing) {
122 base.Dispose(disposing);
123 Clear();
124 }
125 }
126 }
127
@@ -0,0 +1,103
1 using Implab.Diagnostics;
2 using System;
3 using System.Threading;
4
5 namespace Implab.Components {
6 /// <summary>
7 /// Base class the objects which support disposing.
8 /// </summary>
9 public class Disposable : IDisposable {
10
11 int m_disposed;
12
13 public event EventHandler Disposed;
14
15 public bool IsDisposed {
16 get {
17 Thread.MemoryBarrier();
18 return m_disposed != 0;
19 }
20 }
21
22 /// <summary>
23 /// Asserts the object is not disposed.
24 /// </summary>
25 /// <exception cref="ObjectDisposedException">The object is disposed</exception>
26 /// <remarks>
27 /// Успешная проверка того, что объект не освобожден еще не гарантирует, что он не
28 /// будет освобожден сразу после нее, поэтому методы использующие проверку должны
29 /// учитывать, что объект может быть освобожден из параллельного потока.
30 /// Данны метод служит для упрощения отладки ошибок при использовании объекта после его
31 /// освобождения.
32 /// </remarks>
33 /// <example>
34 /// // пример синхронизированного освобождения ресурсов
35 /// class FileStore : Disposable {
36 /// readonly TextWriter m_file;
37 /// readonly obejct m_sync = new object();
38 ///
39 /// public FileStore(string file) {
40 /// m_file = new TextWriter(File.OpenWrite(file));
41 /// }
42 ///
43 /// public void Write(string text) {
44 /// lock(m_sync) {
45 /// AssertNotDisposed();
46 /// m_file.Write(text);
47 /// }
48 /// }
49 ///
50 /// protected override void Dispose(bool disposing) {
51 /// if (disposing)
52 /// lock(m_sync) {
53 /// m_file.Dipose();
54 /// base.Dispose(true);
55 /// }
56 /// else
57 /// base.Dispose(false);
58 /// }
59 /// }
60 /// <example>
61 protected void AssertNotDisposed() {
62 Thread.MemoryBarrier();
63 if (m_disposed != 0)
64 throw new ObjectDisposedException(ToString());
65 }
66 /// <summary>
67 /// Вызывает событие <see cref="Disposed"/>
68 /// </summary>
69 /// <param name="disposing">Признак того, что нужно освободить ресурсы, иначе данный метод
70 /// вызван сборщиком мусора и нужно освобождать ТОЛЬКО неуправляемые ресурсы ТОЛЬКО этого
71 /// объекта.</param>
72 /// <remarks>
73 /// Данный метод вызывается гарантированно один раз даже при одновременном вызове <see cref="Dispose()"/>
74 /// из нескольких потоков.
75 /// </remarks>
76 protected virtual void Dispose(bool disposing) {
77 if (disposing) {
78 EventHandler temp = Disposed;
79 if (temp != null)
80 temp(this, EventArgs.Empty);
81 }
82 }
83
84 public void Dispose() {
85 if (Interlocked.Increment(ref m_disposed) == 1) {
86 Dispose(true);
87 GC.SuppressFinalize(this);
88 }
89 }
90
91 /// <summary>
92 /// Записывает сообщение об утечке объекта.
93 /// </summary>
94 protected virtual void ReportObjectLeaks() {
95 TraceLog.TraceWarning("The object is marked as disposable but isn't disposed properly: {0}", this);
96 }
97
98 ~Disposable() {
99 Dispose(false);
100 ReportObjectLeaks();
101 }
102 }
103 } No newline at end of file
@@ -0,0 +1,102
1 using System;
2 using Implab.Parallels;
3 using System.Threading;
4 using System.Diagnostics;
5 using System.Diagnostics.CodeAnalysis;
6
7 namespace Implab.Components {
8 /// <summary>
9 /// The base class for implementing pools of disposable objects.
10 /// </summary>
11 /// <remarks>
12 /// <para>This class maintains a set of pre-created objects and which are frequently allocated and released
13 /// by clients. The pool maintains maximum number of unsued object, any object above this limit is disposed,
14 /// if the pool is empty it will create new objects on demand.</para>
15 /// <para>Instances of this class are thread-safe.</para>
16 /// </remarks>
17 public abstract class DisposablePool<T> : IDisposable {
18 readonly int m_size;
19 readonly AsyncQueue<T> m_queue = new AsyncQueue<T>();
20
21 [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
22 static readonly bool _isValueType = typeof(T).IsValueType;
23
24 bool m_disposed;
25
26 int m_count;
27
28 protected DisposablePool(int size) {
29 m_size = size;
30 }
31
32 protected DisposablePool() : this(Environment.ProcessorCount+1) {
33 }
34
35 public T Allocate() {
36 if (m_disposed)
37 throw new ObjectDisposedException(ToString());
38
39 T instance;
40 if (m_queue.TryDequeue(out instance)) {
41 Interlocked.Decrement(ref m_count);
42 } else {
43 instance = CreateInstance();
44 Debug.Assert(!Object.Equals(instance, default(T)) || _isValueType);
45 }
46 return instance;
47 }
48
49 protected abstract T CreateInstance();
50
51 protected virtual void CleanupInstance(T instance) {
52 }
53
54 public void Release(T instance) {
55 if ( Object.Equals(instance,default(T)) && !_isValueType)
56 return;
57
58 Thread.MemoryBarrier();
59 if (m_count < m_size && !m_disposed) {
60 Interlocked.Increment(ref m_count);
61
62 CleanupInstance(instance);
63
64 m_queue.Enqueue(instance);
65
66 // пока элемент возвращался в кеш, была начата операция освобождения всего кеша
67 // и возможно уже законцена, в таком случае следует извлечь элемент обратно и
68 // освободить его. Если операция освобождения кеша еще не заврешилась, то будет
69 // изъят и освобожден произвольный элемен, что не повлияет на ход всего процесса.
70 if (m_disposed && m_queue.TryDequeue(out instance) && instance is IDisposable)
71 ((IDisposable)instance).Dispose() ;
72
73 } else {
74 var disposable = instance as IDisposable;
75 if (disposable != null)
76 disposable.Dispose();
77 }
78 }
79
80 protected virtual void Dispose(bool disposing) {
81 if (disposing) {
82 m_disposed = true;
83 T instance;
84 while (m_queue.TryDequeue(out instance)) {
85 var disposable = instance as IDisposable;
86 if (disposable != null)
87 disposable.Dispose();
88 }
89 }
90 }
91
92 #region IDisposable implementation
93
94 public void Dispose() {
95 Dispose(true);
96 GC.SuppressFinalize(this);
97 }
98
99 #endregion
100 }
101 }
102
@@ -0,0 +1,24
1 namespace Implab.Components {
2
3 public enum ExecutionState {
4 Undefined = 0,
5
6 Created,
7
8 Initializing,
9
10 Ready,
11
12 Starting,
13
14 Running,
15
16 Stopping,
17
18 Failed,
19
20 Disposed,
21
22 Last = Disposed
23 }
24 } No newline at end of file
@@ -0,0 +1,8
1 using System;
2
3 namespace Implab.Components {
4 public interface IFactory<out T> {
5 T Create();
6 }
7 }
8
@@ -0,0 +1,21
1 using System;
2
3 namespace Implab.Components {
4 /// <summary>
5 /// Initializable components are created and initialized in two steps, first we have create the component,
6 /// then we have to complete it's creation by calling an <see cref="Init()"/> method. All parameters needed
7 /// to complete the initialization must be passed before the calling <see cref="Init()"/>
8 /// </summary>
9 public interface IInitializable {
10 /// <summary>
11 /// Completes initialization.
12 /// </summary>
13 /// <remarks>
14 /// Normally virtual methods shouldn't be called from the constructor, due to the incomplete object state, but
15 /// they can be called from this method. This method is also usefull when we constructing a complex grpah
16 /// of components where cyclic references may take place.
17 /// </remarks>
18 void Init();
19 }
20 }
21
@@ -0,0 +1,14
1 using System;
2
3 namespace Implab.Components {
4 public interface IRunnable {
5 IPromise Start();
6
7 IPromise Stop();
8
9 ExecutionState State { get; }
10
11 Exception LastError { get; }
12 }
13 }
14
@@ -0,0 +1,64
1 using System;
2 using System.Threading;
3
4 namespace Implab.Components {
5 /// <summary>
6 /// Creates an instace on-demand and allows it to be garbage collected.
7 /// </summary>
8 /// <remarks>
9 /// Usefull when dealing with memory-intensive objects which are frequently used.
10 /// This class is similar to <see cref="ObjectPool{T}"/> except it is a singleton.
11 /// </remarks>
12 public class LazyAndWeak<T> where T : class {
13
14 readonly Func<T> m_factory;
15 readonly object m_lock;
16 WeakReference m_reference;
17
18
19 public LazyAndWeak(Func<T> factory, bool useLock) {
20 Safe.ArgumentNotNull(factory, "factory");
21 m_factory = factory;
22 m_lock = useLock ? new object() : null;
23 }
24
25 public LazyAndWeak(Func<T> factory) : this(factory, false) {
26 }
27
28 public T Value {
29 get {
30 while (true) {
31 var weak = m_reference;
32 T value;
33 if (weak != null) {
34 value = weak.Target as T;
35 if (value != null)
36 return value;
37 }
38
39 if (m_lock == null) {
40 value = m_factory();
41
42 if (Interlocked.CompareExchange(ref m_reference, new WeakReference(value), weak) == weak)
43 return value;
44 } else {
45 lock (m_lock) {
46 // double check
47 weak = m_reference;
48 if (weak != null) {
49 value = weak.Target as T;
50 if (value != null)
51 return value;
52 }
53 // we are safe to write
54 value = m_factory();
55 m_reference = new WeakReference(value);
56 return value;
57 }
58 }
59 }
60 }
61 }
62 }
63 }
64
@@ -0,0 +1,75
1 using Implab.Parallels;
2 using System;
3 using System.Threading;
4
5 namespace Implab.Components {
6 /// <summary>
7 /// The base class for creating object pools.
8 /// </summary>
9 /// <remarks>
10 /// <para>The objects pool is offers frequently requested objects to be reused, this gives
11 /// a gool speed improvement for the 'heavy' objects. To avoid memory overhead the pool uses
12 /// weak references allowing CG to do it's work. If there are no free objects in the pool
13 /// they are created on demand. </para>
14 /// <para>
15 /// Implementors need to defined a <see cref="CreateInstance()"/> method
16 /// </para>
17 /// <para>The instances of this class are thred-safe.</para>
18 /// </remarks>
19 public abstract class ObjectPool<T> where T : class {
20 readonly AsyncQueue<WeakReference> m_queue = new AsyncQueue<WeakReference>();
21 readonly int m_size;
22 int m_count = 0;
23
24 protected ObjectPool() : this(Environment.ProcessorCount+1) {
25
26 }
27
28 protected ObjectPool(int size) {
29 Safe.ArgumentInRange(size,1,size,"size");
30
31 m_size = size;
32 }
33
34 /// <summary>
35 /// Creates the instance if there are no free ones in the pool.
36 /// </summary>
37 /// <returns>The new instance.</returns>
38 protected abstract T CreateInstance();
39
40 /// <summary>
41 /// Cleanups the instance.
42 /// </summary>
43 /// <param name="instance">The instance to cleanup and prepare it for the next use.</param>
44 protected virtual void CleanupInstance(T instance) {
45 }
46
47 /// <summary>
48 /// Allocate free instance from the pool or reates a new one.
49 /// </summary>
50 public T Allocate() {
51 WeakReference reference;
52 while (m_queue.TryDequeue(out reference)) {
53 Interlocked.Decrement(ref m_count);
54 object instance = reference.Target;
55 if (instance == null)
56 continue;
57 return (T)instance;
58 }
59 return CreateInstance();
60 }
61
62 /// <summary>
63 /// Release the specified instance and returns it to the pool of free instances.
64 /// </summary>
65 /// <param name="instance">The instance to return to the pool.</param>
66 /// <remarks>Before the instance is returned to the pool the <see cref="CleanupInstance(T)"/> is called.</remarks>
67 public void Release(T instance) {
68 if (m_count < m_size && instance != null) {
69 Interlocked.Increment(ref m_count);
70 CleanupInstance(instance);
71 m_queue.Enqueue(new WeakReference(instance));
72 }
73 }
74 }
75 }
@@ -0,0 +1,257
1 using System;
2
3 namespace Implab.Components {
4 public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable {
5 enum Commands {
6 Ok = 0,
7 Fail,
8 Init,
9 Start,
10 Stop,
11 Dispose,
12 Last = Dispose
13 }
14
15 class StateMachine {
16 static readonly ExecutionState[,] _transitions;
17
18 static StateMachine() {
19 _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1];
20
21 Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init);
22 Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose);
23
24 Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok);
25 Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail);
26
27 Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start);
28 Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose);
29
30 Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok);
31 Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail);
32 Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop);
33 Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose);
34
35 Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail);
36 Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop);
37 Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose);
38
39 Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail);
40 Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok);
41
42 Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose);
43 }
44
45 static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) {
46 _transitions[(int)s1, (int)cmd] = s2;
47 }
48
49 public ExecutionState State {
50 get;
51 private set;
52 }
53
54 public StateMachine(ExecutionState initial) {
55 State = initial;
56 }
57
58 public bool Move(Commands cmd) {
59 var next = _transitions[(int)State, (int)cmd];
60 if (next == ExecutionState.Undefined)
61 return false;
62 State = next;
63 return true;
64 }
65 }
66
67 IPromise m_pending;
68 Exception m_lastError;
69
70 readonly StateMachine m_stateMachine;
71
72 protected RunnableComponent(bool initialized) {
73 m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created);
74 }
75
76 protected virtual int DisposeTimeout {
77 get {
78 return 10000;
79 }
80 }
81
82 void ThrowInvalidCommand(Commands cmd) {
83 if (m_stateMachine.State == ExecutionState.Disposed)
84 throw new ObjectDisposedException(ToString());
85
86 throw new InvalidOperationException(String.Format("Commnd {0} is not allowed in the state {1}", cmd, m_stateMachine.State));
87 }
88
89 void Move(Commands cmd) {
90 if (!m_stateMachine.Move(cmd))
91 ThrowInvalidCommand(cmd);
92 }
93
94 void Invoke(Commands cmd, Action action) {
95 lock (m_stateMachine)
96 Move(cmd);
97
98 try {
99 action();
100 lock(m_stateMachine)
101 Move(Commands.Ok);
102
103 } catch (Exception err) {
104 lock (m_stateMachine) {
105 Move(Commands.Fail);
106 m_lastError = err;
107 }
108 throw;
109 }
110 }
111
112 IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IDeferred> chain) {
113 IPromise promise = null;
114 IPromise prev;
115
116 var task = new ActionChainTask(action, null, null, true);
117
118 lock (m_stateMachine) {
119 Move(cmd);
120
121 prev = m_pending;
122
123 promise = task.Then(
124 () => {
125 lock(m_stateMachine) {
126 if (m_pending == promise) {
127 Move(Commands.Ok);
128 m_pending = null;
129 }
130 }
131 }, e => {
132 lock(m_stateMachine) {
133 if (m_pending == promise) {
134 Move(Commands.Fail);
135 m_pending = null;
136 m_lastError = e;
137 }
138 }
139 throw new PromiseTransientException(e);
140 }
141 );
142
143 m_pending = promise;
144 }
145
146 if (prev == null)
147 task.Resolve();
148 else
149 chain(prev, task);
150
151 return promise;
152 }
153
154
155 #region IInitializable implementation
156
157 public void Init() {
158 Invoke(Commands.Init, OnInitialize);
159 }
160
161 protected virtual void OnInitialize() {
162 }
163
164 #endregion
165
166 #region IRunnable implementation
167
168 public IPromise Start() {
169 return InvokeAsync(Commands.Start, OnStart, null);
170 }
171
172 protected virtual IPromise OnStart() {
173 return Promise.SUCCESS;
174 }
175
176 public IPromise Stop() {
177 return InvokeAsync(Commands.Stop, OnStop, StopPending).Then(Dispose);
178 }
179
180 protected virtual IPromise OnStop() {
181 return Promise.SUCCESS;
182 }
183
184 /// <summary>
185 /// Stops the current operation if one exists.
186 /// </summary>
187 /// <param name="current">Current.</param>
188 /// <param name="stop">Stop.</param>
189 protected virtual void StopPending(IPromise current, IDeferred stop) {
190 if (current == null) {
191 stop.Resolve();
192 } else {
193 // связваем текущую операцию с операцией остановки
194 current.On(
195 stop.Resolve, // если текущая операция заверщилась, то можно начинать остановку
196 stop.Reject, // если текущая операция дала ошибку - то все плохо, нельзя продолжать
197 e => stop.Resolve() // если текущая отменилась, то можно начинать остановку
198 );
199 // посылаем текущей операции сигнал остановки
200 current.Cancel();
201 }
202 }
203
204 public ExecutionState State {
205 get {
206 return m_stateMachine.State;
207 }
208 }
209
210 public Exception LastError {
211 get {
212 return m_lastError;
213 }
214 }
215
216 #endregion
217
218 #region IDisposable implementation
219
220 public void Dispose() {
221 IPromise pending;
222 lock (m_stateMachine) {
223 if (m_stateMachine.State == ExecutionState.Disposed)
224 return;
225
226 Move(Commands.Dispose);
227
228 GC.SuppressFinalize(this);
229
230 pending = m_pending;
231 m_pending = null;
232 }
233 if (pending != null) {
234 pending.Cancel();
235 pending.Timeout(DisposeTimeout).On(
236 () => Dispose(true, null),
237 err => Dispose(true, err),
238 reason => Dispose(true, new OperationCanceledException("The operation is cancelled", reason))
239 );
240 } else {
241 Dispose(true, m_lastError);
242 }
243 }
244
245 ~RunnableComponent() {
246 Dispose(false, null);
247 }
248
249 #endregion
250
251 protected virtual void Dispose(bool disposing, Exception lastError) {
252
253 }
254
255 }
256 }
257
@@ -0,0 +1,248
1 using System;
2 using System.Collections.Generic;
3
4 namespace Implab.Components {
5 /// <summary>
6 /// Коллекция сервисов, позволяет регистрировать и получать сервисы.
7 /// </summary>
8 public class ServiceLocator: Disposable, IServiceLocator, IServiceProvider {
9 // запись о сервисе
10 struct ServiceEntry : IDisposable {
11 public object service; // сервис
12 public bool shared; // признак того, что сервис НЕ нужно освобождать
13 public Func<object> activator; // активатор сервиса при первом обращении
14 public Action<object> cleanup; // функция для очистки сервиса
15 public List<Type> associated; // ссылки на текущую запись
16 public Type origin; // ссылка на оригинальную запись о сервисе
17
18 #region IDisposable implementation
19
20 public void Dispose() {
21 if (shared)
22 return;
23 if (cleanup != null) {
24 if (service != null)
25 cleanup(service);
26 } else
27 Safe.Dispose(service);
28 }
29
30 #endregion
31 }
32
33 // словарь существующих сервисов
34 readonly Dictionary<Type, ServiceEntry> m_services = new Dictionary<Type,ServiceEntry>();
35
36 /// <summary>
37 /// Получает объект предоставляющий сервис <typeparamref name="T"/>.
38 /// </summary>
39 /// <typeparam name="T">Тип запрашиваемого сервиса</typeparam>
40 /// <returns>Объект, реализующий сервис</returns>
41 /// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception>
42 public T GetService<T>() {
43 object result;
44 if (TryGetService(typeof(T), out result))
45 return (T)result;
46 throw new ApplicationException (String.Format ("{0} doesn't provide {1} service", this, typeof(T)));
47 }
48
49
50 /// <summary>
51 /// Пытается получить указанный сервис, в случае, если компонента не предоставляет требуемый сервис
52 /// не возникает исключений.
53 /// </summary>
54 /// <typeparam name="T">Тип требуемого сервиса.</typeparam>
55 /// <param name="service">Объект реализующий сервис, или <c>default(T)</c> если такового нет.</param>
56 /// <returns><c>true</c> - сервис найден, <c>false</c> - сервис не зарегистрирован.</returns>
57 public bool TryGetService<T>(out T service) {
58 object result;
59 if (TryGetService(typeof(T), out result)) {
60 service = (T)result;
61 return true;
62 }
63 service = default(T);
64 return false;
65 }
66
67 /// <summary>
68 /// Получает объект предоставляющий сервис <paramref name="serviceType"/>
69 /// </summary>
70 /// <param name="serviceType">Тип запрашиваемого сервиса</param>
71 /// <returns>Объект, реализующий сервис</returns>
72 /// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception>
73 public object GetService(Type serviceType) {
74 object result;
75 if (TryGetService(serviceType, out result))
76 return result;
77 throw new ApplicationException (String.Format ("{0} doesn't provide {1} service", this, serviceType));
78 }
79
80 /// <summary>
81 /// Пытается получить требуемый сервис или совместимый с ним.
82 /// </summary>
83 /// <returns><c>true</c>, если сервис был найден, <c>false</c> в противном случае..</returns>
84 /// <param name="serviceType">Тип запрашиваемого сервиса.</param>
85 /// <param name="service">Искомый сервис.</param>
86 public virtual bool TryGetService(Type serviceType, out object service) {
87 Safe.ArgumentNotNull(serviceType, "serviceType");
88 AssertNotDisposed();
89
90 ServiceEntry se;
91 if (!m_services.TryGetValue(serviceType, out se)) {
92 // ищем ближайщий объект, реализующий нужный сервис
93 Type pt = null;
94 foreach (var t in m_services.Keys)
95 if (serviceType.IsAssignableFrom(t) && (pt == null || t.IsAssignableFrom(pt)))
96 pt = t;
97
98 if (pt == null) {
99 // нет нужного сервиса
100 service = null;
101 return false;
102 }
103
104 var pe = m_services[pt];
105
106 // найденная запись может ссылаться на оригинальную запись с сервисом
107 if(pe.origin != null) {
108 pt = pe.origin;
109 pe = m_services[pt];
110 }
111
112 // добавляем список с обратными ссылками
113 if (pe.associated == null)
114 pe.associated = new List<Type>();
115
116 pe.associated.Add(serviceType);
117
118 // обновляем родительскую запись
119 m_services[pt] = pe;
120
121 // создаем запись со ссылкой
122 se = new ServiceEntry {
123 service = pe.service,
124 origin = pt,
125 shared = true // предотвращаем множественные попытки освобождения
126 };
127
128 m_services[serviceType] = se;
129 }
130
131 // запись содержит в себе информацию о сервисе
132 if (se.service != null) {
133 service = se.service;
134 return true;
135 }
136
137 // текущая запись является ссылкой
138 if (se.origin != null) {
139 se.service = GetService(se.origin);
140 m_services[serviceType] = se;
141 service = se.service;
142 return true;
143 }
144
145 // текущая запись не является ссылкой и не имеет информации о сервисе
146 // она должна сожержать информацию об активации
147 if (se.activator != null) {
148 se.service = se.activator();
149
150 m_services[serviceType] = se;
151
152 service = se.service;
153 return true;
154 }
155
156 service = null;
157 return false;
158 }
159
160 /// <summary>
161 /// Регистрирует фабрику для активации сервиса по первому требованию.
162 /// </summary>
163 /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam>
164 /// <param name="activator">Фабрика для создания/получения объекта, предоставляющего сервис.</param>
165 /// <param name = "cleanup">Метод для освобождения экземпляра сервиса, будет вызыван при освобождении сервис-локатора.</param>
166 /// <remarks>При освобождении сервис-локатора, сервисы полученные в результате активации также будут освобождены.</remarks>
167 public void Register<T>(Func<T> activator, Action<T> cleanup) {
168 Safe.ArgumentNotNull(activator, "activator");
169
170 AssertNotDisposed();
171
172 Unregister(typeof(T));
173
174 var serviceEntry = new ServiceEntry();
175 serviceEntry.activator = () => activator();
176 if (cleanup != null)
177 serviceEntry.cleanup = instance => cleanup((T)instance);
178 m_services[typeof(T)] = serviceEntry;
179 }
180
181 public void Register<T>(Func<T> activator) {
182 Register(activator, null);
183 }
184
185 /// <summary>
186 /// Регистрирует объект, предоставляющий сервис.
187 /// </summary>
188 /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam>
189 /// <param name="service">Объект, предоставляющий сервис.</param>
190 /// <exception cref="InvalidOperationException">Указанный сервис уже зарегистрирован.</exception>
191 /// <remarks>Сервис-локатором не управляет временем жизни объекта для зарегистрированного сервиса.</remarks>
192 public void Register<T>(T service) {
193 Register(service, true);
194 }
195
196 /// <summary>
197 /// Регистрирует объект, предоставляющий сервис. Повторная регистрация отменяет уже существующую.
198 /// </summary>
199 /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam>
200 /// <param name="service">Объект, предоставляющий сервис.</param>
201 /// <param name="shared">Признак того, что объект является разделяемым и сервис-локатор не должен его освобождать.</param>
202 public void Register<T>(T service, bool shared) {
203 Safe.ArgumentNotNull(service, "service");
204
205 AssertNotDisposed();
206
207 Unregister(typeof(T));
208
209 m_services[typeof(T)] = new ServiceEntry { service = service, shared = shared };
210 }
211
212 public void Unregister(Type serviceType) {
213 Safe.ArgumentNotNull(serviceType, "serviceType");
214
215 AssertNotDisposed();
216
217 ServiceEntry se;
218 if (m_services.TryGetValue(serviceType, out se)) {
219 if (se.origin != null) {
220 var pe = m_services[se.origin];
221 pe.associated.Remove(serviceType);
222 }
223 // освобождаем ресурсы
224 se.Dispose();
225 m_services.Remove(serviceType);
226
227 // убираем связанные записи
228 if (se.associated != null)
229 foreach (var item in se.associated)
230 m_services.Remove(item);
231 }
232 }
233
234 /// <summary>
235 /// Освобождает зарегистрированные сервисы (которые требуется освобоить).
236 /// </summary>
237 /// <param name="disposing">Призанак того, что нужно освободить ресурсы.</param>
238 protected override void Dispose(bool disposing) {
239 if (disposing) {
240
241 foreach (var entry in m_services.Values)
242 entry.Dispose();
243
244 }
245 base.Dispose(disposing);
246 }
247 }
248 } No newline at end of file
@@ -0,0 +1,41
1 namespace Implab.Diagnostics {
2 public static class Extensions {
3 public static IPromise<T> EndLogicalOperation<T>(this IPromise<T> promise) {
4 Safe.ArgumentNotNull(promise, "promise");
5 var op = TraceContext.Instance.DetachLogicalOperation();
6
7 return promise.On(
8 x => {
9 TraceContext.Instance.EnterLogicalOperation(op,true);
10 TraceLog.TraceInformation("promise = {0}", x);
11 TraceLog.EndLogicalOperation();
12 TraceContext.Instance.Leave();
13 },
14 err =>{
15 TraceContext.Instance.EnterLogicalOperation(op,true);
16 TraceLog.TraceError("promise died {0}", err);
17 TraceLog.EndLogicalOperation();
18 TraceContext.Instance.Leave();
19 },
20 reason => {
21 TraceContext.Instance.EnterLogicalOperation(op,true);
22 TraceLog.TraceInformation("promise cancelled {0}", reason == null ? "<no-reason>" : reason.Message);
23 TraceLog.EndLogicalOperation();
24 TraceContext.Instance.Leave();
25 }
26 );
27 }
28
29 public static IPromise EndLogicalOperation(this IPromise promise) {
30 Safe.ArgumentNotNull(promise, "promise");
31 var op = TraceContext.Instance.DetachLogicalOperation();
32
33 return promise.On(() => {
34 TraceContext.Instance.EnterLogicalOperation(op,true);
35 TraceLog.EndLogicalOperation();
36 TraceContext.Instance.Leave();
37 }, PromiseEventType.All);
38 }
39 }
40 }
41
@@ -0,0 +1,8
1 using System;
2
3 namespace Implab.Diagnostics {
4 public interface ILogWriter<in TEvent> {
5 void Write(LogEventArgs args, TEvent entry);
6 }
7 }
8
@@ -0,0 +1,86
1 using System;
2 using System.Collections.Generic;
3 using Implab.Components;
4
5 namespace Implab.Diagnostics {
6 public abstract class ListenerBase : ServiceLocator, ILogWriter<object>, ILogWriter<TraceEvent> {
7
8 readonly Dictionary<object, Action> m_subscriptions = new Dictionary<object, Action>();
9
10 protected ListenerBase() {
11 Register(this);
12 }
13
14 public void Subscribe(Type eventType) {
15 if (eventType == null)
16 throw new ArgumentNullException("eventType");
17 GetType().GetMethod("Subscribe", new Type[0]).MakeGenericMethod(eventType).Invoke(this, null);
18 }
19
20 public void Subscribe<TEvent>() {
21 Subscribe<TEvent>(LogChannel<TEvent>.Default);
22 }
23
24 public void Subscribe<TEvent>(LogChannel<TEvent> channel) {
25 if (channel == null)
26 throw new ArgumentNullException("channel");
27
28 lock (m_subscriptions) {
29 AssertNotDisposed();
30 if (m_subscriptions.ContainsKey(channel))
31 return;
32
33 var writer = GetService<ILogWriter<TEvent>>();
34
35 EventHandler<LogEventArgs<TEvent>> handler = (sender, args) => writer.Write(args,args.Value);
36
37 channel.Events += handler;
38
39 Action unsubscribe = () => {
40 channel.Events -= handler;
41 };
42
43 m_subscriptions.Add(channel, unsubscribe);
44 }
45 }
46
47 public void Unsubscribe<TEvent>(LogChannel<TEvent> channel) {
48 if (channel == null)
49 throw new ArgumentNullException("channel");
50
51 lock (m_subscriptions) {
52 Action subscription;
53 if (m_subscriptions.TryGetValue(channel, out subscription)) {
54 subscription();
55 m_subscriptions.Remove(channel);
56 }
57 }
58 }
59
60 public void UnsubscribeAll() {
61 lock (m_subscriptions) {
62 foreach (var subscription in m_subscriptions.Values)
63 subscription();
64 m_subscriptions.Clear();
65 }
66 }
67
68 #region ILogWriter implementation
69 public abstract void Write(LogEventArgs args, object entry);
70 #endregion
71
72 #region ILogWriter implementation
73 public virtual void Write(LogEventArgs args, TraceEvent entry) {
74 Write(args, (object)entry);
75 }
76 #endregion
77
78
79 protected override void Dispose(bool disposing) {
80 base.Dispose(disposing);
81 if (disposing) {
82 UnsubscribeAll();
83 }
84 }
85 }
86 }
@@ -0,0 +1,29
1 using System;
2
3 namespace Implab.Diagnostics {
4 public class LogEventArgs : EventArgs {
5 public string ChannelName {
6 get;
7 private set;
8 }
9 public int ThreadId {
10 get;
11 private set;
12 }
13 public LogicalOperation Operation {
14 get;
15 private set;
16 }
17 public int OperationTimeOffset {
18 get;
19 private set;
20 }
21 public LogEventArgs(string channelName, int threadId, LogicalOperation operation, int timeOffset) {
22 ChannelName = channelName;
23 ThreadId = threadId;
24 Operation = operation;
25 OperationTimeOffset = timeOffset;
26 }
27 }
28 }
29
@@ -0,0 +1,13
1 namespace Implab.Diagnostics {
2 public class LogEventArgs<TEvent> : LogEventArgs {
3 public TEvent Value {
4 get;
5 private set;
6 }
7
8 public LogEventArgs(TEvent value,string channelName, int threadId, LogicalOperation operation, int timeOffset) : base(channelName, threadId, operation, timeOffset) {
9 Value = value;
10 }
11 }
12 }
13
@@ -0,0 +1,65
1 namespace Implab.Diagnostics {
2 struct OperationContext {
3 public readonly static OperationContext EMPTY = new OperationContext(LogicalOperation.EMPTY, false);
4
5 LogicalOperation m_initial;
6 LogicalOperation m_current;
7 bool m_ownership;
8
9 public OperationContext(LogicalOperation operation, bool ownership) {
10 Safe.ArgumentNotNull(operation, "operation");
11
12 m_initial = operation;
13 m_current = operation;
14 m_ownership = ownership;
15 }
16
17 public LogicalOperation CurrentOperation {
18 get { return m_current; }
19 }
20
21 public void BeginLogicalOperation(string name) {
22 m_current = new LogicalOperation(name, m_current);
23 }
24
25 public LogicalOperation DetachLogicalOperation() {
26 var detached = m_current;
27 if (m_current != LogicalOperation.EMPTY) {
28 if (m_current != m_initial)
29 m_current = m_current.Parent;
30 else if (m_ownership)
31 m_current = LogicalOperation.EMPTY;
32 else {
33 TraceLog.TraceWarning("DetachLogicalOperation can't be applied in the current context");
34 detached = LogicalOperation.EMPTY;
35 }
36 } else {
37 TraceLog.TraceWarning("DetachLogicalOperation can't be applied in the current context");
38 }
39
40 return detached;
41 }
42
43 public LogicalOperation EndLogicalOperation() {
44 var current = m_current;
45 if (m_current != LogicalOperation.EMPTY && (m_current != m_initial || m_ownership)) {
46 m_current = m_current.Parent;
47 if (current == m_initial) {
48 // we have complete the owned operation
49 m_initial = m_current;
50 m_ownership = false;
51 }
52 } else {
53 TraceLog.TraceWarning("EndLogicalOperation can't be applied in the current context");
54 }
55 return current;
56 }
57
58 public void Leave() {
59
60 if ((m_ownership && m_current != LogicalOperation.EMPTY) || (!m_ownership && m_current != m_initial) )
61 TraceLog.TraceWarning("Trying to leave unfinished logical operation {0}", m_current.Name);
62 }
63 }
64 }
65
@@ -0,0 +1,23
1 using System.Collections.Generic;
2 using System.Linq;
3 using Implab.Automaton;
4
5 namespace Implab.Formats {
6 public class ByteAlphabet : IndexedAlphabetBase<byte> {
7
8 #region implemented abstract members of IndexedAlphabetBase
9
10 public override int GetSymbolIndex(byte symbol) {
11 return (int)symbol;
12 }
13
14 public IEnumerable<byte> InputSymbols {
15 get {
16 return Enumerable.Range(byte.MinValue, byte.MaxValue).Cast<byte>();
17 }
18 }
19
20 #endregion
21 }
22 }
23
@@ -0,0 +1,16
1 using System.Collections.Generic;
2 using System.Linq;
3 using Implab.Automaton;
4
5 namespace Implab.Formats {
6 public class CharAlphabet: IndexedAlphabetBase<char> {
7
8 public override int GetSymbolIndex(char symbol) {
9 return symbol;
10 }
11
12 public IEnumerable<char> InputSymbols {
13 get { return Enumerable.Range(char.MinValue, char.MaxValue).Cast<char>(); }
14 }
15 }
16 }
@@ -0,0 +1,99
1 using Implab;
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
5 using Implab.Automaton;
6 using Implab.Automaton.RegularExpressions;
7
8 namespace Implab.Formats {
9 /// <summary>
10 /// Базовый абстрактный класс. Грамматика, позволяет формулировать выражения над алфавитом типа <c>char</c>.
11 /// </summary>
12 public abstract class Grammar<TSymbol> {
13
14 protected abstract IAlphabetBuilder<TSymbol> AlphabetBuilder {
15 get;
16 }
17
18 protected SymbolToken UnclassifiedToken() {
19 return new SymbolToken(AutomatonConst.UNCLASSIFIED_INPUT);
20 }
21
22 protected void DefineAlphabet(IEnumerable<TSymbol> alphabet) {
23 Safe.ArgumentNotNull(alphabet, "alphabet");
24
25 foreach (var ch in alphabet)
26 AlphabetBuilder.DefineSymbol(ch);
27 }
28
29 protected Token SymbolToken(TSymbol symbol) {
30 return Token.New(TranslateOrAdd(symbol));
31 }
32
33 protected Token SymbolToken(IEnumerable<TSymbol> symbols) {
34 Safe.ArgumentNotNull(symbols, "symbols");
35
36 return Token.New(TranslateOrAdd(symbols).ToArray());
37 }
38
39 protected Token SymbolSetToken(params TSymbol[] set) {
40 return SymbolToken(set);
41 }
42
43 int TranslateOrAdd(TSymbol ch) {
44 var t = AlphabetBuilder.Translate(ch);
45 if (t == AutomatonConst.UNCLASSIFIED_INPUT)
46 t = AlphabetBuilder.DefineSymbol(ch);
47 return t;
48 }
49
50 IEnumerable<int> TranslateOrAdd(IEnumerable<TSymbol> symbols) {
51 return symbols.Distinct().Select(TranslateOrAdd);
52 }
53
54 int TranslateOrDie(TSymbol ch) {
55 var t = AlphabetBuilder.Translate(ch);
56 if (t == AutomatonConst.UNCLASSIFIED_INPUT)
57 throw new ApplicationException(String.Format("Symbol '{0}' is UNCLASSIFIED", ch));
58 return t;
59 }
60
61 IEnumerable<int> TranslateOrDie(IEnumerable<TSymbol> symbols) {
62 return symbols.Distinct().Select(TranslateOrDie);
63 }
64
65 protected Token SymbolTokenExcept(IEnumerable<TSymbol> symbols) {
66 Safe.ArgumentNotNull(symbols, "symbols");
67
68 return Token.New( Enumerable.Range(0, AlphabetBuilder.Count).Except(TranslateOrDie(symbols)).ToArray() );
69 }
70
71 protected abstract IndexedAlphabetBase<TSymbol> CreateAlphabet();
72
73 protected ScannerContext<TTag> BuildScannerContext<TTag>(Token regexp) {
74
75 var dfa = new RegularDFA<TSymbol, TTag>(AlphabetBuilder);
76
77 var visitor = new RegularExpressionVisitor<TTag>(dfa);
78 regexp.Accept(visitor);
79 visitor.BuildDFA();
80
81 if (dfa.IsFinalState(dfa.InitialState))
82 throw new ApplicationException("The specified language contains empty token");
83
84 var ab = CreateAlphabet();
85 var optimal = dfa.Optimize(ab);
86
87 return new ScannerContext<TTag>(
88 optimal.CreateTransitionTable(),
89 optimal.CreateFinalStateTable(),
90 optimal.CreateTagTable(),
91 optimal.InitialState,
92 ab.GetTranslationMap()
93 );
94 }
95
96 }
97
98
99 }
@@ -0,0 +1,11
1 namespace Implab.Formats.JSON {
2 /// <summary>
3 /// internal
4 /// </summary>
5 enum JSONElementContext {
6 None,
7 Object,
8 Array,
9 Closed
10 }
11 }
@@ -0,0 +1,28
1 namespace Implab.Formats.JSON {
2 /// <summary>
3 /// Тип элемента на котором находится парсер
4 /// </summary>
5 public enum JSONElementType {
6 None,
7 /// <summary>
8 /// Начало объекта
9 /// </summary>
10 BeginObject,
11 /// <summary>
12 /// Конец объекта
13 /// </summary>
14 EndObject,
15 /// <summary>
16 /// Начало массива
17 /// </summary>
18 BeginArray,
19 /// <summary>
20 /// Конец массива
21 /// </summary>
22 EndArray,
23 /// <summary>
24 /// Простое значение
25 /// </summary>
26 Value
27 }
28 }
@@ -0,0 +1,121
1 using System.Linq;
2 using Implab.Automaton.RegularExpressions;
3 using System;
4 using Implab.Automaton;
5 using Implab.Components;
6
7 namespace Implab.Formats.JSON {
8 class JSONGrammar : Grammar<char> {
9 public enum TokenType {
10 None,
11 BeginObject,
12 EndObject,
13 BeginArray,
14 EndArray,
15 String,
16 Number,
17 Literal,
18 NameSeparator,
19 ValueSeparator,
20 Whitespace,
21
22 StringBound,
23 EscapedChar,
24 UnescapedChar,
25 EscapedUnicode
26 }
27
28 static LazyAndWeak<JSONGrammar> _instance = new LazyAndWeak<JSONGrammar>(() => new JSONGrammar());
29
30 public static JSONGrammar Instance {
31 get { return _instance.Value; }
32 }
33
34 readonly ScannerContext<TokenType> m_jsonExpression;
35 readonly ScannerContext<TokenType> m_stringExpression;
36 readonly CharAlphabet m_defaultAlphabet = new CharAlphabet();
37
38 public JSONGrammar() {
39 DefineAlphabet(Enumerable.Range(0, 0x20).Select(x => (char)x));
40 var hexDigit = SymbolRangeToken('a','f').Or(SymbolRangeToken('A','F')).Or(SymbolRangeToken('0','9'));
41 var digit9 = SymbolRangeToken('1', '9');
42 var zero = SymbolToken('0');
43 var digit = zero.Or(digit9);
44 var dot = SymbolToken('.');
45 var minus = SymbolToken('-');
46 var sign = SymbolSetToken('-', '+');
47 var expSign = SymbolSetToken('e', 'E');
48 var letters = SymbolRangeToken('a', 'z');
49 var integer = zero.Or(digit9.Cat(digit.EClosure()));
50 var frac = dot.Cat(digit.Closure());
51 var exp = expSign.Cat(sign.Optional()).Cat(digit.Closure());
52 var quote = SymbolToken('"');
53 var backSlash = SymbolToken('\\');
54 var specialEscapeChars = SymbolSetToken('\\', '"', '/', 'b', 'f', 't', 'n', 'r');
55 var unicodeEspace = SymbolToken('u').Cat(hexDigit.Repeat(4));
56 var whitespace = SymbolSetToken('\n', '\r', '\t', ' ').EClosure();
57 var beginObject = whitespace.Cat(SymbolToken('{')).Cat(whitespace);
58 var endObject = whitespace.Cat(SymbolToken('}')).Cat(whitespace);
59 var beginArray = whitespace.Cat(SymbolToken('[')).Cat(whitespace);
60 var endArray = whitespace.Cat(SymbolToken(']')).Cat(whitespace);
61 var nameSep = whitespace.Cat(SymbolToken(':')).Cat(whitespace);
62 var valueSep = whitespace.Cat(SymbolToken(',')).Cat(whitespace);
63
64 var number = minus.Optional().Cat(integer).Cat(frac.Optional()).Cat(exp.Optional());
65 var literal = letters.Closure();
66 var unescaped = SymbolTokenExcept(Enumerable.Range(0, 0x20).Union(new int[] { '\\', '"' }).Select(x => (char)x));
67
68 var jsonExpression =
69 number.Tag(TokenType.Number)
70 .Or(literal.Tag(TokenType.Literal))
71 .Or(quote.Tag(TokenType.StringBound))
72 .Or(beginObject.Tag(TokenType.BeginObject))
73 .Or(endObject.Tag(TokenType.EndObject))
74 .Or(beginArray.Tag(TokenType.BeginArray))
75 .Or(endArray.Tag(TokenType.EndArray))
76 .Or(nameSep.Tag(TokenType.NameSeparator))
77 .Or(valueSep.Tag(TokenType.ValueSeparator))
78 .Or(SymbolSetToken('\n', '\r', '\t', ' ').Closure().Tag(TokenType.Whitespace));
79
80
81 var jsonStringExpression =
82 quote.Tag(TokenType.StringBound)
83 .Or(backSlash.Cat(specialEscapeChars).Tag(TokenType.EscapedChar))
84 .Or(backSlash.Cat(unicodeEspace).Tag(TokenType.EscapedUnicode))
85 .Or(unescaped.Closure().Tag(TokenType.UnescapedChar));
86
87
88 m_jsonExpression = BuildScannerContext<TokenType>(jsonExpression);
89 m_stringExpression = BuildScannerContext<TokenType>(jsonStringExpression);
90
91
92 }
93
94 protected override IAlphabetBuilder<char> AlphabetBuilder {
95 get {
96 return m_defaultAlphabet;
97 }
98 }
99
100 public ScannerContext<TokenType> JsonExpression {
101 get {
102 return m_jsonExpression;
103 }
104 }
105
106 public ScannerContext<TokenType> JsonStringExpression {
107 get {
108 return m_stringExpression;
109 }
110 }
111
112 Token SymbolRangeToken(char start, char stop) {
113 return SymbolToken(Enumerable.Range(start, stop - start + 1).Select(x => (char)x));
114 }
115
116 protected override IndexedAlphabetBase<char> CreateAlphabet() {
117 return new CharAlphabet();
118 }
119
120 }
121 }
@@ -0,0 +1,293
1 using System;
2 using System.Diagnostics;
3 using System.IO;
4 using Implab.Automaton;
5 using Implab.Automaton.RegularExpressions;
6 using System.Linq;
7 using Implab.Components;
8 using System.Collections.Generic;
9
10 namespace Implab.Formats.JSON {
11 /// <summary>
12 /// Pull парсер JSON данных.
13 /// </summary>
14 /// <remarks>
15 /// Следует отметить отдельную интерпретацию свойства <see cref="Level"/>,
16 /// оно означает текущий уровень вложенности объектов, однако закрывающий
17 /// элемент объекта и массива имеет уровень меньше, чем сам объект.
18 /// <code>
19 /// { // Level = 1
20 /// "name" : "Peter", // Level = 1
21 /// "address" : { // Level = 2
22 /// city : "Stern" // Level = 2
23 /// } // Level = 1
24 /// } // Level = 0
25 /// </code>
26 /// </remarks>
27 public class JSONParser : Disposable {
28
29 enum MemberContext {
30 MemberName,
31 MemberValue
32 }
33
34 #region Parser rules
35 struct ParserContext {
36 readonly int[,] m_dfa;
37 int m_state;
38
39 readonly JSONElementContext m_elementContext;
40
41 public ParserContext(int[,] dfa, int state, JSONElementContext context) {
42 m_dfa = dfa;
43 m_state = state;
44 m_elementContext = context;
45 }
46
47 public bool Move(JsonTokenType token) {
48 var next = m_dfa[m_state, (int)token];
49 if (next == AutomatonConst.UNREACHABLE_STATE)
50 return false;
51 m_state = next;
52 return true;
53 }
54
55 public JSONElementContext ElementContext {
56 get { return m_elementContext; }
57 }
58 }
59
60 static readonly ParserContext _jsonContext;
61 static readonly ParserContext _objectContext;
62 static readonly ParserContext _arrayContext;
63
64 static JSONParser() {
65
66 var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String);
67 var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression);
68
69 var objectExpression = memberExpression
70 .Cat(
71 MakeToken(JsonTokenType.ValueSeparator)
72 .Cat(memberExpression)
73 .EClosure()
74 )
75 .Optional()
76 .Cat(MakeToken(JsonTokenType.EndObject))
77 .End();
78
79 var arrayExpression = valueExpression
80 .Cat(
81 MakeToken(JsonTokenType.ValueSeparator)
82 .Cat(valueExpression)
83 .EClosure()
84 )
85 .Optional()
86 .Cat(MakeToken(JsonTokenType.EndArray))
87 .End();
88
89 var jsonExpression = valueExpression.End();
90
91 _jsonContext = CreateParserContext(jsonExpression, JSONElementContext.None);
92 _objectContext = CreateParserContext(objectExpression, JSONElementContext.Object);
93 _arrayContext = CreateParserContext(arrayExpression, JSONElementContext.Array);
94 }
95
96 static Token MakeToken(params JsonTokenType[] input) {
97 return Token.New( input.Select(t => (int)t).ToArray() );
98 }
99
100 static ParserContext CreateParserContext(Token expr, JSONElementContext context) {
101
102 var dfa = new DFATable();
103 var builder = new RegularExpressionVisitor(dfa);
104 expr.Accept(builder);
105 builder.BuildDFA();
106
107 return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context);
108 }
109
110 #endregion
111
112 readonly JSONScanner m_scanner;
113 MemberContext m_memberContext;
114
115 JSONElementType m_elementType;
116 object m_elementValue;
117 string m_memberName = String.Empty;
118
119 Stack<ParserContext> m_stack = new Stack<ParserContext>();
120 ParserContext m_context = _jsonContext;
121
122 /// <summary>
123 /// Создает новый парсер на основе строки, содержащей JSON
124 /// </summary>
125 /// <param name="text"></param>
126 public JSONParser(string text) {
127 Safe.ArgumentNotEmpty(text, "text");
128 m_scanner = new JSONScanner(text);
129 }
130
131 /// <summary>
132 /// Создает новый экземпляр парсера, на основе текстового потока.
133 /// </summary>
134 /// <param name="reader">Текстовый поток.</param>
135 public JSONParser(TextReader reader) {
136 Safe.ArgumentNotNull(reader, "reader");
137 m_scanner = new JSONScanner(reader);
138 }
139
140 public int Level {
141 get { return m_stack.Count; }
142 }
143
144 /// <summary>
145 /// Тип текущего элемента на котором стоит парсер.
146 /// </summary>
147 public JSONElementType ElementType {
148 get { return m_elementType; }
149 }
150
151 /// <summary>
152 /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда
153 /// пустая строка.
154 /// </summary>
155 public string ElementName {
156 get { return m_memberName; }
157 }
158
159 /// <summary>
160 /// Значение элемента. Только для элементов типа <see cref="JSONElementType.Value"/>, для остальных <c>null</c>
161 /// </summary>
162 public object ElementValue {
163 get { return m_elementValue; }
164 }
165
166 /// <summary>
167 /// Читает слеюудущий объект из потока
168 /// </summary>
169 /// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns>
170 public bool Read() {
171 object tokenValue;
172 JsonTokenType tokenType;
173
174 m_memberName = String.Empty;
175
176 while (m_scanner.ReadToken(out tokenValue, out tokenType)) {
177 if(!m_context.Move(tokenType))
178 UnexpectedToken(tokenValue, tokenType);
179
180 switch (tokenType) {
181 case JsonTokenType.BeginObject:
182 m_stack.Push(m_context);
183 m_context = _objectContext;
184
185 m_elementValue = null;
186 m_memberContext = MemberContext.MemberName;
187 m_elementType = JSONElementType.BeginObject;
188 return true;
189 case JsonTokenType.EndObject:
190 if (m_stack.Count == 0)
191 UnexpectedToken(tokenValue, tokenType);
192 m_context = m_stack.Pop();
193
194 m_elementValue = null;
195 m_elementType = JSONElementType.EndObject;
196 return true;
197 case JsonTokenType.BeginArray:
198 m_stack.Push(m_context);
199 m_context = _arrayContext;
200
201 m_elementValue = null;
202 m_memberContext = MemberContext.MemberValue;
203 m_elementType = JSONElementType.BeginArray;
204 return true;
205 case JsonTokenType.EndArray:
206 if (m_stack.Count == 0)
207 UnexpectedToken(tokenValue, tokenType);
208 m_context = m_stack.Pop();
209
210 m_elementValue = null;
211 m_elementType = JSONElementType.EndArray;
212 return true;
213 case JsonTokenType.String:
214 if (m_memberContext == MemberContext.MemberName) {
215 m_memberName = (string)tokenValue;
216 break;
217 }
218 m_elementType = JSONElementType.Value;
219 m_elementValue = tokenValue;
220 return true;
221 case JsonTokenType.Number:
222 m_elementType = JSONElementType.Value;
223 m_elementValue = tokenValue;
224 return true;
225 case JsonTokenType.Literal:
226 m_elementType = JSONElementType.Value;
227 m_elementValue = ParseLiteral((string)tokenValue);
228 return true;
229 case JsonTokenType.NameSeparator:
230 m_memberContext = MemberContext.MemberValue;
231 break;
232 case JsonTokenType.ValueSeparator:
233 m_memberContext = m_context.ElementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue;
234 break;
235 default:
236 UnexpectedToken(tokenValue, tokenType);
237 break;
238 }
239 }
240 if (m_context.ElementContext != JSONElementContext.None)
241 throw new ParserException("Unexpedted end of data");
242
243 EOF = true;
244
245 return false;
246 }
247
248 object ParseLiteral(string literal) {
249 switch (literal) {
250 case "null":
251 return null;
252 case "false":
253 return false;
254 case "true":
255 return true;
256 default:
257 UnexpectedToken(literal, JsonTokenType.Literal);
258 return null; // avoid compliler error
259 }
260 }
261
262 void UnexpectedToken(object value, JsonTokenType tokenType) {
263 throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value));
264 }
265
266
267 /// <summary>
268 /// Признак конца потока
269 /// </summary>
270 public bool EOF {
271 get;
272 private set;
273 }
274
275 protected override void Dispose(bool disposing) {
276 if (disposing)
277 Safe.Dispose(m_scanner);
278 }
279
280 /// <summary>
281 /// Переходит в конец текущего объекта.
282 /// </summary>
283 public void SeekElementEnd() {
284 var level = Level - 1;
285
286 Debug.Assert(level >= 0);
287
288 while (Level != level)
289 Read();
290 }
291 }
292
293 }
@@ -0,0 +1,109
1 using System;
2 using System.Globalization;
3 using Implab.Automaton;
4 using System.Text;
5 using Implab.Components;
6 using System.IO;
7
8 namespace Implab.Formats.JSON {
9 /// <summary>
10 /// Сканнер (лексер), разбивающий поток символов на токены JSON.
11 /// </summary>
12 public class JSONScanner : Disposable {
13 readonly StringBuilder m_builder = new StringBuilder();
14
15 readonly ScannerContext<JSONGrammar.TokenType> m_jsonContext = JSONGrammar.Instance.JsonExpression;
16 readonly ScannerContext<JSONGrammar.TokenType> m_stringContext = JSONGrammar.Instance.JsonStringExpression;
17
18
19 readonly TextScanner m_scanner;
20
21 /// <summary>
22 /// Создает новый экземпляр сканнера
23 /// </summary>
24 public JSONScanner(string text) {
25 Safe.ArgumentNotEmpty(text, "text");
26
27 m_scanner = new StringScanner(text);
28 }
29
30 public JSONScanner(TextReader reader, int bufferMax, int chunkSize) {
31 Safe.ArgumentNotNull(reader, "reader");
32
33 m_scanner = new ReaderScanner(reader, bufferMax, chunkSize);
34 }
35
36 public JSONScanner(TextReader reader) : this(reader, 1024*1024, 1024){
37 }
38
39 /// <summary>
40 /// Читает следующий лексический элемент из входных данных.
41 /// </summary>
42 /// <param name="tokenValue">Возвращает значение прочитанного токена.</param>
43 /// <param name="tokenType">Возвращает тип прочитанного токена.</param>
44 /// <returns><c>true</c> - чтение произведено успешно. <c>false</c> - достигнут конец входных данных</returns>
45 /// <remarks>В случе если токен не распознается, возникает исключение. Значения токенов обрабатываются, т.е.
46 /// в строках обрабатываются экранированные символы, числа становтся типа double.</remarks>
47 public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) {
48 JSONGrammar.TokenType[] tag;
49 while (m_jsonContext.Execute(m_scanner, out tag)) {
50 switch (tag[0]) {
51 case JSONGrammar.TokenType.StringBound:
52 tokenValue = ReadString();
53 tokenType = JsonTokenType.String;
54 break;
55 case JSONGrammar.TokenType.Number:
56 tokenValue = Double.Parse(m_scanner.GetTokenValue(), CultureInfo.InvariantCulture);
57 tokenType = JsonTokenType.Number;
58 break;
59 case JSONGrammar.TokenType.Whitespace:
60 continue;
61 default:
62 tokenType = (JsonTokenType)tag[0];
63 tokenValue = m_scanner.GetTokenValue();
64 break;
65 }
66 return true;
67 }
68 tokenValue = null;
69 tokenType = JsonTokenType.None;
70 return false;
71 }
72
73 string ReadString() {
74 int pos = 0;
75 var buf = new char[6]; // the buffer for unescaping chars
76
77 JSONGrammar.TokenType[] tag;
78 m_builder.Clear();
79
80 while (m_stringContext.Execute(m_scanner, out tag)) {
81 switch (tag[0]) {
82 case JSONGrammar.TokenType.StringBound:
83 return m_builder.ToString();
84 case JSONGrammar.TokenType.UnescapedChar:
85 m_scanner.CopyTokenTo(m_builder);
86 break;
87 case JSONGrammar.TokenType.EscapedUnicode: // \xXXXX - unicode escape sequence
88 m_scanner.CopyTokenTo(buf, 0);
89 m_builder.Append(StringTranslator.TranslateHexUnicode(buf, 2));
90 pos++;
91 break;
92 case JSONGrammar.TokenType.EscapedChar: // \t - escape sequence
93 m_scanner.CopyTokenTo(buf, 0);
94 m_builder.Append(StringTranslator.TranslateEscapedChar(buf[1]));
95 break;
96 }
97
98 }
99
100 throw new ParserException("Unexpected end of data");
101 }
102
103 protected override void Dispose(bool disposing) {
104 if (disposing)
105 Safe.Dispose(m_scanner);
106 base.Dispose(disposing);
107 }
108 }
109 }
@@ -0,0 +1,319
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Globalization;
5 using System.Diagnostics;
6
7 namespace Implab.Formats.JSON {
8 public class JSONWriter {
9 struct Context {
10 public bool needComma;
11 public JSONElementContext element;
12 }
13 Stack<Context> m_contextStack = new Stack<Context>();
14 Context m_context;
15
16 const int BUFFER_SIZE = 64;
17
18 TextWriter m_writer;
19 readonly bool m_indent = true;
20 readonly int m_indentSize = 4;
21 readonly char[] m_buffer = new char[BUFFER_SIZE];
22 int m_bufferPos;
23
24 static readonly char [] _hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
25 static readonly char [] _escapeBKS,
26 _escapeFWD,
27 _escapeCR,
28 _escapeNL,
29 _escapeTAB,
30 _escapeBSLASH,
31 _escapeQ;
32
33 static JSONWriter() {
34 _escapeBKS = "\\b".ToCharArray();
35 _escapeFWD = "\\f".ToCharArray();
36 _escapeCR = "\\r".ToCharArray();
37 _escapeNL = "\\n".ToCharArray();
38 _escapeTAB = "\\t".ToCharArray();
39 _escapeBSLASH = "\\\\".ToCharArray();
40 _escapeQ = "\\\"".ToCharArray();
41 }
42
43 public JSONWriter(TextWriter writer) {
44 Safe.ArgumentNotNull(writer, "writer");
45 m_writer = writer;
46 }
47
48 public JSONWriter(TextWriter writer, bool indent) {
49 Safe.ArgumentNotNull(writer, "writer");
50
51 m_writer = writer;
52 m_indent = indent;
53 }
54
55 void WriteIndent() {
56 if (m_indent) {
57 var indent = new char[m_contextStack.Count * m_indentSize + 1];
58 indent[0] = '\n';
59 for (int i = 1; i < indent.Length; i++)
60 indent[i] = ' ';
61 m_writer.Write(new String(indent));
62 } else {
63 m_writer.Write(' ');
64 }
65 }
66
67 void WriteMemberName(string name) {
68 Safe.ArgumentNotEmpty(name, "name");
69 if (m_context.element != JSONElementContext.Object)
70 OperationNotApplicable("WriteMember");
71 if (m_context.needComma)
72 m_writer.Write(",");
73
74 WriteIndent();
75 m_context.needComma = true;
76 Write(name);
77 m_writer.Write(" : ");
78 }
79
80 public void WriteValue(string name, string value) {
81 WriteMemberName(name);
82 Write(value);
83 }
84
85 public void WriteValue(string name, bool value) {
86 WriteMemberName(name);
87 Write(value);
88 }
89
90 public void WriteValue(string name, double value) {
91 WriteMemberName(name);
92 Write(value);
93 }
94
95 public void WriteValue(string value) {
96 if (m_context.element == JSONElementContext.Array) {
97
98 if (m_context.needComma)
99 m_writer.Write(",");
100 WriteIndent();
101 m_context.needComma = true;
102
103 Write(value);
104 } else if (m_context.element == JSONElementContext.None) {
105 Write(value);
106 m_context.element = JSONElementContext.Closed;
107 } else {
108 OperationNotApplicable("WriteValue");
109 }
110 }
111
112 public void WriteValue(bool value) {
113 if (m_context.element == JSONElementContext.Array) {
114
115 if (m_context.needComma)
116 m_writer.Write(",");
117 WriteIndent();
118 m_context.needComma = true;
119
120 Write(value);
121 } else if (m_context.element == JSONElementContext.None) {
122 Write(value);
123 m_context.element = JSONElementContext.Closed;
124 } else {
125 OperationNotApplicable("WriteValue");
126 }
127 }
128
129 public void WriteValue(double value) {
130 if (m_context.element == JSONElementContext.Array) {
131
132 if (m_context.needComma)
133 m_writer.Write(",");
134 WriteIndent();
135 m_context.needComma = true;
136
137 Write(value);
138 } else if (m_context.element == JSONElementContext.None) {
139 Write(value);
140 m_context.element = JSONElementContext.Closed;
141 } else {
142 OperationNotApplicable("WriteValue");
143 }
144 }
145
146 public void BeginObject() {
147 if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array)
148 OperationNotApplicable("BeginObject");
149 if (m_context.needComma)
150 m_writer.Write(",");
151
152 WriteIndent();
153
154 m_context.needComma = true;
155
156 m_contextStack.Push(m_context);
157
158 m_context = new Context { element = JSONElementContext.Object, needComma = false };
159 m_writer.Write("{");
160 }
161
162 public void BeginObject(string name) {
163 WriteMemberName(name);
164
165 m_contextStack.Push(m_context);
166
167 m_context = new Context { element = JSONElementContext.Object, needComma = false };
168 m_writer.Write("{");
169 }
170
171 public void EndObject() {
172 if (m_context.element != JSONElementContext.Object)
173 OperationNotApplicable("EndObject");
174
175 m_context = m_contextStack.Pop();
176 if (m_contextStack.Count == 0)
177 m_context.element = JSONElementContext.Closed;
178 WriteIndent();
179 m_writer.Write("}");
180 }
181
182 public void BeginArray() {
183 if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array)
184 throw new InvalidOperationException();
185 if (m_context.needComma) {
186 m_writer.Write(",");
187
188 }
189 m_context.needComma = true;
190
191 WriteIndent();
192 m_contextStack.Push(m_context);
193 m_context = new Context { element = JSONElementContext.Array, needComma = false };
194 m_writer.Write("[");
195 }
196
197 public void BeginArray(string name) {
198 WriteMemberName(name);
199
200 m_contextStack.Push(m_context);
201
202 m_context = new Context { element = JSONElementContext.Array, needComma = false };
203 m_writer.Write("[");
204 }
205
206 public void EndArray() {
207 if (m_context.element != JSONElementContext.Array)
208 OperationNotApplicable("EndArray");
209
210 m_context = m_contextStack.Pop();
211 if (m_contextStack.Count == 0)
212 m_context.element = JSONElementContext.Closed;
213 WriteIndent();
214 m_writer.Write("]");
215 }
216
217 void Write(bool value) {
218 m_writer.Write(value ? "true" : "false");
219 }
220
221 void FlushBuffer() {
222 if (m_bufferPos > 0) {
223 m_writer.Write(m_buffer, 0, m_bufferPos);
224 m_bufferPos = 0;
225 }
226 }
227
228 void Write(string value) {
229 if (value == null) {
230 m_writer.Write("null");
231 return;
232 }
233
234 Debug.Assert(m_bufferPos == 0);
235
236 var chars = value.ToCharArray();
237 m_buffer[m_bufferPos++] = '"';
238
239 // Analysis disable once ForCanBeConvertedToForeach
240 for (int i = 0; i < chars.Length; i++) {
241 var ch = chars[i];
242
243 char[] escapeSeq;
244
245 switch (ch) {
246 case '\b':
247 escapeSeq = _escapeBKS;
248 break;
249 case '\f':
250 escapeSeq = _escapeFWD;
251 break;
252 case '\r':
253 escapeSeq = _escapeCR;
254 break;
255 case '\n':
256 escapeSeq = _escapeNL;
257 break;
258 case '\t':
259 escapeSeq = _escapeTAB;
260 break;
261 case '\\':
262 escapeSeq = _escapeBSLASH;
263 break;
264 case '"':
265 escapeSeq = _escapeQ;
266 break;
267 default:
268 if (ch < 0x20) {
269 if (m_bufferPos + 6 > BUFFER_SIZE)
270 FlushBuffer();
271
272 m_buffer[m_bufferPos++] = '\\';
273 m_buffer[m_bufferPos++] = 'u';
274 m_buffer[m_bufferPos++] = '0';
275 m_buffer[m_bufferPos++] = '0';
276 m_buffer[m_bufferPos++] = _hex[ch >> 4 & 0xf];
277 m_buffer[m_bufferPos++] = _hex[ch & 0xf];
278
279 } else {
280 if (m_bufferPos >= BUFFER_SIZE)
281 FlushBuffer();
282 m_buffer[m_bufferPos++] = ch;
283 }
284 continue;
285 }
286
287 if (m_bufferPos + escapeSeq.Length > BUFFER_SIZE)
288 FlushBuffer();
289
290 Array.Copy(escapeSeq, 0, m_buffer, m_bufferPos, escapeSeq.Length);
291 m_bufferPos += escapeSeq.Length;
292
293 }
294
295 if (m_bufferPos >= BUFFER_SIZE)
296 FlushBuffer();
297
298 m_buffer[m_bufferPos++] = '"';
299
300 FlushBuffer();
301 }
302
303 void Write(double value) {
304 if (double.IsNaN(value))
305 Write("NaN");
306 else if (double.IsNegativeInfinity(value))
307 Write("-Infinity");
308 else if (double.IsPositiveInfinity(value))
309 Write("Infinity");
310 else
311 m_writer.Write(value.ToString(CultureInfo.InvariantCulture));
312 }
313
314 void OperationNotApplicable(string opName) {
315 throw new InvalidOperationException(String.Format("The operation '{0}' isn't applicable in the context of '{1}'", opName, m_context.element ));
316 }
317
318 }
319 }
@@ -0,0 +1,335
1 using Implab;
2 using System;
3 using System.Collections.Generic;
4 using System.Globalization;
5 using System.IO;
6 using System.Xml;
7
8 namespace Implab.Formats.JSON {
9 public class JSONXmlReader : XmlReader {
10
11 enum ValueContext {
12 Undefined,
13 ElementStart,
14 ElementValue,
15 ElementEnd,
16 ElementEmpty
17 }
18
19 struct LocalNameContext {
20 public string localName;
21 public bool isArray;
22 }
23
24 JSONParser m_parser;
25 ValueContext m_valueContext;
26 ReadState m_state = ReadState.Initial;
27 Stack<LocalNameContext> m_localNameStack = new Stack<LocalNameContext>();
28 LocalNameContext m_localName;
29 int m_depthCorrection;
30
31 readonly string m_rootName;
32 readonly string m_prefix;
33 readonly string m_namespaceUri;
34 readonly bool m_flattenArrays;
35 readonly string m_arrayItemName;
36 readonly XmlNameTable m_nameTable;
37
38 JSONXmlReader(JSONParser parser, JSONXmlReaderOptions options) {
39 m_parser = parser;
40
41 if (options != null) {
42 m_prefix = options.NodesPrefix ?? String.Empty;
43 m_namespaceUri = options.NamespaceURI ?? String.Empty;
44 m_rootName = options.RootName ?? "json";
45 m_flattenArrays = options.FlattenArrays;
46 m_arrayItemName = options.ArrayItemName ?? "item";
47 m_nameTable = options.NameTable ?? new NameTable();
48 } else {
49 m_prefix = String.Empty;
50 m_namespaceUri = String.Empty;
51 m_rootName = "json";
52 m_flattenArrays = false;
53 m_arrayItemName = "item";
54 m_nameTable = new NameTable();
55 }
56 }
57
58 /// <summary>
59 /// Always 0, JSON doesn't support attributes
60 /// </summary>
61 public override int AttributeCount {
62 get { return 0; }
63 }
64
65 public override string BaseURI {
66 get { return String.Empty; }
67 }
68
69 public override int Depth {
70 get {
71 return m_localNameStack.Count + m_depthCorrection;
72 }
73 }
74
75 public override bool EOF {
76 get { return m_parser.EOF; }
77 }
78
79 /// <summary>
80 /// Always throws an exception
81 /// </summary>
82 /// <param name="i"></param>
83 /// <returns></returns>
84 public override string GetAttribute(int i) {
85 throw new ArgumentOutOfRangeException();
86 }
87
88 /// <summary>
89 /// Always returns empty string
90 /// </summary>
91 /// <param name="name"></param>
92 /// <param name="namespaceURI"></param>
93 /// <returns></returns>
94 public override string GetAttribute(string name, string namespaceURI) {
95 return String.Empty;
96 }
97
98 /// <summary>
99 /// Always returns empty string
100 /// </summary>
101 /// <param name="name"></param>
102 /// <returns></returns>
103 public override string GetAttribute(string name) {
104 return String.Empty;
105 }
106
107 public override bool IsEmptyElement {
108 get { return m_parser.ElementType == JSONElementType.Value && m_valueContext == ValueContext.ElementEmpty; }
109 }
110
111 public override string LocalName {
112 get { return m_localName.localName; }
113 }
114
115 public override string LookupNamespace(string prefix) {
116 if (String.IsNullOrEmpty(prefix) || prefix == m_prefix)
117 return m_namespaceUri;
118
119 return String.Empty;
120 }
121
122 public override bool MoveToAttribute(string name, string ns) {
123 return false;
124 }
125
126 public override bool MoveToAttribute(string name) {
127 return false;
128 }
129
130 public override bool MoveToElement() {
131 return false;
132 }
133
134 public override bool MoveToFirstAttribute() {
135 return false;
136 }
137
138 public override bool MoveToNextAttribute() {
139 return false;
140 }
141
142 public override XmlNameTable NameTable {
143 get { return m_nameTable; }
144 }
145
146 public override string NamespaceURI {
147 get { return m_namespaceUri; }
148 }
149
150 public override XmlNodeType NodeType {
151 get {
152 switch (m_parser.ElementType) {
153 case JSONElementType.BeginObject:
154 case JSONElementType.BeginArray:
155 return XmlNodeType.Element;
156 case JSONElementType.EndObject:
157 case JSONElementType.EndArray:
158 return XmlNodeType.EndElement;
159 case JSONElementType.Value:
160 switch (m_valueContext) {
161 case ValueContext.ElementStart:
162 case ValueContext.ElementEmpty:
163 return XmlNodeType.Element;
164 case ValueContext.ElementValue:
165 return XmlNodeType.Text;
166 case ValueContext.ElementEnd:
167 return XmlNodeType.EndElement;
168 default:
169 throw new InvalidOperationException();
170 }
171 default:
172 throw new InvalidOperationException();
173 }
174 }
175 }
176
177 public override string Prefix {
178 get { return m_prefix; }
179 }
180
181 public override bool Read() {
182 if (m_state != ReadState.Interactive && m_state != ReadState.Initial)
183 return false;
184
185 if (m_state == ReadState.Initial)
186 m_state = ReadState.Interactive;
187
188 try {
189 switch (m_parser.ElementType) {
190 case JSONElementType.Value:
191 switch (m_valueContext) {
192 case ValueContext.ElementStart:
193 SetLocalName(String.Empty);
194 m_valueContext = ValueContext.ElementValue;
195 return true;
196 case ValueContext.ElementValue:
197 RestoreLocalName();
198 m_valueContext = ValueContext.ElementEnd;
199 return true;
200 case ValueContext.ElementEmpty:
201 case ValueContext.ElementEnd:
202 RestoreLocalName();
203 break;
204 }
205 break;
206 case JSONElementType.EndArray:
207 case JSONElementType.EndObject:
208 RestoreLocalName();
209 break;
210 }
211 string itemName = m_parser.ElementType == JSONElementType.None ? m_rootName : m_flattenArrays ? m_localName.localName : m_arrayItemName;
212 while (m_parser.Read()) {
213 if (!String.IsNullOrEmpty(m_parser.ElementName))
214 itemName = m_parser.ElementName;
215
216 switch (m_parser.ElementType) {
217 case JSONElementType.BeginArray:
218 if (m_flattenArrays && !m_localName.isArray) {
219 m_depthCorrection--;
220 SetLocalName(itemName, true);
221 continue;
222 }
223 SetLocalName(itemName, true);
224 break;
225 case JSONElementType.BeginObject:
226 SetLocalName(itemName);
227 break;
228 case JSONElementType.EndArray:
229 if (m_flattenArrays && !m_localNameStack.Peek().isArray) {
230 RestoreLocalName();
231 m_depthCorrection++;
232 continue;
233 }
234 break;
235 case JSONElementType.EndObject:
236 break;
237 case JSONElementType.Value:
238 SetLocalName(itemName);
239 m_valueContext = m_parser.ElementValue == null ? ValueContext.ElementEmpty : ValueContext.ElementStart;
240 break;
241 }
242 return true;
243 }
244
245 m_state = ReadState.EndOfFile;
246 return false;
247 } catch {
248 m_state = ReadState.Error;
249 throw;
250 }
251 }
252
253 public override bool ReadAttributeValue() {
254 return false;
255 }
256
257 public override ReadState ReadState {
258 get { return m_state; }
259 }
260
261 public override void ResolveEntity() {
262 // do nothing
263 }
264
265 public override string Value {
266 get {
267 if (m_parser.ElementValue == null)
268 return String.Empty;
269 if (Convert.GetTypeCode(m_parser.ElementValue) == TypeCode.Double)
270 return ((double)m_parser.ElementValue).ToString(CultureInfo.InvariantCulture);
271 return m_parser.ElementValue.ToString();
272 }
273 }
274
275 void SetLocalName(string name) {
276 m_localNameStack.Push(m_localName);
277 m_localName.localName = name;
278 m_localName.isArray = false;
279 }
280
281 void SetLocalName(string name, bool isArray) {
282 m_localNameStack.Push(m_localName);
283 m_localName.localName = name;
284 m_localName.isArray = isArray;
285 }
286
287 void RestoreLocalName() {
288 m_localName = m_localNameStack.Pop();
289 }
290
291 public override void Close() {
292
293 }
294
295 protected override void Dispose(bool disposing) {
296 #if MONO
297 disposing = true;
298 #endif
299 if (disposing) {
300 m_parser.Dispose();
301 }
302 base.Dispose(disposing);
303 }
304
305 public static JSONXmlReader Create(string file, JSONXmlReaderOptions options) {
306 return Create(File.OpenText(file), options);
307 }
308
309 /// <summary>
310 /// Creates the XmlReader for the specified text stream with JSON data.
311 /// </summary>
312 /// <param name="reader">Text reader.</param>
313 /// <param name="options">Options.</param>
314 /// <remarks>
315 /// The reader will be disposed when the XmlReader is disposed.
316 /// </remarks>
317 public static JSONXmlReader Create(TextReader reader, JSONXmlReaderOptions options) {
318 return new JSONXmlReader(new JSONParser(reader), options);
319 }
320
321 /// <summary>
322 /// Creates the XmlReader for the specified stream with JSON data.
323 /// </summary>
324 /// <param name="stream">Stream.</param>
325 /// <param name="options">Options.</param>
326 /// <remarks>
327 /// The stream will be disposed when the XmlReader is disposed.
328 /// </remarks>
329 public static JSONXmlReader Create(Stream stream, JSONXmlReaderOptions options) {
330 Safe.ArgumentNotNull(stream, "stream");
331 // HACK don't dispose StreaReader to keep stream opened
332 return Create(new StreamReader(stream), options);
333 }
334 }
335 }
@@ -0,0 +1,62
1
2 using System.Xml;
3
4 namespace Implab.Formats.JSON {
5 /// <summary>
6 /// Набор необязательных параметров для <see cref="JSONXmlReader"/>, позволяющий управлять процессом
7 /// интерпретации <c>JSON</c> документа.
8 /// </summary>
9 public class JSONXmlReaderOptions {
10 /// <summary>
11 /// Пространство имен в котором будут располагаться читаемые элементы документа
12 /// </summary>
13 public string NamespaceURI {
14 get;
15 set;
16 }
17
18 /// <summary>
19 /// Интерпретировать массивы как множественные элементы (убирает один уровень вложенности), иначе массив
20 /// представляется в виде узла, дочерними элементами которого являются элементы массива, имена дочерних элементов
21 /// определяются свойством <see cref="ArrayItemName"/>. По умолчанию <c>false</c>.
22 /// </summary>
23 public bool FlattenArrays {
24 get;
25 set;
26 }
27
28 /// <summary>
29 /// Префикс, для узлов документа
30 /// </summary>
31 public string NodesPrefix {
32 get;
33 set;
34 }
35
36 /// <summary>
37 /// Имя корневого элемента в xml документе
38 /// </summary>
39 public string RootName {
40 get;
41 set;
42 }
43
44 /// <summary>
45 /// Имя элемента для массивов, если не включена опция <see cref="FlattenArrays"/>.
46 /// По умолчанию <c>item</c>.
47 /// </summary>
48 public string ArrayItemName {
49 get;
50 set;
51 }
52
53 /// <summary>
54 /// Таблица атомизированных строк для построения документа.
55 /// </summary>
56 public XmlNameTable NameTable {
57 get;
58 set;
59 }
60
61 }
62 }
@@ -0,0 +1,44
1 namespace Implab.Formats.JSON {
2 /// <summary>
3 /// Тип токенов, возвращаемых <see cref="JSONScanner"/>.
4 /// </summary>
5 public enum JsonTokenType : int {
6 None = 0,
7 /// <summary>
8 /// Начало объекта
9 /// </summary>
10 BeginObject,
11 /// <summary>
12 /// Конец объекта
13 /// </summary>
14 EndObject,
15 /// <summary>
16 /// Начало массива
17 /// </summary>
18 BeginArray,
19 /// <summary>
20 /// Конец массива
21 /// </summary>
22 EndArray,
23 /// <summary>
24 /// Строка
25 /// </summary>
26 String,
27 /// <summary>
28 /// Число
29 /// </summary>
30 Number,
31 /// <summary>
32 /// Литерал
33 /// </summary>
34 Literal,
35 /// <summary>
36 /// Разделитель имени <c>:</c>
37 /// </summary>
38 NameSeparator,
39 /// <summary>
40 /// Разделитель имени <c>,</c>
41 /// </summary>
42 ValueSeparator
43 }
44 }
@@ -0,0 +1,52
1 using Implab;
2 using Implab.Formats;
3 using System;
4 using System.Collections.Generic;
5 using System.Diagnostics;
6 using System.Linq;
7 using System.Text;
8 using System.Threading.Tasks;
9
10 namespace Implab.Formats.JSON {
11 /// <summary>
12 /// Класс для преобразования экранированной строки JSON
13 /// </summary>
14 static class StringTranslator {
15 static readonly char[] _escMap;
16 static readonly int[] _hexMap;
17
18 static StringTranslator() {
19 var chars = new char[] { 'b', 'f', 't', 'r', 'n', '\\', '/' };
20 var vals = new char[] { '\b', '\f', '\t', '\r', '\n', '\\', '/' };
21
22 _escMap = new char[chars.Max() + 1];
23
24 for (int i = 0; i < chars.Length; i++)
25 _escMap[chars[i]] = vals[i];
26
27 var hexs = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F' };
28 var ints = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14, 15 };
29
30 _hexMap = new int[hexs.Max() + 1];
31
32 for (int i = 0; i < hexs.Length; i++)
33 _hexMap[hexs[i]] = ints[i];
34
35 }
36
37 internal static char TranslateEscapedChar(char symbol) {
38 return _escMap[symbol];
39 }
40
41 internal static char TranslateHexUnicode(char[] symbols, int offset) {
42 Debug.Assert(symbols != null);
43 Debug.Assert(symbols.Length - offset >= 4);
44
45 int value = (_hexMap[symbols[offset]] << 12)
46 | (_hexMap[symbols[offset + 1]] << 8)
47 | (_hexMap[symbols[offset + 2]] << 4)
48 | (_hexMap[symbols[offset + 3]]);
49 return (char)value;
50 }
51 }
52 }
@@ -0,0 +1,30
1 using System;
2 using System.IO;
3
4 namespace Implab.Formats {
5 public class ReaderScanner: TextScanner {
6 const int CHUNK_SIZE = 1024*4;
7 const int BUFFER_MAX = CHUNK_SIZE*1024;
8
9 readonly TextReader m_reader;
10
11 public ReaderScanner(TextReader reader, int limit, int chunk) : base(limit, chunk) {
12 Safe.ArgumentNotNull(reader, "reader");
13 m_reader = reader;
14 }
15
16 public ReaderScanner(TextReader reader) : this(reader, BUFFER_MAX, CHUNK_SIZE) {
17 }
18
19 protected override int Read(char[] buffer, int offset, int size) {
20 return m_reader.Read(buffer, offset, size);
21 }
22
23 protected override void Dispose(bool disposing) {
24 if (disposing)
25 Safe.Dispose(m_reader);
26 base.Dispose(disposing);
27 }
28 }
29 }
30
@@ -0,0 +1,30
1 namespace Implab.Formats {
2 /// <summary>
3 /// Represents a scanner configuration usefull to recongnize token, based on the DFA.
4 /// </summary>
5 public class ScannerContext<TTag> {
6
7 public int[,] Dfa { get; private set; }
8
9 public bool[] Final { get; private set; }
10
11 public TTag[][] Tags { get; private set; }
12
13 public int State { get; private set; }
14
15 public int[] Alphabet { get; private set; }
16
17 public ScannerContext(int[,] dfa, bool[] final, TTag[][] tags, int state, int[] alphabet) {
18 Dfa = dfa;
19 Final = final;
20 Tags = tags;
21 State = state;
22 Alphabet = alphabet;
23 }
24
25 public bool Execute(TextScanner scanner, out TTag[] tag) {
26 return scanner.ReadToken(Dfa, Final, Tags, State, Alphabet, out tag);
27 }
28 }
29 }
30
@@ -0,0 +1,18
1 using System;
2
3 namespace Implab.Formats {
4 public class StringScanner: TextScanner {
5 const int CHUNK_SIZE = 1024;
6
7 public StringScanner(string text) : base(null) {
8 Safe.ArgumentNotNull(text, "text");
9 var data = text.ToCharArray();
10 Feed(data, 0, data.Length);
11 }
12
13 protected override int Read(char[] buffer, int offset, int size) {
14 return 0;
15 }
16 }
17 }
18
@@ -0,0 +1,157
1 using System;
2 using Implab.Components;
3 using System.Diagnostics;
4 using Implab.Automaton;
5 using System.Text;
6
7 namespace Implab.Formats {
8 public abstract class TextScanner : Disposable {
9 readonly int m_bufferMax;
10 readonly int m_chunkSize;
11
12 char[] m_buffer;
13 int m_bufferOffset;
14 int m_bufferSize;
15 int m_tokenOffset;
16 int m_tokenLength;
17
18 /// <summary>
19 /// Initializes a new instance of the <see cref="Implab.Formats.TextScanner"/> class.
20 /// </summary>
21 /// <param name="bufferMax">Buffer max.</param>
22 /// <param name="chunkSize">Chunk size.</param>
23 protected TextScanner(int bufferMax, int chunkSize) {
24 Debug.Assert(m_chunkSize <= m_bufferMax);
25
26 m_bufferMax = bufferMax;
27 m_chunkSize = chunkSize;
28 }
29
30 /// <summary>
31 /// Initializes a new instance of the <see cref="Implab.Formats.TextScanner"/> class.
32 /// </summary>
33 /// <param name="buffer">Buffer.</param>
34 protected TextScanner(char[] buffer) {
35 if (buffer != null) {
36 m_buffer = buffer;
37 m_bufferSize = buffer.Length;
38 }
39 }
40
41 /// <summary>
42 /// (hungry) Reads the next token.
43 /// </summary>
44 /// <returns><c>true</c>, if token internal was read, <c>false</c> if there is no more tokens in the stream.</returns>
45 /// <param name="dfa">The transition map for the automaton</param>
46 /// <param name="final">Final states of the automaton.</param>
47 /// <param name="tags">Tags.</param>
48 /// <param name="state">The initial state for the automaton.</param>
49 /// <param name="alphabet"></param>
50 /// <param name = "tag"></param>
51 internal bool ReadToken<TTag>(int[,] dfa, bool[] final, TTag[][] tags, int state, int[] alphabet, out TTag[] tag) {
52 m_tokenLength = 0;
53 tag = null;
54
55 var maxSymbol = alphabet.Length - 1;
56 int next;
57 do {
58 // after the next chunk is read the offset in the buffer may change
59 int pos = m_bufferOffset + m_tokenLength;
60 next = state;
61 while (pos < m_bufferSize) {
62 var ch = m_buffer[pos];
63
64 next = dfa[next, ch > maxSymbol ? AutomatonConst.UNCLASSIFIED_INPUT : alphabet[ch]];
65
66 if (next == AutomatonConst.UNREACHABLE_STATE)
67 break;
68
69 state = next;
70 pos++;
71 }
72 m_tokenLength = pos - m_bufferOffset;
73 } while (next != AutomatonConst.UNREACHABLE_STATE && Feed());
74
75 m_tokenOffset = m_bufferOffset;
76 m_bufferOffset += m_tokenLength;
77
78 if (final[state]) {
79 tag = tags[state];
80 return true;
81 }
82
83 if (m_bufferOffset == m_bufferSize) {
84 if (m_tokenLength == 0) //EOF
85 return false;
86
87 throw new ParserException();
88 }
89
90 throw new ParserException(String.Format("Unexpected symbol '{0}'", m_buffer[m_bufferOffset]));
91
92 }
93
94 protected void Feed(char[] buffer, int offset, int length) {
95 m_buffer = buffer;
96 m_bufferOffset = offset;
97 m_bufferSize = offset + length;
98 }
99
100 protected bool Feed() {
101 if (m_chunkSize <= 0)
102 return false;
103
104 if (m_buffer != null) {
105 var free = m_buffer.Length - m_bufferSize;
106
107 if (free < m_chunkSize) {
108 free += m_chunkSize;
109 var used = m_bufferSize - m_bufferOffset;
110 var size = used + free;
111
112 if (size > m_bufferMax)
113 throw new ParserException(String.Format("The buffer limit ({0} Kb) is reached", m_bufferMax / 1024));
114
115 var temp = new char[size];
116
117 var read = Read(temp, used, m_chunkSize);
118 if (read == 0)
119 return false;
120
121 Array.Copy(m_buffer, m_bufferOffset, temp, 0, used);
122
123 m_bufferOffset = 0;
124 m_bufferSize = used + read;
125 m_buffer = temp;
126 } else {
127 var read = Read(m_buffer, m_bufferSize, m_chunkSize);
128 if (read == 0)
129 return false;
130 m_bufferSize += m_chunkSize;
131 }
132 return true;
133 } else {
134 Debug.Assert(m_bufferOffset == 0);
135 m_buffer = new char[m_chunkSize];
136 m_bufferSize = Read(m_buffer, 0, m_chunkSize);
137 return (m_bufferSize != 0);
138 }
139 }
140
141 protected abstract int Read(char[] buffer, int offset, int size);
142
143 public string GetTokenValue() {
144 return new String(m_buffer, m_tokenOffset, m_tokenLength);
145 }
146
147 public void CopyTokenTo(char[] buffer, int offset) {
148 Array.Copy(m_buffer, m_tokenOffset,buffer, offset, m_tokenLength);
149 }
150
151 public void CopyTokenTo(StringBuilder sb) {
152 sb.Append(m_buffer, m_tokenOffset, m_tokenLength);
153 }
154
155 }
156 }
157
@@ -0,0 +1,26
1 using System;
2
3 namespace Implab {
4 public class FuncChainTask<TResult> : FuncChainTaskBase<TResult>, IDeferred {
5 readonly Func<IPromise<TResult>> m_task;
6
7 public FuncChainTask(Func<IPromise<TResult>> task, Func<Exception, IPromise<TResult>> error, Func<Exception, IPromise<TResult>> cancel, bool autoCancellable)
8 : base(error, cancel, autoCancellable) {
9 m_task = task;
10 }
11
12 public void Resolve() {
13 if (m_task != null && LockCancelation()) {
14 try {
15 var operation = m_task();
16 operation.On(SetResult, HandleErrorInternal, HandleCancelInternal);
17 CancellationRequested(operation.Cancel);
18 } catch (OperationCanceledException reason) {
19 HandleCancelInternal(reason);
20 } catch (Exception err) {
21 HandleErrorInternal(err);
22 }
23 }
24 }
25 }
26 } No newline at end of file
@@ -0,0 +1,54
1 using System;
2
3 namespace Implab {
4 public class FuncChainTaskBase<TResult> : AbstractTask<TResult> {
5 readonly Func<Exception, IPromise<TResult>> m_error;
6 readonly Func<Exception, IPromise<TResult>> m_cancel;
7
8 protected FuncChainTaskBase( Func<Exception, IPromise<TResult>> error, Func<Exception, IPromise<TResult>> cancel, bool autoCancellable) {
9 m_error = error;
10 m_cancel = cancel;
11 if (autoCancellable)
12 CancellationRequested(CancelOperation);
13 }
14
15 public void Reject(Exception error) {
16 if (LockCancelation())
17 HandleErrorInternal(error);
18 }
19
20 public override void CancelOperation(Exception reason) {
21 if (LockCancelation())
22 HandleCancelInternal(reason);
23 }
24
25 protected void HandleErrorInternal(Exception error) {
26 if (m_error != null) {
27 try {
28 var p = m_error(error);
29 p.On(SetResult, SetErrorInternal, SetCancelledInternal);
30 CancellationRequested(p.Cancel);
31 } catch(Exception err) {
32 SetErrorInternal(err);
33 }
34 } else {
35 SetErrorInternal(error);
36 }
37 }
38
39 protected void HandleCancelInternal(Exception reason) {
40 if (m_cancel != null) {
41 try {
42 var p = m_cancel(reason);
43 p.On(SetResult, HandleErrorInternal, SetCancelledInternal);
44 CancellationRequested(p.Cancel);
45 } catch (Exception err) {
46 HandleErrorInternal(err);
47 }
48 } else {
49 HandleErrorInternal(reason ?? new OperationCanceledException());
50 }
51 }
52 }
53 }
54
@@ -0,0 +1,25
1 using System;
2
3 namespace Implab {
4 public class FuncChainTask<TArg,TResult> : FuncChainTaskBase<TResult>, IDeferred<TArg> {
5 readonly Func<TArg, IPromise<TResult>> m_task;
6
7 public FuncChainTask(Func<TArg, IPromise<TResult>> task, Func<Exception, IPromise<TResult>> error, Func<Exception, IPromise<TResult>> cancel, bool autoCancellable) : base(error, cancel, autoCancellable){
8 m_task = task;
9 }
10
11 public void Resolve(TArg value) {
12 if (m_task != null && LockCancelation()) {
13 try {
14 var operation = m_task(value);
15 operation.On(SetResult, HandleErrorInternal, SetCancelled);
16 CancellationRequested(operation.Cancel);
17 } catch (OperationCanceledException reason) {
18 HandleCancelInternal(reason);
19 } catch (Exception err) {
20 HandleErrorInternal(err);
21 }
22 }
23 }
24 }
25 } No newline at end of file
@@ -0,0 +1,25
1 using System;
2 using System.Threading;
3
4 namespace Implab {
5 public class FuncTask<T> : FuncTaskBase<T>, IDeferred {
6 readonly Func<T> m_task;
7
8 public FuncTask(Func<T> task, Func<Exception, T> error, Func<Exception, T> cancel, bool autoCancellable) : base(error, cancel, autoCancellable) {
9 m_task = task;
10 }
11
12 public void Resolve() {
13 if (m_task != null && LockCancelation()) {
14 try {
15 SetResult(m_task());
16 } catch(OperationCanceledException reason) {
17 HandleCancelInternal(reason);
18 } catch(Exception err) {
19 HandleErrorInternal(err);
20 }
21 }
22 }
23 }
24 }
25
@@ -0,0 +1,52
1 using System;
2
3 namespace Implab {
4 public class FuncTaskBase<TResult> : AbstractTask<TResult> {
5 readonly Func<Exception, TResult> m_cancel;
6 readonly Func<Exception, TResult> m_error;
7
8 protected FuncTaskBase( Func<Exception, TResult> error, Func<Exception, TResult> cancel, bool autoCancellable) {
9 m_error = error;
10 m_cancel = cancel;
11 if (autoCancellable)
12 CancellationRequested(CancelOperation);
13 }
14
15 public void Reject(Exception error) {
16 Safe.ArgumentNotNull(error, "error");
17 if (LockCancelation())
18 HandleErrorInternal(error);
19 }
20
21 protected void HandleErrorInternal(Exception error) {
22 if (m_error != null) {
23 try {
24 SetResult(m_error(error));
25 } catch(Exception err) {
26 SetErrorInternal(err);
27 }
28 } else {
29 SetErrorInternal(error);
30 }
31 }
32
33 public override void CancelOperation(Exception reason) {
34 if (LockCancelation())
35 HandleCancelInternal(reason);
36 }
37
38 protected void HandleCancelInternal(Exception reason) {
39 if (m_cancel != null) {
40 try {
41 SetResult(m_cancel(reason));
42 } catch (Exception err) {
43 HandleErrorInternal(err);
44 }
45 } else {
46 HandleErrorInternal(reason ?? new OperationCanceledException());
47 }
48 }
49
50 }
51 }
52
@@ -0,0 +1,24
1 using System;
2
3 namespace Implab {
4 public class FuncTask<TArg, TResult> : FuncTaskBase<TResult>, IDeferred<TArg> {
5 readonly Func<TArg, TResult> m_task;
6
7 public FuncTask(Func<TArg, TResult> task, Func<Exception, TResult> error,Func<Exception, TResult> cancel, bool autoCancellable) : base(error,cancel, autoCancellable) {
8 m_task = task;
9 }
10
11 public void Resolve(TArg value) {
12 if (m_task != null && LockCancelation()) {
13 try {
14 SetResult(m_task(value));
15 } catch(OperationCanceledException reason) {
16 HandleCancelInternal(reason);
17 } catch(Exception err) {
18 HandleErrorInternal(err);
19 }
20 }
21 }
22 }
23 }
24
@@ -0,0 +1,36
1 using System;
2
3 namespace Implab {
4 public interface ICancellationToken {
5 /// <summary>
6 /// Indicates wherther the cancellation was requested.
7 /// </summary>
8 bool IsCancellationRequested { get ; }
9
10 /// <summary>
11 /// The reason why the operation should be cancelled.
12 /// </summary>
13 Exception CancellationReason { get ; }
14
15 /// <summary>
16 /// Accepts if requested.
17 /// </summary>
18 /// <returns><c>true</c>, if if requested was accepted, <c>false</c> otherwise.</returns>
19 bool CancelOperationIfRequested();
20
21 /// <summary>
22 /// Sets the token to cancelled state.
23 /// </summary>
24 /// <param name="reason">The reason why the operation was cancelled.</param>
25 void CancelOperation(Exception reason);
26
27 /// <summary>
28 /// Adds the listener for the cancellation request, is the cancellation was requested the <paramref name="handler"/>
29 /// is executed immediatelly.
30 /// </summary>
31 /// <param name="handler">The handler which will be executed if the cancel occurs.</param>
32 void CancellationRequested(Action<Exception> handler);
33
34 }
35 }
36
@@ -0,0 +1,24
1 using System;
2
3 namespace Implab {
4 /// <summary>
5 /// Deferred result, usually used by asynchronous services as the service part of the promise.
6 /// </summary>
7 public interface IDeferred : ICancellationToken {
8
9 void Resolve();
10
11 /// <summary>
12 /// Reject the promise with the specified error.
13 /// </summary>
14 /// <param name="error">The reason why the promise is rejected.</param>
15 /// <remarks>
16 /// Some exceptions are treated in a special case:
17 /// <see cref="OperationCanceledException"/> is interpreted as call to <see cref="Cancel()"/> method,
18 /// and <see cref="PromiseTransientException"/> is always unwrapped and its
19 /// <see cref="PromiseTransientException.InnerException"> is used as the reason to reject promise.
20 /// </remarks>
21 void Reject(Exception error);
22 }
23 }
24
@@ -0,0 +1,10
1 using System;
2
3 namespace Implab {
4 public interface IDeferred<in T> : ICancellationToken {
5 void Resolve(T value);
6
7 void Reject(Exception error);
8 }
9 }
10
This diff has been collapsed as it changes many lines, (631 lines changed) Show them Hide them
@@ -0,0 +1,631
1 using System.Threading;
2 using System.Collections.Generic;
3 using System;
4 using System.Collections;
5 using System.Diagnostics;
6
7 namespace Implab.Parallels {
8 public class AsyncQueue<T> : IEnumerable<T> {
9 class Chunk {
10 public Chunk next;
11
12 int m_low;
13 int m_hi;
14 int m_alloc;
15 readonly int m_size;
16 readonly T[] m_data;
17
18 public Chunk(int size) {
19 m_size = size;
20 m_data = new T[size];
21 }
22
23 public Chunk(int size, T value) {
24 m_size = size;
25 m_hi = 1;
26 m_alloc = 1;
27 m_data = new T[size];
28 m_data[0] = value;
29 }
30
31 public Chunk(int size, T[] data, int offset, int length, int alloc) {
32 m_size = size;
33 m_hi = length;
34 m_alloc = alloc;
35 m_data = new T[size];
36 Array.Copy(data, offset, m_data, 0, length);
37 }
38
39 public int Low {
40 get { return m_low; }
41 }
42
43 public int Hi {
44 get { return m_hi; }
45 }
46
47 public int Size {
48 get { return m_size; }
49 }
50
51 public bool TryEnqueue(T value, out bool extend) {
52 var alloc = Interlocked.Increment(ref m_alloc) - 1;
53
54 if (alloc >= m_size) {
55 extend = alloc == m_size;
56 return false;
57 }
58
59 extend = false;
60 m_data[alloc] = value;
61
62 while (alloc != Interlocked.CompareExchange(ref m_hi, alloc + 1, alloc)) {
63 // spin wait for commit
64 }
65 return true;
66 }
67
68 /// <summary>
69 /// Prevents from allocating new space in the chunk and waits for all write operations to complete
70 /// </summary>
71 public void Commit() {
72 var actual = Math.Min(Interlocked.Exchange(ref m_alloc, m_size + 1), m_size);
73
74 while (m_hi != actual)
75 Thread.MemoryBarrier();
76 }
77
78 public bool TryDequeue(out T value, out bool recycle) {
79 int low;
80 do {
81 low = m_low;
82 if (low >= m_hi) {
83 value = default(T);
84 recycle = (low == m_size);
85 return false;
86 }
87 } while(low != Interlocked.CompareExchange(ref m_low, low + 1, low));
88
89 recycle = (low == m_size - 1);
90 value = m_data[low];
91
92 return true;
93 }
94
95 public bool TryEnqueueBatch(T[] batch, int offset, int length, out int enqueued, out bool extend) {
96 //int alloc;
97 //int allocSize;
98
99 var alloc = Interlocked.Add(ref m_alloc, length) - length;
100 if (alloc > m_size) {
101 // the chunk is full and someone already
102 // creating the new one
103 enqueued = 0; // nothing was added
104 extend = false; // the caller shouldn't try to extend the queue
105 return false; // nothing was added
106 }
107
108 enqueued = Math.Min(m_size - alloc, length);
109 extend = length > enqueued;
110
111 if (enqueued == 0)
112 return false;
113
114
115 Array.Copy(batch, offset, m_data, alloc, enqueued);
116
117 while (alloc != Interlocked.CompareExchange(ref m_hi, alloc + enqueued, alloc)) {
118 // spin wait for commit
119 }
120
121 return true;
122 }
123
124 public bool TryDequeueBatch(T[] buffer, int offset, int length,out int dequeued, out bool recycle) {
125 int low, hi, batchSize;
126
127 do {
128 low = m_low;
129 hi = m_hi;
130 if (low >= hi) {
131 dequeued = 0;
132 recycle = (low == m_size); // recycling could be restarted and we need to signal again
133 return false;
134 }
135 batchSize = Math.Min(hi - low, length);
136 } while(low != Interlocked.CompareExchange(ref m_low, low + batchSize, low));
137
138 recycle = (low == m_size - batchSize);
139 dequeued = batchSize;
140
141 Array.Copy(m_data, low, buffer, offset, batchSize);
142
143 return true;
144 }
145
146 public T GetAt(int pos) {
147 return m_data[pos];
148 }
149 }
150
151 public const int DEFAULT_CHUNK_SIZE = 32;
152 public const int MAX_CHUNK_SIZE = 262144;
153
154 Chunk m_first;
155 Chunk m_last;
156
157 /// <summary>
158 /// Adds the specified value to the queue.
159 /// </summary>
160 /// <param name="value">Tha value which will be added to the queue.</param>
161 public virtual void Enqueue(T value) {
162 var last = m_last;
163 // spin wait to the new chunk
164 bool extend = true;
165 while (last == null || !last.TryEnqueue(value, out extend)) {
166 // try to extend queue
167 if (extend || last == null) {
168 var chunk = new Chunk(DEFAULT_CHUNK_SIZE, value);
169 if (EnqueueChunk(last, chunk))
170 break; // success! exit!
171 last = m_last;
172 } else {
173 while (last == m_last) {
174 Thread.MemoryBarrier();
175 }
176 last = m_last;
177 }
178 }
179 }
180
181 /// <summary>
182 /// Adds the specified data to the queue.
183 /// </summary>
184 /// <param name="data">The buffer which contains the data to be enqueued.</param>
185 /// <param name="offset">The offset of the data in the buffer.</param>
186 /// <param name="length">The size of the data to read from the buffer.</param>
187 public virtual void EnqueueRange(T[] data, int offset, int length) {
188 if (data == null)
189 throw new ArgumentNullException("data");
190 if (length == 0)
191 return;
192 if (offset < 0)
193 throw new ArgumentOutOfRangeException("offset");
194 if (length < 1 || offset + length > data.Length)
195 throw new ArgumentOutOfRangeException("length");
196
197 var last = m_last;
198
199 bool extend;
200 int enqueued;
201
202 while (length > 0) {
203 extend = true;
204 if (last != null && last.TryEnqueueBatch(data, offset, length, out enqueued, out extend)) {
205 length -= enqueued;
206 offset += enqueued;
207 }
208
209 if (extend) {
210 // there was no enough space in the chunk
211 // or there was no chunks in the queue
212
213 while (length > 0) {
214
215 var size = Math.Min(length, MAX_CHUNK_SIZE);
216
217 var chunk = new Chunk(
218 Math.Max(size, DEFAULT_CHUNK_SIZE),
219 data,
220 offset,
221 size,
222 length // length >= size
223 );
224
225 if (!EnqueueChunk(last, chunk)) {
226 // looks like the queue has been updated then proceed from the beginning
227 last = m_last;
228 break;
229 }
230
231 // we have successfully added the new chunk
232 last = chunk;
233 length -= size;
234 offset += size;
235 }
236 } else {
237 // we don't need to extend the queue, if we successfully enqueued data
238 if (length == 0)
239 break;
240
241 // if we need to wait while someone is extending the queue
242 // spinwait
243 while (last == m_last) {
244 Thread.MemoryBarrier();
245 }
246
247 last = m_last;
248 }
249 }
250 }
251
252 /// <summary>
253 /// Tries to retrieve the first element from the queue.
254 /// </summary>
255 /// <returns><c>true</c>, if element is dequeued, <c>false</c> otherwise.</returns>
256 /// <param name="value">The value of the dequeued element.</param>
257 public bool TryDequeue(out T value) {
258 var chunk = m_first;
259 bool recycle;
260 while (chunk != null) {
261
262 var result = chunk.TryDequeue(out value, out recycle);
263
264 if (recycle) // this chunk is waste
265 RecycleFirstChunk(chunk);
266 else
267 return result; // this chunk is usable and returned actual result
268
269 if (result) // this chunk is waste but the true result is always actual
270 return true;
271
272 // try again
273 chunk = m_first;
274 }
275
276 // the queue is empty
277 value = default(T);
278 return false;
279 }
280
281 /// <summary>
282 /// Tries to dequeue the specified amount of data from the queue.
283 /// </summary>
284 /// <returns><c>true</c>, if data was deuqueued, <c>false</c> otherwise.</returns>
285 /// <param name="buffer">The buffer to which the data will be written.</param>
286 /// <param name="offset">The offset in the buffer at which the data will be written.</param>
287 /// <param name="length">The maximum amount of data to be retrieved.</param>
288 /// <param name="dequeued">The actual amout of the retrieved data.</param>
289 public bool TryDequeueRange(T[] buffer, int offset, int length, out int dequeued) {
290 if (buffer == null)
291 throw new ArgumentNullException("buffer");
292 if (offset < 0)
293 throw new ArgumentOutOfRangeException("offset");
294 if (length < 1 || offset + length > buffer.Length)
295 throw new ArgumentOutOfRangeException("length");
296
297 var chunk = m_first;
298 bool recycle;
299 dequeued = 0;
300 while (chunk != null) {
301
302 int actual;
303 if (chunk.TryDequeueBatch(buffer, offset, length, out actual, out recycle)) {
304 offset += actual;
305 length -= actual;
306 dequeued += actual;
307 }
308
309 if (recycle) // this chunk is waste
310 RecycleFirstChunk(chunk);
311 else if (actual == 0)
312 break; // the chunk is usable, but doesn't contain any data (it's the last chunk in the queue)
313
314 if (length == 0)
315 return true;
316
317 // we still may dequeue something
318 // try again
319 chunk = m_first;
320 }
321
322 return dequeued != 0;
323 }
324
325 /// <summary>
326 /// Tries to dequeue all remaining data in the first chunk.
327 /// </summary>
328 /// <returns><c>true</c>, if data was dequeued, <c>false</c> otherwise.</returns>
329 /// <param name="buffer">The buffer to which the data will be written.</param>
330 /// <param name="offset">The offset in the buffer at which the data will be written.</param>
331 /// <param name="length">Tha maximum amount of the data to be dequeued.</param>
332 /// <param name="dequeued">The actual amount of the dequeued data.</param>
333 public bool TryDequeueChunk(T[] buffer, int offset, int length, out int dequeued) {
334 if (buffer == null)
335 throw new ArgumentNullException("buffer");
336 if (offset < 0)
337 throw new ArgumentOutOfRangeException("offset");
338 if (length < 1 || offset + length > buffer.Length)
339 throw new ArgumentOutOfRangeException("length");
340
341 var chunk = m_first;
342 bool recycle;
343 dequeued = 0;
344
345 while (chunk != null) {
346
347 int actual;
348 if (chunk.TryDequeueBatch(buffer, offset, length, out actual, out recycle)) {
349 dequeued = actual;
350 }
351
352 if (recycle) // this chunk is waste
353 RecycleFirstChunk(chunk);
354
355 // if we have dequeued any data, then return
356 if (dequeued != 0)
357 return true;
358
359 // we still may dequeue something
360 // try again
361 chunk = m_first;
362 }
363
364 return false;
365 }
366
367 bool EnqueueChunk(Chunk last, Chunk chunk) {
368 if (Interlocked.CompareExchange(ref m_last, chunk, last) != last)
369 return false;
370
371 if (last != null)
372 last.next = chunk;
373 else {
374 m_first = chunk;
375 }
376 return true;
377 }
378
379 void RecycleFirstChunk(Chunk first) {
380 var next = first.next;
381
382 if (first != Interlocked.CompareExchange(ref m_first, next, first))
383 return;
384
385 if (next == null) {
386
387 if (first != Interlocked.CompareExchange(ref m_last, null, first)) {
388 /*while (first.next == null)
389 Thread.MemoryBarrier();*/
390
391 // race
392 // someone already updated the tail, restore the pointer to the queue head
393 m_first = first;
394 }
395 // the tail is updated
396 }
397
398 // we need to update the head
399 //Interlocked.CompareExchange(ref m_first, next, first);
400 // if the head is already updated then give up
401 //return;
402
403 }
404
405 public void Clear() {
406 // start the new queue
407 var chunk = new Chunk(DEFAULT_CHUNK_SIZE);
408
409 do {
410 Thread.MemoryBarrier();
411 var first = m_first;
412 var last = m_last;
413
414 if (last == null) // nothing to clear
415 return;
416
417 if (first == null || (first.next == null && first != last)) // inconcistency
418 continue;
419
420 // here we will create inconsistency which will force others to spin
421 // and prevent from fetching. chunk.next = null
422 if (first != Interlocked.CompareExchange(ref m_first, chunk, first))
423 continue;// inconsistent
424
425 m_last = chunk;
426
427 return;
428
429 } while(true);
430 }
431
432 public T[] Drain() {
433 // start the new queue
434 var chunk = new Chunk(DEFAULT_CHUNK_SIZE);
435
436 do {
437 Thread.MemoryBarrier();
438 var first = m_first;
439 var last = m_last;
440
441 if (last == null)
442 return new T[0];
443
444 if (first == null || (first.next == null && first != last))
445 continue;
446
447 // here we will create inconsistency which will force others to spin
448 // and prevent from fetching. chunk.next = null
449 if (first != Interlocked.CompareExchange(ref m_first, chunk, first))
450 continue;// inconsistent
451
452 last = Interlocked.Exchange(ref m_last, chunk);
453
454 return ReadChunks(first, last);
455
456 } while(true);
457 }
458
459 static T[] ReadChunks(Chunk chunk, object last) {
460 var result = new List<T>();
461 var buffer = new T[DEFAULT_CHUNK_SIZE];
462 int actual;
463 bool recycle;
464 while (chunk != null) {
465 // ensure all write operations on the chunk are complete
466 chunk.Commit();
467
468 // we need to read the chunk using this way
469 // since some client still may completing the dequeue
470 // operation, such clients most likely won't get results
471 while (chunk.TryDequeueBatch(buffer, 0, buffer.Length, out actual, out recycle))
472 result.AddRange(new ArraySegmentCollection(buffer, 0, actual));
473
474 if (chunk == last) {
475 chunk = null;
476 } else {
477 while (chunk.next == null)
478 Thread.MemoryBarrier();
479 chunk = chunk.next;
480 }
481 }
482
483 return result.ToArray();
484 }
485
486 struct ArraySegmentCollection : ICollection<T> {
487 readonly T[] m_data;
488 readonly int m_offset;
489 readonly int m_length;
490
491 public ArraySegmentCollection(T[] data, int offset, int length) {
492 m_data = data;
493 m_offset = offset;
494 m_length = length;
495 }
496
497 #region ICollection implementation
498
499 public void Add(T item) {
500 throw new NotSupportedException();
501 }
502
503 public void Clear() {
504 throw new NotSupportedException();
505 }
506
507 public bool Contains(T item) {
508 return false;
509 }
510
511 public void CopyTo(T[] array, int arrayIndex) {
512 Array.Copy(m_data,m_offset,array,arrayIndex, m_length);
513 }
514
515 public bool Remove(T item) {
516 throw new NotSupportedException();
517 }
518
519 public int Count {
520 get {
521 return m_length;
522 }
523 }
524
525 public bool IsReadOnly {
526 get {
527 return true;
528 }
529 }
530
531 #endregion
532
533 #region IEnumerable implementation
534
535 public IEnumerator<T> GetEnumerator() {
536 for (int i = m_offset; i < m_length + m_offset; i++)
537 yield return m_data[i];
538 }
539
540 #endregion
541
542 #region IEnumerable implementation
543
544 IEnumerator IEnumerable.GetEnumerator() {
545 return GetEnumerator();
546 }
547
548 #endregion
549 }
550
551 #region IEnumerable implementation
552
553 class Enumerator : IEnumerator<T> {
554 Chunk m_current;
555 int m_pos = -1;
556
557 public Enumerator(Chunk fisrt) {
558 m_current = fisrt;
559 }
560
561 #region IEnumerator implementation
562
563 public bool MoveNext() {
564 if (m_current == null)
565 return false;
566
567 if (m_pos == -1)
568 m_pos = m_current.Low;
569 else
570 m_pos++;
571
572 if (m_pos == m_current.Hi) {
573
574 m_current = m_pos == m_current.Size ? m_current.next : null;
575
576 m_pos = 0;
577
578 if (m_current == null)
579 return false;
580 }
581
582 return true;
583 }
584
585 public void Reset() {
586 throw new NotSupportedException();
587 }
588
589 object IEnumerator.Current {
590 get {
591 return Current;
592 }
593 }
594
595 #endregion
596
597 #region IDisposable implementation
598
599 public void Dispose() {
600 }
601
602 #endregion
603
604 #region IEnumerator implementation
605
606 public T Current {
607 get {
608 if (m_pos == -1 || m_current == null)
609 throw new InvalidOperationException();
610 return m_current.GetAt(m_pos);
611 }
612 }
613
614 #endregion
615 }
616
617 public IEnumerator<T> GetEnumerator() {
618 return new Enumerator(m_first);
619 }
620
621 #endregion
622
623 #region IEnumerable implementation
624
625 IEnumerator IEnumerable.GetEnumerator() {
626 return GetEnumerator();
627 }
628
629 #endregion
630 }
631 }
@@ -0,0 +1,101
1 using System;
2 using System.Threading;
3
4 namespace Implab.Parallels {
5 public class BlockingQueue<T> : AsyncQueue<T> {
6 readonly object m_lock = new object();
7
8 public override void Enqueue(T value) {
9 base.Enqueue(value);
10 lock (m_lock)
11 Monitor.Pulse(m_lock);
12 }
13
14 public override void EnqueueRange(T[] data, int offset, int length) {
15 base.EnqueueRange(data, offset, length);
16 if (length > 1)
17 lock (m_lock)
18 Monitor.PulseAll(m_lock);
19 else
20 lock (m_lock)
21 Monitor.Pulse(m_lock);
22 }
23
24 public T GetItem(int timeout) {
25 T item;
26
27 if (!TryDequeue(out item)) {
28 var t1 = Environment.TickCount;
29 var dt = timeout;
30
31 lock (m_lock) {
32 while (!TryDequeue(out item)) {
33 if (!Monitor.Wait(m_lock, dt))
34 throw new TimeoutException();
35 if (timeout >= 0) {
36 dt = timeout - Environment.TickCount + t1;
37 if (dt < 0)
38 throw new TimeoutException();
39 }
40 }
41 }
42 }
43 return item;
44 }
45
46 public T GetItem() {
47 T item;
48 if (!TryDequeue(out item))
49 lock (m_lock) {
50 while (!TryDequeue(out item))
51 Monitor.Wait(m_lock);
52 }
53 return item;
54 }
55
56 public T[] GetRange(int max, int timeout) {
57 Safe.ArgumentInRange(max, 1, Int32.MaxValue, "max");
58
59 var buffer = new T[max];
60 int actual;
61 if (!TryDequeueRange(buffer, 0, max, out actual)) {
62 var t1 = Environment.TickCount;
63 var dt = timeout;
64
65 lock (m_lock) {
66 while (!TryDequeueRange(buffer, 0, max, out actual)) {
67
68 if (!Monitor.Wait(m_lock, dt))
69 throw new TimeoutException();
70
71 if (timeout >= 0) {
72 dt = timeout - Environment.TickCount + t1;
73 if (dt < 0)
74 throw new TimeoutException();
75 }
76 }
77 }
78 }
79
80 var data = new T[actual];
81 Array.Copy(buffer, data, actual);
82 return data;
83 }
84
85 public T[] GetRange(int max) {
86 Safe.ArgumentInRange(max, 1, Int32.MaxValue, "max");
87
88 var buffer = new T[max];
89 int actual;
90 if (!TryDequeueRange(buffer, 0, max, out actual))
91 lock (m_lock)
92 while (!TryDequeueRange(buffer, 0, max, out actual))
93 Monitor.Wait(m_lock);
94
95 var data = new T[actual];
96 Array.Copy(buffer, data, actual);
97 return data;
98 }
99 }
100 }
101
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,15 +1,20
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/
13 _ReSharper.Implab/
14 Implab.Diagnostics.Interactive/bin/
14 Implab.Diagnostics.Interactive/bin/
15 Implab.Diagnostics.Interactive/obj/
15 Implab.Diagnostics.Interactive/obj/
16 MonoPlay/bin/
17 MonoPlay/obj/
18 Implab.Test/Implab.Format.Test/bin/
19 Implab.Test/Implab.Format.Test/obj/
20 *.suo
@@ -1,122 +1,122
1 using Implab.Parallels;
1 using Implab.Parallels;
2 using System;
2 using System;
3 using System.Collections.Generic;
3 using System.Collections.Generic;
4 using System.Linq;
4 using System.Linq;
5 using System.Text;
5 using System.Text;
6 using System.Threading;
6 using System.Threading;
7 using System.Threading.Tasks;
7 using System.Threading.Tasks;
8 using System.Windows.Forms;
8 using System.Windows.Forms;
9
9
10 namespace Implab.Diagnostics.Interactive
10 namespace Implab.Diagnostics.Interactive
11 {
11 {
12 public class InteractiveListener: TextListenerBase
12 public class InteractiveListener: ListenerBase
13 {
13 {
14 TraceForm m_form;
14 TraceForm m_form;
15
15
16 SynchronizationContext m_syncGuiThread;
16 SynchronizationContext m_syncGuiThread;
17 readonly Promise<object> m_guiStarted = new Promise<object>();
17 readonly Promise m_guiStarted = new Promise();
18
18
19 readonly IPromise m_guiFinished;
19 readonly IPromise m_guiFinished;
20 readonly IPromise m_workerFinished = new Promise<object>();
20 // readonly IPromise m_workerFinished = new Promise<object>();
21
21
22 readonly MTQueue<TraceViewItem> m_queue = new MTQueue<TraceViewItem>();
22 readonly MTQueue<TraceViewItem> m_queue = new MTQueue<TraceViewItem>();
23 readonly AutoResetEvent m_queueEvent = new AutoResetEvent(false);
23 readonly AutoResetEvent m_queueEvent = new AutoResetEvent(false);
24
24
25 int m_queueLength;
25 int m_queueLength;
26 bool m_exitPending;
26 bool m_exitPending;
27
27
28 readonly object m_pauseLock = new object();
28 readonly object m_pauseLock = new object();
29 bool m_paused;
29 bool m_paused;
30 readonly ManualResetEvent m_pauseEvent = new ManualResetEvent(true);
30 readonly ManualResetEvent m_pauseEvent = new ManualResetEvent(true);
31
31
32 public InteractiveListener(bool global) : base(global) {
32 public InteractiveListener() {
33 m_guiFinished = AsyncPool.InvokeNewThread(GuiThread);
33 m_guiFinished = AsyncPool.RunThread(GuiThread);
34 m_workerFinished = AsyncPool.InvokeNewThread(QueueThread);
34 /*m_workerFinished = */AsyncPool.RunThread(QueueThread);
35
35
36 m_guiStarted.Join();
36 m_guiStarted.Join();
37 }
37 }
38
38
39 void GuiThread() {
39 void GuiThread() {
40 m_form = new TraceForm(); // will create SynchronizationContext
40 m_form = new TraceForm(); // will create SynchronizationContext
41
41
42 m_form.PauseEvents += (s,a) => Pause();
42 m_form.PauseEvents += (s,a) => Pause();
43 m_form.ResumeEvents += (s, a) => Resume();
43 m_form.ResumeEvents += (s, a) => Resume();
44
44
45 m_syncGuiThread = SynchronizationContext.Current;
45 m_syncGuiThread = SynchronizationContext.Current;
46 m_guiStarted.Resolve();
46 m_guiStarted.Resolve();
47 Application.Run();
47 Application.Run();
48 }
48 }
49
49
50 void QueueThread() {
50 void QueueThread() {
51 while (!m_exitPending) {
51 while (!m_exitPending) {
52 if (m_paused)
52 if (m_paused)
53 m_pauseEvent.WaitOne();
53 m_pauseEvent.WaitOne();
54
54
55 TraceViewItem item;
55 TraceViewItem item;
56 if (m_queue.TryDequeue(out item)) {
56 if (m_queue.TryDequeue(out item)) {
57 Interlocked.Decrement(ref m_queueLength);
57 Interlocked.Decrement(ref m_queueLength);
58
58
59 m_syncGuiThread.Send(x => m_form.AddTraceEvent(item),null);
59 m_syncGuiThread.Send(x => m_form.AddTraceEvent(item),null);
60 } else {
60 } else {
61 m_queueEvent.WaitOne();
61 m_queueEvent.WaitOne();
62 }
62 }
63 }
63 }
64 }
64 }
65
65
66 public void Pause() {
66 public void Pause() {
67 // for consistency we need to set this properties atomically
67 // for consistency we need to set this properties atomically
68 lock (m_pauseLock) {
68 lock (m_pauseLock) {
69 m_pauseEvent.Reset();
69 m_pauseEvent.Reset();
70 m_paused = true;
70 m_paused = true;
71 }
71 }
72 }
72 }
73
73
74 public void Resume() {
74 public void Resume() {
75 // for consistency we need to set this properties atomically
75 // for consistency we need to set this properties atomically
76 lock (m_pauseLock) {
76 lock (m_pauseLock) {
77 m_paused = false;
77 m_paused = false;
78 m_pauseEvent.Set();
78 m_pauseEvent.Set();
79 }
79 }
80 }
80 }
81
81
82 void Enqueue(TraceViewItem item) {
82 void Enqueue(TraceViewItem item) {
83 m_queue.Enqueue(item);
83 m_queue.Enqueue(item);
84 if (Interlocked.Increment(ref m_queueLength) == 1)
84 if (Interlocked.Increment(ref m_queueLength) == 1)
85 m_queueEvent.Set();
85 m_queueEvent.Set();
86 }
86 }
87
87
88 public void ShowForm() {
88 public void ShowForm() {
89 m_syncGuiThread.Post(x => m_form.Show(), null);
89 m_syncGuiThread.Post(x => m_form.Show(), null);
90 }
90 }
91
91
92 public void HideForm() {
92 public void HideForm() {
93 m_syncGuiThread.Post(x => m_form.Hide(), null);
93 m_syncGuiThread.Post(x => m_form.Hide(), null);
94 }
94 }
95
95
96 void Terminate() {
96 void Terminate() {
97 m_exitPending = true;
97 m_exitPending = true;
98 Resume();
98 Resume();
99 m_syncGuiThread.Post(x => Application.ExitThread(), null);
99 m_syncGuiThread.Post(x => Application.ExitThread(), null);
100 }
100 }
101
101
102 protected override void Dispose(bool disposing) {
102 protected override void Dispose(bool disposing) {
103 if (disposing) {
103 if (disposing) {
104 Terminate();
104 Terminate();
105 m_guiFinished.Join();
105 m_guiFinished.Join();
106 }
106 }
107 base.Dispose(disposing);
107 base.Dispose(disposing);
108 }
108 }
109
109
110 protected override void WriteEntry(TraceContext context, EventText text, string channel) {
110 public override void Write(LogEventArgs args, object entry) {
111 var item = new TraceViewItem {
111 var item = new TraceViewItem {
112 Indent = text.indent,
112 Indent = args.Operation.Level,
113 Message = text.content,
113 Message = entry.ToString(),
114 Thread = context.ThreadId,
114 Thread = args.ThreadId,
115 Channel = channel,
115 Channel = args.ChannelName,
116 Timestamp = Environment.TickCount
116 Timestamp = Environment.TickCount
117 };
117 };
118
118
119 Enqueue(item);
119 Enqueue(item);
120 }
120 }
121 }
121 }
122 }
122 }
@@ -1,94 +1,114
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>8.0.30703</ProductVersion>
7 </ProductVersion>
8 <SchemaVersion>2.0</SchemaVersion>
7 <SchemaVersion>2.0</SchemaVersion>
9 <ProjectGuid>{2F31E405-E267-4195-A05D-574093C21209}</ProjectGuid>
8 <ProjectGuid>{2F31E405-E267-4195-A05D-574093C21209}</ProjectGuid>
10 <OutputType>Library</OutputType>
9 <OutputType>Library</OutputType>
11 <AppDesignerFolder>Properties</AppDesignerFolder>
10 <AppDesignerFolder>Properties</AppDesignerFolder>
12 <RootNamespace>Implab.Fx.Test</RootNamespace>
11 <RootNamespace>Implab.Fx.Test</RootNamespace>
13 <AssemblyName>Implab.Fx.Test</AssemblyName>
12 <AssemblyName>Implab.Fx.Test</AssemblyName>
14 <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
13 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
15 <FileAlignment>512</FileAlignment>
14 <FileAlignment>512</FileAlignment>
16 <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
15 <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
16 <TargetFrameworkProfile />
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 <Prefer32Bit>false</Prefer32Bit>
26 </PropertyGroup>
27 </PropertyGroup>
27 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
28 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
28 <DebugType>pdbonly</DebugType>
29 <DebugType>pdbonly</DebugType>
29 <Optimize>true</Optimize>
30 <Optimize>true</Optimize>
30 <OutputPath>bin\Release\</OutputPath>
31 <OutputPath>bin\Release\</OutputPath>
31 <DefineConstants>TRACE</DefineConstants>
32 <DefineConstants>TRACE</DefineConstants>
32 <ErrorReport>prompt</ErrorReport>
33 <ErrorReport>prompt</ErrorReport>
33 <WarningLevel>4</WarningLevel>
34 <WarningLevel>4</WarningLevel>
35 <Prefer32Bit>false</Prefer32Bit>
36 </PropertyGroup>
37 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
38 <DebugSymbols>true</DebugSymbols>
39 <DebugType>full</DebugType>
40 <Optimize>false</Optimize>
41 <OutputPath>bin\Debug\</OutputPath>
42 <DefineConstants>DEBUG;TRACE</DefineConstants>
43 <ErrorReport>prompt</ErrorReport>
44 <WarningLevel>4</WarningLevel>
45 <Prefer32Bit>false</Prefer32Bit>
46 </PropertyGroup>
47 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
48 <DebugType>pdbonly</DebugType>
49 <Optimize>true</Optimize>
50 <OutputPath>bin\Release\</OutputPath>
51 <DefineConstants>TRACE</DefineConstants>
52 <ErrorReport>prompt</ErrorReport>
53 <WarningLevel>4</WarningLevel>
54 <Prefer32Bit>false</Prefer32Bit>
34 </PropertyGroup>
55 </PropertyGroup>
35 <ItemGroup>
56 <ItemGroup>
36 <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
57 <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
37 <Reference Include="System" />
58 <Reference Include="System" />
38 <Reference Include="System.Core">
59 <Reference Include="System.Core">
39 <RequiredTargetFramework>3.5</RequiredTargetFramework>
60 <RequiredTargetFramework>3.5</RequiredTargetFramework>
40 </Reference>
61 </Reference>
41 <Reference Include="System.Data" />
62 <Reference Include="System.Data" />
42 <Reference Include="System.Drawing" />
63 <Reference Include="System.Drawing" />
43 <Reference Include="System.Windows.Forms" />
64 <Reference Include="System.Windows.Forms" />
44 <Reference Include="System.Xml" />
65 <Reference Include="System.Xml" />
45 <Reference Include="WindowsBase" />
66 <Reference Include="WindowsBase" />
46 </ItemGroup>
67 </ItemGroup>
47 <ItemGroup>
68 <ItemGroup>
48 <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
49 <Visible>False</Visible>
50 </CodeAnalysisDependentAssemblyPaths>
51 </ItemGroup>
52 <ItemGroup>
53 <Compile Include="Properties\AssemblyInfo.cs" />
69 <Compile Include="Properties\AssemblyInfo.cs" />
54 <Compile Include="OverlayTest.cs" />
70 <Compile Include="OverlayTest.cs" />
55 <Compile Include="Sample\MainForm.cs">
71 <Compile Include="Sample\MainForm.cs">
56 <SubType>Form</SubType>
72 <SubType>Form</SubType>
57 </Compile>
73 </Compile>
58 <Compile Include="Sample\MainForm.Designer.cs">
74 <Compile Include="Sample\MainForm.Designer.cs">
59 <DependentUpon>MainForm.cs</DependentUpon>
75 <DependentUpon>MainForm.cs</DependentUpon>
60 </Compile>
76 </Compile>
61 <Compile Include="Sample\OverlayForm.cs">
77 <Compile Include="Sample\OverlayForm.cs">
62 <SubType>Form</SubType>
78 <SubType>Form</SubType>
63 </Compile>
79 </Compile>
64 <Compile Include="Sample\OverlayForm.Designer.cs">
80 <Compile Include="Sample\OverlayForm.Designer.cs">
65 <DependentUpon>OverlayForm.cs</DependentUpon>
81 <DependentUpon>OverlayForm.cs</DependentUpon>
66 </Compile>
82 </Compile>
67 </ItemGroup>
83 </ItemGroup>
68 <ItemGroup>
84 <ItemGroup>
69 <EmbeddedResource Include="Sample\MainForm.resx">
85 <EmbeddedResource Include="Sample\MainForm.resx">
70 <DependentUpon>MainForm.cs</DependentUpon>
86 <DependentUpon>MainForm.cs</DependentUpon>
87 <LogicalName>
88 </LogicalName>
71 </EmbeddedResource>
89 </EmbeddedResource>
72 <EmbeddedResource Include="Sample\OverlayForm.resx">
90 <EmbeddedResource Include="Sample\OverlayForm.resx">
73 <DependentUpon>OverlayForm.cs</DependentUpon>
91 <DependentUpon>OverlayForm.cs</DependentUpon>
92 <LogicalName>
93 </LogicalName>
74 </EmbeddedResource>
94 </EmbeddedResource>
75 </ItemGroup>
95 </ItemGroup>
76 <ItemGroup>
96 <ItemGroup>
77 <ProjectReference Include="..\Implab.Fx\Implab.Fx.csproj">
97 <ProjectReference Include="..\Implab.Fx\Implab.Fx.csproj">
78 <Project>{06E706F8-6881-43EB-927E-FFC503AF6ABC}</Project>
98 <Project>{06E706F8-6881-43EB-927E-FFC503AF6ABC}</Project>
79 <Name>Implab.Fx</Name>
99 <Name>Implab.Fx</Name>
80 </ProjectReference>
100 </ProjectReference>
81 <ProjectReference Include="..\Implab\Implab.csproj">
101 <ProjectReference Include="..\Implab\Implab.csproj">
82 <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
102 <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
83 <Name>Implab</Name>
103 <Name>Implab</Name>
84 </ProjectReference>
104 </ProjectReference>
85 </ItemGroup>
105 </ItemGroup>
86 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
106 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
87 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
107 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
88 Other similar extension points exist, see Microsoft.Common.targets.
108 Other similar extension points exist, see Microsoft.Common.targets.
89 <Target Name="BeforeBuild">
109 <Target Name="BeforeBuild">
90 </Target>
110 </Target>
91 <Target Name="AfterBuild">
111 <Target Name="AfterBuild">
92 </Target>
112 </Target>
93 -->
113 -->
94 </Project> No newline at end of file
114 </Project>
@@ -1,35 +1,38
1 using System;
1 using System.Windows.Forms;
2 using System.Text;
2 using Implab.Fx.Test.Sample;
3 using System.Collections.Generic;
3 using Implab.Fx;
4 using System.Linq;
4
5 #if MONO
6
7 using NUnit.Framework;
8 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
9 using TestMethod = NUnit.Framework.TestAttribute;
10
11 #else
12
5 using Microsoft.VisualStudio.TestTools.UnitTesting;
13 using Microsoft.VisualStudio.TestTools.UnitTesting;
6 using System.Windows.Forms;
14
7 using Implab.Fx.Test.Sample;
15 #endif
8 using System.Drawing;
9 using Implab.Fx;
10
16
11 namespace Implab.Fx.Test
17 namespace Implab.Fx.Test
12 {
18 {
13 [TestClass]
19 [TestClass]
14 public class OverlayTest
20 public class OverlayTest
15 {
21 {
16 [TestMethod]
22 [TestMethod]
17 public void TestMethod1()
23 public void TestMethod1()
18 {
24 {
19 var mainForm = new MainForm();
25 var mainForm = new MainForm();
20
26
21 mainForm.ButtonEvent += (sender, args) =>
27 mainForm.ButtonEvent += (sender, args) =>
22 {
28 {
23 var overlay = new OverlayForm();
29 var overlay = new OverlayForm();
24 mainForm.OverlayFadeIn(overlay).Then(
30 mainForm.OverlayFadeIn(overlay).On(
25 o => o.ButtonEvent += (s2, args2) =>
31 o => o.ButtonEvent += (s2, args2) => o.CloseFadeOut()
26 {
27 o.CloseFadeOut();
28 }
29 );
32 );
30 };
33 };
31
34
32 Application.Run(mainForm);
35 Application.Run(mainForm);
33 }
36 }
34 }
37 }
35 }
38 }
@@ -1,89 +1,95
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System.Linq;
4 using System.Text;
4 using System.Text;
5 using System.Windows.Forms;
5 using System.Windows.Forms;
6 using System.Diagnostics;
6 using System.Diagnostics;
7
7
8 namespace Implab.Fx
8 namespace Implab.Fx
9 {
9 {
10 public static class AnimationHelpers
10 public static class AnimationHelpers
11 {
11 {
12 public static Animation<TTarget> AnimateProperty<TTarget, TVal>(this Animation<TTarget> animation, Action<TTarget, TVal> setter, Func<TTarget, TVal> getter, TVal newValue, Func<TVal, TVal, int, int, TVal> fx) where TTarget : class
12 public static Animation<TTarget> AnimateProperty<TTarget, TVal>(this Animation<TTarget> animation, Action<TTarget, TVal> setter, Func<TTarget, TVal> getter, TVal newValue, Func<TVal, TVal, int, int, TVal> fx) where TTarget : class
13 {
13 {
14 if (animation == null)
14 if (animation == null)
15 throw new ArgumentNullException("animation");
15 throw new ArgumentNullException("animation");
16
16
17 TVal oldValue = getter(animation.Traget);
17 TVal oldValue = getter(animation.Traget);
18
18
19 animation.Step += (target, elaped, duration) =>
19 animation.Step += (target, elaped, duration) =>
20 {
20 {
21 var value = fx(oldValue, newValue, elaped, duration);
21 var value = fx(oldValue, newValue, elaped, duration);
22 setter(target, value);
22 setter(target, value);
23 };
23 };
24
24
25 return animation;
25 return animation;
26 }
26 }
27
27
28 public static Animation<T> AnimateTransparency<T>(this T ctl, float newValue) where T : Form
28 public static Animation<T> AnimateTransparency<T>(this T ctl, float newValue) where T : Form
29 {
29 {
30 var anim = new Animation<T>(ctl);
30 var anim = new Animation<T>(ctl);
31
31
32 anim.AnimateProperty(
32 anim.AnimateProperty(
33 (target, value) => target.Opacity = value,
33 (target, value) => target.Opacity = value,
34 target => target.Opacity,
34 target => target.Opacity,
35 newValue,
35 newValue,
36 (ov, nv, el, du) => ov + ((float)el / du) * (nv - ov)
36 (ov, nv, el, du) => ov + ((float)el / du) * (nv - ov)
37 );
37 );
38 return anim;
38 return anim;
39 }
39 }
40
40
41 public static IPromise<T> CloseFadeOut<T>(this T ctl) where T : Form
41 public static IPromise<T> CloseFadeOut<T>(this T ctl) where T : Form
42 {
42 {
43 var anim = ctl.AnimateTransparency(0);
43 var anim = ctl.AnimateTransparency(0);
44
44
45 return anim.Play().DispatchToControl(ctl).Then(frm => frm.Close());
45 return anim
46 .Play()
47 .DispatchToControl(ctl)
48 .Then(frm => {
49 frm.Close();
50 return frm;
51 });
46 }
52 }
47
53
48 public static IPromise<T> OverlayFadeIn<T>(this Form that, T overlay) where T : Form
54 public static IPromise<T> OverlayFadeIn<T>(this Form that, T overlay) where T : Form
49 {
55 {
50 if (that == null)
56 if (that == null)
51 throw new ArgumentNullException("that");
57 throw new ArgumentNullException("that");
52 if (overlay == null)
58 if (overlay == null)
53 throw new ArgumentNullException("overlay");
59 throw new ArgumentNullException("overlay");
54
60
55 // setup overlay
61 // setup overlay
56 overlay.Opacity = 0;
62 overlay.Opacity = 0;
57 overlay.FormBorderStyle = FormBorderStyle.None;
63 overlay.FormBorderStyle = FormBorderStyle.None;
58 overlay.ShowInTaskbar = false;
64 overlay.ShowInTaskbar = false;
59
65
60 that.AddOwnedForm(overlay);
66 that.AddOwnedForm(overlay);
61
67
62 EventHandler handler = (object sender, EventArgs args) =>
68 EventHandler handler = (object sender, EventArgs args) =>
63 {
69 {
64 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
70 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
65 };
71 };
66
72
67 // attach handlers
73 // attach handlers
68 that.Move += handler;
74 that.Move += handler;
69 that.Resize += handler;
75 that.Resize += handler;
70 that.Shown += handler;
76 that.Shown += handler;
71
77
72 // remove handlers to release overlay
78 // remove handlers to release overlay
73 overlay.FormClosed += (sender, args) =>
79 overlay.FormClosed += (sender, args) =>
74 {
80 {
75 that.Move -= handler;
81 that.Move -= handler;
76 that.Resize -= handler;
82 that.Resize -= handler;
77 that.Shown -= handler;
83 that.Shown -= handler;
78 };
84 };
79
85
80 overlay.Show(that);
86 overlay.Show(that);
81 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
87 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
82
88
83 return overlay
89 return overlay
84 .AnimateTransparency(1)
90 .AnimateTransparency(1)
85 .Play()
91 .Play()
86 .DispatchToControl(overlay);
92 .DispatchToControl(overlay);
87 }
93 }
88 }
94 }
89 }
95 }
@@ -1,64 +1,88
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>8.0.30703</ProductVersion>
6 <ProductVersion>8.0.30703</ProductVersion>
7 <SchemaVersion>2.0</SchemaVersion>
7 <SchemaVersion>2.0</SchemaVersion>
8 <ProjectGuid>{06E706F8-6881-43EB-927E-FFC503AF6ABC}</ProjectGuid>
8 <ProjectGuid>{06E706F8-6881-43EB-927E-FFC503AF6ABC}</ProjectGuid>
9 <OutputType>Library</OutputType>
9 <OutputType>Library</OutputType>
10 <AppDesignerFolder>Properties</AppDesignerFolder>
10 <AppDesignerFolder>Properties</AppDesignerFolder>
11 <RootNamespace>Implab.Fx</RootNamespace>
11 <RootNamespace>Implab.Fx</RootNamespace>
12 <AssemblyName>Implab.Fx</AssemblyName>
12 <AssemblyName>Implab.Fx</AssemblyName>
13 <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
13 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
14 <FileAlignment>512</FileAlignment>
14 <FileAlignment>512</FileAlignment>
15 <ReleaseVersion>0.2</ReleaseVersion>
16 <TargetFrameworkProfile />
15 </PropertyGroup>
17 </PropertyGroup>
16 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
18 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
17 <DebugSymbols>true</DebugSymbols>
19 <DebugSymbols>true</DebugSymbols>
18 <DebugType>full</DebugType>
20 <DebugType>full</DebugType>
19 <Optimize>false</Optimize>
21 <Optimize>false</Optimize>
20 <OutputPath>bin\Debug\</OutputPath>
22 <OutputPath>bin\Debug\</OutputPath>
21 <DefineConstants>DEBUG;TRACE</DefineConstants>
23 <DefineConstants>DEBUG;TRACE</DefineConstants>
22 <ErrorReport>prompt</ErrorReport>
24 <ErrorReport>prompt</ErrorReport>
23 <WarningLevel>4</WarningLevel>
25 <WarningLevel>4</WarningLevel>
26 <Prefer32Bit>false</Prefer32Bit>
24 </PropertyGroup>
27 </PropertyGroup>
25 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
28 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
26 <DebugType>pdbonly</DebugType>
29 <DebugType>pdbonly</DebugType>
27 <Optimize>true</Optimize>
30 <Optimize>true</Optimize>
28 <OutputPath>bin\Release\</OutputPath>
31 <OutputPath>bin\Release\</OutputPath>
29 <DefineConstants>TRACE</DefineConstants>
32 <DefineConstants>TRACE</DefineConstants>
30 <ErrorReport>prompt</ErrorReport>
33 <ErrorReport>prompt</ErrorReport>
31 <WarningLevel>4</WarningLevel>
34 <WarningLevel>4</WarningLevel>
35 <Prefer32Bit>false</Prefer32Bit>
36 </PropertyGroup>
37 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
38 <DebugSymbols>true</DebugSymbols>
39 <DebugType>full</DebugType>
40 <Optimize>false</Optimize>
41 <OutputPath>bin\Debug\</OutputPath>
42 <DefineConstants>DEBUG;TRACE</DefineConstants>
43 <ErrorReport>prompt</ErrorReport>
44 <WarningLevel>4</WarningLevel>
45 <Prefer32Bit>false</Prefer32Bit>
46 </PropertyGroup>
47 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
48 <DebugType>pdbonly</DebugType>
49 <Optimize>true</Optimize>
50 <OutputPath>bin\Release\</OutputPath>
51 <DefineConstants>TRACE</DefineConstants>
52 <ErrorReport>prompt</ErrorReport>
53 <WarningLevel>4</WarningLevel>
54 <Prefer32Bit>false</Prefer32Bit>
32 </PropertyGroup>
55 </PropertyGroup>
33 <ItemGroup>
56 <ItemGroup>
34 <Reference Include="System" />
57 <Reference Include="System" />
35 <Reference Include="System.Core" />
58 <Reference Include="System.Core" />
36 <Reference Include="System.Drawing" />
59 <Reference Include="System.Drawing" />
37 <Reference Include="System.Windows.Forms" />
60 <Reference Include="System.Windows.Forms" />
38 <Reference Include="System.Xml.Linq" />
61 <Reference Include="System.Xml.Linq" />
39 <Reference Include="System.Data.DataSetExtensions" />
62 <Reference Include="System.Data.DataSetExtensions" />
40 <Reference Include="Microsoft.CSharp" />
63 <Reference Include="Microsoft.CSharp" />
41 <Reference Include="System.Data" />
64 <Reference Include="System.Data" />
42 <Reference Include="System.Xml" />
65 <Reference Include="System.Xml" />
43 </ItemGroup>
66 </ItemGroup>
44 <ItemGroup>
67 <ItemGroup>
45 <Compile Include="Animation.cs" />
68 <Compile Include="Animation.cs" />
46 <Compile Include="AnimationHelpers.cs" />
69 <Compile Include="AnimationHelpers.cs" />
47 <Compile Include="PromiseHelpers.cs" />
70 <Compile Include="PromiseHelpers.cs" />
48 <Compile Include="Properties\AssemblyInfo.cs" />
71 <Compile Include="Properties\AssemblyInfo.cs" />
72 <Compile Include="ControlBoundPromise.cs" />
49 </ItemGroup>
73 </ItemGroup>
50 <ItemGroup>
74 <ItemGroup>
51 <ProjectReference Include="..\Implab\Implab.csproj">
75 <ProjectReference Include="..\Implab\Implab.csproj">
52 <Project>{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}</Project>
76 <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
53 <Name>Implab</Name>
77 <Name>Implab</Name>
54 </ProjectReference>
78 </ProjectReference>
55 </ItemGroup>
79 </ItemGroup>
56 <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
80 <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
57 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
81 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
58 Other similar extension points exist, see Microsoft.Common.targets.
82 Other similar extension points exist, see Microsoft.Common.targets.
59 <Target Name="BeforeBuild">
83 <Target Name="BeforeBuild">
60 </Target>
84 </Target>
61 <Target Name="AfterBuild">
85 <Target Name="AfterBuild">
62 </Target>
86 </Target>
63 -->
87 -->
64 </Project> No newline at end of file
88 </Project>
@@ -1,95 +1,43
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Windows.Forms;
2 using System.Windows.Forms;
6 using System.Threading;
3 using System.Threading;
7
4
8 namespace Implab.Fx
5 namespace Implab.Fx
9 {
6 {
10 public static class PromiseHelpers
7 public static class PromiseHelpers
11 {
8 {
12 /// <summary>
9 /// <summary>
13 /// Перенаправляет обработку обещания в поток указанного элемента управления.
10 /// Перенаправляет обработку обещания в поток указанного элемента управления.
14 /// </summary>
11 /// </summary>
15 /// <typeparam name="T">Тип результата обещания</typeparam>
12 /// <typeparam name="T">Тип результата обещания</typeparam>
16 /// <param name="that">Исходное обещание</param>
13 /// <param name="that">Исходное обещание</param>
17 /// <param name="ctl">Элемент управления</param>
14 /// <param name="ctl">Элемент управления</param>
18 /// <returns>Новое обещание, обработчики которого будут выполнены в потоке элемента управления.</returns>
15 /// <returns>Новое обещание, обработчики которого будут выполнены в потоке элемента управления.</returns>
19 /// <exception cref="ArgumentNullException">Параметр не может быть <c>null</c>.</exception>
16 /// <exception cref="ArgumentNullException">Параметр не может быть <c>null</c>.</exception>
20 /// <example>
17 /// <example>
21 /// client
18 /// client
22 /// .Get("description.txt") // returns a promise
19 /// .Get("description.txt") // returns a promise
23 /// .DirectToControl(m_ctl) // handle the promise in the thread of the control
20 /// .DispatchToControl(m_ctl) // handle the promise in the thread of the control
24 /// .Then(
21 /// .Then(
25 /// description => m_ctl.Text = description // now it's safe
22 /// description => m_ctl.Text = description // now it's safe
26 /// )
23 /// )
27 /// </example>
24 /// </example>
28 public static Promise<T> DispatchToControl<T>(this Promise<T> that, Control ctl)
25 public static IPromise<T> DispatchToControl<T>(this IPromise<T> that, Control ctl)
29 {
30 if (that == null)
31 throw new ArgumentNullException("that");
32 if (ctl == null)
33 throw new ArgumentNullException("ctl");
34
35 var directed = new Promise<T>();
36
37 that.Then(
38 res =>
39 {
26 {
40 if (ctl.InvokeRequired)
27 Safe.ArgumentNotNull(that, "that");
41 ctl.Invoke(new Action<T>(directed.Resolve), res);
28 Safe.ArgumentNotNull(ctl, "ctl");
42 else
29
43 directed.Resolve(res);
30 var directed = new ControlBoundPromise<T>(ctl);
44 },
31
45 err =>
32 directed.On(that.Cancel, PromiseEventType.Cancelled);
46 {
33
47 if (ctl.InvokeRequired)
34 that.On(
48 ctl.Invoke(new Action<Exception>(directed.Reject), err);
35 directed.Resolve,
49 else
36 directed.Reject,
50 directed.Reject(err);
37 directed.Cancel
51 }
52 );
38 );
53
39
54 return directed;
40 return directed;
55 }
41 }
56
57 /// <summary>
58 /// Направляет обработку обещания в текущий поток, если у него существует контекст синхронизации.
59 /// </summary>
60 /// <typeparam name="T">Тип результата обещания.</typeparam>
61 /// <param name="that">Обещание которое нужно обработать в текущем потоке.</param>
62 /// <returns>Перенаправленное обещание.</returns>
63 public static Promise<T> DispatchToCurrentThread<T>(this Promise<T> that)
64 {
65 var sync = SynchronizationContext.Current;
66 if (sync == null)
67 throw new InvalidOperationException("The current thread doesn't have a syncronization context");
68 return DispatchToSyncContext(that, sync);
69 }
70
71 /// <summary>
72 /// Направляет обработку обещания в указанный контекст синхронизации.
73 /// </summary>
74 /// <typeparam name="T">Тип результата обещания.</typeparam>
75 /// <param name="that">Обещание, которое требуется обработать в указанном контексте синхронизации.</param>
76 /// <param name="sync">Контекст синхронизации в который будет направлено обещание.</param>
77 /// <returns>Новое обещание, которое будет обрабатываться в указанном контексте.</returns>
78 public static Promise<T> DispatchToSyncContext<T>(this Promise<T> that, SynchronizationContext sync)
79 {
80 if (that == null)
81 throw new ArgumentNullException("that");
82 if (sync == null)
83 throw new ArgumentNullException("sync");
84
85 var d = new Promise<T>();
86
87 that.Then(
88 res => sync.Post(state => d.Resolve(res), null),
89 err => sync.Post(state => d.Reject(err), null)
90 );
91
92 return d;
93 }
42 }
94 }
43 }
95 }
@@ -1,36 +1,35
1 using System.Reflection;
1 using System.Reflection;
2 using System.Runtime.CompilerServices;
2 using System.Runtime.CompilerServices;
3 using System.Runtime.InteropServices;
3 using System.Runtime.InteropServices;
4
4
5 // General Information about an assembly is controlled through the following
5 // General Information about an assembly is controlled through the following
6 // set of attributes. Change these attribute values to modify the information
6 // set of attributes. Change these attribute values to modify the information
7 // associated with an assembly.
7 // associated with an assembly.
8 [assembly: AssemblyTitle("Implab.Fx")]
8 [assembly: AssemblyTitle("Implab.Fx")]
9 [assembly: AssemblyDescription("")]
9 [assembly: AssemblyDescription("")]
10 [assembly: AssemblyConfiguration("")]
10 [assembly: AssemblyConfiguration("")]
11 [assembly: AssemblyCompany("")]
11 [assembly: AssemblyCompany("")]
12 [assembly: AssemblyProduct("Implab.Fx")]
12 [assembly: AssemblyProduct("Implab.Fx")]
13 [assembly: AssemblyCopyright("Copyright © 2013")]
13 [assembly: AssemblyCopyright("Copyright © 2013")]
14 [assembly: AssemblyTrademark("")]
14 [assembly: AssemblyTrademark("")]
15 [assembly: AssemblyCulture("")]
15 [assembly: AssemblyCulture("")]
16
16
17 // Setting ComVisible to false makes the types in this assembly not visible
17 // Setting ComVisible to false makes the types in this assembly not visible
18 // to COM components. If you need to access a type in this assembly from
18 // to COM components. If you need to access a type in this assembly from
19 // COM, set the ComVisible attribute to true on that type.
19 // COM, set the ComVisible attribute to true on that type.
20 [assembly: ComVisible(false)]
20 [assembly: ComVisible(false)]
21
21
22 // The following GUID is for the ID of the typelib if this project is exposed to COM
22 // The following GUID is for the ID of the typelib if this project is exposed to COM
23 [assembly: Guid("d239c29f-98e2-4942-9569-554a8511d07b")]
23 [assembly: Guid("d239c29f-98e2-4942-9569-554a8511d07b")]
24
24
25 // Version information for an assembly consists of the following four values:
25 // Version information for an assembly consists of the following four values:
26 //
26 //
27 // Major Version
27 // Major Version
28 // Minor Version
28 // Minor Version
29 // Build Number
29 // Build Number
30 // Revision
30 // Revision
31 //
31 //
32 // You can specify all the values or you can default the Build and Revision Numbers
32 // You can specify all the values or you can default the Build and Revision Numbers
33 // by using the '*' as shown below:
33 // by using the '*' as shown below:
34 // [assembly: AssemblyVersion("1.0.*")]
34 // [assembly: AssemblyVersion("1.0.*")]
35 [assembly: AssemblyVersion("1.0.0.0")]
35 [assembly: AssemblyVersion("2.0.*")]
36 [assembly: AssemblyFileVersion("1.0.0.0")]
This diff has been collapsed as it changes many lines, (571 lines changed) Show them Hide them
@@ -1,386 +1,863
1 using System;
1 using System;
2 using Microsoft.VisualStudio.TestTools.UnitTesting;
3 using System.Reflection;
2 using System.Reflection;
4 using System.Threading;
3 using System.Threading;
5 using Implab.Parallels;
4 using Implab.Parallels;
6
5
6 #if MONO
7
8 using NUnit.Framework;
9 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
10 using TestMethodAttribute = NUnit.Framework.TestAttribute;
11
12 #else
13
14 using Microsoft.VisualStudio.TestTools.UnitTesting;
15
16 #endif
17
7 namespace Implab.Test {
18 namespace Implab.Test {
8 [TestClass]
19 [TestClass]
9 public class AsyncTests {
20 public class AsyncTests {
10 [TestMethod]
21 [TestMethod]
11 public void ResolveTest() {
22 public void ResolveTest() {
12 int res = -1;
23 int res = -1;
13 var p = new Promise<int>();
24 var p = new Promise<int>();
14 p.Then(x => res = x);
25 p.Then(x => res = x);
15 p.Resolve(100);
26 p.Resolve(100);
16
27
17 Assert.AreEqual(100, res);
28 Assert.AreEqual(100, res);
18 }
29 }
19
30
20 [TestMethod]
31 [TestMethod]
21 public void RejectTest() {
32 public void RejectTest() {
22 int res = -1;
33 int res = -1;
23 Exception err = null;
34 Exception err = null;
24
35
25 var p = new Promise<int>();
36 var p = new Promise<int>();
26 p.Then(x => res = x, e => err = e);
37 p.Then(
38 x => res = x,
39 e => {
40 err = e;
41 return -2;
42 }
43 );
27 p.Reject(new ApplicationException("error"));
44 p.Reject(new ApplicationException("error"));
28
45
29 Assert.AreEqual(res, -1);
46 Assert.AreEqual(res, -1);
30 Assert.AreEqual(err.Message, "error");
47 Assert.AreEqual(err.Message, "error");
31
48
32 }
49 }
33
50
34 [TestMethod]
51 [TestMethod]
52 public void CancelExceptionTest() {
53 var p = new Promise<bool>();
54 p.CancelOperation(null);
55
56 var p2 = p.Then(x => x, null, reason => {
57 throw new ApplicationException("CANCELLED");
58 });
59
60 try {
61 p2.Join();
62 Assert.Fail();
63 } catch (ApplicationException err) {
64 Assert.AreEqual("CANCELLED", err.InnerException.Message);
65 }
66
67 }
68
69 [TestMethod]
70 public void ContinueOnCancelTest() {
71 var p = new Promise<bool>();
72 p.CancelOperation(null);
73
74 var p2 = p
75 .Then(x => x, null, reason => {
76 throw new ApplicationException("CANCELLED");
77 })
78 .Then(x => x, e => true);
79
80 Assert.AreEqual(true, p2.Join());
81 }
82
83 [TestMethod]
35 public void JoinSuccessTest() {
84 public void JoinSuccessTest() {
36 var p = new Promise<int>();
85 var p = new Promise<int>();
37 p.Resolve(100);
86 p.Resolve(100);
38 Assert.AreEqual(p.Join(), 100);
87 Assert.AreEqual(p.Join(), 100);
39 }
88 }
40
89
41 [TestMethod]
90 [TestMethod]
42 public void JoinFailTest() {
91 public void JoinFailTest() {
43 var p = new Promise<int>();
92 var p = new Promise<int>();
44 p.Reject(new ApplicationException("failed"));
93 p.Reject(new ApplicationException("failed"));
45
94
46 try {
95 try {
47 p.Join();
96 p.Join();
48 throw new ApplicationException("WRONG!");
97 throw new ApplicationException("WRONG!");
49 } catch (TargetInvocationException err) {
98 } catch (TargetInvocationException err) {
50 Assert.AreEqual(err.InnerException.Message, "failed");
99 Assert.AreEqual(err.InnerException.Message, "failed");
51 } catch {
100 } catch {
52 Assert.Fail("Got wrong excaption");
101 Assert.Fail("Got wrong excaption");
53 }
102 }
54 }
103 }
55
104
56 [TestMethod]
105 [TestMethod]
57 public void MapTest() {
106 public void MapTest() {
58 var p = new Promise<int>();
107 var p = new Promise<int>();
59
108
60 var p2 = p.Map(x => x.ToString());
109 var p2 = p.Then(x => x.ToString());
61 p.Resolve(100);
110 p.Resolve(100);
62
111
63 Assert.AreEqual(p2.Join(), "100");
112 Assert.AreEqual(p2.Join(), "100");
64 }
113 }
65
114
66 [TestMethod]
115 [TestMethod]
67 public void FixErrorTest() {
116 public void FixErrorTest() {
68 var p = new Promise<int>();
117 var p = new Promise<int>();
69
118
70 var p2 = p.Error(e => 101);
119 var p2 = p.Then(x => x, e => 101);
71
120
72 p.Reject(new Exception());
121 p.Reject(new Exception());
73
122
74 Assert.AreEqual(p2.Join(), 101);
123 Assert.AreEqual(p2.Join(), 101);
75 }
124 }
76
125
77 [TestMethod]
126 [TestMethod]
78 public void ChainTest() {
127 public void ChainTest() {
79 var p1 = new Promise<int>();
128 var p1 = new Promise<int>();
80
129
81 var p3 = p1.Chain(x => {
130 var p3 = p1.Chain(x => {
82 var p2 = new Promise<string>();
131 var p2 = new Promise<string>();
83 p2.Resolve(x.ToString());
132 p2.Resolve(x.ToString());
84 return p2;
133 return p2;
85 });
134 });
86
135
87 p1.Resolve(100);
136 p1.Resolve(100);
88
137
89 Assert.AreEqual(p3.Join(), "100");
138 Assert.AreEqual(p3.Join(), "100");
90 }
139 }
91
140
92 [TestMethod]
141 [TestMethod]
142 public void ChainFailTest() {
143 var p1 = new Promise<int>();
144
145 var p3 = p1.Chain(x => {
146 var p2 = new Promise<string>();
147 p2.Reject(new Exception("DIE!!!"));
148 return p2;
149 });
150
151 p1.Resolve(100);
152
153 Assert.IsTrue(p3.IsResolved);
154 }
155
156 [TestMethod]
93 public void PoolTest() {
157 public void PoolTest() {
94 var pid = Thread.CurrentThread.ManagedThreadId;
158 var pid = Thread.CurrentThread.ManagedThreadId;
95 var p = AsyncPool.Invoke(() => Thread.CurrentThread.ManagedThreadId);
159 var p = AsyncPool.Invoke(() => Thread.CurrentThread.ManagedThreadId);
96
160
97 Assert.AreNotEqual(pid, p.Join());
161 Assert.AreNotEqual(pid, p.Join());
98 }
162 }
99
163
100 [TestMethod]
164 [TestMethod]
101 public void WorkerPoolSizeTest() {
165 public void WorkerPoolSizeTest() {
102 var pool = new WorkerPool(5, 10, 0);
166 var pool = new WorkerPool(5, 10, 1);
103
167
104 Assert.AreEqual(5, pool.PoolSize);
168 Assert.AreEqual(5, pool.PoolSize);
105
169
106 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
170 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
107 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
171 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
108 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
172 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
109
173
110 Assert.AreEqual(5, pool.PoolSize);
174 Assert.AreEqual(5, pool.PoolSize);
111
175
112 for (int i = 0; i < 100; i++)
176 for (int i = 0; i < 100; i++)
113 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
177 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
114 Thread.Sleep(200);
178 Thread.Sleep(200);
115 Assert.AreEqual(10, pool.PoolSize);
179 Assert.AreEqual(10, pool.PoolSize);
116
180
117 pool.Dispose();
181 pool.Dispose();
118 }
182 }
119
183
120 [TestMethod]
184 [TestMethod]
121 public void WorkerPoolCorrectTest() {
185 public void WorkerPoolCorrectTest() {
122 var pool = new WorkerPool(0,1000,100);
186 var pool = new WorkerPool(0,1000,100);
123
187
124 int iterations = 1000;
188 const int iterations = 1000;
125 int pending = iterations;
189 int pending = iterations;
126 var stop = new ManualResetEvent(false);
190 var stop = new ManualResetEvent(false);
127
191
128 var count = 0;
192 var count = 0;
129 for (int i = 0; i < iterations; i++) {
193 for (int i = 0; i < iterations; i++) {
130 pool
194 pool
131 .Invoke(() => 1)
195 .Invoke(() => 1)
132 .Then(x => Interlocked.Add(ref count, x))
196 .Then(x => Interlocked.Add(ref count, x))
133 .Then(x => Math.Log10(x))
197 .Then(x => Math.Log10(x))
134 .Anyway(() => {
198 .On(() => {
135 Interlocked.Decrement(ref pending);
199 Interlocked.Decrement(ref pending);
136 if (pending == 0)
200 if (pending == 0)
137 stop.Set();
201 stop.Set();
138 });
202 }, PromiseEventType.All);
139 }
203 }
140
204
141 stop.WaitOne();
205 stop.WaitOne();
142
206
143 Assert.AreEqual(iterations, count);
207 Assert.AreEqual(iterations, count);
144 Console.WriteLine("Max threads: {0}", pool.MaxRunningThreads);
208 Console.WriteLine("Max threads: {0}", pool.MaxRunningThreads);
145 pool.Dispose();
209 pool.Dispose();
146
210
147 }
211 }
148
212
149 [TestMethod]
213 [TestMethod]
150 public void WorkerPoolDisposeTest() {
214 public void WorkerPoolDisposeTest() {
151 var pool = new WorkerPool(5, 20);
215 var pool = new WorkerPool(5, 20);
152 Assert.AreEqual(5, pool.PoolSize);
216 Assert.AreEqual(5, pool.PoolSize);
153 pool.Dispose();
217 pool.Dispose();
154 Thread.Sleep(500);
218 Thread.Sleep(500);
155 Assert.AreEqual(0, pool.PoolSize);
219 Assert.AreEqual(0, pool.PoolSize);
156 pool.Dispose();
220 pool.Dispose();
157 }
221 }
158
222
159 [TestMethod]
223 [TestMethod]
160 public void MTQueueTest() {
224 public void MTQueueTest() {
161 var queue = new MTQueue<int>();
225 var queue = new MTQueue<int>();
162 int res;
226 int res;
163
227
164 queue.Enqueue(10);
228 queue.Enqueue(10);
165 Assert.IsTrue(queue.TryDequeue(out res));
229 Assert.IsTrue(queue.TryDequeue(out res));
166 Assert.AreEqual(10, res);
230 Assert.AreEqual(10, res);
167 Assert.IsFalse(queue.TryDequeue(out res));
231 Assert.IsFalse(queue.TryDequeue(out res));
168
232
169 for (int i = 0; i < 1000; i++)
233 for (int i = 0; i < 1000; i++)
170 queue.Enqueue(i);
234 queue.Enqueue(i);
171
235
172 for (int i = 0; i < 1000; i++) {
236 for (int i = 0; i < 1000; i++) {
173 queue.TryDequeue(out res);
237 queue.TryDequeue(out res);
174 Assert.AreEqual(i, res);
238 Assert.AreEqual(i, res);
175 }
239 }
176
240
177 int writers = 0;
241 int writers = 0;
178 int readers = 0;
242 int readers = 0;
179 var stop = new ManualResetEvent(false);
243 var stop = new ManualResetEvent(false);
180 int total = 0;
244 int total = 0;
181
245
182 int itemsPerWriter = 1000;
246 const int itemsPerWriter = 10000;
183 int writersCount = 3;
247 const int writersCount = 10;
184
248
185 for (int i = 0; i < writersCount; i++) {
249 for (int i = 0; i < writersCount; i++) {
186 Interlocked.Increment(ref writers);
250 Interlocked.Increment(ref writers);
187 var wn = i;
188 AsyncPool
251 AsyncPool
189 .InvokeNewThread(() => {
252 .RunThread(() => {
190 for (int ii = 0; ii < itemsPerWriter; ii++) {
253 for (int ii = 0; ii < itemsPerWriter; ii++) {
191 queue.Enqueue(1);
254 queue.Enqueue(1);
192 }
255 }
193 return 1;
256 return 1;
194 })
257 })
195 .Anyway(() => Interlocked.Decrement(ref writers));
258 .On(() => Interlocked.Decrement(ref writers), PromiseEventType.All);
196 }
259 }
197
260
198 for (int i = 0; i < 10; i++) {
261 for (int i = 0; i < 10; i++) {
199 Interlocked.Increment(ref readers);
262 Interlocked.Increment(ref readers);
200 var wn = i;
201 AsyncPool
263 AsyncPool
202 .InvokeNewThread(() => {
264 .RunThread(() => {
203 int t;
265 int t;
204 do {
266 do {
205 while (queue.TryDequeue(out t))
267 while (queue.TryDequeue(out t))
206 Interlocked.Add(ref total, t);
268 Interlocked.Add(ref total, t);
207 } while (writers > 0);
269 } while (writers > 0);
208 return 1;
270 return 1;
209 })
271 })
210 .Anyway(() => {
272 .On(() => {
211 Interlocked.Decrement(ref readers);
273 Interlocked.Decrement(ref readers);
212 if (readers == 0)
274 if (readers == 0)
213 stop.Set();
275 stop.Set();
214 });
276 }, PromiseEventType.All);
215 }
277 }
216
278
217 stop.WaitOne();
279 stop.WaitOne();
218
280
219 Assert.AreEqual(itemsPerWriter * writersCount, total);
281 Assert.AreEqual(100000, total);
282 }
283
284 [TestMethod]
285 public void AsyncQueueTest() {
286 var queue = new AsyncQueue<int>();
287 int res;
288
289 queue.Enqueue(10);
290 Assert.IsTrue(queue.TryDequeue(out res));
291 Assert.AreEqual(10, res);
292 Assert.IsFalse(queue.TryDequeue(out res));
293
294 for (int i = 0; i < 1000; i++)
295 queue.Enqueue(i);
296
297 for (int i = 0; i < 1000; i++) {
298 queue.TryDequeue(out res);
299 Assert.AreEqual(i, res);
300 }
301
302 const int count = 10000000;
303
304 int res1 = 0, res2 = 0;
305 var t1 = Environment.TickCount;
306
307 AsyncPool.RunThread(
308 () => {
309 for (var i = 0; i < count; i++)
310 queue.Enqueue(1);
311 Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1);
312 },
313 () => {
314 for (var i = 0; i < count; i++)
315 queue.Enqueue(2);
316 Console.WriteLine("done writer #2: {0} ms", Environment.TickCount - t1);
317 },
318 () => {
319 int temp;
320 int i = 0;
321 while (i < count)
322 if (queue.TryDequeue(out temp)) {
323 i++;
324 res1 += temp;
325 }
326 Console.WriteLine("done reader #1: {0} ms", Environment.TickCount - t1);
327 },
328 () => {
329 int temp;
330 int i = 0;
331 while (i < count)
332 if (queue.TryDequeue(out temp)) {
333 i++;
334 res2 += temp;
335 }
336 Console.WriteLine("done reader #2: {0} ms", Environment.TickCount - t1);
337 }
338 )
339 .Bundle()
340 .Join();
341
342 Assert.AreEqual(count * 3, res1 + res2);
343
344 Console.WriteLine(
345 "done: {0} ms, summ#1: {1}, summ#2: {2}, total: {3}, count: {4}",
346 Environment.TickCount - t1,
347 res1,
348 res2,
349 res1 + res2,
350 count
351 );
352 }
353
354 [TestMethod]
355 public void AsyncQueueBatchTest() {
356 var queue = new AsyncQueue<int>();
357
358 const int wBatch = 29;
359 const int wCount = 400000;
360 const int total = wBatch * wCount * 2;
361 const int summ = wBatch * wCount * 3;
362
363 int r1 = 0, r2 = 0;
364 const int rBatch = 111;
365 int read = 0;
366
367 var t1 = Environment.TickCount;
368
369 AsyncPool.RunThread(
370 () => {
371 var buffer = new int[wBatch];
372 for(int i = 0; i<wBatch; i++)
373 buffer[i] = 1;
374
375 for(int i =0; i < wCount; i++)
376 queue.EnqueueRange(buffer,0,wBatch);
377 Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1);
378 },
379 () => {
380 var buffer = new int[wBatch];
381 for(int i = 0; i<wBatch; i++)
382 buffer[i] = 2;
383
384 for(int i =0; i < wCount; i++)
385 queue.EnqueueRange(buffer,0,wBatch);
386 Console.WriteLine("done writer #2: {0} ms", Environment.TickCount - t1);
387 },
388 () => {
389 var buffer = new int[rBatch];
390
391 while(read < total) {
392 int actual;
393 if (queue.TryDequeueRange(buffer,0,rBatch,out actual)) {
394 for(int i=0; i< actual; i++)
395 r1 += buffer[i];
396 Interlocked.Add(ref read, actual);
397 }
398 }
399
400 Console.WriteLine("done reader #1: {0} ms", Environment.TickCount - t1);
401 },
402 () => {
403 var buffer = new int[rBatch];
404
405 while(read < total) {
406 int actual;
407 if (queue.TryDequeueRange(buffer,0,rBatch,out actual)) {
408 for(int i=0; i< actual; i++)
409 r2 += buffer[i];
410 Interlocked.Add(ref read, actual);
411 }
412 }
413
414 Console.WriteLine("done reader #2: {0} ms", Environment.TickCount - t1);
415 }
416 )
417 .Bundle()
418 .Join();
419
420 Assert.AreEqual(summ , r1 + r2);
421
422 Console.WriteLine(
423 "done: {0} ms, summ#1: {1}, summ#2: {2}, total: {3}, count: {4}",
424 Environment.TickCount - t1,
425 r1,
426 r2,
427 r1 + r2,
428 total
429 );
430 }
431
432 [TestMethod]
433 public void AsyncQueueChunkDequeueTest() {
434 var queue = new AsyncQueue<int>();
435
436 const int wBatch = 31;
437 const int wCount = 200000;
438 const int total = wBatch * wCount * 3;
439 const int summ = wBatch * wCount * 6;
440
441 int r1 = 0, r2 = 0;
442 const int rBatch = 1024;
443 int read = 0;
444
445 var t1 = Environment.TickCount;
446
447 AsyncPool.RunThread(
448 () => {
449 var buffer = new int[wBatch];
450 for(int i = 0; i<wBatch; i++)
451 buffer[i] = 1;
452
453 for(int i =0; i < wCount; i++)
454 queue.EnqueueRange(buffer,0,wBatch);
455 Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1);
456 },
457 () => {
458 var buffer = new int[wBatch];
459 for(int i = 0; i<wBatch; i++)
460 buffer[i] = 2;
461
462 for(int i =0; i < wCount; i++)
463 queue.EnqueueRange(buffer,0,wBatch);
464 Console.WriteLine("done writer #2: {0} ms", Environment.TickCount - t1);
465 },
466 () => {
467 var buffer = new int[wBatch];
468 for(int i = 0; i<wBatch; i++)
469 buffer[i] = 3;
470
471 for(int i =0; i < wCount; i++)
472 queue.EnqueueRange(buffer,0,wBatch);
473 Console.WriteLine("done writer #3: {0} ms", Environment.TickCount - t1);
474 },
475 () => {
476 var buffer = new int[rBatch];
477 int count = 1;
478 double avgchunk = 0;
479 while(read < total) {
480 int actual;
481 if (queue.TryDequeueChunk(buffer,0,rBatch,out actual)) {
482 for(int i=0; i< actual; i++)
483 r2 += buffer[i];
484 Interlocked.Add(ref read, actual);
485 avgchunk = avgchunk*(count-1)/count + actual/(double)count;
486 count ++;
487 }
488 }
489
490 Console.WriteLine("done reader #2: {0} ms, avg chunk size: {1}", Environment.TickCount - t1, avgchunk);
491 }
492 )
493 .Bundle()
494 .Join();
495
496 Assert.AreEqual(summ , r1 + r2);
497
498 Console.WriteLine(
499 "done: {0} ms, summ#1: {1}, summ#2: {2}, total: {3}, count: {4}",
500 Environment.TickCount - t1,
501 r1,
502 r2,
503 r1 + r2,
504 total
505 );
506 }
507
508 [TestMethod]
509 public void AsyncQueueDrainTest() {
510 var queue = new AsyncQueue<int>();
511
512 const int wBatch = 11;
513 const int wCount = 200000;
514 const int total = wBatch * wCount * 3;
515 const int summ = wBatch * wCount * 3;
516
517 int r1 = 0, r2 = 0;
518 const int rBatch = 11;
519 int read = 0;
520
521 var t1 = Environment.TickCount;
522
523 AsyncPool.RunThread(
524 () => {
525 var buffer = new int[wBatch];
526 for(int i = 0; i<wBatch; i++)
527 buffer[i] = 1;
528
529 for(int i =0; i < wCount; i++)
530 queue.EnqueueRange(buffer,0,wBatch);
531 Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1);
532 },
533 () => {
534 for(int i =0; i < wCount * wBatch; i++)
535 queue.Enqueue(1);
536 Console.WriteLine("done writer #2: {0} ms", Environment.TickCount - t1);
537 },
538 () => {
539 var buffer = new int[wBatch];
540 for(int i = 0; i<wBatch; i++)
541 buffer[i] = 1;
542
543 for(int i =0; i < wCount; i++)
544 queue.EnqueueRange(buffer,0,wBatch);
545 Console.WriteLine("done writer #3: {0} ms", Environment.TickCount - t1);
546 },
547 /*() => {
548 int temp;
549 int count = 0;
550 while (read < total)
551 if (queue.TryDequeue(out temp)) {
552 count++;
553 r1 += temp;
554 Interlocked.Increment(ref read);
555 }
556 Console.WriteLine("done reader #1: {0} ms, {1} count", Environment.TickCount - t1, count);
557 },*/
558 /*() => {
559 var buffer = new int[rBatch];
560 var count = 0;
561 while(read < total) {
562 int actual;
563 if (queue.TryDequeueRange(buffer,0,rBatch,out actual)) {
564 for(int i=0; i< actual; i++)
565 r1 += buffer[i];
566 Interlocked.Add(ref read, actual);
567 count += actual;
568 }
569 }
570
571 Console.WriteLine("done reader #1: {0} ms, {1} items", Environment.TickCount - t1, count);
572 },*/
573 () => {
574 var count = 0;
575 while(read < total) {
576 var buffer = queue.Drain();
577 for(int i=0; i< buffer.Length; i++)
578 r1 += buffer[i];
579 Interlocked.Add(ref read, buffer.Length);
580 count += buffer.Length;
581 }
582 Console.WriteLine("done reader #1: {0} ms, {1} items", Environment.TickCount - t1, count);
583 },
584 () => {
585 var count = 0;
586 while(read < total) {
587 var buffer = queue.Drain();
588 for(int i=0; i< buffer.Length; i++)
589 r2 += buffer[i];
590 Interlocked.Add(ref read, buffer.Length);
591 count += buffer.Length;
592 }
593 Console.WriteLine("done reader #2: {0} ms, {1} items", Environment.TickCount - t1, count);
594 }
595 )
596 .Bundle()
597 .Join();
598
599 Assert.AreEqual(summ , r1 + r2);
600
601 Console.WriteLine(
602 "done: {0} ms, summ#1: {1}, summ#2: {2}, total: {3}, count: {4}",
603 Environment.TickCount - t1,
604 r1,
605 r2,
606 r1 + r2,
607 total
608 );
220 }
609 }
221
610
222 [TestMethod]
611 [TestMethod]
223 public void ParallelMapTest() {
612 public void ParallelMapTest() {
224
613
225 int count = 100000;
614 const int count = 100000;
226
615
227 double[] args = new double[count];
616 var args = new double[count];
228 var rand = new Random();
617 var rand = new Random();
229
618
230 for (int i = 0; i < count; i++)
619 for (int i = 0; i < count; i++)
231 args[i] = rand.NextDouble();
620 args[i] = rand.NextDouble();
232
621
233 var t = Environment.TickCount;
622 var t = Environment.TickCount;
234 var res = args.ParallelMap(x => Math.Sin(x*x), 4).Join();
623 var res = args.ParallelMap(x => Math.Sin(x*x), 4).Join();
235
624
236 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
625 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
237
626
238 t = Environment.TickCount;
627 t = Environment.TickCount;
239 for (int i = 0; i < count; i++)
628 for (int i = 0; i < count; i++)
240 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
629 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
241 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
630 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
242 }
631 }
243
632
244 [TestMethod]
633 [TestMethod]
245 public void ChainedMapTest() {
634 public void ChainedMapTest() {
246
635
247 using (var pool = new WorkerPool(0,100,100)) {
636 using (var pool = new WorkerPool()) {
248 int count = 10000;
637 const int count = 10000;
249
638
250 double[] args = new double[count];
639 var args = new double[count];
251 var rand = new Random();
640 var rand = new Random();
252
641
253 for (int i = 0; i < count; i++)
642 for (int i = 0; i < count; i++)
254 args[i] = rand.NextDouble();
643 args[i] = rand.NextDouble();
255
644
256 var t = Environment.TickCount;
645 var t = Environment.TickCount;
257 var res = args
646 var res = args
258 .ChainedMap(
647 .ChainedMap(
648 // Analysis disable once AccessToDisposedClosure
259 x => pool.Invoke(
649 x => pool.Invoke(
260 () => Math.Sin(x * x)
650 () => Math.Sin(x * x)
261 ),
651 ),
262 4
652 4
263 )
653 )
264 .Join();
654 .Join();
265
655
266 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
656 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
267
657
268 t = Environment.TickCount;
658 t = Environment.TickCount;
269 for (int i = 0; i < count; i++)
659 for (int i = 0; i < count; i++)
270 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
660 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
271 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
661 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
272 Console.WriteLine("Max workers: {0}", pool.MaxRunningThreads);
662 Console.WriteLine("Max workers: {0}", pool.MaxRunningThreads);
273 }
663 }
274 }
664 }
275
665
276 [TestMethod]
666 [TestMethod]
277 public void ParallelForEachTest() {
667 public void ParallelForEachTest() {
278
668
279 int count = 100000;
669 const int count = 100000;
280
670
281 int[] args = new int[count];
671 var args = new int[count];
282 var rand = new Random();
672 var rand = new Random();
283
673
284 for (int i = 0; i < count; i++)
674 for (int i = 0; i < count; i++)
285 args[i] = (int)(rand.NextDouble() * 100);
675 args[i] = (int)(rand.NextDouble() * 100);
286
676
287 int result = 0;
677 int result = 0;
288
678
289 var t = Environment.TickCount;
679 var t = Environment.TickCount;
290 args.ParallelForEach(x => Interlocked.Add(ref result, x), 4).Join();
680 args.ParallelForEach(x => Interlocked.Add(ref result, x), 4).Join();
291
681
292 Console.WriteLine("Iteration complete in {0} ms, result: {1}", Environment.TickCount - t, result);
682 Console.WriteLine("Iteration complete in {0} ms, result: {1}", Environment.TickCount - t, result);
293
683
294 int result2 = 0;
684 int result2 = 0;
295
685
296 t = Environment.TickCount;
686 t = Environment.TickCount;
297 for (int i = 0; i < count; i++)
687 for (int i = 0; i < count; i++)
298 result2 += args[i];
688 result2 += args[i];
299 Assert.AreEqual(result2, result);
689 Assert.AreEqual(result2, result);
300 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
690 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
301 }
691 }
302
692
303 [TestMethod]
693 [TestMethod]
304 public void ComplexCase1Test() {
694 public void ComplexCase1Test() {
305 var flags = new bool[3];
695 var flags = new bool[3];
306
696
307 // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map)
697 // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map)
308
698
309 var p = PromiseHelper
699 var step1 = PromiseHelper
310 .Sleep(200, "Alan")
700 .Sleep(200, "Alan")
311 .Cancelled(() => flags[0] = true)
701 .On(() => flags[0] = true, PromiseEventType.Cancelled);
702 var p = step1
312 .Chain(x =>
703 .Chain(x =>
313 PromiseHelper
704 PromiseHelper
314 .Sleep(200, "Hi, " + x)
705 .Sleep(200, "Hi, " + x)
315 .Map(y => y)
706 .Then(y => y)
316 .Cancelled(() => flags[1] = true)
707 .On(() => flags[1] = true, PromiseEventType.Cancelled)
317 )
708 )
318 .Cancelled(() => flags[2] = true);
709 .On(() => flags[2] = true, PromiseEventType.Cancelled);
319 Thread.Sleep(300);
710 step1.Join();
320 p.Cancel();
711 p.Cancel();
321 try {
712 try {
322 Assert.AreEqual(p.Join(), "Hi, Alan");
713 Assert.AreEqual(p.Join(), "Hi, Alan");
323 Assert.Fail("Shouldn't get here");
714 Assert.Fail("Shouldn't get here");
324 } catch (OperationCanceledException) {
715 } catch (OperationCanceledException) {
325 }
716 }
326
717
327 Assert.IsFalse(flags[0]);
718 Assert.IsFalse(flags[0]);
328 Assert.IsTrue(flags[1]);
719 Assert.IsTrue(flags[1]);
329 Assert.IsTrue(flags[2]);
720 Assert.IsTrue(flags[2]);
330 }
721 }
331
722
332 [TestMethod]
723 [TestMethod]
333 public void ChainedCancel1Test() {
724 public void ChainedCancel1Test() {
334 // при отмене сцепленной асинхронной операции все обещание должно
725 // при отмене сцепленной асинхронной операции все обещание должно
335 // завершаться ошибкой OperationCanceledException
726 // завершаться ошибкой OperationCanceledException
336 var p = PromiseHelper
727 var p = PromiseHelper
337 .Sleep(1, "Hi, HAL!")
728 .Sleep(1, "Hi, HAL!")
338 .Chain(x => {
729 .Then(x => {
339 // запускаем две асинхронные операции
730 // запускаем две асинхронные операции
340 var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!");
731 var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!");
341 // вторая операция отменяет первую до завершения
732 // вторая операция отменяет первую до завершения
342 PromiseHelper
733 PromiseHelper
343 .Sleep(100, "HAL, STOP!")
734 .Sleep(100, "HAL, STOP!")
344 .Then(() => result.Cancel());
735 .Then(result.Cancel);
345 return result;
736 return result;
346 });
737 });
347 try {
738 try {
348 p.Join();
739 p.Join();
349 } catch (TargetInvocationException err) {
740 } catch (TargetInvocationException err) {
350 Assert.IsTrue(err.InnerException is OperationCanceledException);
741 Assert.IsTrue(err.InnerException is OperationCanceledException);
351 }
742 }
352 }
743 }
353
744
354 [TestMethod]
745 [TestMethod]
355 public void ChainedCancel2Test() {
746 public void ChainedCancel2Test() {
356 // при отмене цепочки обещаний, вложенные операции также должны отменяться
747 // при отмене цепочки обещаний, вложенные операции также должны отменяться
357 IPromiseBase p = null;
358 var pSurvive = new Promise<bool>();
748 var pSurvive = new Promise<bool>();
359 var hemStarted = new ManualResetEvent(false);
749 var hemStarted = new Signal();
360 p = PromiseHelper
750 var p = PromiseHelper
361 .Sleep(1, "Hi, HAL!")
751 .Sleep(1, "Hi, HAL!")
362 .Chain(x => {
752 .Chain(() => {
363 hemStarted.Set();
753 hemStarted.Set();
364 // запускаем две асинхронные операции
754 // запускаем две асинхронные операции
365 var result = PromiseHelper
755 var result = PromiseHelper
366 .Sleep(1000, "HEM ENABLED!!!")
756 .Sleep(2000, "HEM ENABLED!!!")
367 .Then(s => pSurvive.Resolve(false));
757 .Then(() => pSurvive.Resolve(false));
368
758
369 result
759 result
370 .Cancelled(() => pSurvive.Resolve(true));
760 .On(() => pSurvive.Resolve(true), PromiseEventType.Cancelled);
371
761
372 return result;
762 return result;
373 });
763 });
374
764
375 hemStarted.WaitOne();
765 hemStarted.Wait();
376 p.Cancel();
766 p.Cancel();
377
767
378 try {
768 try {
379 p.Join();
769 p.Join();
770 Assert.Fail();
380 } catch (OperationCanceledException) {
771 } catch (OperationCanceledException) {
772 }
381 Assert.IsTrue(pSurvive.Join());
773 Assert.IsTrue(pSurvive.Join());
382 }
774 }
775
776 [TestMethod]
777 public void SharedLockTest() {
778 var l = new SharedLock();
779 int shared = 0;
780 int exclusive = 0;
781 var s1 = new Signal();
782 var log = new AsyncQueue<string>();
783
784 try {
785 AsyncPool.RunThread(
786 () => {
787 log.Enqueue("Reader #1 started");
788 try {
789 l.LockShared();
790 log.Enqueue("Reader #1 lock got");
791 if (Interlocked.Increment(ref shared) == 2)
792 s1.Set();
793 s1.Wait();
794 log.Enqueue("Reader #1 finished");
795 Interlocked.Decrement(ref shared);
796 } finally {
797 l.Release();
798 log.Enqueue("Reader #1 lock released");
383 }
799 }
800 },
801 () => {
802 log.Enqueue("Reader #2 started");
803
804 try {
805 l.LockShared();
806 log.Enqueue("Reader #2 lock got");
807
808 if (Interlocked.Increment(ref shared) == 2)
809 s1.Set();
810 s1.Wait();
811 log.Enqueue("Reader #2 upgrading to writer");
812 Interlocked.Decrement(ref shared);
813 l.Upgrade();
814 log.Enqueue("Reader #2 upgraded");
815
816 Assert.AreEqual(1, Interlocked.Increment(ref exclusive));
817 Assert.AreEqual(0, shared);
818 log.Enqueue("Reader #2 finished");
819 Interlocked.Decrement(ref exclusive);
820 } finally {
821 l.Release();
822 log.Enqueue("Reader #2 lock released");
823 }
824 },
825 () => {
826 log.Enqueue("Writer #1 started");
827 try {
828 l.LockExclusive();
829 log.Enqueue("Writer #1 got the lock");
830 Assert.AreEqual(1, Interlocked.Increment(ref exclusive));
831 Interlocked.Decrement(ref exclusive);
832 log.Enqueue("Writer #1 is finished");
833 } finally {
834 l.Release();
835 log.Enqueue("Writer #1 lock released");
836 }
837 }
838 ).Bundle().Join(1000);
839 log.Enqueue("Done");
840 } catch(Exception error) {
841 log.Enqueue(error.Message);
842 throw;
843 } finally {
844 foreach (var m in log)
845 Console.WriteLine(m);
384 }
846 }
385 }
847 }
386
848
849 #if NET_4_5
850
851 [TestMethod]
852 public async void TaskInteropTest() {
853 var promise = new Promise<int>();
854 promise.Resolve(10);
855 var res = await promise;
856
857 Assert.AreEqual(10, res);
858 }
859
860 #endif
861 }
862 }
863
@@ -1,66 +1,84
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>8.0.30703</ProductVersion>
7 </ProductVersion>
8 <SchemaVersion>2.0</SchemaVersion>
7 <SchemaVersion>2.0</SchemaVersion>
9 <ProjectGuid>{63F92C0C-61BF-48C0-A377-8D67C3C661D0}</ProjectGuid>
8 <ProjectGuid>{63F92C0C-61BF-48C0-A377-8D67C3C661D0}</ProjectGuid>
10 <OutputType>Library</OutputType>
9 <OutputType>Library</OutputType>
11 <AppDesignerFolder>Properties</AppDesignerFolder>
10 <AppDesignerFolder>Properties</AppDesignerFolder>
12 <RootNamespace>Implab.Test</RootNamespace>
11 <RootNamespace>Implab.Test</RootNamespace>
13 <AssemblyName>Implab.Test</AssemblyName>
12 <AssemblyName>Implab.Test</AssemblyName>
14 <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
13 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
15 <FileAlignment>512</FileAlignment>
14 <FileAlignment>512</FileAlignment>
16 <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
15 <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
16 <TargetFrameworkProfile />
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 <Prefer32Bit>false</Prefer32Bit>
26 </PropertyGroup>
27 </PropertyGroup>
27 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
28 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
28 <DebugType>pdbonly</DebugType>
29 <DebugType>pdbonly</DebugType>
29 <Optimize>true</Optimize>
30 <Optimize>true</Optimize>
30 <OutputPath>bin\Release\</OutputPath>
31 <OutputPath>bin\Release\</OutputPath>
31 <DefineConstants>TRACE</DefineConstants>
32 <DefineConstants>TRACE</DefineConstants>
32 <ErrorReport>prompt</ErrorReport>
33 <ErrorReport>prompt</ErrorReport>
33 <WarningLevel>4</WarningLevel>
34 <WarningLevel>4</WarningLevel>
35 <Prefer32Bit>false</Prefer32Bit>
36 </PropertyGroup>
37 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
38 <DebugSymbols>true</DebugSymbols>
39 <DebugType>full</DebugType>
40 <Optimize>false</Optimize>
41 <OutputPath>bin\Debug\</OutputPath>
42 <DefineConstants>DEBUG;TRACE</DefineConstants>
43 <ErrorReport>prompt</ErrorReport>
44 <WarningLevel>4</WarningLevel>
45 <Prefer32Bit>false</Prefer32Bit>
46 </PropertyGroup>
47 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
48 <DebugType>pdbonly</DebugType>
49 <Optimize>true</Optimize>
50 <OutputPath>bin\Release\</OutputPath>
51 <DefineConstants>TRACE</DefineConstants>
52 <ErrorReport>prompt</ErrorReport>
53 <WarningLevel>4</WarningLevel>
54 <Prefer32Bit>false</Prefer32Bit>
34 </PropertyGroup>
55 </PropertyGroup>
35 <ItemGroup>
56 <ItemGroup>
36 <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
57 <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
37 <Reference Include="System" />
58 <Reference Include="System" />
38 <Reference Include="System.Core">
59 <Reference Include="System.Core">
39 <RequiredTargetFramework>3.5</RequiredTargetFramework>
60 <RequiredTargetFramework>3.5</RequiredTargetFramework>
40 </Reference>
61 </Reference>
41 </ItemGroup>
62 </ItemGroup>
42 <ItemGroup>
63 <ItemGroup>
43 <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
44 <Visible>False</Visible>
45 </CodeAnalysisDependentAssemblyPaths>
46 </ItemGroup>
47 <ItemGroup>
48 <Compile Include="AsyncTests.cs" />
64 <Compile Include="AsyncTests.cs" />
65 <Compile Include="CancelationTests.cs" />
49 <Compile Include="PromiseHelper.cs" />
66 <Compile Include="PromiseHelper.cs" />
50 <Compile Include="Properties\AssemblyInfo.cs" />
67 <Compile Include="Properties\AssemblyInfo.cs" />
68 <Compile Include="RunnableComponentTests.cs" />
51 </ItemGroup>
69 </ItemGroup>
52 <ItemGroup>
70 <ItemGroup>
53 <ProjectReference Include="..\Implab\Implab.csproj">
71 <ProjectReference Include="..\Implab\Implab.csproj">
54 <Project>{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}</Project>
72 <Project>{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}</Project>
55 <Name>Implab</Name>
73 <Name>Implab</Name>
56 </ProjectReference>
74 </ProjectReference>
57 </ItemGroup>
75 </ItemGroup>
58 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
76 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
59 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
77 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
60 Other similar extension points exist, see Microsoft.Common.targets.
78 Other similar extension points exist, see Microsoft.Common.targets.
61 <Target Name="BeforeBuild">
79 <Target Name="BeforeBuild">
62 </Target>
80 </Target>
63 <Target Name="AfterBuild">
81 <Target Name="AfterBuild">
64 </Target>
82 </Target>
65 -->
83 -->
66 </Project> No newline at end of file
84 </Project>
@@ -1,17 +1,14
1 using Implab.Parallels;
1 using Implab.Parallels;
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Text;
6 using System.Threading;
2 using System.Threading;
7
3
8 namespace Implab.Test {
4 namespace Implab.Test {
9 class PromiseHelper {
5 static class PromiseHelper {
10 public static Promise<T> Sleep<T>(int timeout, T retVal) {
6 public static IPromise<T> Sleep<T>(int timeout, T retVal) {
11 return AsyncPool.Invoke(() => {
7 return AsyncPool.Invoke((ct) => {
8 ct.CancellationRequested(ct.CancelOperation);
12 Thread.Sleep(timeout);
9 Thread.Sleep(timeout);
13 return retVal;
10 return retVal;
14 });
11 });
15 }
12 }
16 }
13 }
17 }
14 }
@@ -1,35 +1,34
1 using System.Reflection;
1 using System.Reflection;
2 using System.Runtime.CompilerServices;
2 using System.Runtime.CompilerServices;
3 using System.Runtime.InteropServices;
3 using System.Runtime.InteropServices;
4
4
5 // General Information about an assembly is controlled through the following
5 // General Information about an assembly is controlled through the following
6 // set of attributes. Change these attribute values to modify the information
6 // set of attributes. Change these attribute values to modify the information
7 // associated with an assembly.
7 // associated with an assembly.
8 [assembly: AssemblyTitle("Implab.Test")]
8 [assembly: AssemblyTitle("Implab.Test")]
9 [assembly: AssemblyDescription("")]
9 [assembly: AssemblyDescription("")]
10 [assembly: AssemblyConfiguration("")]
10 [assembly: AssemblyConfiguration("")]
11 [assembly: AssemblyCompany("")]
11 [assembly: AssemblyCompany("")]
12 [assembly: AssemblyProduct("Implab.Test")]
12 [assembly: AssemblyProduct("Implab.Test")]
13 [assembly: AssemblyCopyright("Copyright © 2013")]
13 [assembly: AssemblyCopyright("Copyright © 2013")]
14 [assembly: AssemblyTrademark("")]
14 [assembly: AssemblyTrademark("")]
15 [assembly: AssemblyCulture("")]
15 [assembly: AssemblyCulture("")]
16
16
17 // Setting ComVisible to false makes the types in this assembly not visible
17 // Setting ComVisible to false makes the types in this assembly not visible
18 // to COM components. If you need to access a type in this assembly from
18 // to COM components. If you need to access a type in this assembly from
19 // COM, set the ComVisible attribute to true on that type.
19 // COM, set the ComVisible attribute to true on that type.
20 [assembly: ComVisible(false)]
20 [assembly: ComVisible(false)]
21
21
22 // The following GUID is for the ID of the typelib if this project is exposed to COM
22 // The following GUID is for the ID of the typelib if this project is exposed to COM
23 [assembly: Guid("bfcae720-21eb-4411-b70a-6eeab99071de")]
23 [assembly: Guid("bfcae720-21eb-4411-b70a-6eeab99071de")]
24
24
25 // Version information for an assembly consists of the following four values:
25 // Version information for an assembly consists of the following four values:
26 //
26 //
27 // Major Version
27 // Major Version
28 // Minor Version
28 // Minor Version
29 // Build Number
29 // Build Number
30 // Revision
30 // Revision
31 //
31 //
32 // You can specify all the values or you can default the Build and Revision Numbers
32 // You can specify all the values or you can default the Build and Revision Numbers
33 // by using the '*' as shown below:
33 // by using the '*' as shown below:
34 [assembly: AssemblyVersion("1.0.0.0")]
34 [assembly: AssemblyVersion("0.0.*")]
35 [assembly: AssemblyFileVersion("1.0.0.0")]
@@ -1,51 +1,270
1 
1 
2 Microsoft Visual Studio Solution File, Format Version 11.00
2 Microsoft Visual Studio Solution File, Format Version 11.00
3 # Visual Studio 2010
3 # Visual Studio 2010
4 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab", "Implab\Implab.csproj", "{F550F1F8-8746-4AD0-9614-855F4C4B7F05}"
4 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab", "Implab\Implab.csproj", "{F550F1F8-8746-4AD0-9614-855F4C4B7F05}"
5 EndProject
5 EndProject
6 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CE8D8D18-437A-445C-B662-4C2CE79A76F6}"
6 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CE8D8D18-437A-445C-B662-4C2CE79A76F6}"
7 ProjectSection(SolutionItems) = preProject
7 ProjectSection(SolutionItems) = preProject
8 Implab.vsmdi = Implab.vsmdi
8 Implab.vsmdi = Implab.vsmdi
9 Local.testsettings = Local.testsettings
9 Local.testsettings = Local.testsettings
10 TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings
10 TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings
11 EndProjectSection
11 EndProjectSection
12 EndProject
12 EndProject
13 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Test", "Implab.Test\Implab.Test.csproj", "{63F92C0C-61BF-48C0-A377-8D67C3C661D0}"
13 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Test", "Implab.Test\Implab.Test.csproj", "{63F92C0C-61BF-48C0-A377-8D67C3C661D0}"
14 EndProject
14 EndProject
15 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx", "Implab.Fx\Implab.Fx.csproj", "{06E706F8-6881-43EB-927E-FFC503AF6ABC}"
15 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx", "Implab.Fx\Implab.Fx.csproj", "{06E706F8-6881-43EB-927E-FFC503AF6ABC}"
16 EndProject
16 EndProject
17 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx.Test", "Implab.Fx.Test\Implab.Fx.Test.csproj", "{2F31E405-E267-4195-A05D-574093C21209}"
17 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx.Test", "Implab.Fx.Test\Implab.Fx.Test.csproj", "{2F31E405-E267-4195-A05D-574093C21209}"
18 EndProject
18 EndProject
19 Global
19 Global
20 GlobalSection(TestCaseManagementSettings) = postSolution
21 CategoryFile = Implab.vsmdi
22 EndGlobalSection
23 GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 Debug|Any CPU = Debug|Any CPU
21 Debug|Any CPU = Debug|Any CPU
25 Release|Any CPU = Release|Any CPU
22 Release|Any CPU = Release|Any CPU
23 Debug 4.5|Any CPU = Debug 4.5|Any CPU
24 Release 4.5|Any CPU = Release 4.5|Any CPU
26 EndGlobalSection
25 EndGlobalSection
27 GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
29 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
30 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU
30 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
32 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
33 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
34 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.Build.0 = Release|Any CPU
35 {2F31E405-E267-4195-A05D-574093C21209}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
36 {2F31E405-E267-4195-A05D-574093C21209}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
37 {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.Build.0 = Debug|Any CPU
39 {2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
40 {2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
41 {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.ActiveCfg = Release|Any CPU
42 {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.Build.0 = Release|Any CPU
43 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
44 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
32 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
47 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
48 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
34 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
49 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.Build.0 = Release|Any CPU
50 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.Build.0 = Release|Any CPU
36 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
37 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU
52 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
38 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
53 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.Build.0 = Release|Any CPU
54 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
41 {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.Build.0 = Debug|Any CPU
56 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
42 {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.ActiveCfg = Release|Any CPU
57 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.Build.0 = Release|Any CPU
58 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU
59 EndGlobalSection
60 GlobalSection(NestedProjects) = preSolution
61 EndGlobalSection
62 GlobalSection(MonoDevelopProperties) = preSolution
63 StartupItem = Implab\Implab.csproj
64 Policies = $0
65 $0.CSharpFormattingPolicy = $1
66 $1.IndentSwitchBody = True
67 $1.NamespaceBraceStyle = EndOfLine
68 $1.ClassBraceStyle = EndOfLine
69 $1.InterfaceBraceStyle = EndOfLine
70 $1.StructBraceStyle = EndOfLine
71 $1.EnumBraceStyle = EndOfLine
72 $1.MethodBraceStyle = EndOfLine
73 $1.ConstructorBraceStyle = EndOfLine
74 $1.DestructorBraceStyle = EndOfLine
75 $1.BeforeMethodDeclarationParentheses = False
76 $1.BeforeMethodCallParentheses = False
77 $1.BeforeConstructorDeclarationParentheses = False
78 $1.NewLineBeforeConstructorInitializerColon = NewLine
79 $1.NewLineAfterConstructorInitializerColon = SameLine
80 $1.BeforeIndexerDeclarationBracket = False
81 $1.BeforeDelegateDeclarationParentheses = False
82 $1.NewParentheses = False
83 $1.SpacesBeforeBrackets = False
84 $1.inheritsSet = Mono
85 $1.inheritsScope = text/x-csharp
86 $1.scope = text/x-csharp
87 $0.TextStylePolicy = $2
88 $2.FileWidth = 120
89 $2.EolMarker = Unix
90 $2.inheritsSet = VisualStudio
91 $2.inheritsScope = text/plain
92 $2.scope = text/x-csharp
93 $0.DotNetNamingPolicy = $3
94 $3.DirectoryNamespaceAssociation = PrefixedHierarchical
95 $3.ResourceNamePolicy = MSBuild
96 $0.TextStylePolicy = $4
97 $4.FileWidth = 120
98 $4.TabsToSpaces = False
99 $4.inheritsSet = VisualStudio
100 $4.inheritsScope = text/plain
101 $4.scope = application/xml
102 $0.XmlFormattingPolicy = $5
103 $5.inheritsSet = Mono
104 $5.inheritsScope = application/xml
105 $5.scope = application/xml
106 $0.TextStylePolicy = $6
107 $6.FileWidth = 120
108 $6.TabsToSpaces = False
109 $6.inheritsSet = VisualStudio
110 $6.inheritsScope = text/plain
111 $6.scope = text/plain
112 $0.NameConventionPolicy = $7
113 $7.Rules = $8
114 $8.NamingRule = $9
115 $9.Name = Namespaces
116 $9.AffectedEntity = Namespace
117 $9.VisibilityMask = VisibilityMask
118 $9.NamingStyle = PascalCase
119 $9.IncludeInstanceMembers = True
120 $9.IncludeStaticEntities = True
121 $8.NamingRule = $10
122 $10.Name = Types
123 $10.AffectedEntity = Class, Struct, Enum, Delegate
124 $10.VisibilityMask = VisibilityMask
125 $10.NamingStyle = PascalCase
126 $10.IncludeInstanceMembers = True
127 $10.IncludeStaticEntities = True
128 $8.NamingRule = $11
129 $11.Name = Interfaces
130 $11.RequiredPrefixes = $12
131 $12.String = I
132 $11.AffectedEntity = Interface
133 $11.VisibilityMask = VisibilityMask
134 $11.NamingStyle = PascalCase
135 $11.IncludeInstanceMembers = True
136 $11.IncludeStaticEntities = True
137 $8.NamingRule = $13
138 $13.Name = Attributes
139 $13.RequiredSuffixes = $14
140 $14.String = Attribute
141 $13.AffectedEntity = CustomAttributes
142 $13.VisibilityMask = VisibilityMask
143 $13.NamingStyle = PascalCase
144 $13.IncludeInstanceMembers = True
145 $13.IncludeStaticEntities = True
146 $8.NamingRule = $15
147 $15.Name = Event Arguments
148 $15.RequiredSuffixes = $16
149 $16.String = EventArgs
150 $15.AffectedEntity = CustomEventArgs
151 $15.VisibilityMask = VisibilityMask
152 $15.NamingStyle = PascalCase
153 $15.IncludeInstanceMembers = True
154 $15.IncludeStaticEntities = True
155 $8.NamingRule = $17
156 $17.Name = Exceptions
157 $17.RequiredSuffixes = $18
158 $18.String = Exception
159 $17.AffectedEntity = CustomExceptions
160 $17.VisibilityMask = VisibilityMask
161 $17.NamingStyle = PascalCase
162 $17.IncludeInstanceMembers = True
163 $17.IncludeStaticEntities = True
164 $8.NamingRule = $19
165 $19.Name = Methods
166 $19.AffectedEntity = Methods
167 $19.VisibilityMask = VisibilityMask
168 $19.NamingStyle = PascalCase
169 $19.IncludeInstanceMembers = True
170 $19.IncludeStaticEntities = True
171 $8.NamingRule = $20
172 $20.Name = Static Readonly Fields
173 $20.AffectedEntity = ReadonlyField
174 $20.VisibilityMask = Internal, Protected, Public
175 $20.NamingStyle = CamelCase
176 $20.IncludeInstanceMembers = False
177 $20.IncludeStaticEntities = True
178 $8.NamingRule = $21
179 $21.Name = Fields (Non Private)
180 $21.AffectedEntity = Field
181 $21.VisibilityMask = Internal, Public
182 $21.NamingStyle = CamelCase
183 $21.IncludeInstanceMembers = True
184 $21.IncludeStaticEntities = True
185 $8.NamingRule = $22
186 $22.Name = ReadOnly Fields (Non Private)
187 $22.AffectedEntity = ReadonlyField
188 $22.VisibilityMask = Internal, Public
189 $22.NamingStyle = CamelCase
190 $22.IncludeInstanceMembers = True
191 $22.IncludeStaticEntities = False
192 $8.NamingRule = $23
193 $23.Name = Fields (Private)
194 $23.RequiredPrefixes = $24
195 $24.String = m_
196 $23.AffectedEntity = Field, ReadonlyField
197 $23.VisibilityMask = Private, Protected
198 $23.NamingStyle = CamelCase
199 $23.IncludeInstanceMembers = True
200 $23.IncludeStaticEntities = False
201 $8.NamingRule = $25
202 $25.Name = Static Fields (Private)
203 $25.RequiredPrefixes = $26
204 $26.String = _
205 $25.AffectedEntity = Field
206 $25.VisibilityMask = Private
207 $25.NamingStyle = CamelCase
208 $25.IncludeInstanceMembers = False
209 $25.IncludeStaticEntities = True
210 $8.NamingRule = $27
211 $27.Name = ReadOnly Fields (Private)
212 $27.RequiredPrefixes = $28
213 $28.String = m_
214 $27.AffectedEntity = ReadonlyField
215 $27.VisibilityMask = Private, Protected
216 $27.NamingStyle = CamelCase
217 $27.IncludeInstanceMembers = True
218 $27.IncludeStaticEntities = False
219 $8.NamingRule = $29
220 $29.Name = Constant Fields
221 $29.AffectedEntity = ConstantField
222 $29.VisibilityMask = VisibilityMask
223 $29.NamingStyle = AllUpper
224 $29.IncludeInstanceMembers = True
225 $29.IncludeStaticEntities = True
226 $8.NamingRule = $30
227 $30.Name = Properties
228 $30.AffectedEntity = Property
229 $30.VisibilityMask = VisibilityMask
230 $30.NamingStyle = PascalCase
231 $30.IncludeInstanceMembers = True
232 $30.IncludeStaticEntities = True
233 $8.NamingRule = $31
234 $31.Name = Events
235 $31.AffectedEntity = Event
236 $31.VisibilityMask = VisibilityMask
237 $31.NamingStyle = PascalCase
238 $31.IncludeInstanceMembers = True
239 $31.IncludeStaticEntities = True
240 $8.NamingRule = $32
241 $32.Name = Enum Members
242 $32.AffectedEntity = EnumMember
243 $32.VisibilityMask = VisibilityMask
244 $32.NamingStyle = PascalCase
245 $32.IncludeInstanceMembers = True
246 $32.IncludeStaticEntities = True
247 $8.NamingRule = $33
248 $33.Name = Parameters
249 $33.AffectedEntity = Parameter, LocalVariable
250 $33.VisibilityMask = VisibilityMask
251 $33.NamingStyle = CamelCase
252 $33.IncludeInstanceMembers = True
253 $33.IncludeStaticEntities = True
254 $8.NamingRule = $34
255 $34.Name = Type Parameters
256 $34.RequiredPrefixes = $35
257 $35.String = T
258 $34.AffectedEntity = TypeParameter
259 $34.VisibilityMask = VisibilityMask
260 $34.NamingStyle = PascalCase
261 $34.IncludeInstanceMembers = True
262 $34.IncludeStaticEntities = True
263 EndGlobalSection
264 GlobalSection(TestCaseManagementSettings) = postSolution
265 CategoryFile = Implab.vsmdi
44 EndGlobalSection
266 EndGlobalSection
45 GlobalSection(SolutionProperties) = preSolution
267 GlobalSection(SolutionProperties) = preSolution
46 HideSolutionNode = FALSE
268 HideSolutionNode = FALSE
47 EndGlobalSection
269 EndGlobalSection
48 GlobalSection(MonoDevelopProperties) = preSolution
49 StartupItem = Implab\Implab.csproj
50 EndGlobalSection
51 EndGlobal
270 EndGlobal
@@ -1,34 +1,22
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
2 using System.Text;
5
3
6 namespace Implab.Diagnostics {
4 namespace Implab.Diagnostics {
7 public class ConsoleTraceListener: TextListenerBase {
5 public class ConsoleTraceListener: ListenerBase {
8
6
9 static readonly object _consoleLock = new object();
7 static readonly object _consoleLock = new object();
10
8
11 public ConsoleTraceListener()
9 public override void Write(LogEventArgs args, object entry) {
12 : base(true) {
13
14 }
15
16 public ConsoleTraceListener(bool global)
17 : base(global) {
18
19 }
20
21 protected override void WriteEntry(TraceContext context, EventText text, string channel) {
22 var msg = new StringBuilder();
10 var msg = new StringBuilder();
23
11
24 for (int i = 0; i < text.indent; i++)
12 for (int i = 0; i < args.Operation.Level; i++)
25 msg.Append(" ");
13 msg.Append(" ");
26 msg.AppendFormat("[{0}]:{1}: {2}", context.ThreadId, channel, text.content);
14 msg.AppendFormat("[{0}]: {1}", args.ThreadId, entry);
27
15
28 lock (_consoleLock) {
16 lock (_consoleLock) {
29 Console.ForegroundColor = (ConsoleColor)(context.ThreadId % 15 + 1);
17 Console.ForegroundColor = (ConsoleColor)(args.ThreadId % 15 + 1);
30 Console.WriteLine(msg.ToString());
18 Console.WriteLine(msg);
31 }
19 }
32 }
20 }
33 }
21 }
34 }
22 }
@@ -1,81 +1,81
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.Diagnostics {
6 namespace Implab.Diagnostics {
7 /// <summary>
7 /// <summary>
8 /// Канал, через который публикуются события журнала.
8 /// Канал, через который публикуются события журнала.
9 /// </summary>
9 /// </summary>
10 /// <typeparam name="TEvent">Тип событий в канале</typeparam>
10 /// <typeparam name="TEvent">Тип событий в канале</typeparam>
11 /// <remarks>
11 /// <remarks>
12 /// Событиями журнала могут быть любые типы, например строки, в которых будет передаваться
12 /// Событиями журнала могут быть любые типы, например строки, в которых будет передаваться
13 /// информация, или структуры с набором полей, описывающих важность, текст и другую информацию.
13 /// информация, или структуры с набором полей, описывающих важность, текст и другую информацию.
14 /// </remarks>
14 /// </remarks>
15 public class LogChannel<TEvent> {
15 public class LogChannel<TEvent> {
16 static LogChannel<TEvent> _default = new LogChannel<TEvent>();
16 static LogChannel<TEvent> _default = new LogChannel<TEvent>();
17
17
18 /// <summary>
18 /// <summary>
19 /// Канал по-умолчанию для событий типа <typeparam name="TEvent"/>.
19 /// Канал по-умолчанию для событий типа <typeparam name="TEvent"/>.
20 /// </summary>
20 /// </summary>
21 public static LogChannel<TEvent> Default {
21 public static LogChannel<TEvent> Default {
22 get {
22 get {
23 return _default;
23 return _default;
24 }
24 }
25 }
25 }
26
26
27 /// <summary>
27 /// <summary>
28 /// Событие появление новой записи в журнале, на это событие подписываются слушатели.
28 /// Событие появление новой записи в журнале, на это событие подписываются слушатели.
29 /// </summary>
29 /// </summary>
30 public event EventHandler<ValueEventArgs<TEvent>> Events;
30 public event EventHandler<LogEventArgs<TEvent>> Events;
31
31
32 /// <summary>
32 /// <summary>
33 /// Имя канала, полезно для отображения в журнале
33 /// Имя канала, полезно для отображения в журнале
34 /// </summary>
34 /// </summary>
35 public string Name {
35 public string Name {
36 get;
36 get;
37 private set;
37 private set;
38 }
38 }
39
39
40 /// <summary>
40 /// <summary>
41 /// Создает журнал, имя типа событий назначается в качетве имени канала.
41 /// Создает журнал, имя типа событий назначается в качетве имени канала.
42 /// </summary>
42 /// </summary>
43 public LogChannel()
43 public LogChannel()
44 : this(null) {
44 : this(null) {
45 }
45 }
46
46
47 /// <summary>
47 /// <summary>
48 /// Содает канал с указанным именем.
48 /// Содает канал с указанным именем.
49 /// </summary>
49 /// </summary>
50 /// <param name="name">Имя канала.</param>
50 /// <param name="name">Имя канала.</param>
51 public LogChannel(string name) {
51 public LogChannel(string name) {
52 if (String.IsNullOrEmpty(name))
52 if (String.IsNullOrEmpty(name))
53 name = typeof(TEvent).Name;
53 name = typeof(TEvent).Name;
54 Name = name;
54 Name = name;
55 }
55 }
56
56
57 /// <summary>
57 /// <summary>
58 /// Отправляет запись журнала через канал подписчикам.
58 /// Отправляет запись журнала через канал подписчикам.
59 /// </summary>
59 /// </summary>
60 /// <param name="data">Запись журнала.</param>
60 /// <param name="data">Запись журнала.</param>
61 /// <remarks>
61 /// <remarks>
62 /// Контекст трассировки от которого рассылается сообщение определяется автоматически из текущего потока.
62 /// Контекст трассировки от которого рассылается сообщение определяется автоматически из текущего потока.
63 /// </remarks>
63 /// </remarks>
64 public void LogEvent(TEvent data) {
64 public void LogEvent(TEvent data) {
65 var t = Events;
65 var t = Events;
66 if (t!= null)
66 if (t != null) {
67 t(TraceContext.Current,new ValueEventArgs<TEvent>(data));
67 var traceContext = TraceContext.Instance;
68 }
68 t(
69
69 this,
70 /// <summary>
70 new LogEventArgs<TEvent>(
71 /// Отправляет запись журнала через канал подписчикам.
71 data,
72 /// </summary>
72 Name,
73 /// <param name="data">Запись журнала.</param>
73 traceContext.ThreadId,
74 /// <param name="context">Контекст трассировки от которого рассылается сообщение/</param>
74 traceContext.CurrentOperation,
75 public void LogEvent(TraceContext context,TEvent data) {
75 traceContext.CurrentOperation.Duration
76 var t = Events;
76 )
77 if (t != null)
77 );
78 t(context, new ValueEventArgs<TEvent>(data));
79 }
78 }
80 }
79 }
81 }
80 }
81 }
@@ -1,47 +1,45
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
2
7 namespace Implab.Diagnostics {
3 namespace Implab.Diagnostics {
8 public class LogicalOperation {
4 public class LogicalOperation {
5 public static readonly LogicalOperation EMPTY = new LogicalOperation("__EMPTY__", null);
6
9 readonly LogicalOperation m_parent;
7 readonly LogicalOperation m_parent;
10 readonly string m_name;
8 readonly string m_name;
11 readonly int m_level;
9 readonly int m_level;
12 readonly int m_timestamp;
10 readonly int m_timestamp;
13
11
14 public LogicalOperation()
12 public LogicalOperation()
15 : this(null, null) {
13 : this(null, null) {
16 }
14 }
17
15
18 public LogicalOperation(string name, LogicalOperation parent) {
16 public LogicalOperation(string name, LogicalOperation parent) {
19 m_name = name ?? String.Empty;
17 m_name = name ?? String.Empty;
20 m_parent = parent;
18 m_parent = parent;
21
19
22 m_level = parent == null ? 0 : parent.Level + 1;
20 m_level = parent == null ? 0 : parent.Level + 1;
23 m_timestamp = Environment.TickCount;
21 m_timestamp = Environment.TickCount;
24 }
22 }
25
23
26 public int Duration {
24 public int Duration {
27 get {
25 get {
28 var dt = Environment.TickCount - m_timestamp;
26 var dt = Environment.TickCount - m_timestamp;
29 return dt < 0 ? int.MaxValue + dt : dt; // handle overflow
27 return dt < 0 ? int.MaxValue + dt : dt; // handle overflow
30 }
28 }
31 }
29 }
32
30
33 public LogicalOperation Parent {
31 public LogicalOperation Parent {
34 get {
32 get {
35 return m_parent;
33 return m_parent;
36 }
34 }
37 }
35 }
38
36
39 public int Level {
37 public int Level {
40 get { return m_level; }
38 get { return m_level; }
41 }
39 }
42
40
43 public string Name {
41 public string Name {
44 get { return m_name; }
42 get { return m_name; }
45 }
43 }
46 }
44 }
47 }
45 }
@@ -1,47 +1,46
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
2 using System.IO;
4 using System.Linq;
5 using System.Text;
3 using System.Text;
6
4
7 namespace Implab.Diagnostics {
5 namespace Implab.Diagnostics {
8 public class TextFileListener: TextListenerBase {
6 public class TextFileListener: ListenerBase {
9 readonly TextWriter m_textWriter;
7 readonly TextWriter m_textWriter;
10
8
11 public TextFileListener(string fileName, bool global)
9 public TextFileListener(string fileName) {
12 : base(global) {
13 m_textWriter = File.CreateText(fileName);
10 m_textWriter = File.CreateText(fileName);
14
11
15 m_textWriter.WriteLine("LOG {0}", DateTime.Now);
12 m_textWriter.WriteLine("LOG {0}", DateTime.Now);
16 Register(this);
17 }
13 }
18
14
19 protected override void WriteEntry(TraceContext context, EventText text, string channel) {
15 #region implemented abstract members of ListenerBase
16
17 public override void Write(LogEventArgs args, object entry) {
20 var msg = new StringBuilder();
18 var msg = new StringBuilder();
21 for (int i = 0; i < text.indent; i++)
19 for (int i = 0; i < args.Operation.Level; i++)
22 msg.Append(" ");
20 msg.Append(" ");
23 msg.AppendFormat("[{0}]:{1}: {2}", context.ThreadId, channel, text.content);
21 msg.AppendFormat("[{0}]:{1}: {2}", args.ThreadId, args.ChannelName, entry);
24
22
25 lock (m_textWriter) {
23 lock (m_textWriter) {
26 if (!IsDisposed) {
24 if (!IsDisposed) {
27 // тут гарантировано еще не освобожден m_textWriter
25 // тут гарантировано еще не освобожден m_textWriter
28 m_textWriter.WriteLine(msg.ToString());
26 m_textWriter.WriteLine(msg);
29 m_textWriter.Flush();
27 m_textWriter.Flush();
30 }
28 }
31 }
29 }
32 }
30 }
33
31
32 #endregion
34
33
35 protected override void Dispose(bool disposing) {
34 protected override void Dispose(bool disposing) {
36 base.Dispose(disposing);
35 base.Dispose(disposing);
37 if (disposing) {
36 if (disposing) {
38 // IsDisposed = true
37 // IsDisposed = true
39 lock (m_textWriter) {
38 lock (m_textWriter) {
40 Safe.Dispose(m_textWriter);
39 Safe.Dispose(m_textWriter);
41 }
40 }
42 }
41 }
43 }
42 }
44
43
45
44
46 }
45 }
47 }
46 }
@@ -1,238 +1,83
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading;
3 using System.Threading;
6 using System.Threading.Tasks;
7
4
8 namespace Implab.Diagnostics {
5 namespace Implab.Diagnostics {
9 /// <summary>
6 /// <summary>
10 /// Контекст трассировки, привязывается к потоку и содержит в себе информацию о стеке логических операций.
7 /// Trace context is bound to the specific thread, each thread has it's own ThreadContext.
11 /// </summary>
8 /// </summary>
12 /// <remarks>
9 /// <remarks>
13 /// Контекст трассировки передается слушателям событий для определения места, где возникло событие.
10 /// ThreadContext manages relations between logical operations and threads.
14 /// </remarks>
11 /// </remarks>
15 public class TraceContext {
12 public class TraceContext {
16 LogicalOperation m_currentOperation;
17 readonly LogicalOperation m_bound;
18 readonly int m_threadId;
19
13
20 [ThreadStatic]
14 [ThreadStatic]
21 static TraceContext _current;
15 static TraceContext _instance;
16
17 OperationContext m_current = OperationContext.EMPTY;
18 readonly Stack<OperationContext> m_stack = new Stack<OperationContext>();
19 readonly int m_threadId;
22
20
23 /// <summary>
21 public static TraceContext Instance {
24 /// Текущий контекст трассировки для потока, создается астоматически при первом обращении.
25 /// </summary>
26 public static TraceContext Current {
27 get {
22 get {
28 if (_current == null) {
23 if (_instance == null)
29 _current = new TraceContext();
24 _instance = new TraceContext();
30 _current.LogEvent(TraceEventType.Created,"[{0}]", _current.ThreadId);
25 return _instance;
31 }
32 return _current;
33 }
26 }
34 }
27 }
35
28
36 TraceContext(TraceContext context)
29 public TraceContext() {
37 : this(context, false) {
38 }
39
40 TraceContext(TraceContext context, bool attach) {
41 if (context == null)
42 throw new ArgumentNullException("context");
43
44 m_currentOperation = context.CurrentOperation;
45 m_bound = attach ? context.BoundOperation : context.CurrentOperation;
46 m_threadId = Thread.CurrentThread.ManagedThreadId;
47 }
48
49 TraceContext() {
50 m_currentOperation = new LogicalOperation();
51 m_bound = m_currentOperation;
52 m_threadId = Thread.CurrentThread.ManagedThreadId;
30 m_threadId = Thread.CurrentThread.ManagedThreadId;
53 }
31 }
54
32
55 /// <summary>
33 public int ThreadId {
56 /// При необходимости копирует состояние контекста трассивровки в текущий поток.
34 get { return m_threadId; }
57 /// </summary>
58 /// <param name="from">Исходный контекст трассировки, который передается.</param>
59 /// <remarks>
60 /// <para>
61 /// Копирование происходит за счет создания нового контекста трассировки и заполнением его
62 /// состояния из переданного контекста. При этом копируется стек операций, однако в новом
63 /// контексте ранее начатые логические операции не могут быть завершены.
64 /// </para>
65 /// <para>
66 /// Если передача состояния состоялась, то вызывается событие трассировки <see cref="TraceEventType.Fork"/>.
67 /// </para>
68 /// </remarks>
69 public static void Fork(TraceContext from) {
70 if (_current == from)
71 return;
72 if (from != null) {
73 var context = new TraceContext(from);
74 context.LogEvent(TraceEventType.Fork, "[{0}]-->[{1}]",from.ThreadId, context.ThreadId);
75 _current = context;
76 } else {
77 _current = new TraceContext();
78 }
79 }
35 }
80
36
81 /// <summary>
37 public LogicalOperation CurrentOperation {
82 /// Задает текущему потоку указанный контекст, текущей поток может заканчивать ранее начатые
38 get {
83 /// логические операции в указанном контексте.
39 return m_current.CurrentOperation;
84 /// </summary>
85 /// <param name="source"></param>
86 public static void Attach(TraceContext source) {
87 if (_current == source)
88 return;
89 if (source != null) {
90 var context = new TraceContext(source, true);
91 context.LogEvent(TraceEventType.Attach, "[{0}]-->[{1}]", source.ThreadId, context.ThreadId);
92 _current = context;
93 } else {
94 _current = new TraceContext();
95 }
40 }
96 }
41 }
97
42
98 /// <summary>
43 public void EnterLogicalOperation(LogicalOperation operation, bool takeOwnership) {
99 /// Отсоединяет текущий контекст трассировки от потока, для дальнейшей его передачи другому потоку
44 //var prev = CurrentOperation;
100 /// <see cref="Attach(TraceContext)"/>.
45 //LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(takeOwnership ? TraceEventType.Attach : TraceEventType.Enter, String.Format("{0} -> {1}",prev.Name, operation.Name)));
101 /// </summary>
46 m_stack.Push(m_current);
102 /// <returns>Контекст трассировки потока</returns>
47 m_current = new OperationContext(operation, takeOwnership);
103 /// <remarks>
104 /// После отсоединения контекста трассировки от потока, при первом обращении к трассировке в этом
105 /// потоке будет создан новый контекст.
106 /// </remarks>
107 public static TraceContext Detach() {
108 var context = Current;
109 context.LogEvent(TraceEventType.Detach, null);
110 _current = null;
111 return context;
112 }
48 }
113
49
114 /// <summary>
50 public void StartLogicalOperation(string name) {
115 /// Создает постоянную копию текущего контекста, данную копию можно хранить и использовать для передачи через <see cref="Fork(TraceContext)"/>
51 LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(TraceEventType.OperationStarted, name));
116 /// </summary>
52 m_current.BeginLogicalOperation(name);
117 /// <returns>Копия текущего контекста трассировки.</returns>
53 }
118 public static TraceContext Snapshot() {
54
119 return _current == null ? new TraceContext() : new TraceContext(_current,false);
55 public void StartLogicalOperation() {
56 StartLogicalOperation(String.Empty);
120 }
57 }
121
58
122 /// <summary>
59 public void EndLogicalOperation() {
123 /// Выполняет переданное действие в указанном контексте трассировки, по окончании восстанавливает предыдущий контекст трассировки потока.
60 var op = m_current.EndLogicalOperation();
124 /// </summary>
61 LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(TraceEventType.OperationCompleted, String.Format("-{0} : {1}ms",op.Name, op.Duration)));
125 /// <param name="action"></param>
126 public void Invoke(Action action) {
127 if (action == null)
128 throw new ArgumentNullException("action");
129 var old = _current;
130 Fork(this);
131 try {
132 action();
133 } finally {
134 if(_current != null)
135 _current.EndAllOperations();
136 _current = old;
137 }
62 }
63
64 public LogicalOperation DetachLogicalOperation() {
65 var prev = m_current.DetachLogicalOperation();
66 //LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(TraceEventType.Detach, String.Format("{0} -> {1}",prev.Name, CurrentOperation.Name)));
67 return prev;
138 }
68 }
139
69
140 /// <summary>
70 public void Leave() {
141 /// Текущая логическая операция.
71 if (m_stack.Count > 0) {
142 /// </summary>
72 m_current.Leave();
143 public LogicalOperation CurrentOperation {
73 //var prev = CurrentOperation;
144 get {
74 m_current = m_stack.Pop();
145 return m_currentOperation;
75 //LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(TraceEventType.Leave, String.Format("{0} -> {1}", prev.Name, CurrentOperation.Name)));
76 } else {
77 TraceLog.TraceWarning("Attempt to leave the last operation context");
78 m_current = OperationContext.EMPTY;
146 }
79 }
147 }
80 }
148
149 /// <summary>
150 /// Операция ниже которой нельзя опускаться в стеке логических операций, т.е. она не может быть завершена в текущем контексте.
151 /// </summary>
152 public LogicalOperation BoundOperation {
153 get {
154 return m_bound;
155 }
156 }
157
158 /// <summary>
159 /// Поток, в котором создан контекст трассировки.
160 /// </summary>
161 public int ThreadId {
162 get {
163 return m_threadId;
164 }
81 }
165 }
82 }
166
83
167 /// <summary>
168 /// Начинает безымянную логическую операцию.
169 /// </summary>
170 public void StartLogicalOperation() {
171 StartLogicalOperation(null);
172 }
173
174 /// <summary>
175 /// Начинает логическую операцию с указанным именем. Созданная операция будет добвалена в стек логических операций контекста, затем будет создано соответсвующее событие.
176 /// </summary>
177 /// <param name="name">Имя начинаемой операции.</param>
178 public void StartLogicalOperation(string name) {
179 m_currentOperation = new LogicalOperation(name, m_currentOperation);
180 LogEvent(TraceEventType.OperationStarted, name);
181 }
182
183 /// <summary>
184 /// Заканчивает логическую операцию начатую в текущем контексте. Операции, начатые в других контекстах не могут быть закончены в текущем контексте.
185 /// </summary>
186 /// <remarks>
187 /// При вызове данного метода создается событие журнала трассировки, либо о завершении операции, либо об ошибки, поскольку данная операция
188 /// начата в другом контексте.
189 /// </remarks>
190 public void EndLogicalOperation() {
191 if (m_bound == m_currentOperation) {
192 LogEvent(TraceEventType.Error, "Trying to end the operation which isn't belongs to current trace");
193 } else {
194 var op = m_currentOperation;
195 LogEvent(TraceEventType.OperationCompleted, "{0} {1} ms", op.Name, op.Duration);
196 m_currentOperation = m_currentOperation.Parent;
197 }
198 }
199
200 /// <summary>
201 /// Создает копию контекста и возвращается на предыдущую операцию в текущем контексте, это позволяет начать операцию в одном потоке, а завершить - в другом.
202 /// </summary>
203 /// <returns>Контекст трассировки, который можно присоединить к другому потоку.</returns>
204 public TraceContext DetachLogicalOperation() {
205 if (m_bound == m_currentOperation) {
206 return new TraceContext();
207 } else {
208 var detached = new TraceContext(this, true);
209 m_currentOperation = m_currentOperation.Parent;
210 return detached;
211 }
212 }
213
214 public void BindLogicalOperationToPromise(IPromise promise) {
215 Safe.ArgumentNotNull(promise, "promise");
216
217 var ctx = DetachLogicalOperation();
218 promise.Finally(() => {
219 var old = _current;
220 TraceContext.Attach(ctx);
221 TraceContext.Current.EndLogicalOperation();
222 _current = old;
223 });
224 }
225
226 /// <summary>
227 /// Заврешает все начатые в этом контексте операции
228 /// </summary>
229 public void EndAllOperations() {
230 while (m_bound != m_currentOperation)
231 EndLogicalOperation();
232 }
233
234 void LogEvent(TraceEventType type, string format, params object[] args) {
235 LogChannel<TraceEvent>.Default.LogEvent(this, TraceEvent.Create(type, format, args));
236 }
237 }
238 }
@@ -1,34 +1,29
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
2
6 namespace Implab.Diagnostics {
3 namespace Implab.Diagnostics {
7 public class TraceEvent {
4 public class TraceEvent {
8 public string Message {
5 public string Message {
9 get;
6 get;
10 private set;
7 private set;
11 }
8 }
12
9
13 public TraceEventType EventType {
10 public TraceEventType EventType {
14 get;
11 get;
15 private set;
12 private set;
16 }
13 }
17
14
18 public TraceEvent(TraceEventType type, string message) {
15 public TraceEvent(TraceEventType type, string message) {
19 EventType = type;
16 EventType = type;
20 Message = message;
17 Message = message;
21 }
18 }
22
19
23 public override string ToString() {
20 public override string ToString() {
24 if (EventType == TraceEventType.Information)
21 /*return EventType == TraceEventType.Information ? Message : String.Format("{0}: {1}", EventType, Message);*/
25 return Message;
22 return Message;
26 else
27 return String.Format("{0}: {1}", EventType, Message);
28 }
23 }
29
24
30 public static TraceEvent Create(TraceEventType type, string format, params object[] args) {
25 public static TraceEvent Create(TraceEventType type, string format, params object[] args) {
31 return new TraceEvent(type, format == null ? String.Empty : String.Format(format, args));
26 return new TraceEvent(type, format == null ? String.Empty : String.Format(format, args));
32 }
27 }
33 }
28 }
34 }
29 }
@@ -1,19 +1,19
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.Tasks;
5 using System.Threading.Tasks;
6
6
7 namespace Implab.Diagnostics {
7 namespace Implab.Diagnostics {
8 public enum TraceEventType {
8 public enum TraceEventType {
9 Information = 1,
9 Information = 1,
10 Warning,
10 Warning,
11 Error,
11 Error,
12 OperationStarted,
12 OperationStarted,
13 OperationCompleted,
13 OperationCompleted,
14 Fork,
15 Attach,
14 Attach,
16 Detach,
15 Detach,
17 Created
16 Enter,
17 Leave
18 }
18 }
19 }
19 }
@@ -1,55 +1,50
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Diagnostics;
3 using System.Diagnostics;
4 using System.Linq;
4 using System.Linq;
5 using System.Text;
5 using System.Text;
6 using System.Threading.Tasks;
6 using System.Threading.Tasks;
7
7
8 namespace Implab.Diagnostics {
8 namespace Implab.Diagnostics {
9 /// <summary>
9 /// <summary>
10 /// Класс для публикации событий выполнения программы, события публикуются через <see cref="LogChannel{TraceEvent}"/>.
10 /// Класс для публикации событий выполнения программы, события публикуются через <see cref="LogChannel{TraceEvent}"/>.
11 /// Журнал трассировки отражает логический ход выполнения программы и существует всегда, поскольку тесно связан с
11 /// Журнал трассировки отражает логический ход выполнения программы и существует всегда, поскольку тесно связан с
12 /// контекстом трассировки.
12 /// контекстом трассировки.
13 /// </summary>
13 /// </summary>
14 public static class TraceLog {
14 public static class TraceLog {
15 [Conditional("TRACE")]
15 [Conditional("TRACE")]
16 public static void StartLogicalOperation() {
16 public static void StartLogicalOperation() {
17 TraceContext.Current.StartLogicalOperation();
17 TraceContext.Instance.StartLogicalOperation();
18 }
18 }
19
19
20 [Conditional("TRACE")]
20 [Conditional("TRACE")]
21 public static void StartLogicalOperation(string name) {
21 public static void StartLogicalOperation(string name) {
22 TraceContext.Current.StartLogicalOperation(name);
22 TraceContext.Instance.StartLogicalOperation(name);
23 }
23 }
24
24
25 [Conditional("TRACE")]
25 [Conditional("TRACE")]
26 public static void EndLogicalOperation() {
26 public static void EndLogicalOperation() {
27 TraceContext.Current.EndLogicalOperation();
27 TraceContext.Instance.EndLogicalOperation();
28 }
29
30 [Conditional("TRACE")]
31 public static void BindLogicalOperationToPromise(IPromise promise) {
32 TraceContext.Current.BindLogicalOperationToPromise(promise);
33 }
28 }
34
29
35 [Conditional("TRACE")]
30 [Conditional("TRACE")]
36 public static void TraceInformation(string format, params object[] arguments) {
31 public static void TraceInformation(string format, params object[] arguments) {
37 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Information, format, arguments));
32 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Information, format, arguments));
38 }
33 }
39
34
40 [Conditional("TRACE")]
35 [Conditional("TRACE")]
41 public static void TraceWarning(string format, params object[] arguments) {
36 public static void TraceWarning(string format, params object[] arguments) {
42 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Warning, format, arguments));
37 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Warning, format, arguments));
43 }
38 }
44
39
45 [Conditional("TRACE")]
40 [Conditional("TRACE")]
46 public static void TraceError(string format, params object[] arguments) {
41 public static void TraceError(string format, params object[] arguments) {
47 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Error, format, arguments));
42 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Error, format, arguments));
48 }
43 }
49
44
50 [Conditional("TRACE")]
45 [Conditional("TRACE")]
51 public static void TraceError(Exception err) {
46 public static void TraceError(Exception err) {
52 TraceError("{0}", err);
47 TraceError("{0}", err);
53 }
48 }
54 }
49 }
55 }
50 }
@@ -1,10 +1,8
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
2
6 namespace Implab {
3 namespace Implab {
7 public interface ICancellable {
4 public interface ICancellable {
8 bool Cancel();
5 void Cancel();
6 void Cancel(Exception reason);
9 }
7 }
10 }
8 }
@@ -1,37 +1,66
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System.Linq;
4 using System.Text;
4 using System.Text;
5
5
6 namespace Implab {
6 namespace Implab {
7 public interface IPromise: ICancellable {
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 }
14
8
15 /// <summary>
9 /// <summary>
16 /// Тип результата, получаемого через данное обещание.
10 /// Тип результата, получаемого через данное обещание.
17 /// </summary>
11 /// </summary>
18 Type PromiseType { get; }
12 Type PromiseType { get; }
19
13
14 /// <summary>
15 /// Обещание является выполненым, либо успешно, либо с ошибкой, либо отменено.
16 /// </summary>
20 bool IsResolved { get; }
17 bool IsResolved { get; }
21
18
19 /// <summary>
20 /// Обещание было отменено.
21 /// </summary>
22 bool IsCancelled { get; }
22 bool IsCancelled { get; }
23
23
24 IPromise Then(Action success,ErrorHandler error);
24 /// <summary>
25 IPromise Then(Action success);
25 /// Исключение возникшее в результате выполнения обещания, либо причина отмены.
26 IPromise Error(ErrorHandler error);
26 /// </summary>
27 IPromise Anyway(Action handler);
27 Exception Error { get; }
28 IPromise Finally(Action handler);
29 IPromise Cancelled(Action handler);
30
28
29 /// <summary>
30 /// Adds specified listeners to the current promise.
31 /// </summary>
32 /// <param name="success">The handler called on the successful promise completion.</param>
33 /// <param name="error">The handler is called if an error while completing the promise occurred.</param>
34 /// <param name="cancel">The handler is called in case of promise cancellation.</param>
35 /// <returns>The current promise.</returns>
36 IPromise On(Action success, Action<Exception> error, Action<Exception> cancel);
37 IPromise On(Action success, Action<Exception> error);
38 IPromise On(Action success);
39
40 /// <summary>
41 /// Adds specified listeners to the current promise.
42 /// </summary>
43 /// <param name="handler">The handler called on the specified events.</param>
44 /// <param name = "events">The combination of flags denoting the events for which the
45 /// handler shoud be called.</param>
46 /// <returns>The current promise.</returns>
47 IPromise On(Action handler, PromiseEventType events);
48
49 /// <summary>
50 /// Преобразует результат обещания к заданному типу и возвращает новое обещание.
51 /// </summary>
31 IPromise<T> Cast<T>();
52 IPromise<T> Cast<T>();
32
53
54 /// <summary>
55 /// Синхронизирует текущий поток с обещанием.
56 /// </summary>
33 void Join();
57 void Join();
58 /// <summary>
59 /// Синхронизирует текущий поток с обещанием.
60 /// </summary>
61 /// <param name="timeout">Время ожидания, по его истечению возникнет исключение.</param>
62 /// <exception cref="TimeoutException">Превышено время ожидания.</exception>
34 void Join(int timeout);
63 void Join(int timeout);
35
64
36 }
65 }
37 }
66 }
@@ -1,31 +1,25
1 using System;
1 using System;
2 using System.Collections.Generic;
2
3 using System.Linq;
3 namespace Implab {
4 using System.Text;
4 public interface IPromise<out T> : IPromise {
5
5
6 namespace Implab
6 IPromise<T> On(Action<T> success, Action<Exception> error, Action<Exception> cancel);
7 {
7
8 public interface IPromise<T>: IPromise
8 IPromise<T> On(Action<T> success, Action<Exception> error);
9 {
9
10 IPromise<T> On(Action<T> success);
10
11
11 new T Join();
12 new T Join();
13
12 new T Join(int timeout);
14 new T Join(int timeout);
13
15
14 IPromise<T> Then(ResultHandler<T> success, ErrorHandler error);
16 new IPromise<T> On(Action success, Action<Exception> error, Action<Exception> cancel);
15 IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error);
17
16 IPromise<T> Then(ResultHandler<T> success);
18 new IPromise<T> On(Action success, Action<Exception> error);
17 new IPromise<T> Error(ErrorHandler error);
18 IPromise<T> Error(ErrorHandler<T> error);
19
19
20 IPromise<T2> Map<T2>(ResultMapper<T,T2> mapper, ErrorHandler error);
20 new IPromise<T> On(Action success);
21 IPromise<T2> Map<T2>(ResultMapper<T, T2> mapper);
22
21
23 IPromise<T2> Chain<T2>(ChainedOperation<T, T2> chained, ErrorHandler error);
22 new IPromise<T> On(Action handler, PromiseEventType events);
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
23
30 }
24 }
31 }
25 }
@@ -1,14 +1,11
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
2
6 namespace Implab {
3 namespace Implab {
7 public interface ITaskController: IProgressHandler, ICancellable {
4 public interface ITaskController: IProgressHandler, ICancellable {
8 bool IsCancelled {
5 bool IsCancelled {
9 get;
6 get;
10 }
7 }
11
8
12 event EventHandler Cancelled;
9 event EventHandler Cancelled;
13 }
10 }
14 }
11 }
@@ -1,105 +1,276
1 <?xml version="1.0" encoding="utf-8"?>
1 <?xml version="1.0" encoding="utf-8"?>
2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <PropertyGroup>
3 <PropertyGroup>
4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
6 <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
7 <OutputType>Library</OutputType>
7 <OutputType>Library</OutputType>
8 <RootNamespace>Implab</RootNamespace>
8 <RootNamespace>Implab</RootNamespace>
9 <AssemblyName>Implab</AssemblyName>
9 <AssemblyName>Implab</AssemblyName>
10 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
11 <ReleaseVersion>0.2</ReleaseVersion>
12 <ProductVersion>8.0.30703</ProductVersion>
13 <SchemaVersion>2.0</SchemaVersion>
10 </PropertyGroup>
14 </PropertyGroup>
11 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
15 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
12 <DebugSymbols>true</DebugSymbols>
16 <DebugSymbols>true</DebugSymbols>
13 <DebugType>full</DebugType>
17 <DebugType>full</DebugType>
14 <Optimize>false</Optimize>
18 <Optimize>false</Optimize>
15 <OutputPath>bin\Debug</OutputPath>
19 <OutputPath>bin\Debug</OutputPath>
16 <DefineConstants>TRACE;DEBUG;</DefineConstants>
20 <DefineConstants>TRACE;DEBUG;</DefineConstants>
17 <ErrorReport>prompt</ErrorReport>
21 <ErrorReport>prompt</ErrorReport>
18 <WarningLevel>4</WarningLevel>
22 <WarningLevel>4</WarningLevel>
19 <ConsolePause>false</ConsolePause>
23 <ConsolePause>false</ConsolePause>
20 <RunCodeAnalysis>true</RunCodeAnalysis>
24 <RunCodeAnalysis>true</RunCodeAnalysis>
21 </PropertyGroup>
25 </PropertyGroup>
22 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
26 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
23 <DebugType>full</DebugType>
27 <DebugType>full</DebugType>
24 <Optimize>true</Optimize>
28 <Optimize>true</Optimize>
25 <OutputPath>bin\Release</OutputPath>
29 <OutputPath>bin\Release</OutputPath>
26 <ErrorReport>prompt</ErrorReport>
30 <ErrorReport>prompt</ErrorReport>
27 <WarningLevel>4</WarningLevel>
31 <WarningLevel>4</WarningLevel>
28 <ConsolePause>false</ConsolePause>
32 <ConsolePause>false</ConsolePause>
29 </PropertyGroup>
33 </PropertyGroup>
34 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
35 <DebugSymbols>true</DebugSymbols>
36 <DebugType>full</DebugType>
37 <Optimize>false</Optimize>
38 <OutputPath>bin\Debug</OutputPath>
39 <DefineConstants>TRACE;DEBUG;NET_4_5</DefineConstants>
40 <ErrorReport>prompt</ErrorReport>
41 <WarningLevel>4</WarningLevel>
42 <RunCodeAnalysis>true</RunCodeAnalysis>
43 <ConsolePause>false</ConsolePause>
44 </PropertyGroup>
45 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
46 <Optimize>true</Optimize>
47 <OutputPath>bin\Release</OutputPath>
48 <ErrorReport>prompt</ErrorReport>
49 <WarningLevel>4</WarningLevel>
50 <ConsolePause>false</ConsolePause>
51 <DefineConstants>NET_4_5</DefineConstants>
52 </PropertyGroup>
53 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugMono|AnyCPU' ">
54 <DebugSymbols>true</DebugSymbols>
55 <DebugType>full</DebugType>
56 <Optimize>false</Optimize>
57 <OutputPath>bin\Debug</OutputPath>
58 <DefineConstants>TRACE;DEBUG;NET_4_5;MONO</DefineConstants>
59 <ErrorReport>prompt</ErrorReport>
60 <WarningLevel>4</WarningLevel>
61 <RunCodeAnalysis>true</RunCodeAnalysis>
62 <ConsolePause>false</ConsolePause>
63 </PropertyGroup>
64 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseMono|AnyCPU' ">
65 <Optimize>true</Optimize>
66 <OutputPath>bin\Release</OutputPath>
67 <DefineConstants>NET_4_5;MONO;</DefineConstants>
68 <ErrorReport>prompt</ErrorReport>
69 <WarningLevel>4</WarningLevel>
70 <ConsolePause>false</ConsolePause>
71 </PropertyGroup>
30 <ItemGroup>
72 <ItemGroup>
31 <Reference Include="System" />
73 <Reference Include="System" />
32 <Reference Include="System.Xml" />
74 <Reference Include="System.Xml" />
75 <Reference Include="mscorlib" />
33 </ItemGroup>
76 </ItemGroup>
34 <ItemGroup>
77 <ItemGroup>
35 <Compile Include="Component.cs" />
36 <Compile Include="CustomEqualityComparer.cs" />
78 <Compile Include="CustomEqualityComparer.cs" />
37 <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
79 <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
38 <Compile Include="Diagnostics\EventText.cs" />
80 <Compile Include="Diagnostics\EventText.cs" />
39 <Compile Include="Diagnostics\IEventTextFormatter.cs" />
40 <Compile Include="Diagnostics\LogChannel.cs" />
81 <Compile Include="Diagnostics\LogChannel.cs" />
41 <Compile Include="Diagnostics\LogicalOperation.cs" />
82 <Compile Include="Diagnostics\LogicalOperation.cs" />
42 <Compile Include="Diagnostics\TextFileListener.cs" />
83 <Compile Include="Diagnostics\TextFileListener.cs" />
43 <Compile Include="Diagnostics\TextListenerBase.cs" />
44 <Compile Include="Diagnostics\TraceLog.cs" />
84 <Compile Include="Diagnostics\TraceLog.cs" />
45 <Compile Include="Diagnostics\TraceContext.cs" />
46 <Compile Include="Diagnostics\TraceEvent.cs" />
85 <Compile Include="Diagnostics\TraceEvent.cs" />
47 <Compile Include="Diagnostics\TraceEventType.cs" />
86 <Compile Include="Diagnostics\TraceEventType.cs" />
48 <Compile Include="Disposable.cs" />
49 <Compile Include="ICancellable.cs" />
87 <Compile Include="ICancellable.cs" />
50 <Compile Include="IProgressHandler.cs" />
88 <Compile Include="IProgressHandler.cs" />
51 <Compile Include="IProgressNotifier.cs" />
89 <Compile Include="IProgressNotifier.cs" />
52 <Compile Include="IPromiseT.cs" />
90 <Compile Include="IPromiseT.cs" />
53 <Compile Include="IPromise.cs" />
91 <Compile Include="IPromise.cs" />
54 <Compile Include="IServiceLocator.cs" />
92 <Compile Include="IServiceLocator.cs" />
55 <Compile Include="ITaskController.cs" />
93 <Compile Include="ITaskController.cs" />
56 <Compile Include="JSON\JSONElementContext.cs" />
57 <Compile Include="JSON\JSONElementType.cs" />
58 <Compile Include="JSON\JSONGrammar.cs" />
59 <Compile Include="JSON\JSONParser.cs" />
60 <Compile Include="JSON\JSONScanner.cs" />
61 <Compile Include="JSON\JsonTokenType.cs" />
62 <Compile Include="JSON\JSONWriter.cs" />
63 <Compile Include="JSON\JSONXmlReader.cs" />
64 <Compile Include="JSON\JSONXmlReaderOptions.cs" />
65 <Compile Include="JSON\StringTranslator.cs" />
66 <Compile Include="Parallels\DispatchPool.cs" />
94 <Compile Include="Parallels\DispatchPool.cs" />
67 <Compile Include="Parallels\ArrayTraits.cs" />
95 <Compile Include="Parallels\ArrayTraits.cs" />
68 <Compile Include="Parallels\MTQueue.cs" />
96 <Compile Include="Parallels\MTQueue.cs" />
69 <Compile Include="Parallels\WorkerPool.cs" />
97 <Compile Include="Parallels\WorkerPool.cs" />
70 <Compile Include="Parsing\Alphabet.cs" />
71 <Compile Include="Parsing\AlphabetBase.cs" />
72 <Compile Include="Parsing\AltToken.cs" />
73 <Compile Include="Parsing\BinaryToken.cs" />
74 <Compile Include="Parsing\CatToken.cs" />
75 <Compile Include="Parsing\CDFADefinition.cs" />
76 <Compile Include="Parsing\DFABuilder.cs" />
77 <Compile Include="Parsing\DFADefinitionBase.cs" />
78 <Compile Include="Parsing\DFAStateDescriptor.cs" />
79 <Compile Include="Parsing\DFAutomaton.cs" />
80 <Compile Include="Parsing\EDFADefinition.cs" />
81 <Compile Include="Parsing\EmptyToken.cs" />
82 <Compile Include="Parsing\EndToken.cs" />
83 <Compile Include="Parsing\EnumAlphabet.cs" />
84 <Compile Include="Parsing\Grammar.cs" />
85 <Compile Include="Parsing\IAlphabet.cs" />
86 <Compile Include="Parsing\IDFADefinition.cs" />
87 <Compile Include="Parsing\IVisitor.cs" />
88 <Compile Include="Parsing\ParserException.cs" />
89 <Compile Include="Parsing\Scanner.cs" />
90 <Compile Include="Parsing\StarToken.cs" />
91 <Compile Include="Parsing\SymbolToken.cs" />
92 <Compile Include="Parsing\Token.cs" />
93 <Compile Include="SafePool.cs" />
94 <Compile Include="ServiceLocator.cs" />
95 <Compile Include="TaskController.cs" />
96 <Compile Include="ProgressInitEventArgs.cs" />
98 <Compile Include="ProgressInitEventArgs.cs" />
97 <Compile Include="Properties\AssemblyInfo.cs" />
99 <Compile Include="Properties\AssemblyInfo.cs" />
98 <Compile Include="Promise.cs" />
99 <Compile Include="Parallels\AsyncPool.cs" />
100 <Compile Include="Parallels\AsyncPool.cs" />
100 <Compile Include="Safe.cs" />
101 <Compile Include="Safe.cs" />
101 <Compile Include="ValueEventArgs.cs" />
102 <Compile Include="ValueEventArgs.cs" />
103 <Compile Include="PromiseExtensions.cs" />
104 <Compile Include="SyncContextPromise.cs" />
105 <Compile Include="Diagnostics\OperationContext.cs" />
106 <Compile Include="Diagnostics\TraceContext.cs" />
107 <Compile Include="Diagnostics\LogEventArgs.cs" />
108 <Compile Include="Diagnostics\LogEventArgsT.cs" />
109 <Compile Include="Diagnostics\Extensions.cs" />
110 <Compile Include="PromiseEventType.cs" />
111 <Compile Include="Parallels\AsyncQueue.cs" />
112 <Compile Include="PromiseT.cs" />
113 <Compile Include="IDeferred.cs" />
114 <Compile Include="IDeferredT.cs" />
115 <Compile Include="Promise.cs" />
116 <Compile Include="PromiseTransientException.cs" />
117 <Compile Include="Parallels\Signal.cs" />
118 <Compile Include="Parallels\SharedLock.cs" />
119 <Compile Include="Diagnostics\ILogWriter.cs" />
120 <Compile Include="Diagnostics\ListenerBase.cs" />
121 <Compile Include="Parallels\BlockingQueue.cs" />
122 <Compile Include="AbstractEvent.cs" />
123 <Compile Include="AbstractPromise.cs" />
124 <Compile Include="AbstractPromiseT.cs" />
125 <Compile Include="FuncTask.cs" />
126 <Compile Include="FuncTaskBase.cs" />
127 <Compile Include="FuncTaskT.cs" />
128 <Compile Include="ActionChainTaskBase.cs" />
129 <Compile Include="ActionChainTask.cs" />
130 <Compile Include="ActionChainTaskT.cs" />
131 <Compile Include="FuncChainTaskBase.cs" />
132 <Compile Include="FuncChainTask.cs" />
133 <Compile Include="FuncChainTaskT.cs" />
134 <Compile Include="ActionTaskBase.cs" />
135 <Compile Include="ActionTask.cs" />
136 <Compile Include="ActionTaskT.cs" />
137 <Compile Include="ICancellationToken.cs" />
138 <Compile Include="SuccessPromise.cs" />
139 <Compile Include="SuccessPromiseT.cs" />
140 <Compile Include="PromiseAwaiterT.cs" />
141 <Compile Include="PromiseAwaiter.cs" />
142 <Compile Include="Components\ComponentContainer.cs" />
143 <Compile Include="Components\Disposable.cs" />
144 <Compile Include="Components\DisposablePool.cs" />
145 <Compile Include="Components\ObjectPool.cs" />
146 <Compile Include="Components\ServiceLocator.cs" />
147 <Compile Include="Components\IInitializable.cs" />
148 <Compile Include="TaskController.cs" />
149 <Compile Include="Components\App.cs" />
150 <Compile Include="Components\IRunnable.cs" />
151 <Compile Include="Components\ExecutionState.cs" />
152 <Compile Include="Components\RunnableComponent.cs" />
153 <Compile Include="Components\IFactory.cs" />
154 <Compile Include="Automaton\IAlphabet.cs" />
155 <Compile Include="Automaton\ParserException.cs" />
156 <Compile Include="Automaton\IndexedAlphabetBase.cs" />
157 <Compile Include="Automaton\IAlphabetBuilder.cs" />
158 <Compile Include="Automaton\RegularExpressions\AltToken.cs" />
159 <Compile Include="Automaton\RegularExpressions\BinaryToken.cs" />
160 <Compile Include="Automaton\RegularExpressions\CatToken.cs" />
161 <Compile Include="Automaton\RegularExpressions\StarToken.cs" />
162 <Compile Include="Automaton\RegularExpressions\SymbolToken.cs" />
163 <Compile Include="Automaton\RegularExpressions\EmptyToken.cs" />
164 <Compile Include="Automaton\RegularExpressions\Token.cs" />
165 <Compile Include="Automaton\RegularExpressions\IVisitor.cs" />
166 <Compile Include="Automaton\AutomatonTransition.cs" />
167 <Compile Include="Formats\JSON\JSONElementContext.cs" />
168 <Compile Include="Formats\JSON\JSONElementType.cs" />
169 <Compile Include="Formats\JSON\JSONGrammar.cs" />
170 <Compile Include="Formats\JSON\JSONParser.cs" />
171 <Compile Include="Formats\JSON\JSONScanner.cs" />
172 <Compile Include="Formats\JSON\JsonTokenType.cs" />
173 <Compile Include="Formats\JSON\JSONWriter.cs" />
174 <Compile Include="Formats\JSON\JSONXmlReader.cs" />
175 <Compile Include="Formats\JSON\JSONXmlReaderOptions.cs" />
176 <Compile Include="Formats\JSON\StringTranslator.cs" />
177 <Compile Include="Automaton\MapAlphabet.cs" />
178 <Compile Include="Formats\CharAlphabet.cs" />
179 <Compile Include="Formats\ByteAlphabet.cs" />
180 <Compile Include="Automaton\IDFATable.cs" />
181 <Compile Include="Automaton\IDFATableBuilder.cs" />
182 <Compile Include="Automaton\DFATable.cs" />
183 <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitor.cs" />
184 <Compile Include="Automaton\RegularExpressions\ITaggedDFABuilder.cs" />
185 <Compile Include="Formats\TextScanner.cs" />
186 <Compile Include="Formats\StringScanner.cs" />
187 <Compile Include="Formats\ReaderScanner.cs" />
188 <Compile Include="Formats\ScannerContext.cs" />
189 <Compile Include="Formats\Grammar.cs" />
190 <Compile Include="Automaton\RegularExpressions\EndTokenT.cs" />
191 <Compile Include="Automaton\RegularExpressions\EndToken.cs" />
192 <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitorT.cs" />
193 <Compile Include="Automaton\AutomatonConst.cs" />
194 <Compile Include="Automaton\RegularExpressions\RegularDFA.cs" />
195 <Compile Include="Components\LazyAndWeak.cs" />
196 <Compile Include="AbstractTask.cs" />
197 <Compile Include="AbstractTaskT.cs" />
102 </ItemGroup>
198 </ItemGroup>
103 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
199 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
104 <ItemGroup />
200 <ItemGroup />
201 <ProjectExtensions>
202 <MonoDevelop>
203 <Properties>
204 <Policies>
205 <CSharpFormattingPolicy IndentSwitchBody="True" NamespaceBraceStyle="EndOfLine" ClassBraceStyle="EndOfLine" InterfaceBraceStyle="EndOfLine" StructBraceStyle="EndOfLine" EnumBraceStyle="EndOfLine" MethodBraceStyle="EndOfLine" ConstructorBraceStyle="EndOfLine" DestructorBraceStyle="EndOfLine" BeforeMethodDeclarationParentheses="False" BeforeMethodCallParentheses="False" BeforeConstructorDeclarationParentheses="False" NewLineBeforeConstructorInitializerColon="NewLine" NewLineAfterConstructorInitializerColon="SameLine" BeforeIndexerDeclarationBracket="False" BeforeDelegateDeclarationParentheses="False" NewParentheses="False" SpacesBeforeBrackets="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
206 <TextStylePolicy FileWidth="120" EolMarker="Unix" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
207 <DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" />
208 <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="application/xml" />
209 <XmlFormattingPolicy inheritsSet="Mono" inheritsScope="application/xml" scope="application/xml" />
210 <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/plain" />
211 <NameConventionPolicy>
212 <Rules>
213 <NamingRule Name="Namespaces" AffectedEntity="Namespace" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
214 <NamingRule Name="Types" AffectedEntity="Class, Struct, Enum, Delegate" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
215 <NamingRule Name="Interfaces" AffectedEntity="Interface" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
216 <RequiredPrefixes>
217 <String>I</String>
218 </RequiredPrefixes>
219 </NamingRule>
220 <NamingRule Name="Attributes" AffectedEntity="CustomAttributes" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
221 <RequiredSuffixes>
222 <String>Attribute</String>
223 </RequiredSuffixes>
224 </NamingRule>
225 <NamingRule Name="Event Arguments" AffectedEntity="CustomEventArgs" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
226 <RequiredSuffixes>
227 <String>EventArgs</String>
228 </RequiredSuffixes>
229 </NamingRule>
230 <NamingRule Name="Exceptions" AffectedEntity="CustomExceptions" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
231 <RequiredSuffixes>
232 <String>Exception</String>
233 </RequiredSuffixes>
234 </NamingRule>
235 <NamingRule Name="Methods" AffectedEntity="Methods" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
236 <NamingRule Name="Static Readonly Fields" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Protected, Public" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True" />
237 <NamingRule Name="Fields (Non Private)" AffectedEntity="Field" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
238 <NamingRule Name="ReadOnly Fields (Non Private)" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False" />
239 <NamingRule Name="Fields (Private)" AffectedEntity="Field, ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
240 <RequiredPrefixes>
241 <String>m_</String>
242 </RequiredPrefixes>
243 </NamingRule>
244 <NamingRule Name="Static Fields (Private)" AffectedEntity="Field" VisibilityMask="Private" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True">
245 <RequiredPrefixes>
246 <String>_</String>
247 </RequiredPrefixes>
248 </NamingRule>
249 <NamingRule Name="ReadOnly Fields (Private)" AffectedEntity="ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
250 <RequiredPrefixes>
251 <String>m_</String>
252 </RequiredPrefixes>
253 </NamingRule>
254 <NamingRule Name="Constant Fields" AffectedEntity="ConstantField" VisibilityMask="VisibilityMask" NamingStyle="AllUpper" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
255 <NamingRule Name="Properties" AffectedEntity="Property" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
256 <NamingRule Name="Events" AffectedEntity="Event" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
257 <NamingRule Name="Enum Members" AffectedEntity="EnumMember" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
258 <NamingRule Name="Parameters" AffectedEntity="Parameter, LocalVariable" VisibilityMask="VisibilityMask" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
259 <NamingRule Name="Type Parameters" AffectedEntity="TypeParameter" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
260 <RequiredPrefixes>
261 <String>T</String>
262 </RequiredPrefixes>
263 </NamingRule>
264 </Rules>
265 </NameConventionPolicy>
266 </Policies>
267 </Properties>
268 </MonoDevelop>
269 </ProjectExtensions>
270 <ItemGroup>
271 <Folder Include="Components\" />
272 <Folder Include="Automaton\RegularExpressions\" />
273 <Folder Include="Formats\" />
274 <Folder Include="Formats\JSON\" />
275 </ItemGroup>
105 </Project> No newline at end of file
276 </Project>
@@ -1,191 +1,207
1 using Implab.Diagnostics;
1 using Implab.Diagnostics;
2 using System;
2 using System;
3 using System.Collections.Generic;
4 using System.Diagnostics;
3 using System.Diagnostics;
5 using System.Linq;
6 using System.Text;
7 using System.Threading;
4 using System.Threading;
8
5
9 namespace Implab.Parallels {
6 namespace Implab.Parallels {
10 public static class ArrayTraits {
7 public static class ArrayTraits {
11 class ArrayIterator<TSrc> : DispatchPool<int> {
8 class ArrayIterator<TSrc> : DispatchPool<int> {
12 readonly Action<TSrc> m_action;
9 readonly Action<TSrc> m_action;
13 readonly TSrc[] m_source;
10 readonly TSrc[] m_source;
14 readonly Promise<int> m_promise = new Promise<int>();
11 readonly Promise<int> m_promise = new Promise<int>();
15 readonly TraceContext m_traceContext;
12 readonly LogicalOperation m_logicalOperation;
16
13
17 int m_pending;
14 int m_pending;
18 int m_next;
15 int m_next;
19
16
20 public ArrayIterator(TSrc[] source, Action<TSrc> action, int threads)
17 public ArrayIterator(TSrc[] source, Action<TSrc> action, int threads)
21 : base(threads) {
18 : base(threads) {
22
19
23 Debug.Assert(source != null);
20 Debug.Assert(source != null);
24 Debug.Assert(action != null);
21 Debug.Assert(action != null);
25
22
26 m_traceContext = TraceContext.Snapshot();
23 m_logicalOperation = TraceContext.Instance.CurrentOperation;
27 m_next = 0;
24 m_next = 0;
28 m_source = source;
25 m_source = source;
29 m_pending = source.Length;
26 m_pending = source.Length;
30 m_action = action;
27 m_action = action;
31
28
32 m_promise.Anyway(() => Dispose());
29 m_promise.On(Dispose, PromiseEventType.All);
33 m_promise.Cancelled(() => Dispose());
34
30
35 InitPool();
31 InitPool();
36 }
32 }
37
33
38 public Promise<int> Promise {
34 public Promise<int> Promise {
39 get {
35 get {
40 return m_promise;
36 return m_promise;
41 }
37 }
42 }
38 }
43
39
44 protected override void Worker() {
40 protected override void Worker() {
45 TraceContext.Fork(m_traceContext);
41 TraceContext.Instance.EnterLogicalOperation(m_logicalOperation, false);
42 try {
46 base.Worker();
43 base.Worker();
44 } finally {
45 TraceContext.Instance.Leave();
46 }
47 }
47 }
48
48
49 protected override bool TryDequeue(out int unit) {
49 protected override bool TryDequeue(out int unit) {
50 unit = Interlocked.Increment(ref m_next) - 1;
50 unit = Interlocked.Increment(ref m_next) - 1;
51 return unit >= m_source.Length ? false : true;
51 return unit < m_source.Length;
52 }
52 }
53
53
54 protected override void InvokeUnit(int unit) {
54 protected override void InvokeUnit(int unit) {
55 try {
55 try {
56 m_action(m_source[unit]);
56 m_action(m_source[unit]);
57 var pending = Interlocked.Decrement(ref m_pending);
57 var pending = Interlocked.Decrement(ref m_pending);
58 if (pending == 0)
58 if (pending == 0)
59 m_promise.Resolve(m_source.Length);
59 m_promise.Resolve(m_source.Length);
60 } catch (Exception e) {
60 } catch (Exception e) {
61 m_promise.Reject(e);
61 m_promise.Reject(e);
62 }
62 }
63 }
63 }
64 }
64 }
65
65
66 class ArrayMapper<TSrc, TDst>: DispatchPool<int> {
66 class ArrayMapper<TSrc, TDst>: DispatchPool<int> {
67 readonly Func<TSrc, TDst> m_transform;
67 readonly Func<TSrc, TDst> m_transform;
68 readonly TSrc[] m_source;
68 readonly TSrc[] m_source;
69 readonly TDst[] m_dest;
69 readonly TDst[] m_dest;
70 readonly Promise<TDst[]> m_promise = new Promise<TDst[]>();
70 readonly Promise<TDst[]> m_promise = new Promise<TDst[]>();
71 readonly TraceContext m_traceContext;
71 readonly LogicalOperation m_logicalOperation;
72
72
73 int m_pending;
73 int m_pending;
74 int m_next;
74 int m_next;
75
75
76 public ArrayMapper(TSrc[] source, Func<TSrc, TDst> transform, int threads)
76 public ArrayMapper(TSrc[] source, Func<TSrc, TDst> transform, int threads)
77 : base(threads) {
77 : base(threads) {
78
78
79 Debug.Assert (source != null);
79 Debug.Assert (source != null);
80 Debug.Assert( transform != null);
80 Debug.Assert( transform != null);
81
81
82 m_next = 0;
82 m_next = 0;
83 m_source = source;
83 m_source = source;
84 m_dest = new TDst[source.Length];
84 m_dest = new TDst[source.Length];
85 m_pending = source.Length;
85 m_pending = source.Length;
86 m_transform = transform;
86 m_transform = transform;
87 m_traceContext = TraceContext.Snapshot();
87 m_logicalOperation = TraceContext.Instance.CurrentOperation;
88
88
89 m_promise.Anyway(() => Dispose());
89 m_promise.On(Dispose, PromiseEventType.All);
90 m_promise.Cancelled(() => Dispose());
91
90
92 InitPool();
91 InitPool();
93 }
92 }
94
93
95 public Promise<TDst[]> Promise {
94 public Promise<TDst[]> Promise {
96 get {
95 get {
97 return m_promise;
96 return m_promise;
98 }
97 }
99 }
98 }
100
99
101 protected override void Worker() {
100 protected override void Worker() {
102 TraceContext.Fork(m_traceContext);
101 TraceContext.Instance.EnterLogicalOperation(m_logicalOperation,false);
102 try {
103 base.Worker();
103 base.Worker();
104 } finally {
105 TraceContext.Instance.Leave();
106 }
104 }
107 }
105
108
106 protected override bool TryDequeue(out int unit) {
109 protected override bool TryDequeue(out int unit) {
107 unit = Interlocked.Increment(ref m_next) - 1;
110 unit = Interlocked.Increment(ref m_next) - 1;
108 return unit >= m_source.Length ? false : true;
111 return unit < m_source.Length;
109 }
112 }
110
113
111 protected override void InvokeUnit(int unit) {
114 protected override void InvokeUnit(int unit) {
112 try {
115 try {
113 m_dest[unit] = m_transform(m_source[unit]);
116 m_dest[unit] = m_transform(m_source[unit]);
114 var pending = Interlocked.Decrement(ref m_pending);
117 var pending = Interlocked.Decrement(ref m_pending);
115 if (pending == 0)
118 if (pending == 0)
116 m_promise.Resolve(m_dest);
119 m_promise.Resolve(m_dest);
117 } catch (Exception e) {
120 } catch (Exception e) {
118 m_promise.Reject(e);
121 m_promise.Reject(e);
119 }
122 }
120 }
123 }
121 }
124 }
122
125
123 public static IPromise<TDst[]> ParallelMap<TSrc, TDst> (this TSrc[] source, Func<TSrc,TDst> transform, int threads) {
126 public static IPromise<TDst[]> ParallelMap<TSrc, TDst> (this TSrc[] source, Func<TSrc,TDst> transform, int threads) {
124 if (source == null)
127 if (source == null)
125 throw new ArgumentNullException("source");
128 throw new ArgumentNullException("source");
126 if (transform == null)
129 if (transform == null)
127 throw new ArgumentNullException("transform");
130 throw new ArgumentNullException("transform");
128
131
129 var mapper = new ArrayMapper<TSrc, TDst>(source, transform, threads);
132 var mapper = new ArrayMapper<TSrc, TDst>(source, transform, threads);
130 return mapper.Promise;
133 return mapper.Promise;
131 }
134 }
132
135
133 public static IPromise<int> ParallelForEach<TSrc>(this TSrc[] source, Action<TSrc> action, int threads) {
136 public static IPromise<int> ParallelForEach<TSrc>(this TSrc[] source, Action<TSrc> action, int threads) {
134 if (source == null)
137 if (source == null)
135 throw new ArgumentNullException("source");
138 throw new ArgumentNullException("source");
136 if (action == null)
139 if (action == null)
137 throw new ArgumentNullException("action");
140 throw new ArgumentNullException("action");
138
141
139 var iter = new ArrayIterator<TSrc>(source, action, threads);
142 var iter = new ArrayIterator<TSrc>(source, action, threads);
140 return iter.Promise;
143 return iter.Promise;
141 }
144 }
142
145
143 public static IPromise<TDst[]> ChainedMap<TSrc, TDst>(this TSrc[] source, ChainedOperation<TSrc, TDst> transform, int threads) {
146 public static IPromise<TDst[]> ChainedMap<TSrc, TDst>(this TSrc[] source, Func<TSrc, IPromise<TDst>> transform, int threads) {
144 if (source == null)
147 if (source == null)
145 throw new ArgumentNullException("source");
148 throw new ArgumentNullException("source");
146 if (transform == null)
149 if (transform == null)
147 throw new ArgumentNullException("transform");
150 throw new ArgumentNullException("transform");
148 if (threads <= 0)
151 if (threads <= 0)
149 throw new ArgumentOutOfRangeException("Threads number must be greater then zero");
152 throw new ArgumentOutOfRangeException("threads","Threads number must be greater then zero");
150
153
151 if (source.Length == 0)
154 if (source.Length == 0)
152 return Promise<TDst[]>.ResultToPromise(new TDst[0]);
155 return Promise<TDst[]>.FromResult(new TDst[0]);
153
156
154 var promise = new Promise<TDst[]>();
157 var promise = new Promise<TDst[]>();
155 var res = new TDst[source.Length];
158 var res = new TDst[source.Length];
156 var pending = source.Length;
159 var pending = source.Length;
157
160
158 var semaphore = new Semaphore(threads, threads);
161 object locker = new object();
162 int slots = threads;
159
163
160 AsyncPool.InvokeNewThread(() => {
164 // Analysis disable AccessToDisposedClosure
165 AsyncPool.RunThread<int>(() => {
161 for (int i = 0; i < source.Length; i++) {
166 for (int i = 0; i < source.Length; i++) {
162 if(promise.IsResolved)
167 if(promise.IsResolved)
163 break; // stop processing in case of error or cancellation
168 break; // stop processing in case of error or cancellation
164 var idx = i;
169 var idx = i;
165 semaphore.WaitOne();
170
171 if (Interlocked.Decrement(ref slots) < 0) {
172 lock(locker) {
173 while(slots < 0)
174 Monitor.Wait(locker);
175 }
176 }
177
166 try {
178 try {
167 var p1 = transform(source[i]);
179 transform(source[i])
168 p1.Anyway(() => semaphore.Release());
180 .On( x => {
169 p1.Cancelled(() => semaphore.Release());
181 Interlocked.Increment(ref slots);
170 p1.Then(
182 lock (locker) {
183 Monitor.Pulse(locker);
184 }
185 })
186 .On(
171 x => {
187 x => {
172 res[idx] = x;
188 res[idx] = x;
173 var left = Interlocked.Decrement(ref pending);
189 var left = Interlocked.Decrement(ref pending);
174 if (left == 0)
190 if (left == 0)
175 promise.Resolve(res);
191 promise.Resolve(res);
176 },
192 },
177 e => promise.Reject(e)
193 promise.Reject
178 );
194 );
179
195
180 } catch (Exception e) {
196 } catch (Exception e) {
181 promise.Reject(e);
197 promise.Reject(e);
182 }
198 }
183 }
199 }
184 return 0;
200 return 0;
185 });
201 });
186
202
187 return promise.Anyway(() => semaphore.Dispose());
203 return promise;
188 }
204 }
189
205
190 }
206 }
191 }
207 }
@@ -1,71 +1,155
1 using Implab.Diagnostics;
1 using Implab.Diagnostics;
2 using System;
2 using System;
3 using System.Threading;
3 using System.Threading;
4 using System.Linq;
4
5
5 namespace Implab.Parallels {
6 namespace Implab.Parallels {
6 /// <summary>
7 /// <summary>
7 /// Класс для распаралеливания задач.
8 /// Класс для распаралеливания задач.
8 /// </summary>
9 /// </summary>
9 /// <remarks>
10 /// <remarks>
10 /// Используя данный класс и лямда выражения можно распараллелить
11 /// Используя данный класс и лямда выражения можно распараллелить
11 /// вычисления, для этого используется концепция обещаний.
12 /// вычисления, для этого используется концепция обещаний.
12 /// </remarks>
13 /// </remarks>
13 public static class AsyncPool {
14 public static class AsyncPool {
14
15
15 public static IPromise<T> Invoke<T>(Func<T> func) {
16 public static IPromise<T> Invoke<T>(Func<T> func) {
16 var p = new Promise<T>();
17 var p = new Promise<T>();
17 var caller = TraceContext.Snapshot();
18 var caller = TraceContext.Instance.CurrentOperation;
18
19
19 ThreadPool.QueueUserWorkItem(param => {
20 ThreadPool.QueueUserWorkItem(param => {
20 TraceContext.Fork(caller);
21 TraceContext.Instance.EnterLogicalOperation(caller,false);
21 try {
22 try {
22 p.Resolve(func());
23 p.Resolve(func());
23 } catch(Exception e) {
24 } catch(Exception e) {
24 p.Reject(e);
25 p.Reject(e);
26 } finally {
27 TraceContext.Instance.Leave();
28 }
29 });
30
31 return p;
32 }
33
34 public static IPromise<T> Invoke<T>(Func<ICancellationToken, T> func) {
35 var p = new Promise<T>();
36 var caller = TraceContext.Instance.CurrentOperation;
37
38 ThreadPool.QueueUserWorkItem(param => {
39 TraceContext.Instance.EnterLogicalOperation(caller,false);
40 try {
41 p.Resolve(func(p));
42 } catch(Exception e) {
43 p.Reject(e);
44 } finally {
45 TraceContext.Instance.Leave();
25 }
46 }
26 });
47 });
27
48
28 return p;
49 return p;
29 }
50 }
30
51
31 public static IPromise<T> InvokeNewThread<T>(Func<T> func) {
52 public static IPromise<T> RunThread<T>(Func<T> func) {
32 var p = new Promise<T>();
53 var p = new Promise<T>();
33
54
34 var caller = TraceContext.Snapshot();
55 var caller = TraceContext.Instance.CurrentOperation;
35
56
36 var worker = new Thread(() => {
57 var worker = new Thread(() => {
37 TraceContext.Fork(caller);
58 TraceContext.Instance.EnterLogicalOperation(caller,false);
38 try {
59 try {
39 p.Resolve(func());
60 p.Resolve(func());
40 } catch (Exception e) {
61 } catch (Exception e) {
41 p.Reject(e);
62 p.Reject(e);
63 } finally {
64 TraceContext.Instance.Leave();
65 }
66 });
67 worker.IsBackground = true;
68 worker.Start();
69
70 return p;
71 }
72
73 public static IPromise<T> RunThread<T>(Func<ICancellationToken, T> func) {
74 var p = new Promise<T>();
75
76 var caller = TraceContext.Instance.CurrentOperation;
77
78 var worker = new Thread(() => {
79 TraceContext.Instance.EnterLogicalOperation(caller,false);
80 try {
81 p.Resolve(func(p));
82 } catch (Exception e) {
83 p.Reject(e);
84 } finally {
85 TraceContext.Instance.Leave();
42 }
86 }
43 });
87 });
44 worker.IsBackground = true;
88 worker.IsBackground = true;
45 worker.Start();
89 worker.Start();
46
90
47 return p;
91 return p;
48 }
92 }
49
93
50
94
51 public static IPromise InvokeNewThread(Action func) {
95 public static IPromise RunThread(Action func) {
52 var p = new Promise<object>();
96 var p = new Promise();
53
97
54 var caller = TraceContext.Snapshot();
98 var caller = TraceContext.Instance.CurrentOperation;
55
99
56 var worker = new Thread(() => {
100 var worker = new Thread(() => {
57 TraceContext.Fork(caller);
101 TraceContext.Instance.EnterLogicalOperation(caller,false);
58 try {
102 try {
59 func();
103 func();
60 p.Resolve();
104 p.Resolve();
61 } catch (Exception e) {
105 } catch (Exception e) {
62 p.Reject(e);
106 p.Reject(e);
107 } finally {
108 TraceContext.Instance.Leave();
63 }
109 }
64 });
110 });
65 worker.IsBackground = true;
111 worker.IsBackground = true;
66 worker.Start();
112 worker.Start();
67
113
68 return p;
114 return p;
69 }
115 }
116
117 public static IPromise RunThread(Action<ICancellationToken> func) {
118 var p = new Promise();
119
120 var caller = TraceContext.Instance.CurrentOperation;
121
122 var worker = new Thread(() => {
123 TraceContext.Instance.EnterLogicalOperation(caller,false);
124 try {
125 func(p);
126 p.Resolve();
127 } catch (Exception e) {
128 p.Reject(e);
129 } finally {
130 TraceContext.Instance.Leave();
131 }
132 });
133 worker.IsBackground = true;
134 worker.Start();
135
136 return p;
137 }
138
139 public static IPromise[] RunThread(params Action[] func) {
140 return func.Select(f => RunThread(f)).ToArray();
141 }
142
143 public static IPromise[] RunThread(params Action<ICancellationToken>[] func) {
144 return func.Select(f => RunThread(f)).ToArray();
145 }
146
147 public static IPromise<T>[] RunThread<T>(params Func<T>[] func) {
148 return func.Select(f => RunThread(f)).ToArray();
149 }
150
151 public static IPromise<T>[] RunThread<T>(params Func<ICancellationToken, T>[] func) {
152 return func.Select(f => RunThread(f)).ToArray();
70 }
153 }
71 }
154 }
155 }
@@ -1,334 +1,197
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading;
2 using System.Threading;
6 using System.Diagnostics;
7
3
8 namespace Implab.Parallels {
4 namespace Implab.Parallels {
9 public abstract class DispatchPool<TUnit> : IDisposable {
5 public abstract class DispatchPool<TUnit> : IDisposable {
10 readonly int m_minThreads;
6 readonly int m_minThreadsLimit;
11 readonly int m_maxThreads;
7 readonly int m_maxThreadsLimit;
8 readonly int m_releaseTimeout = 1000; // the timeout while the working thread will wait for the new tasks before exit
12
9
13 int m_createdThreads = 0; // the current size of the pool
10 int m_threads; // the current size of the pool
14 int m_activeThreads = 0; // the count of threads which are active
11 int m_maxRunningThreads; // the meximum reached size of the pool
15 int m_sleepingThreads = 0; // the count of currently inactive threads
12 int m_exit; // the pool is going to shutdown, all unused workers are released
16 int m_maxRunningThreads = 0; // the meximum reached size of the pool
17 int m_exitRequired = 0; // the pool is going to shutdown, all unused workers are released
18 int m_releaseTimeout = 100; // the timeout while the working thread will wait for the new tasks before exit
19 int m_wakeEvents = 0; // the count of wake events
20
13
21 AutoResetEvent m_hasTasks = new AutoResetEvent(false);
14 readonly object m_signal = new object(); // used to pulse waiting threads
22
15
23 protected DispatchPool(int min, int max) {
16 protected DispatchPool(int min, int max) {
24 if (min < 0)
17 if (min < 0)
25 throw new ArgumentOutOfRangeException("min");
18 throw new ArgumentOutOfRangeException("min");
26 if (max <= 0)
19 if (max <= 0)
27 throw new ArgumentOutOfRangeException("max");
20 throw new ArgumentOutOfRangeException("max");
28
21
29 if (min > max)
22 if (min > max)
30 min = max;
23 min = max;
31 m_minThreads = min;
24 m_minThreadsLimit = min;
32 m_maxThreads = max;
25 m_maxThreadsLimit = max;
33 }
26 }
34
27
35 protected DispatchPool(int threads)
28 protected DispatchPool(int threads)
36 : this(threads, threads) {
29 : this(threads, threads) {
37 }
30 }
38
31
39 protected DispatchPool() {
32 protected DispatchPool() {
40 int maxThreads, maxCP;
41 ThreadPool.GetMaxThreads(out maxThreads, out maxCP);
42
33
43 m_minThreads = 0;
34 m_minThreadsLimit = 0;
44 m_maxThreads = maxThreads;
35 m_maxThreadsLimit = Environment.ProcessorCount;
45 }
36 }
46
37
47 protected void InitPool() {
38 protected void InitPool() {
48 for (int i = 0; i < m_minThreads; i++)
39 for (int i = 0; i < m_minThreadsLimit; i++)
49 StartWorker();
40 StartWorker();
50 }
41 }
51
42
52 public int PoolSize {
43 public int PoolSize {
53 get {
44 get {
54 return m_createdThreads;
45 Thread.MemoryBarrier();
55 }
46 return m_threads;
56 }
57
58 public int ActiveThreads {
59 get {
60 return m_activeThreads;
61 }
47 }
62 }
48 }
63
49
64 public int MaxRunningThreads {
50 public int MaxRunningThreads {
65 get {
51 get {
52 Thread.MemoryBarrier();
66 return m_maxRunningThreads;
53 return m_maxRunningThreads;
67 }
54 }
68 }
55 }
69
56
70 protected bool IsDisposed {
57 protected bool IsDisposed {
71 get {
58 get {
72 return m_exitRequired != 0;
59 Thread.MemoryBarrier();
60 return m_exit == 1;
73 }
61 }
74 }
62 }
75
63
76 protected abstract bool TryDequeue(out TUnit unit);
64 protected abstract bool TryDequeue(out TUnit unit);
77
65
78 #region thread execution traits
66 bool Dequeue(out TUnit unit, int timeout) {
79 int SignalThread() {
67 int ts = Environment.TickCount;
80 var signals = Interlocked.Increment(ref m_wakeEvents);
68 if (TryDequeue(out unit))
81 if(signals == 1)
69 return true;
82 m_hasTasks.Set();
70 lock (m_signal) {
83 return signals;
71 while (!TryDequeue(out unit) && m_exit == 0)
72 if(!Monitor.Wait(m_signal, Math.Max(0, ts + timeout - Environment.TickCount))) {
73 // timeout
74 return false;
84 }
75 }
85
76 // queue item or terminate
86 bool FetchSignalOrWait(int timeout) {
77 Monitor.Pulse(m_signal);
87 var start = Environment.TickCount;
78 if (m_exit == 1)
88
79 return false;
89 // означает, что поток владеет блокировкой и при успешном получении сигнала должен
80 }
90 // ее вернуть, чтобы другой ожидающий поток смог
91 bool hasLock = false;
92 do {
93 int signals;
94 do {
95 signals = m_wakeEvents;
96 if (signals == 0)
97 break;
98 } while (Interlocked.CompareExchange(ref m_wakeEvents, signals - 1, signals) != signals);
99
100 if (signals >= 1) {
101 if (signals > 1 && hasLock)
102 m_hasTasks.Set();
103 return true;
81 return true;
104 }
82 }
105
83
106 if (timeout != -1)
84 protected void SignalThread() {
107 timeout = Math.Max(0, timeout - (Environment.TickCount - start));
85 lock (m_signal) {
108
86 Monitor.Pulse(m_signal);
109 // если сигналов больше не осталось, то первый поток, который дошел сюда сбросит событие
110 // и уйдет на пустой цикл, после чего заблокируется
111
112 hasLock = true;
113 } while (m_hasTasks.WaitOne(timeout));
114
115 return false;
116 }
117
118 bool Sleep(int timeout) {
119 Interlocked.Increment(ref m_sleepingThreads);
120 if (FetchSignalOrWait(timeout)) {
121 Interlocked.Decrement(ref m_sleepingThreads);
122 return true;
123 } else {
124 Interlocked.Decrement(ref m_sleepingThreads);
125 return false;
126 }
127 }
87 }
128 #endregion
129
130 /// <summary>
131 /// Запускает либо новый поток, если раньше не было ни одного потока, либо устанавливает событие пробуждение одного спящего потока
132 /// </summary>
133 protected void GrowPool() {
134 if (m_exitRequired != 0)
135 return;
136 if (m_sleepingThreads > m_wakeEvents) {
137 //Console.WriteLine("Waking threads (sleeps {0}, pending {1})", m_sleepingThreads, m_wakeEvents);
138
139 // all sleeping threads may gone
140 SignalThread(); // wake a sleeping thread;
141
142 // we can't check whether signal has been processed
143 // anyway it may take some time for the thread to start
144 // we will ensure that at least one thread is running
145
146 EnsurePoolIsAlive();
147 } else {
148 // if there is no sleeping threads in the pool
149 if (!StartWorker()) {
150 // we haven't started a new thread, but the current can be on the way to terminate and it can't process the queue
151 // send it a signal to spin again
152 SignalThread();
153 EnsurePoolIsAlive();
154 }
155 }
156 }
157
158 protected void EnsurePoolIsAlive() {
159 if (AllocateThreadSlot(1)) {
160 // if there were no threads in the pool
161 var worker = new Thread(this.Worker);
162 worker.IsBackground = true;
163 worker.Start();
164 }
165 }
166
167 protected virtual bool Suspend() {
168 //no tasks left, exit if the thread is no longer needed
169 bool last;
170 bool requestExit;
171
172 // if threads have a timeout before releasing
173 if (m_releaseTimeout > 0)
174 requestExit = !Sleep(m_releaseTimeout);
175 else
176 requestExit = true;
177
178 if (!requestExit)
179 return true;
180
181 // release unsused thread
182 if (requestExit && ReleaseThreadSlot(out last)) {
183 // in case at the moment the last thread was being released
184 // a new task was added to the queue, we need to try
185 // to revoke the thread to avoid the situation when the task is left unprocessed
186 if (last && FetchSignalOrWait(0)) { // FetchSignalOrWait(0) will fetch pending task or will return false
187 SignalThread(); // since FetchSignalOrWait(0) has fetched the signal we need to reschedule it
188 return AllocateThreadSlot(1); // ensure that at least one thread is alive
189 }
190
191 return false;
192 }
193
194 // wait till infinity
195 Sleep(-1);
196
197 return true;
198 }
88 }
199
89
200 #region thread slots traits
90 #region thread slots traits
201
91
202 bool AllocateThreadSlot() {
92 bool AllocateThreadSlot() {
203 int current;
93 int current;
204 // use spins to allocate slot for the new thread
94 // use spins to allocate slot for the new thread
205 do {
95 do {
206 current = m_createdThreads;
96 current = m_threads;
207 if (current >= m_maxThreads || m_exitRequired != 0)
97 if (current >= m_maxThreadsLimit || m_exit == 1)
208 // no more slots left or the pool has been disposed
98 // no more slots left or the pool has been disposed
209 return false;
99 return false;
210 } while (current != Interlocked.CompareExchange(ref m_createdThreads, current + 1, current));
100 } while (current != Interlocked.CompareExchange(ref m_threads, current + 1, current));
211
101
212 UpdateMaxThreads(current + 1);
102 UpdateMaxThreads(current + 1);
213
103
214 return true;
104 return true;
215 }
105 }
216
106
217 bool AllocateThreadSlot(int desired) {
107 bool AllocateThreadSlot(int desired) {
218 if (desired - 1 != Interlocked.CompareExchange(ref m_createdThreads, desired, desired - 1))
108 if (desired - 1 != Interlocked.CompareExchange(ref m_threads, desired, desired - 1))
219 return false;
109 return false;
220
110
221 UpdateMaxThreads(desired);
111 UpdateMaxThreads(desired);
222
112
223 return true;
113 return true;
224 }
114 }
225
115
226 bool ReleaseThreadSlot(out bool last) {
116 bool ReleaseThreadSlot(out bool last) {
227 last = false;
117 last = false;
228 int current;
118 int current;
229 // use spins to release slot for the new thread
119 // use spins to release slot for the new thread
120 Thread.MemoryBarrier();
230 do {
121 do {
231 current = m_createdThreads;
122 current = m_threads;
232 if (current <= m_minThreads && m_exitRequired == 0)
123 if (current <= m_minThreadsLimit && m_exit == 0)
233 // the thread is reserved
124 // the thread is reserved
234 return false;
125 return false;
235 } while (current != Interlocked.CompareExchange(ref m_createdThreads, current - 1, current));
126 } while (current != Interlocked.CompareExchange(ref m_threads, current - 1, current));
236
127
237 last = (current == 1);
128 last = (current == 1);
238
129
239 return true;
130 return true;
240 }
131 }
241
132
242 /// <summary>
243 /// releases thread slot unconditionally, used during cleanup
244 /// </summary>
245 /// <returns>true - no more threads left</returns>
246 bool ReleaseThreadSlotAnyway() {
247 var left = Interlocked.Decrement(ref m_createdThreads);
248 return left == 0;
249 }
250
251 void UpdateMaxThreads(int count) {
133 void UpdateMaxThreads(int count) {
252 int max;
134 int max;
253 do {
135 do {
254 max = m_maxRunningThreads;
136 max = m_maxRunningThreads;
255 if (max >= count)
137 if (max >= count)
256 break;
138 break;
257 } while(max != Interlocked.CompareExchange(ref m_maxRunningThreads, count, max));
139 } while(max != Interlocked.CompareExchange(ref m_maxRunningThreads, count, max));
258 }
140 }
259
141
260 #endregion
142 #endregion
261
143
262 bool StartWorker() {
144 protected bool StartWorker() {
263 if (AllocateThreadSlot()) {
145 if (AllocateThreadSlot()) {
264 // slot successfully allocated
146 // slot successfully allocated
265 var worker = new Thread(this.Worker);
147 var worker = new Thread(Worker);
266 worker.IsBackground = true;
148 worker.IsBackground = true;
267 worker.Start();
149 worker.Start();
268
150
269 return true;
151 return true;
270 } else {
152 }
271 return false;
153 return false;
272 }
154 }
273 }
274
155
275 protected abstract void InvokeUnit(TUnit unit);
156 protected abstract void InvokeUnit(TUnit unit);
276
157
277 protected virtual void Worker() {
158 protected virtual void Worker() {
278 TUnit unit;
159 TUnit unit;
279 //Console.WriteLine("{0}: Active", Thread.CurrentThread.ManagedThreadId);
160 bool last;
280 Interlocked.Increment(ref m_activeThreads);
281 do {
161 do {
282 // exit if requested
162 while (Dequeue(out unit, m_releaseTimeout)) {
283 if (m_exitRequired != 0) {
163 InvokeUnit(unit);
284 // release the thread slot
164 }
285 Interlocked.Decrement(ref m_activeThreads);
165 if(!ReleaseThreadSlot(out last))
286 if (ReleaseThreadSlotAnyway()) // it was the last worker
166 continue;
287 m_hasTasks.Dispose();
167 // queue may be not empty
288 else
168 if (last && TryDequeue(out unit)) {
289 SignalThread(); // wake next worker
169 InvokeUnit(unit);
170 if (AllocateThreadSlot(1))
171 continue;
172 // we can safely exit since pool is alive
173 }
290 break;
174 break;
175 } while(true);
291 }
176 }
292
177
293 // fetch task
294 if (TryDequeue(out unit)) {
295 InvokeUnit(unit);
296 continue;
297 }
298 Interlocked.Decrement(ref m_activeThreads);
299
300 // entering suspend state
301 // keep this thread and wait
302 if (!Suspend())
303 break;
304 //Console.WriteLine("{0}: Awake", Thread.CurrentThread.ManagedThreadId);
305 Interlocked.Increment(ref m_activeThreads);
306 } while (true);
307 //Console.WriteLine("{0}: Exited", Thread.CurrentThread.ManagedThreadId);
308 }
309
178
310 protected virtual void Dispose(bool disposing) {
179 protected virtual void Dispose(bool disposing) {
311 if (disposing) {
180 if (disposing) {
312 if (m_exitRequired == 0) {
181 if (0 == Interlocked.CompareExchange(ref m_exit, 1, 0)) { // implies memory barrier
313 if (Interlocked.CompareExchange(ref m_exitRequired, 1, 0) != 0)
314 return;
315
316 // wake sleeping threads
182 // wake sleeping threads
317 if (m_createdThreads > 0)
318 SignalThread();
183 SignalThread();
319 else
320 m_hasTasks.Dispose();
321 GC.SuppressFinalize(this);
184 GC.SuppressFinalize(this);
322 }
185 }
323 }
186 }
324 }
187 }
325
188
326 public void Dispose() {
189 public void Dispose() {
327 Dispose(true);
190 Dispose(true);
328 }
191 }
329
192
330 ~DispatchPool() {
193 ~DispatchPool() {
331 Dispose(false);
194 Dispose(false);
332 }
195 }
333 }
196 }
334 }
197 }
@@ -1,75 +1,143
1 using System;
1 using System.Threading;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System;
4 using System.Text;
4 using System.Collections;
5 using System.Threading;
6
5
7 namespace Implab.Parallels {
6 namespace Implab.Parallels {
8 public class MTQueue<T> {
7 public class MTQueue<T> : IEnumerable<T> {
9 class Node {
8 class Node {
10 public Node(T value) {
9 public Node(T value) {
11 this.value = value;
10 this.value = value;
12 }
11 }
13 public readonly T value;
12 public readonly T value;
14 public Node next;
13 public Node next;
15 }
14 }
16
15
17 Node m_first;
16 Node m_first;
18 Node m_last;
17 Node m_last;
19
18
20 public void Enqueue(T value) {
19 public void Enqueue(T value) {
20 Thread.MemoryBarrier();
21
21 var last = m_last;
22 var last = m_last;
22 var next = new Node(value);
23 var next = new Node(value);
23
24
25 // Interlocaked.CompareExchange implies Thread.MemoryBarrier();
26 // to ensure that the next node is completely constructed
24 while (last != Interlocked.CompareExchange(ref m_last, next, last))
27 while (last != Interlocked.CompareExchange(ref m_last, next, last))
25 last = m_last;
28 last = m_last;
26
29
27 if (last != null)
30 if (last != null)
28 last.next = next;
31 last.next = next;
29 else
32 else
30 m_first = next;
33 m_first = next;
31 }
34 }
32
35
33 public bool TryDequeue(out T value) {
36 public bool TryDequeue(out T value) {
34 Node first;
37 Node first;
35 Node next = null;
38 Node next;
36 value = default(T);
39 value = default(T);
37
40
41 Thread.MemoryBarrier();
38 do {
42 do {
39 first = m_first;
43 first = m_first;
40 if (first == null)
44 if (first == null)
41 return false;
45 return false;
42 next = first.next;
46 next = first.next;
43 if (next == null) {
47 if (next == null) {
44 // this is the last element,
48 // this is the last element,
45 // then try to update the tail
49 // then try to update the tail
46 if (first != Interlocked.CompareExchange(ref m_last, null, first)) {
50 if (first != Interlocked.CompareExchange(ref m_last, null, first)) {
47 // this is the race condition
51 // this is the race condition
48 if (m_last == null)
52 if (m_last == null)
49 // the queue is empty
53 // the queue is empty
50 return false;
54 return false;
51 // tail has been changed, we need to restart
55 // tail has been changed, we need to restart
52 continue;
56 continue;
53 }
57 }
54
58
55 // tail succesfully updated and first.next will never be changed
59 // tail succesfully updated and first.next will never be changed
56 // other readers will fail due to inconsistency m_last != m_fist && m_first.next == null
60 // other readers will fail due to inconsistency m_last != m_fist && m_first.next == null
57 // however the parallel writer may update the m_first since the m_last is null
61 // however the parallel writer may update the m_first since the m_last is null
58
62
59 // so we need to fix inconsistency by setting m_first to null or if it has been
63 // so we need to fix inconsistency by setting m_first to null or if it has been
60 // updated by the writer already then we should just to give up
64 // updated by the writer already then we should just to give up
61 Interlocked.CompareExchange(ref m_first, null, first);
65 Interlocked.CompareExchange(ref m_first, null, first);
62 break;
66 break;
63
67
64 } else {
68 }
65 if (first == Interlocked.CompareExchange(ref m_first, next, first))
69 if (first == Interlocked.CompareExchange(ref m_first, next, first))
66 // head succesfully updated
70 // head succesfully updated
67 break;
71 break;
68 }
69 } while (true);
72 } while (true);
70
73
71 value = first.value;
74 value = first.value;
72 return true;
75 return true;
73 }
76 }
77
78 #region IEnumerable implementation
79
80 class Enumerator : IEnumerator<T> {
81 Node m_current;
82 Node m_first;
83
84 public Enumerator(Node first) {
85 m_first = first;
86 }
87
88 #region IEnumerator implementation
89
90 public bool MoveNext() {
91 m_current = m_current == null ? m_first : m_current.next;
92 return m_current != null;
93 }
94
95 public void Reset() {
96 m_current = null;
97 }
98
99 object IEnumerator.Current {
100 get {
101 if (m_current == null)
102 throw new InvalidOperationException();
103 return m_current.value;
74 }
104 }
75 }
105 }
106
107 #endregion
108
109 #region IDisposable implementation
110
111 public void Dispose() {
112 }
113
114 #endregion
115
116 #region IEnumerator implementation
117
118 public T Current {
119 get {
120 if (m_current == null)
121 throw new InvalidOperationException();
122 return m_current.value;
123 }
124 }
125
126 #endregion
127 }
128
129 public IEnumerator<T> GetEnumerator() {
130 return new Enumerator(m_first);
131 }
132
133 #endregion
134
135 #region IEnumerable implementation
136
137 IEnumerator IEnumerable.GetEnumerator() {
138 return GetEnumerator();
139 }
140
141 #endregion
142 }
143 }
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now