##// 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
1 NO CONTENT: new file 100644, binary diff hidden
1 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
1 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
1 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
1 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
1 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
1 NO CONTENT: new file 100644, binary diff hidden
1 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 1 syntax: glob
2 2 Implab.Test/bin/
3 3 *.user
4 4 Implab.Test/obj/
5 5 *.userprefs
6 6 Implab/bin/
7 7 Implab/obj/
8 8 TestResults/
9 9 Implab.Fx/obj/
10 10 Implab.Fx/bin/
11 11 Implab.Fx.Test/bin/
12 12 Implab.Fx.Test/obj/
13 13 _ReSharper.Implab/
14 14 Implab.Diagnostics.Interactive/bin/
15 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 1 using Implab.Parallels;
2 2 using System;
3 3 using System.Collections.Generic;
4 4 using System.Linq;
5 5 using System.Text;
6 6 using System.Threading;
7 7 using System.Threading.Tasks;
8 8 using System.Windows.Forms;
9 9
10 10 namespace Implab.Diagnostics.Interactive
11 11 {
12 public class InteractiveListener: TextListenerBase
12 public class InteractiveListener: ListenerBase
13 13 {
14 14 TraceForm m_form;
15 15
16 16 SynchronizationContext m_syncGuiThread;
17 readonly Promise<object> m_guiStarted = new Promise<object>();
17 readonly Promise m_guiStarted = new Promise();
18 18
19 19 readonly IPromise m_guiFinished;
20 readonly IPromise m_workerFinished = new Promise<object>();
20 // readonly IPromise m_workerFinished = new Promise<object>();
21 21
22 22 readonly MTQueue<TraceViewItem> m_queue = new MTQueue<TraceViewItem>();
23 23 readonly AutoResetEvent m_queueEvent = new AutoResetEvent(false);
24 24
25 25 int m_queueLength;
26 26 bool m_exitPending;
27 27
28 28 readonly object m_pauseLock = new object();
29 29 bool m_paused;
30 30 readonly ManualResetEvent m_pauseEvent = new ManualResetEvent(true);
31 31
32 public InteractiveListener(bool global) : base(global) {
33 m_guiFinished = AsyncPool.InvokeNewThread(GuiThread);
34 m_workerFinished = AsyncPool.InvokeNewThread(QueueThread);
32 public InteractiveListener() {
33 m_guiFinished = AsyncPool.RunThread(GuiThread);
34 /*m_workerFinished = */AsyncPool.RunThread(QueueThread);
35 35
36 36 m_guiStarted.Join();
37 37 }
38 38
39 39 void GuiThread() {
40 40 m_form = new TraceForm(); // will create SynchronizationContext
41 41
42 42 m_form.PauseEvents += (s,a) => Pause();
43 43 m_form.ResumeEvents += (s, a) => Resume();
44 44
45 45 m_syncGuiThread = SynchronizationContext.Current;
46 46 m_guiStarted.Resolve();
47 47 Application.Run();
48 48 }
49 49
50 50 void QueueThread() {
51 51 while (!m_exitPending) {
52 52 if (m_paused)
53 53 m_pauseEvent.WaitOne();
54 54
55 55 TraceViewItem item;
56 56 if (m_queue.TryDequeue(out item)) {
57 57 Interlocked.Decrement(ref m_queueLength);
58 58
59 59 m_syncGuiThread.Send(x => m_form.AddTraceEvent(item),null);
60 60 } else {
61 61 m_queueEvent.WaitOne();
62 62 }
63 63 }
64 64 }
65 65
66 66 public void Pause() {
67 67 // for consistency we need to set this properties atomically
68 68 lock (m_pauseLock) {
69 69 m_pauseEvent.Reset();
70 70 m_paused = true;
71 71 }
72 72 }
73 73
74 74 public void Resume() {
75 75 // for consistency we need to set this properties atomically
76 76 lock (m_pauseLock) {
77 77 m_paused = false;
78 78 m_pauseEvent.Set();
79 79 }
80 80 }
81 81
82 82 void Enqueue(TraceViewItem item) {
83 83 m_queue.Enqueue(item);
84 84 if (Interlocked.Increment(ref m_queueLength) == 1)
85 85 m_queueEvent.Set();
86 86 }
87 87
88 88 public void ShowForm() {
89 89 m_syncGuiThread.Post(x => m_form.Show(), null);
90 90 }
91 91
92 92 public void HideForm() {
93 93 m_syncGuiThread.Post(x => m_form.Hide(), null);
94 94 }
95 95
96 96 void Terminate() {
97 97 m_exitPending = true;
98 98 Resume();
99 99 m_syncGuiThread.Post(x => Application.ExitThread(), null);
100 100 }
101 101
102 102 protected override void Dispose(bool disposing) {
103 103 if (disposing) {
104 104 Terminate();
105 105 m_guiFinished.Join();
106 106 }
107 107 base.Dispose(disposing);
108 108 }
109 109
110 protected override void WriteEntry(TraceContext context, EventText text, string channel) {
110 public override void Write(LogEventArgs args, object entry) {
111 111 var item = new TraceViewItem {
112 Indent = text.indent,
113 Message = text.content,
114 Thread = context.ThreadId,
115 Channel = channel,
112 Indent = args.Operation.Level,
113 Message = entry.ToString(),
114 Thread = args.ThreadId,
115 Channel = args.ChannelName,
116 116 Timestamp = Environment.TickCount
117 117 };
118 118
119 119 Enqueue(item);
120 120 }
121 121 }
122 122 }
@@ -1,94 +1,114
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 <ProductVersion>
7 </ProductVersion>
6 <ProductVersion>8.0.30703</ProductVersion>
8 7 <SchemaVersion>2.0</SchemaVersion>
9 8 <ProjectGuid>{2F31E405-E267-4195-A05D-574093C21209}</ProjectGuid>
10 9 <OutputType>Library</OutputType>
11 10 <AppDesignerFolder>Properties</AppDesignerFolder>
12 11 <RootNamespace>Implab.Fx.Test</RootNamespace>
13 12 <AssemblyName>Implab.Fx.Test</AssemblyName>
14 <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
13 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
15 14 <FileAlignment>512</FileAlignment>
16 15 <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
16 <TargetFrameworkProfile />
17 17 </PropertyGroup>
18 18 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
19 19 <DebugSymbols>true</DebugSymbols>
20 20 <DebugType>full</DebugType>
21 21 <Optimize>false</Optimize>
22 22 <OutputPath>bin\Debug\</OutputPath>
23 23 <DefineConstants>DEBUG;TRACE</DefineConstants>
24 24 <ErrorReport>prompt</ErrorReport>
25 25 <WarningLevel>4</WarningLevel>
26 <Prefer32Bit>false</Prefer32Bit>
26 27 </PropertyGroup>
27 28 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
28 29 <DebugType>pdbonly</DebugType>
29 30 <Optimize>true</Optimize>
30 31 <OutputPath>bin\Release\</OutputPath>
31 32 <DefineConstants>TRACE</DefineConstants>
32 33 <ErrorReport>prompt</ErrorReport>
33 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 55 </PropertyGroup>
35 56 <ItemGroup>
36 57 <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
37 58 <Reference Include="System" />
38 59 <Reference Include="System.Core">
39 60 <RequiredTargetFramework>3.5</RequiredTargetFramework>
40 61 </Reference>
41 62 <Reference Include="System.Data" />
42 63 <Reference Include="System.Drawing" />
43 64 <Reference Include="System.Windows.Forms" />
44 65 <Reference Include="System.Xml" />
45 66 <Reference Include="WindowsBase" />
46 67 </ItemGroup>
47 68 <ItemGroup>
48 <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
49 <Visible>False</Visible>
50 </CodeAnalysisDependentAssemblyPaths>
51 </ItemGroup>
52 <ItemGroup>
53 69 <Compile Include="Properties\AssemblyInfo.cs" />
54 70 <Compile Include="OverlayTest.cs" />
55 71 <Compile Include="Sample\MainForm.cs">
56 72 <SubType>Form</SubType>
57 73 </Compile>
58 74 <Compile Include="Sample\MainForm.Designer.cs">
59 75 <DependentUpon>MainForm.cs</DependentUpon>
60 76 </Compile>
61 77 <Compile Include="Sample\OverlayForm.cs">
62 78 <SubType>Form</SubType>
63 79 </Compile>
64 80 <Compile Include="Sample\OverlayForm.Designer.cs">
65 81 <DependentUpon>OverlayForm.cs</DependentUpon>
66 82 </Compile>
67 83 </ItemGroup>
68 84 <ItemGroup>
69 85 <EmbeddedResource Include="Sample\MainForm.resx">
70 86 <DependentUpon>MainForm.cs</DependentUpon>
87 <LogicalName>
88 </LogicalName>
71 89 </EmbeddedResource>
72 90 <EmbeddedResource Include="Sample\OverlayForm.resx">
73 91 <DependentUpon>OverlayForm.cs</DependentUpon>
92 <LogicalName>
93 </LogicalName>
74 94 </EmbeddedResource>
75 95 </ItemGroup>
76 96 <ItemGroup>
77 97 <ProjectReference Include="..\Implab.Fx\Implab.Fx.csproj">
78 98 <Project>{06E706F8-6881-43EB-927E-FFC503AF6ABC}</Project>
79 99 <Name>Implab.Fx</Name>
80 100 </ProjectReference>
81 101 <ProjectReference Include="..\Implab\Implab.csproj">
82 102 <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
83 103 <Name>Implab</Name>
84 104 </ProjectReference>
85 105 </ItemGroup>
86 106 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
87 107 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
88 108 Other similar extension points exist, see Microsoft.Common.targets.
89 109 <Target Name="BeforeBuild">
90 110 </Target>
91 111 <Target Name="AfterBuild">
92 112 </Target>
93 113 -->
94 114 </Project> No newline at end of file
@@ -1,35 +1,38
1 using System;
2 using System.Text;
3 using System.Collections.Generic;
4 using System.Linq;
1 using System.Windows.Forms;
2 using Implab.Fx.Test.Sample;
3 using Implab.Fx;
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 13 using Microsoft.VisualStudio.TestTools.UnitTesting;
6 using System.Windows.Forms;
7 using Implab.Fx.Test.Sample;
8 using System.Drawing;
9 using Implab.Fx;
14
15 #endif
10 16
11 17 namespace Implab.Fx.Test
12 18 {
13 19 [TestClass]
14 20 public class OverlayTest
15 21 {
16 22 [TestMethod]
17 23 public void TestMethod1()
18 24 {
19 25 var mainForm = new MainForm();
20 26
21 27 mainForm.ButtonEvent += (sender, args) =>
22 28 {
23 29 var overlay = new OverlayForm();
24 mainForm.OverlayFadeIn(overlay).Then(
25 o => o.ButtonEvent += (s2, args2) =>
26 {
27 o.CloseFadeOut();
28 }
30 mainForm.OverlayFadeIn(overlay).On(
31 o => o.ButtonEvent += (s2, args2) => o.CloseFadeOut()
29 32 );
30 33 };
31 34
32 35 Application.Run(mainForm);
33 36 }
34 37 }
35 38 }
@@ -1,89 +1,95
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5 using System.Windows.Forms;
6 6 using System.Diagnostics;
7 7
8 8 namespace Implab.Fx
9 9 {
10 10 public static class AnimationHelpers
11 11 {
12 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 14 if (animation == null)
15 15 throw new ArgumentNullException("animation");
16 16
17 17 TVal oldValue = getter(animation.Traget);
18 18
19 19 animation.Step += (target, elaped, duration) =>
20 20 {
21 21 var value = fx(oldValue, newValue, elaped, duration);
22 22 setter(target, value);
23 23 };
24 24
25 25 return animation;
26 26 }
27 27
28 28 public static Animation<T> AnimateTransparency<T>(this T ctl, float newValue) where T : Form
29 29 {
30 30 var anim = new Animation<T>(ctl);
31 31
32 32 anim.AnimateProperty(
33 33 (target, value) => target.Opacity = value,
34 34 target => target.Opacity,
35 35 newValue,
36 36 (ov, nv, el, du) => ov + ((float)el / du) * (nv - ov)
37 37 );
38 38 return anim;
39 39 }
40 40
41 41 public static IPromise<T> CloseFadeOut<T>(this T ctl) where T : Form
42 42 {
43 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 54 public static IPromise<T> OverlayFadeIn<T>(this Form that, T overlay) where T : Form
49 55 {
50 56 if (that == null)
51 57 throw new ArgumentNullException("that");
52 58 if (overlay == null)
53 59 throw new ArgumentNullException("overlay");
54 60
55 61 // setup overlay
56 62 overlay.Opacity = 0;
57 63 overlay.FormBorderStyle = FormBorderStyle.None;
58 64 overlay.ShowInTaskbar = false;
59 65
60 66 that.AddOwnedForm(overlay);
61 67
62 68 EventHandler handler = (object sender, EventArgs args) =>
63 69 {
64 70 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
65 71 };
66 72
67 73 // attach handlers
68 74 that.Move += handler;
69 75 that.Resize += handler;
70 76 that.Shown += handler;
71 77
72 78 // remove handlers to release overlay
73 79 overlay.FormClosed += (sender, args) =>
74 80 {
75 81 that.Move -= handler;
76 82 that.Resize -= handler;
77 83 that.Shown -= handler;
78 84 };
79 85
80 86 overlay.Show(that);
81 87 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
82 88
83 89 return overlay
84 90 .AnimateTransparency(1)
85 91 .Play()
86 92 .DispatchToControl(overlay);
87 93 }
88 94 }
89 95 }
@@ -1,64 +1,88
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 6 <ProductVersion>8.0.30703</ProductVersion>
7 7 <SchemaVersion>2.0</SchemaVersion>
8 8 <ProjectGuid>{06E706F8-6881-43EB-927E-FFC503AF6ABC}</ProjectGuid>
9 9 <OutputType>Library</OutputType>
10 10 <AppDesignerFolder>Properties</AppDesignerFolder>
11 11 <RootNamespace>Implab.Fx</RootNamespace>
12 12 <AssemblyName>Implab.Fx</AssemblyName>
13 <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
13 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
14 14 <FileAlignment>512</FileAlignment>
15 <ReleaseVersion>0.2</ReleaseVersion>
16 <TargetFrameworkProfile />
15 17 </PropertyGroup>
16 18 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
17 19 <DebugSymbols>true</DebugSymbols>
18 20 <DebugType>full</DebugType>
19 21 <Optimize>false</Optimize>
20 22 <OutputPath>bin\Debug\</OutputPath>
21 23 <DefineConstants>DEBUG;TRACE</DefineConstants>
22 24 <ErrorReport>prompt</ErrorReport>
23 25 <WarningLevel>4</WarningLevel>
26 <Prefer32Bit>false</Prefer32Bit>
24 27 </PropertyGroup>
25 28 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
26 29 <DebugType>pdbonly</DebugType>
27 30 <Optimize>true</Optimize>
28 31 <OutputPath>bin\Release\</OutputPath>
29 32 <DefineConstants>TRACE</DefineConstants>
30 33 <ErrorReport>prompt</ErrorReport>
31 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 55 </PropertyGroup>
33 56 <ItemGroup>
34 57 <Reference Include="System" />
35 58 <Reference Include="System.Core" />
36 59 <Reference Include="System.Drawing" />
37 60 <Reference Include="System.Windows.Forms" />
38 61 <Reference Include="System.Xml.Linq" />
39 62 <Reference Include="System.Data.DataSetExtensions" />
40 63 <Reference Include="Microsoft.CSharp" />
41 64 <Reference Include="System.Data" />
42 65 <Reference Include="System.Xml" />
43 66 </ItemGroup>
44 67 <ItemGroup>
45 68 <Compile Include="Animation.cs" />
46 69 <Compile Include="AnimationHelpers.cs" />
47 70 <Compile Include="PromiseHelpers.cs" />
48 71 <Compile Include="Properties\AssemblyInfo.cs" />
72 <Compile Include="ControlBoundPromise.cs" />
49 73 </ItemGroup>
50 74 <ItemGroup>
51 75 <ProjectReference Include="..\Implab\Implab.csproj">
52 <Project>{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}</Project>
76 <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
53 77 <Name>Implab</Name>
54 78 </ProjectReference>
55 79 </ItemGroup>
56 80 <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
57 81 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
58 82 Other similar extension points exist, see Microsoft.Common.targets.
59 83 <Target Name="BeforeBuild">
60 84 </Target>
61 85 <Target Name="AfterBuild">
62 86 </Target>
63 87 -->
64 88 </Project> No newline at end of file
@@ -1,95 +1,43
1 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 2 using System.Windows.Forms;
6 3 using System.Threading;
7 4
8 5 namespace Implab.Fx
9 6 {
10 7 public static class PromiseHelpers
11 8 {
12 9 /// <summary>
13 10 /// Перенаправляет обработку обещания в поток указанного элемента управления.
14 11 /// </summary>
15 12 /// <typeparam name="T">Тип результата обещания</typeparam>
16 13 /// <param name="that">Исходное обещание</param>
17 14 /// <param name="ctl">Элемент управления</param>
18 15 /// <returns>Новое обещание, обработчики которого будут выполнены в потоке элемента управления.</returns>
19 16 /// <exception cref="ArgumentNullException">Параметр не может быть <c>null</c>.</exception>
20 17 /// <example>
21 18 /// client
22 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 21 /// .Then(
25 22 /// description => m_ctl.Text = description // now it's safe
26 23 /// )
27 24 /// </example>
28 public static Promise<T> DispatchToControl<T>(this Promise<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 =>
25 public static IPromise<T> DispatchToControl<T>(this IPromise<T> that, Control ctl)
39 26 {
40 if (ctl.InvokeRequired)
41 ctl.Invoke(new Action<T>(directed.Resolve), res);
42 else
43 directed.Resolve(res);
44 },
45 err =>
46 {
47 if (ctl.InvokeRequired)
48 ctl.Invoke(new Action<Exception>(directed.Reject), err);
49 else
50 directed.Reject(err);
51 }
27 Safe.ArgumentNotNull(that, "that");
28 Safe.ArgumentNotNull(ctl, "ctl");
29
30 var directed = new ControlBoundPromise<T>(ctl);
31
32 directed.On(that.Cancel, PromiseEventType.Cancelled);
33
34 that.On(
35 directed.Resolve,
36 directed.Reject,
37 directed.Cancel
52 38 );
53 39
54 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 1 using System.Reflection;
2 2 using System.Runtime.CompilerServices;
3 3 using System.Runtime.InteropServices;
4 4
5 5 // General Information about an assembly is controlled through the following
6 6 // set of attributes. Change these attribute values to modify the information
7 7 // associated with an assembly.
8 8 [assembly: AssemblyTitle("Implab.Fx")]
9 9 [assembly: AssemblyDescription("")]
10 10 [assembly: AssemblyConfiguration("")]
11 11 [assembly: AssemblyCompany("")]
12 12 [assembly: AssemblyProduct("Implab.Fx")]
13 13 [assembly: AssemblyCopyright("Copyright © 2013")]
14 14 [assembly: AssemblyTrademark("")]
15 15 [assembly: AssemblyCulture("")]
16 16
17 17 // Setting ComVisible to false makes the types in this assembly not visible
18 18 // to COM components. If you need to access a type in this assembly from
19 19 // COM, set the ComVisible attribute to true on that type.
20 20 [assembly: ComVisible(false)]
21 21
22 22 // The following GUID is for the ID of the typelib if this project is exposed to COM
23 23 [assembly: Guid("d239c29f-98e2-4942-9569-554a8511d07b")]
24 24
25 25 // Version information for an assembly consists of the following four values:
26 26 //
27 27 // Major Version
28 28 // Minor Version
29 29 // Build Number
30 30 // Revision
31 31 //
32 32 // You can specify all the values or you can default the Build and Revision Numbers
33 33 // by using the '*' as shown below:
34 34 // [assembly: AssemblyVersion("1.0.*")]
35 [assembly: AssemblyVersion("1.0.0.0")]
36 [assembly: AssemblyFileVersion("1.0.0.0")]
35 [assembly: AssemblyVersion("2.0.*")]
This diff has been collapsed as it changes many lines, (571 lines changed) Show them Hide them
@@ -1,386 +1,863
1 1 using System;
2 using Microsoft.VisualStudio.TestTools.UnitTesting;
3 2 using System.Reflection;
4 3 using System.Threading;
5 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 18 namespace Implab.Test {
8 19 [TestClass]
9 20 public class AsyncTests {
10 21 [TestMethod]
11 22 public void ResolveTest() {
12 23 int res = -1;
13 24 var p = new Promise<int>();
14 25 p.Then(x => res = x);
15 26 p.Resolve(100);
16 27
17 28 Assert.AreEqual(100, res);
18 29 }
19 30
20 31 [TestMethod]
21 32 public void RejectTest() {
22 33 int res = -1;
23 34 Exception err = null;
24 35
25 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 44 p.Reject(new ApplicationException("error"));
28 45
29 46 Assert.AreEqual(res, -1);
30 47 Assert.AreEqual(err.Message, "error");
31 48
32 49 }
33 50
34 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 84 public void JoinSuccessTest() {
36 85 var p = new Promise<int>();
37 86 p.Resolve(100);
38 87 Assert.AreEqual(p.Join(), 100);
39 88 }
40 89
41 90 [TestMethod]
42 91 public void JoinFailTest() {
43 92 var p = new Promise<int>();
44 93 p.Reject(new ApplicationException("failed"));
45 94
46 95 try {
47 96 p.Join();
48 97 throw new ApplicationException("WRONG!");
49 98 } catch (TargetInvocationException err) {
50 99 Assert.AreEqual(err.InnerException.Message, "failed");
51 100 } catch {
52 101 Assert.Fail("Got wrong excaption");
53 102 }
54 103 }
55 104
56 105 [TestMethod]
57 106 public void MapTest() {
58 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 110 p.Resolve(100);
62 111
63 112 Assert.AreEqual(p2.Join(), "100");
64 113 }
65 114
66 115 [TestMethod]
67 116 public void FixErrorTest() {
68 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 121 p.Reject(new Exception());
73 122
74 123 Assert.AreEqual(p2.Join(), 101);
75 124 }
76 125
77 126 [TestMethod]
78 127 public void ChainTest() {
79 128 var p1 = new Promise<int>();
80 129
81 130 var p3 = p1.Chain(x => {
82 131 var p2 = new Promise<string>();
83 132 p2.Resolve(x.ToString());
84 133 return p2;
85 134 });
86 135
87 136 p1.Resolve(100);
88 137
89 138 Assert.AreEqual(p3.Join(), "100");
90 139 }
91 140
92 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 157 public void PoolTest() {
94 158 var pid = Thread.CurrentThread.ManagedThreadId;
95 159 var p = AsyncPool.Invoke(() => Thread.CurrentThread.ManagedThreadId);
96 160
97 161 Assert.AreNotEqual(pid, p.Join());
98 162 }
99 163
100 164 [TestMethod]
101 165 public void WorkerPoolSizeTest() {
102 var pool = new WorkerPool(5, 10, 0);
166 var pool = new WorkerPool(5, 10, 1);
103 167
104 168 Assert.AreEqual(5, pool.PoolSize);
105 169
106 170 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
107 171 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
108 172 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
109 173
110 174 Assert.AreEqual(5, pool.PoolSize);
111 175
112 176 for (int i = 0; i < 100; i++)
113 177 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
114 178 Thread.Sleep(200);
115 179 Assert.AreEqual(10, pool.PoolSize);
116 180
117 181 pool.Dispose();
118 182 }
119 183
120 184 [TestMethod]
121 185 public void WorkerPoolCorrectTest() {
122 186 var pool = new WorkerPool(0,1000,100);
123 187
124 int iterations = 1000;
188 const int iterations = 1000;
125 189 int pending = iterations;
126 190 var stop = new ManualResetEvent(false);
127 191
128 192 var count = 0;
129 193 for (int i = 0; i < iterations; i++) {
130 194 pool
131 195 .Invoke(() => 1)
132 196 .Then(x => Interlocked.Add(ref count, x))
133 197 .Then(x => Math.Log10(x))
134 .Anyway(() => {
198 .On(() => {
135 199 Interlocked.Decrement(ref pending);
136 200 if (pending == 0)
137 201 stop.Set();
138 });
202 }, PromiseEventType.All);
139 203 }
140 204
141 205 stop.WaitOne();
142 206
143 207 Assert.AreEqual(iterations, count);
144 208 Console.WriteLine("Max threads: {0}", pool.MaxRunningThreads);
145 209 pool.Dispose();
146 210
147 211 }
148 212
149 213 [TestMethod]
150 214 public void WorkerPoolDisposeTest() {
151 215 var pool = new WorkerPool(5, 20);
152 216 Assert.AreEqual(5, pool.PoolSize);
153 217 pool.Dispose();
154 218 Thread.Sleep(500);
155 219 Assert.AreEqual(0, pool.PoolSize);
156 220 pool.Dispose();
157 221 }
158 222
159 223 [TestMethod]
160 224 public void MTQueueTest() {
161 225 var queue = new MTQueue<int>();
162 226 int res;
163 227
164 228 queue.Enqueue(10);
165 229 Assert.IsTrue(queue.TryDequeue(out res));
166 230 Assert.AreEqual(10, res);
167 231 Assert.IsFalse(queue.TryDequeue(out res));
168 232
169 233 for (int i = 0; i < 1000; i++)
170 234 queue.Enqueue(i);
171 235
172 236 for (int i = 0; i < 1000; i++) {
173 237 queue.TryDequeue(out res);
174 238 Assert.AreEqual(i, res);
175 239 }
176 240
177 241 int writers = 0;
178 242 int readers = 0;
179 243 var stop = new ManualResetEvent(false);
180 244 int total = 0;
181 245
182 int itemsPerWriter = 1000;
183 int writersCount = 3;
246 const int itemsPerWriter = 10000;
247 const int writersCount = 10;
184 248
185 249 for (int i = 0; i < writersCount; i++) {
186 250 Interlocked.Increment(ref writers);
187 var wn = i;
188 251 AsyncPool
189 .InvokeNewThread(() => {
252 .RunThread(() => {
190 253 for (int ii = 0; ii < itemsPerWriter; ii++) {
191 254 queue.Enqueue(1);
192 255 }
193 256 return 1;
194 257 })
195 .Anyway(() => Interlocked.Decrement(ref writers));
258 .On(() => Interlocked.Decrement(ref writers), PromiseEventType.All);
196 259 }
197 260
198 261 for (int i = 0; i < 10; i++) {
199 262 Interlocked.Increment(ref readers);
200 var wn = i;
201 263 AsyncPool
202 .InvokeNewThread(() => {
264 .RunThread(() => {
203 265 int t;
204 266 do {
205 267 while (queue.TryDequeue(out t))
206 268 Interlocked.Add(ref total, t);
207 269 } while (writers > 0);
208 270 return 1;
209 271 })
210 .Anyway(() => {
272 .On(() => {
211 273 Interlocked.Decrement(ref readers);
212 274 if (readers == 0)
213 275 stop.Set();
214 });
276 }, PromiseEventType.All);
215 277 }
216 278
217 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 611 [TestMethod]
223 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 617 var rand = new Random();
229 618
230 619 for (int i = 0; i < count; i++)
231 620 args[i] = rand.NextDouble();
232 621
233 622 var t = Environment.TickCount;
234 623 var res = args.ParallelMap(x => Math.Sin(x*x), 4).Join();
235 624
236 625 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
237 626
238 627 t = Environment.TickCount;
239 628 for (int i = 0; i < count; i++)
240 629 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
241 630 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
242 631 }
243 632
244 633 [TestMethod]
245 634 public void ChainedMapTest() {
246 635
247 using (var pool = new WorkerPool(0,100,100)) {
248 int count = 10000;
636 using (var pool = new WorkerPool()) {
637 const int count = 10000;
249 638
250 double[] args = new double[count];
639 var args = new double[count];
251 640 var rand = new Random();
252 641
253 642 for (int i = 0; i < count; i++)
254 643 args[i] = rand.NextDouble();
255 644
256 645 var t = Environment.TickCount;
257 646 var res = args
258 647 .ChainedMap(
648 // Analysis disable once AccessToDisposedClosure
259 649 x => pool.Invoke(
260 650 () => Math.Sin(x * x)
261 651 ),
262 652 4
263 653 )
264 654 .Join();
265 655
266 656 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
267 657
268 658 t = Environment.TickCount;
269 659 for (int i = 0; i < count; i++)
270 660 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
271 661 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
272 662 Console.WriteLine("Max workers: {0}", pool.MaxRunningThreads);
273 663 }
274 664 }
275 665
276 666 [TestMethod]
277 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 672 var rand = new Random();
283 673
284 674 for (int i = 0; i < count; i++)
285 675 args[i] = (int)(rand.NextDouble() * 100);
286 676
287 677 int result = 0;
288 678
289 679 var t = Environment.TickCount;
290 680 args.ParallelForEach(x => Interlocked.Add(ref result, x), 4).Join();
291 681
292 682 Console.WriteLine("Iteration complete in {0} ms, result: {1}", Environment.TickCount - t, result);
293 683
294 684 int result2 = 0;
295 685
296 686 t = Environment.TickCount;
297 687 for (int i = 0; i < count; i++)
298 688 result2 += args[i];
299 689 Assert.AreEqual(result2, result);
300 690 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
301 691 }
302 692
303 693 [TestMethod]
304 694 public void ComplexCase1Test() {
305 695 var flags = new bool[3];
306 696
307 697 // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map)
308 698
309 var p = PromiseHelper
699 var step1 = PromiseHelper
310 700 .Sleep(200, "Alan")
311 .Cancelled(() => flags[0] = true)
701 .On(() => flags[0] = true, PromiseEventType.Cancelled);
702 var p = step1
312 703 .Chain(x =>
313 704 PromiseHelper
314 705 .Sleep(200, "Hi, " + x)
315 .Map(y => y)
316 .Cancelled(() => flags[1] = true)
706 .Then(y => y)
707 .On(() => flags[1] = true, PromiseEventType.Cancelled)
317 708 )
318 .Cancelled(() => flags[2] = true);
319 Thread.Sleep(300);
709 .On(() => flags[2] = true, PromiseEventType.Cancelled);
710 step1.Join();
320 711 p.Cancel();
321 712 try {
322 713 Assert.AreEqual(p.Join(), "Hi, Alan");
323 714 Assert.Fail("Shouldn't get here");
324 715 } catch (OperationCanceledException) {
325 716 }
326 717
327 718 Assert.IsFalse(flags[0]);
328 719 Assert.IsTrue(flags[1]);
329 720 Assert.IsTrue(flags[2]);
330 721 }
331 722
332 723 [TestMethod]
333 724 public void ChainedCancel1Test() {
334 // при отмене сцепленной асинхронной операции все обещание должно
335 // завершаться ошибкой OperationCanceledException
725 // при отмене сцепленной асинхронной операции все обещание должно
726 // завершаться ошибкой OperationCanceledException
336 727 var p = PromiseHelper
337 728 .Sleep(1, "Hi, HAL!")
338 .Chain(x => {
339 // запускаем две асинхронные операции
729 .Then(x => {
730 // запускаем две асинхронные операции
340 731 var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!");
341 // вторая операция отменяет первую до завершения
732 // вторая операция отменяет первую до завершения
342 733 PromiseHelper
343 734 .Sleep(100, "HAL, STOP!")
344 .Then(() => result.Cancel());
735 .Then(result.Cancel);
345 736 return result;
346 737 });
347 738 try {
348 739 p.Join();
349 740 } catch (TargetInvocationException err) {
350 741 Assert.IsTrue(err.InnerException is OperationCanceledException);
351 742 }
352 743 }
353 744
354 745 [TestMethod]
355 746 public void ChainedCancel2Test() {
356 // при отмене цепочки обещаний, вложенные операции также должны отменяться
357 IPromiseBase p = null;
747 // при отмене цепочки обещаний, вложенные операции также должны отменяться
358 748 var pSurvive = new Promise<bool>();
359 var hemStarted = new ManualResetEvent(false);
360 p = PromiseHelper
749 var hemStarted = new Signal();
750 var p = PromiseHelper
361 751 .Sleep(1, "Hi, HAL!")
362 .Chain(x => {
752 .Chain(() => {
363 753 hemStarted.Set();
364 // запускаем две асинхронные операции
754 // запускаем две асинхронные операции
365 755 var result = PromiseHelper
366 .Sleep(1000, "HEM ENABLED!!!")
367 .Then(s => pSurvive.Resolve(false));
756 .Sleep(2000, "HEM ENABLED!!!")
757 .Then(() => pSurvive.Resolve(false));
368 758
369 759 result
370 .Cancelled(() => pSurvive.Resolve(true));
760 .On(() => pSurvive.Resolve(true), PromiseEventType.Cancelled);
371 761
372 762 return result;
373 763 });
374 764
375 hemStarted.WaitOne();
765 hemStarted.Wait();
376 766 p.Cancel();
377 767
378 768 try {
379 769 p.Join();
770 Assert.Fail();
380 771 } catch (OperationCanceledException) {
772 }
381 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 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 <ProductVersion>
7 </ProductVersion>
6 <ProductVersion>8.0.30703</ProductVersion>
8 7 <SchemaVersion>2.0</SchemaVersion>
9 8 <ProjectGuid>{63F92C0C-61BF-48C0-A377-8D67C3C661D0}</ProjectGuid>
10 9 <OutputType>Library</OutputType>
11 10 <AppDesignerFolder>Properties</AppDesignerFolder>
12 11 <RootNamespace>Implab.Test</RootNamespace>
13 12 <AssemblyName>Implab.Test</AssemblyName>
14 <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
13 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
15 14 <FileAlignment>512</FileAlignment>
16 15 <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
16 <TargetFrameworkProfile />
17 17 </PropertyGroup>
18 18 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
19 19 <DebugSymbols>true</DebugSymbols>
20 20 <DebugType>full</DebugType>
21 21 <Optimize>false</Optimize>
22 22 <OutputPath>bin\Debug\</OutputPath>
23 23 <DefineConstants>DEBUG;TRACE</DefineConstants>
24 24 <ErrorReport>prompt</ErrorReport>
25 25 <WarningLevel>4</WarningLevel>
26 <Prefer32Bit>false</Prefer32Bit>
26 27 </PropertyGroup>
27 28 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
28 29 <DebugType>pdbonly</DebugType>
29 30 <Optimize>true</Optimize>
30 31 <OutputPath>bin\Release\</OutputPath>
31 32 <DefineConstants>TRACE</DefineConstants>
32 33 <ErrorReport>prompt</ErrorReport>
33 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 55 </PropertyGroup>
35 56 <ItemGroup>
36 57 <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
37 58 <Reference Include="System" />
38 59 <Reference Include="System.Core">
39 60 <RequiredTargetFramework>3.5</RequiredTargetFramework>
40 61 </Reference>
41 62 </ItemGroup>
42 63 <ItemGroup>
43 <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
44 <Visible>False</Visible>
45 </CodeAnalysisDependentAssemblyPaths>
46 </ItemGroup>
47 <ItemGroup>
48 64 <Compile Include="AsyncTests.cs" />
65 <Compile Include="CancelationTests.cs" />
49 66 <Compile Include="PromiseHelper.cs" />
50 67 <Compile Include="Properties\AssemblyInfo.cs" />
68 <Compile Include="RunnableComponentTests.cs" />
51 69 </ItemGroup>
52 70 <ItemGroup>
53 71 <ProjectReference Include="..\Implab\Implab.csproj">
54 72 <Project>{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}</Project>
55 73 <Name>Implab</Name>
56 74 </ProjectReference>
57 75 </ItemGroup>
58 76 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
59 77 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
60 78 Other similar extension points exist, see Microsoft.Common.targets.
61 79 <Target Name="BeforeBuild">
62 80 </Target>
63 81 <Target Name="AfterBuild">
64 82 </Target>
65 83 -->
66 84 </Project> No newline at end of file
@@ -1,17 +1,14
1 1 using Implab.Parallels;
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Text;
6 2 using System.Threading;
7 3
8 4 namespace Implab.Test {
9 class PromiseHelper {
10 public static Promise<T> Sleep<T>(int timeout, T retVal) {
11 return AsyncPool.Invoke(() => {
5 static class PromiseHelper {
6 public static IPromise<T> Sleep<T>(int timeout, T retVal) {
7 return AsyncPool.Invoke((ct) => {
8 ct.CancellationRequested(ct.CancelOperation);
12 9 Thread.Sleep(timeout);
13 10 return retVal;
14 11 });
15 12 }
16 13 }
17 14 }
@@ -1,35 +1,34
1 1 using System.Reflection;
2 2 using System.Runtime.CompilerServices;
3 3 using System.Runtime.InteropServices;
4 4
5 5 // General Information about an assembly is controlled through the following
6 6 // set of attributes. Change these attribute values to modify the information
7 7 // associated with an assembly.
8 8 [assembly: AssemblyTitle("Implab.Test")]
9 9 [assembly: AssemblyDescription("")]
10 10 [assembly: AssemblyConfiguration("")]
11 11 [assembly: AssemblyCompany("")]
12 12 [assembly: AssemblyProduct("Implab.Test")]
13 13 [assembly: AssemblyCopyright("Copyright © 2013")]
14 14 [assembly: AssemblyTrademark("")]
15 15 [assembly: AssemblyCulture("")]
16 16
17 17 // Setting ComVisible to false makes the types in this assembly not visible
18 18 // to COM components. If you need to access a type in this assembly from
19 19 // COM, set the ComVisible attribute to true on that type.
20 20 [assembly: ComVisible(false)]
21 21
22 22 // The following GUID is for the ID of the typelib if this project is exposed to COM
23 23 [assembly: Guid("bfcae720-21eb-4411-b70a-6eeab99071de")]
24 24
25 25 // Version information for an assembly consists of the following four values:
26 26 //
27 27 // Major Version
28 28 // Minor Version
29 29 // Build Number
30 30 // Revision
31 31 //
32 32 // You can specify all the values or you can default the Build and Revision Numbers
33 33 // by using the '*' as shown below:
34 [assembly: AssemblyVersion("1.0.0.0")]
35 [assembly: AssemblyFileVersion("1.0.0.0")]
34 [assembly: AssemblyVersion("0.0.*")]
@@ -1,51 +1,270
1 1 
2 2 Microsoft Visual Studio Solution File, Format Version 11.00
3 3 # Visual Studio 2010
4 4 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab", "Implab\Implab.csproj", "{F550F1F8-8746-4AD0-9614-855F4C4B7F05}"
5 5 EndProject
6 6 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CE8D8D18-437A-445C-B662-4C2CE79A76F6}"
7 7 ProjectSection(SolutionItems) = preProject
8 8 Implab.vsmdi = Implab.vsmdi
9 9 Local.testsettings = Local.testsettings
10 10 TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings
11 11 EndProjectSection
12 12 EndProject
13 13 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Test", "Implab.Test\Implab.Test.csproj", "{63F92C0C-61BF-48C0-A377-8D67C3C661D0}"
14 14 EndProject
15 15 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx", "Implab.Fx\Implab.Fx.csproj", "{06E706F8-6881-43EB-927E-FFC503AF6ABC}"
16 16 EndProject
17 17 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx.Test", "Implab.Fx.Test\Implab.Fx.Test.csproj", "{2F31E405-E267-4195-A05D-574093C21209}"
18 18 EndProject
19 19 Global
20 GlobalSection(TestCaseManagementSettings) = postSolution
21 CategoryFile = Implab.vsmdi
22 EndGlobalSection
23 20 GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 21 Debug|Any CPU = Debug|Any CPU
25 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 25 EndGlobalSection
27 26 GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU
27 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
28 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
29 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.ActiveCfg = Debug|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 45 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 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 49 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 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
37 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.Build.0 = Release|Any CPU
40 {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.Build.0 = Release|Any CPU
51 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
52 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
53 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
55 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
56 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
57 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = 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 266 EndGlobalSection
45 267 GlobalSection(SolutionProperties) = preSolution
46 268 HideSolutionNode = FALSE
47 269 EndGlobalSection
48 GlobalSection(MonoDevelopProperties) = preSolution
49 StartupItem = Implab\Implab.csproj
50 EndGlobalSection
51 270 EndGlobal
@@ -1,34 +1,22
1 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 2 using System.Text;
5 3
6 4 namespace Implab.Diagnostics {
7 public class ConsoleTraceListener: TextListenerBase {
5 public class ConsoleTraceListener: ListenerBase {
8 6
9 7 static readonly object _consoleLock = new object();
10 8
11 public ConsoleTraceListener()
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) {
9 public override void Write(LogEventArgs args, object entry) {
22 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 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 16 lock (_consoleLock) {
29 Console.ForegroundColor = (ConsoleColor)(context.ThreadId % 15 + 1);
30 Console.WriteLine(msg.ToString());
17 Console.ForegroundColor = (ConsoleColor)(args.ThreadId % 15 + 1);
18 Console.WriteLine(msg);
31 19 }
32 20 }
33 21 }
34 22 }
@@ -1,81 +1,81
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5
6 6 namespace Implab.Diagnostics {
7 7 /// <summary>
8 8 /// Канал, через который публикуются события журнала.
9 9 /// </summary>
10 10 /// <typeparam name="TEvent">Тип событий в канале</typeparam>
11 11 /// <remarks>
12 12 /// Событиями журнала могут быть любые типы, например строки, в которых будет передаваться
13 13 /// информация, или структуры с набором полей, описывающих важность, текст и другую информацию.
14 14 /// </remarks>
15 15 public class LogChannel<TEvent> {
16 16 static LogChannel<TEvent> _default = new LogChannel<TEvent>();
17 17
18 18 /// <summary>
19 19 /// Канал по-умолчанию для событий типа <typeparam name="TEvent"/>.
20 20 /// </summary>
21 21 public static LogChannel<TEvent> Default {
22 22 get {
23 23 return _default;
24 24 }
25 25 }
26 26
27 27 /// <summary>
28 28 /// Событие появление новой записи в журнале, на это событие подписываются слушатели.
29 29 /// </summary>
30 public event EventHandler<ValueEventArgs<TEvent>> Events;
30 public event EventHandler<LogEventArgs<TEvent>> Events;
31 31
32 32 /// <summary>
33 33 /// Имя канала, полезно для отображения в журнале
34 34 /// </summary>
35 35 public string Name {
36 36 get;
37 37 private set;
38 38 }
39 39
40 40 /// <summary>
41 41 /// Создает журнал, имя типа событий назначается в качетве имени канала.
42 42 /// </summary>
43 43 public LogChannel()
44 44 : this(null) {
45 45 }
46 46
47 47 /// <summary>
48 48 /// Содает канал с указанным именем.
49 49 /// </summary>
50 50 /// <param name="name">Имя канала.</param>
51 51 public LogChannel(string name) {
52 52 if (String.IsNullOrEmpty(name))
53 53 name = typeof(TEvent).Name;
54 54 Name = name;
55 55 }
56 56
57 57 /// <summary>
58 58 /// Отправляет запись журнала через канал подписчикам.
59 59 /// </summary>
60 60 /// <param name="data">Запись журнала.</param>
61 61 /// <remarks>
62 62 /// Контекст трассировки от которого рассылается сообщение определяется автоматически из текущего потока.
63 63 /// </remarks>
64 64 public void LogEvent(TEvent data) {
65 65 var t = Events;
66 if (t!= null)
67 t(TraceContext.Current,new ValueEventArgs<TEvent>(data));
68 }
69
70 /// <summary>
71 /// Отправляет запись журнала через канал подписчикам.
72 /// </summary>
73 /// <param name="data">Запись журнала.</param>
74 /// <param name="context">Контекст трассировки от которого рассылается сообщение/</param>
75 public void LogEvent(TraceContext context,TEvent data) {
76 var t = Events;
77 if (t != null)
78 t(context, new ValueEventArgs<TEvent>(data));
66 if (t != null) {
67 var traceContext = TraceContext.Instance;
68 t(
69 this,
70 new LogEventArgs<TEvent>(
71 data,
72 Name,
73 traceContext.ThreadId,
74 traceContext.CurrentOperation,
75 traceContext.CurrentOperation.Duration
76 )
77 );
79 78 }
80 79 }
81 80 }
81 }
@@ -1,47 +1,45
1 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 2
7 3 namespace Implab.Diagnostics {
8 4 public class LogicalOperation {
5 public static readonly LogicalOperation EMPTY = new LogicalOperation("__EMPTY__", null);
6
9 7 readonly LogicalOperation m_parent;
10 8 readonly string m_name;
11 9 readonly int m_level;
12 10 readonly int m_timestamp;
13 11
14 12 public LogicalOperation()
15 13 : this(null, null) {
16 14 }
17 15
18 16 public LogicalOperation(string name, LogicalOperation parent) {
19 17 m_name = name ?? String.Empty;
20 18 m_parent = parent;
21 19
22 20 m_level = parent == null ? 0 : parent.Level + 1;
23 21 m_timestamp = Environment.TickCount;
24 22 }
25 23
26 24 public int Duration {
27 25 get {
28 26 var dt = Environment.TickCount - m_timestamp;
29 27 return dt < 0 ? int.MaxValue + dt : dt; // handle overflow
30 28 }
31 29 }
32 30
33 31 public LogicalOperation Parent {
34 32 get {
35 33 return m_parent;
36 34 }
37 35 }
38 36
39 37 public int Level {
40 38 get { return m_level; }
41 39 }
42 40
43 41 public string Name {
44 42 get { return m_name; }
45 43 }
46 44 }
47 45 }
@@ -1,47 +1,46
1 1 using System;
2 using System.Collections.Generic;
3 2 using System.IO;
4 using System.Linq;
5 3 using System.Text;
6 4
7 5 namespace Implab.Diagnostics {
8 public class TextFileListener: TextListenerBase {
6 public class TextFileListener: ListenerBase {
9 7 readonly TextWriter m_textWriter;
10 8
11 public TextFileListener(string fileName, bool global)
12 : base(global) {
9 public TextFileListener(string fileName) {
13 10 m_textWriter = File.CreateText(fileName);
14 11
15 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 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 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 23 lock (m_textWriter) {
26 24 if (!IsDisposed) {
27 25 // тут гарантировано еще не освобожден m_textWriter
28 m_textWriter.WriteLine(msg.ToString());
26 m_textWriter.WriteLine(msg);
29 27 m_textWriter.Flush();
30 28 }
31 29 }
32 30 }
33 31
32 #endregion
34 33
35 34 protected override void Dispose(bool disposing) {
36 35 base.Dispose(disposing);
37 36 if (disposing) {
38 37 // IsDisposed = true
39 38 lock (m_textWriter) {
40 39 Safe.Dispose(m_textWriter);
41 40 }
42 41 }
43 42 }
44 43
45 44
46 45 }
47 46 }
@@ -1,238 +1,83
1 1 using System;
2 2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 3 using System.Threading;
6 using System.Threading.Tasks;
7 4
8 5 namespace Implab.Diagnostics {
9 6 /// <summary>
10 /// Контекст трассировки, привязывается к потоку и содержит в себе информацию о стеке логических операций.
7 /// Trace context is bound to the specific thread, each thread has it's own ThreadContext.
11 8 /// </summary>
12 9 /// <remarks>
13 /// Контекст трассировки передается слушателям событий для определения места, где возникло событие.
10 /// ThreadContext manages relations between logical operations and threads.
14 11 /// </remarks>
15 12 public class TraceContext {
16 LogicalOperation m_currentOperation;
17 readonly LogicalOperation m_bound;
18 readonly int m_threadId;
19 13
20 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>
24 /// Текущий контекст трассировки для потока, создается астоматически при первом обращении.
25 /// </summary>
26 public static TraceContext Current {
21 public static TraceContext Instance {
27 22 get {
28 if (_current == null) {
29 _current = new TraceContext();
30 _current.LogEvent(TraceEventType.Created,"[{0}]", _current.ThreadId);
31 }
32 return _current;
23 if (_instance == null)
24 _instance = new TraceContext();
25 return _instance;
33 26 }
34 27 }
35 28
36 TraceContext(TraceContext context)
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;
29 public TraceContext() {
52 30 m_threadId = Thread.CurrentThread.ManagedThreadId;
53 31 }
54 32
55 /// <summary>
56 /// При необходимости копирует состояние контекста трассивровки в текущий поток.
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 }
33 public int ThreadId {
34 get { return m_threadId; }
79 35 }
80 36
81 /// <summary>
82 /// Задает текущему потоку указанный контекст, текущей поток может заканчивать ранее начатые
83 /// логические операции в указанном контексте.
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();
37 public LogicalOperation CurrentOperation {
38 get {
39 return m_current.CurrentOperation;
95 40 }
96 41 }
97 42
98 /// <summary>
99 /// Отсоединяет текущий контекст трассировки от потока, для дальнейшей его передачи другому потоку
100 /// <see cref="Attach(TraceContext)"/>.
101 /// </summary>
102 /// <returns>Контекст трассировки потока</returns>
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;
43 public void EnterLogicalOperation(LogicalOperation operation, bool takeOwnership) {
44 //var prev = CurrentOperation;
45 //LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(takeOwnership ? TraceEventType.Attach : TraceEventType.Enter, String.Format("{0} -> {1}",prev.Name, operation.Name)));
46 m_stack.Push(m_current);
47 m_current = new OperationContext(operation, takeOwnership);
112 48 }
113 49
114 /// <summary>
115 /// Создает постоянную копию текущего контекста, данную копию можно хранить и использовать для передачи через <see cref="Fork(TraceContext)"/>
116 /// </summary>
117 /// <returns>Копия текущего контекста трассировки.</returns>
118 public static TraceContext Snapshot() {
119 return _current == null ? new TraceContext() : new TraceContext(_current,false);
50 public void StartLogicalOperation(string name) {
51 LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(TraceEventType.OperationStarted, name));
52 m_current.BeginLogicalOperation(name);
53 }
54
55 public void StartLogicalOperation() {
56 StartLogicalOperation(String.Empty);
120 57 }
121 58
122 /// <summary>
123 /// Выполняет переданное действие в указанном контексте трассировки, по окончании восстанавливает предыдущий контекст трассировки потока.
124 /// </summary>
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;
59 public void EndLogicalOperation() {
60 var op = m_current.EndLogicalOperation();
61 LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(TraceEventType.OperationCompleted, String.Format("-{0} : {1}ms",op.Name, op.Duration)));
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>
141 /// Текущая логическая операция.
142 /// </summary>
143 public LogicalOperation CurrentOperation {
144 get {
145 return m_currentOperation;
70 public void Leave() {
71 if (m_stack.Count > 0) {
72 m_current.Leave();
73 //var prev = CurrentOperation;
74 m_current = m_stack.Pop();
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 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 2
6 3 namespace Implab.Diagnostics {
7 4 public class TraceEvent {
8 5 public string Message {
9 6 get;
10 7 private set;
11 8 }
12 9
13 10 public TraceEventType EventType {
14 11 get;
15 12 private set;
16 13 }
17 14
18 15 public TraceEvent(TraceEventType type, string message) {
19 16 EventType = type;
20 17 Message = message;
21 18 }
22 19
23 20 public override string ToString() {
24 if (EventType == TraceEventType.Information)
21 /*return EventType == TraceEventType.Information ? Message : String.Format("{0}: {1}", EventType, Message);*/
25 22 return Message;
26 else
27 return String.Format("{0}: {1}", EventType, Message);
28 23 }
29 24
30 25 public static TraceEvent Create(TraceEventType type, string format, params object[] args) {
31 26 return new TraceEvent(type, format == null ? String.Empty : String.Format(format, args));
32 27 }
33 28 }
34 29 }
@@ -1,19 +1,19
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5 using System.Threading.Tasks;
6 6
7 7 namespace Implab.Diagnostics {
8 8 public enum TraceEventType {
9 9 Information = 1,
10 10 Warning,
11 11 Error,
12 12 OperationStarted,
13 13 OperationCompleted,
14 Fork,
15 14 Attach,
16 15 Detach,
17 Created
16 Enter,
17 Leave
18 18 }
19 19 }
@@ -1,55 +1,50
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Diagnostics;
4 4 using System.Linq;
5 5 using System.Text;
6 6 using System.Threading.Tasks;
7 7
8 8 namespace Implab.Diagnostics {
9 9 /// <summary>
10 10 /// Класс для публикации событий выполнения программы, события публикуются через <see cref="LogChannel{TraceEvent}"/>.
11 11 /// Журнал трассировки отражает логический ход выполнения программы и существует всегда, поскольку тесно связан с
12 12 /// контекстом трассировки.
13 13 /// </summary>
14 14 public static class TraceLog {
15 15 [Conditional("TRACE")]
16 16 public static void StartLogicalOperation() {
17 TraceContext.Current.StartLogicalOperation();
17 TraceContext.Instance.StartLogicalOperation();
18 18 }
19 19
20 20 [Conditional("TRACE")]
21 21 public static void StartLogicalOperation(string name) {
22 TraceContext.Current.StartLogicalOperation(name);
22 TraceContext.Instance.StartLogicalOperation(name);
23 23 }
24 24
25 25 [Conditional("TRACE")]
26 26 public static void EndLogicalOperation() {
27 TraceContext.Current.EndLogicalOperation();
28 }
29
30 [Conditional("TRACE")]
31 public static void BindLogicalOperationToPromise(IPromise promise) {
32 TraceContext.Current.BindLogicalOperationToPromise(promise);
27 TraceContext.Instance.EndLogicalOperation();
33 28 }
34 29
35 30 [Conditional("TRACE")]
36 31 public static void TraceInformation(string format, params object[] arguments) {
37 32 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Information, format, arguments));
38 33 }
39 34
40 35 [Conditional("TRACE")]
41 36 public static void TraceWarning(string format, params object[] arguments) {
42 37 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Warning, format, arguments));
43 38 }
44 39
45 40 [Conditional("TRACE")]
46 41 public static void TraceError(string format, params object[] arguments) {
47 42 LogChannel<TraceEvent>.Default.LogEvent(TraceEvent.Create(TraceEventType.Error, format, arguments));
48 43 }
49 44
50 45 [Conditional("TRACE")]
51 46 public static void TraceError(Exception err) {
52 47 TraceError("{0}", err);
53 48 }
54 49 }
55 50 }
@@ -1,10 +1,8
1 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 2
6 3 namespace Implab {
7 4 public interface ICancellable {
8 bool Cancel();
5 void Cancel();
6 void Cancel(Exception reason);
9 7 }
10 8 }
@@ -1,37 +1,66
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5
6 6 namespace Implab {
7 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 9 /// <summary>
16 10 /// Тип результата, получаемого через данное обещание.
17 11 /// </summary>
18 12 Type PromiseType { get; }
19 13
14 /// <summary>
15 /// Обещание является выполненым, либо успешно, либо с ошибкой, либо отменено.
16 /// </summary>
20 17 bool IsResolved { get; }
21 18
19 /// <summary>
20 /// Обещание было отменено.
21 /// </summary>
22 22 bool IsCancelled { get; }
23 23
24 IPromise Then(Action success,ErrorHandler error);
25 IPromise Then(Action success);
26 IPromise Error(ErrorHandler error);
27 IPromise Anyway(Action handler);
28 IPromise Finally(Action handler);
29 IPromise Cancelled(Action handler);
24 /// <summary>
25 /// Исключение возникшее в результате выполнения обещания, либо причина отмены.
26 /// </summary>
27 Exception Error { get; }
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 52 IPromise<T> Cast<T>();
32 53
54 /// <summary>
55 /// Синхронизирует текущий поток с обещанием.
56 /// </summary>
33 57 void Join();
58 /// <summary>
59 /// Синхронизирует текущий поток с обещанием.
60 /// </summary>
61 /// <param name="timeout">Время ожидания, по его истечению возникнет исключение.</param>
62 /// <exception cref="TimeoutException">Превышено время ожидания.</exception>
34 63 void Join(int timeout);
35 64
36 65 }
37 66 }
@@ -1,31 +1,25
1 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
2
3 namespace Implab {
4 public interface IPromise<out T> : IPromise {
5 5
6 namespace Implab
7 {
8 public interface IPromise<T>: IPromise
9 {
6 IPromise<T> On(Action<T> success, Action<Exception> error, Action<Exception> cancel);
7
8 IPromise<T> On(Action<T> success, Action<Exception> error);
9
10 IPromise<T> On(Action<T> success);
10 11
11 12 new T Join();
13
12 14 new T Join(int timeout);
13 15
14 IPromise<T> Then(ResultHandler<T> success, ErrorHandler error);
15 IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error);
16 IPromise<T> Then(ResultHandler<T> success);
17 new IPromise<T> Error(ErrorHandler error);
18 IPromise<T> Error(ErrorHandler<T> error);
16 new IPromise<T> On(Action success, Action<Exception> error, Action<Exception> cancel);
17
18 new IPromise<T> On(Action success, Action<Exception> error);
19 19
20 IPromise<T2> Map<T2>(ResultMapper<T,T2> mapper, ErrorHandler error);
21 IPromise<T2> Map<T2>(ResultMapper<T, T2> mapper);
20 new IPromise<T> On(Action success);
22 21
23 IPromise<T2> Chain<T2>(ChainedOperation<T, T2> chained, ErrorHandler error);
24 IPromise<T2> Chain<T2>(ChainedOperation<T, T2> chained);
25
26 new IPromise<T> Cancelled(Action handler);
27 new IPromise<T> Finally(Action handler);
28 new IPromise<T> Anyway(Action handler);
22 new IPromise<T> On(Action handler, PromiseEventType events);
29 23
30 24 }
31 25 }
@@ -1,14 +1,11
1 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 2
6 3 namespace Implab {
7 4 public interface ITaskController: IProgressHandler, ICancellable {
8 5 bool IsCancelled {
9 6 get;
10 7 }
11 8
12 9 event EventHandler Cancelled;
13 10 }
14 11 }
@@ -1,105 +1,276
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 6 <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
7 7 <OutputType>Library</OutputType>
8 8 <RootNamespace>Implab</RootNamespace>
9 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 14 </PropertyGroup>
11 15 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
12 16 <DebugSymbols>true</DebugSymbols>
13 17 <DebugType>full</DebugType>
14 18 <Optimize>false</Optimize>
15 19 <OutputPath>bin\Debug</OutputPath>
16 20 <DefineConstants>TRACE;DEBUG;</DefineConstants>
17 21 <ErrorReport>prompt</ErrorReport>
18 22 <WarningLevel>4</WarningLevel>
19 23 <ConsolePause>false</ConsolePause>
20 24 <RunCodeAnalysis>true</RunCodeAnalysis>
21 25 </PropertyGroup>
22 26 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
23 27 <DebugType>full</DebugType>
24 28 <Optimize>true</Optimize>
25 29 <OutputPath>bin\Release</OutputPath>
26 30 <ErrorReport>prompt</ErrorReport>
27 31 <WarningLevel>4</WarningLevel>
28 32 <ConsolePause>false</ConsolePause>
29 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 72 <ItemGroup>
31 73 <Reference Include="System" />
32 74 <Reference Include="System.Xml" />
75 <Reference Include="mscorlib" />
33 76 </ItemGroup>
34 77 <ItemGroup>
35 <Compile Include="Component.cs" />
36 78 <Compile Include="CustomEqualityComparer.cs" />
37 79 <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
38 80 <Compile Include="Diagnostics\EventText.cs" />
39 <Compile Include="Diagnostics\IEventTextFormatter.cs" />
40 81 <Compile Include="Diagnostics\LogChannel.cs" />
41 82 <Compile Include="Diagnostics\LogicalOperation.cs" />
42 83 <Compile Include="Diagnostics\TextFileListener.cs" />
43 <Compile Include="Diagnostics\TextListenerBase.cs" />
44 84 <Compile Include="Diagnostics\TraceLog.cs" />
45 <Compile Include="Diagnostics\TraceContext.cs" />
46 85 <Compile Include="Diagnostics\TraceEvent.cs" />
47 86 <Compile Include="Diagnostics\TraceEventType.cs" />
48 <Compile Include="Disposable.cs" />
49 87 <Compile Include="ICancellable.cs" />
50 88 <Compile Include="IProgressHandler.cs" />
51 89 <Compile Include="IProgressNotifier.cs" />
52 90 <Compile Include="IPromiseT.cs" />
53 91 <Compile Include="IPromise.cs" />
54 92 <Compile Include="IServiceLocator.cs" />
55 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 94 <Compile Include="Parallels\DispatchPool.cs" />
67 95 <Compile Include="Parallels\ArrayTraits.cs" />
68 96 <Compile Include="Parallels\MTQueue.cs" />
69 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 98 <Compile Include="ProgressInitEventArgs.cs" />
97 99 <Compile Include="Properties\AssemblyInfo.cs" />
98 <Compile Include="Promise.cs" />
99 100 <Compile Include="Parallels\AsyncPool.cs" />
100 101 <Compile Include="Safe.cs" />
101 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 198 </ItemGroup>
103 199 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
104 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 276 </Project> No newline at end of file
@@ -1,191 +1,207
1 1 using Implab.Diagnostics;
2 2 using System;
3 using System.Collections.Generic;
4 3 using System.Diagnostics;
5 using System.Linq;
6 using System.Text;
7 4 using System.Threading;
8 5
9 6 namespace Implab.Parallels {
10 7 public static class ArrayTraits {
11 8 class ArrayIterator<TSrc> : DispatchPool<int> {
12 9 readonly Action<TSrc> m_action;
13 10 readonly TSrc[] m_source;
14 11 readonly Promise<int> m_promise = new Promise<int>();
15 readonly TraceContext m_traceContext;
12 readonly LogicalOperation m_logicalOperation;
16 13
17 14 int m_pending;
18 15 int m_next;
19 16
20 17 public ArrayIterator(TSrc[] source, Action<TSrc> action, int threads)
21 18 : base(threads) {
22 19
23 20 Debug.Assert(source != null);
24 21 Debug.Assert(action != null);
25 22
26 m_traceContext = TraceContext.Snapshot();
23 m_logicalOperation = TraceContext.Instance.CurrentOperation;
27 24 m_next = 0;
28 25 m_source = source;
29 26 m_pending = source.Length;
30 27 m_action = action;
31 28
32 m_promise.Anyway(() => Dispose());
33 m_promise.Cancelled(() => Dispose());
29 m_promise.On(Dispose, PromiseEventType.All);
34 30
35 31 InitPool();
36 32 }
37 33
38 34 public Promise<int> Promise {
39 35 get {
40 36 return m_promise;
41 37 }
42 38 }
43 39
44 40 protected override void Worker() {
45 TraceContext.Fork(m_traceContext);
41 TraceContext.Instance.EnterLogicalOperation(m_logicalOperation, false);
42 try {
46 43 base.Worker();
44 } finally {
45 TraceContext.Instance.Leave();
46 }
47 47 }
48 48
49 49 protected override bool TryDequeue(out int unit) {
50 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 54 protected override void InvokeUnit(int unit) {
55 55 try {
56 56 m_action(m_source[unit]);
57 57 var pending = Interlocked.Decrement(ref m_pending);
58 58 if (pending == 0)
59 59 m_promise.Resolve(m_source.Length);
60 60 } catch (Exception e) {
61 61 m_promise.Reject(e);
62 62 }
63 63 }
64 64 }
65 65
66 66 class ArrayMapper<TSrc, TDst>: DispatchPool<int> {
67 67 readonly Func<TSrc, TDst> m_transform;
68 68 readonly TSrc[] m_source;
69 69 readonly TDst[] m_dest;
70 70 readonly Promise<TDst[]> m_promise = new Promise<TDst[]>();
71 readonly TraceContext m_traceContext;
71 readonly LogicalOperation m_logicalOperation;
72 72
73 73 int m_pending;
74 74 int m_next;
75 75
76 76 public ArrayMapper(TSrc[] source, Func<TSrc, TDst> transform, int threads)
77 77 : base(threads) {
78 78
79 79 Debug.Assert (source != null);
80 80 Debug.Assert( transform != null);
81 81
82 82 m_next = 0;
83 83 m_source = source;
84 84 m_dest = new TDst[source.Length];
85 85 m_pending = source.Length;
86 86 m_transform = transform;
87 m_traceContext = TraceContext.Snapshot();
87 m_logicalOperation = TraceContext.Instance.CurrentOperation;
88 88
89 m_promise.Anyway(() => Dispose());
90 m_promise.Cancelled(() => Dispose());
89 m_promise.On(Dispose, PromiseEventType.All);
91 90
92 91 InitPool();
93 92 }
94 93
95 94 public Promise<TDst[]> Promise {
96 95 get {
97 96 return m_promise;
98 97 }
99 98 }
100 99
101 100 protected override void Worker() {
102 TraceContext.Fork(m_traceContext);
101 TraceContext.Instance.EnterLogicalOperation(m_logicalOperation,false);
102 try {
103 103 base.Worker();
104 } finally {
105 TraceContext.Instance.Leave();
106 }
104 107 }
105 108
106 109 protected override bool TryDequeue(out int unit) {
107 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 114 protected override void InvokeUnit(int unit) {
112 115 try {
113 116 m_dest[unit] = m_transform(m_source[unit]);
114 117 var pending = Interlocked.Decrement(ref m_pending);
115 118 if (pending == 0)
116 119 m_promise.Resolve(m_dest);
117 120 } catch (Exception e) {
118 121 m_promise.Reject(e);
119 122 }
120 123 }
121 124 }
122 125
123 126 public static IPromise<TDst[]> ParallelMap<TSrc, TDst> (this TSrc[] source, Func<TSrc,TDst> transform, int threads) {
124 127 if (source == null)
125 128 throw new ArgumentNullException("source");
126 129 if (transform == null)
127 130 throw new ArgumentNullException("transform");
128 131
129 132 var mapper = new ArrayMapper<TSrc, TDst>(source, transform, threads);
130 133 return mapper.Promise;
131 134 }
132 135
133 136 public static IPromise<int> ParallelForEach<TSrc>(this TSrc[] source, Action<TSrc> action, int threads) {
134 137 if (source == null)
135 138 throw new ArgumentNullException("source");
136 139 if (action == null)
137 140 throw new ArgumentNullException("action");
138 141
139 142 var iter = new ArrayIterator<TSrc>(source, action, threads);
140 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 147 if (source == null)
145 148 throw new ArgumentNullException("source");
146 149 if (transform == null)
147 150 throw new ArgumentNullException("transform");
148 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 154 if (source.Length == 0)
152 return Promise<TDst[]>.ResultToPromise(new TDst[0]);
155 return Promise<TDst[]>.FromResult(new TDst[0]);
153 156
154 157 var promise = new Promise<TDst[]>();
155 158 var res = new TDst[source.Length];
156 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 166 for (int i = 0; i < source.Length; i++) {
162 167 if(promise.IsResolved)
163 168 break; // stop processing in case of error or cancellation
164 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 178 try {
167 var p1 = transform(source[i]);
168 p1.Anyway(() => semaphore.Release());
169 p1.Cancelled(() => semaphore.Release());
170 p1.Then(
179 transform(source[i])
180 .On( x => {
181 Interlocked.Increment(ref slots);
182 lock (locker) {
183 Monitor.Pulse(locker);
184 }
185 })
186 .On(
171 187 x => {
172 188 res[idx] = x;
173 189 var left = Interlocked.Decrement(ref pending);
174 190 if (left == 0)
175 191 promise.Resolve(res);
176 192 },
177 e => promise.Reject(e)
193 promise.Reject
178 194 );
179 195
180 196 } catch (Exception e) {
181 197 promise.Reject(e);
182 198 }
183 199 }
184 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 1 using Implab.Diagnostics;
2 2 using System;
3 3 using System.Threading;
4 using System.Linq;
4 5
5 6 namespace Implab.Parallels {
6 7 /// <summary>
7 8 /// Класс для распаралеливания задач.
8 9 /// </summary>
9 10 /// <remarks>
10 11 /// Используя данный класс и лямда выражения можно распараллелить
11 12 /// вычисления, для этого используется концепция обещаний.
12 13 /// </remarks>
13 14 public static class AsyncPool {
14 15
15 16 public static IPromise<T> Invoke<T>(Func<T> func) {
16 17 var p = new Promise<T>();
17 var caller = TraceContext.Snapshot();
18 var caller = TraceContext.Instance.CurrentOperation;
18 19
19 20 ThreadPool.QueueUserWorkItem(param => {
20 TraceContext.Fork(caller);
21 TraceContext.Instance.EnterLogicalOperation(caller,false);
21 22 try {
22 23 p.Resolve(func());
23 24 } catch(Exception e) {
24 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 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 53 var p = new Promise<T>();
33 54
34 var caller = TraceContext.Snapshot();
55 var caller = TraceContext.Instance.CurrentOperation;
35 56
36 57 var worker = new Thread(() => {
37 TraceContext.Fork(caller);
58 TraceContext.Instance.EnterLogicalOperation(caller,false);
38 59 try {
39 60 p.Resolve(func());
40 61 } catch (Exception e) {
41 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 88 worker.IsBackground = true;
45 89 worker.Start();
46 90
47 91 return p;
48 92 }
49 93
50 94
51 public static IPromise InvokeNewThread(Action func) {
52 var p = new Promise<object>();
95 public static IPromise RunThread(Action func) {
96 var p = new Promise();
53 97
54 var caller = TraceContext.Snapshot();
98 var caller = TraceContext.Instance.CurrentOperation;
55 99
56 100 var worker = new Thread(() => {
57 TraceContext.Fork(caller);
101 TraceContext.Instance.EnterLogicalOperation(caller,false);
58 102 try {
59 103 func();
60 104 p.Resolve();
61 105 } catch (Exception e) {
62 106 p.Reject(e);
107 } finally {
108 TraceContext.Instance.Leave();
63 109 }
64 110 });
65 111 worker.IsBackground = true;
66 112 worker.Start();
67 113
68 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 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 2 using System.Threading;
6 using System.Diagnostics;
7 3
8 4 namespace Implab.Parallels {
9 5 public abstract class DispatchPool<TUnit> : IDisposable {
10 readonly int m_minThreads;
11 readonly int m_maxThreads;
6 readonly int m_minThreadsLimit;
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
14 int m_activeThreads = 0; // the count of threads which are active
15 int m_sleepingThreads = 0; // the count of currently inactive threads
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
10 int m_threads; // the current size of the pool
11 int m_maxRunningThreads; // the meximum reached size of the pool
12 int m_exit; // the pool is going to shutdown, all unused workers are released
20 13
21 AutoResetEvent m_hasTasks = new AutoResetEvent(false);
14 readonly object m_signal = new object(); // used to pulse waiting threads
22 15
23 16 protected DispatchPool(int min, int max) {
24 17 if (min < 0)
25 18 throw new ArgumentOutOfRangeException("min");
26 19 if (max <= 0)
27 20 throw new ArgumentOutOfRangeException("max");
28 21
29 22 if (min > max)
30 23 min = max;
31 m_minThreads = min;
32 m_maxThreads = max;
24 m_minThreadsLimit = min;
25 m_maxThreadsLimit = max;
33 26 }
34 27
35 28 protected DispatchPool(int threads)
36 29 : this(threads, threads) {
37 30 }
38 31
39 32 protected DispatchPool() {
40 int maxThreads, maxCP;
41 ThreadPool.GetMaxThreads(out maxThreads, out maxCP);
42 33
43 m_minThreads = 0;
44 m_maxThreads = maxThreads;
34 m_minThreadsLimit = 0;
35 m_maxThreadsLimit = Environment.ProcessorCount;
45 36 }
46 37
47 38 protected void InitPool() {
48 for (int i = 0; i < m_minThreads; i++)
39 for (int i = 0; i < m_minThreadsLimit; i++)
49 40 StartWorker();
50 41 }
51 42
52 43 public int PoolSize {
53 44 get {
54 return m_createdThreads;
55 }
56 }
57
58 public int ActiveThreads {
59 get {
60 return m_activeThreads;
45 Thread.MemoryBarrier();
46 return m_threads;
61 47 }
62 48 }
63 49
64 50 public int MaxRunningThreads {
65 51 get {
52 Thread.MemoryBarrier();
66 53 return m_maxRunningThreads;
67 54 }
68 55 }
69 56
70 57 protected bool IsDisposed {
71 58 get {
72 return m_exitRequired != 0;
59 Thread.MemoryBarrier();
60 return m_exit == 1;
73 61 }
74 62 }
75 63
76 64 protected abstract bool TryDequeue(out TUnit unit);
77 65
78 #region thread execution traits
79 int SignalThread() {
80 var signals = Interlocked.Increment(ref m_wakeEvents);
81 if(signals == 1)
82 m_hasTasks.Set();
83 return signals;
66 bool Dequeue(out TUnit unit, int timeout) {
67 int ts = Environment.TickCount;
68 if (TryDequeue(out unit))
69 return true;
70 lock (m_signal) {
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
86 bool FetchSignalOrWait(int timeout) {
87 var start = Environment.TickCount;
88
89 // означает, что поток владеет блокировкой и при успешном получении сигнала должен
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();
76 // queue item or terminate
77 Monitor.Pulse(m_signal);
78 if (m_exit == 1)
79 return false;
80 }
103 81 return true;
104 82 }
105 83
106 if (timeout != -1)
107 timeout = Math.Max(0, timeout - (Environment.TickCount - start));
108
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 }
84 protected void SignalThread() {
85 lock (m_signal) {
86 Monitor.Pulse(m_signal);
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 90 #region thread slots traits
201 91
202 92 bool AllocateThreadSlot() {
203 93 int current;
204 94 // use spins to allocate slot for the new thread
205 95 do {
206 current = m_createdThreads;
207 if (current >= m_maxThreads || m_exitRequired != 0)
96 current = m_threads;
97 if (current >= m_maxThreadsLimit || m_exit == 1)
208 98 // no more slots left or the pool has been disposed
209 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 102 UpdateMaxThreads(current + 1);
213 103
214 104 return true;
215 105 }
216 106
217 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 109 return false;
220 110
221 111 UpdateMaxThreads(desired);
222 112
223 113 return true;
224 114 }
225 115
226 116 bool ReleaseThreadSlot(out bool last) {
227 117 last = false;
228 118 int current;
229 119 // use spins to release slot for the new thread
120 Thread.MemoryBarrier();
230 121 do {
231 current = m_createdThreads;
232 if (current <= m_minThreads && m_exitRequired == 0)
122 current = m_threads;
123 if (current <= m_minThreadsLimit && m_exit == 0)
233 124 // the thread is reserved
234 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 128 last = (current == 1);
238 129
239 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 133 void UpdateMaxThreads(int count) {
252 134 int max;
253 135 do {
254 136 max = m_maxRunningThreads;
255 137 if (max >= count)
256 138 break;
257 139 } while(max != Interlocked.CompareExchange(ref m_maxRunningThreads, count, max));
258 140 }
259 141
260 142 #endregion
261 143
262 bool StartWorker() {
144 protected bool StartWorker() {
263 145 if (AllocateThreadSlot()) {
264 146 // slot successfully allocated
265 var worker = new Thread(this.Worker);
147 var worker = new Thread(Worker);
266 148 worker.IsBackground = true;
267 149 worker.Start();
268 150
269 151 return true;
270 } else {
152 }
271 153 return false;
272 154 }
273 }
274 155
275 156 protected abstract void InvokeUnit(TUnit unit);
276 157
277 158 protected virtual void Worker() {
278 159 TUnit unit;
279 //Console.WriteLine("{0}: Active", Thread.CurrentThread.ManagedThreadId);
280 Interlocked.Increment(ref m_activeThreads);
160 bool last;
281 161 do {
282 // exit if requested
283 if (m_exitRequired != 0) {
284 // release the thread slot
285 Interlocked.Decrement(ref m_activeThreads);
286 if (ReleaseThreadSlotAnyway()) // it was the last worker
287 m_hasTasks.Dispose();
288 else
289 SignalThread(); // wake next worker
162 while (Dequeue(out unit, m_releaseTimeout)) {
163 InvokeUnit(unit);
164 }
165 if(!ReleaseThreadSlot(out last))
166 continue;
167 // queue may be not empty
168 if (last && TryDequeue(out unit)) {
169 InvokeUnit(unit);
170 if (AllocateThreadSlot(1))
171 continue;
172 // we can safely exit since pool is alive
173 }
290 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 179 protected virtual void Dispose(bool disposing) {
311 180 if (disposing) {
312 if (m_exitRequired == 0) {
313 if (Interlocked.CompareExchange(ref m_exitRequired, 1, 0) != 0)
314 return;
315
181 if (0 == Interlocked.CompareExchange(ref m_exit, 1, 0)) { // implies memory barrier
316 182 // wake sleeping threads
317 if (m_createdThreads > 0)
318 183 SignalThread();
319 else
320 m_hasTasks.Dispose();
321 184 GC.SuppressFinalize(this);
322 185 }
323 186 }
324 187 }
325 188
326 189 public void Dispose() {
327 190 Dispose(true);
328 191 }
329 192
330 193 ~DispatchPool() {
331 194 Dispose(false);
332 195 }
333 196 }
334 197 }
@@ -1,75 +1,143
1 using System;
1 using System.Threading;
2 2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading;
3 using System;
4 using System.Collections;
6 5
7 6 namespace Implab.Parallels {
8 public class MTQueue<T> {
7 public class MTQueue<T> : IEnumerable<T> {
9 8 class Node {
10 9 public Node(T value) {
11 10 this.value = value;
12 11 }
13 12 public readonly T value;
14 13 public Node next;
15 14 }
16 15
17 16 Node m_first;
18 17 Node m_last;
19 18
20 19 public void Enqueue(T value) {
20 Thread.MemoryBarrier();
21
21 22 var last = m_last;
22 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 27 while (last != Interlocked.CompareExchange(ref m_last, next, last))
25 28 last = m_last;
26 29
27 30 if (last != null)
28 31 last.next = next;
29 32 else
30 33 m_first = next;
31 34 }
32 35
33 36 public bool TryDequeue(out T value) {
34 37 Node first;
35 Node next = null;
38 Node next;
36 39 value = default(T);
37 40
41 Thread.MemoryBarrier();
38 42 do {
39 43 first = m_first;
40 44 if (first == null)
41 45 return false;
42 46 next = first.next;
43 47 if (next == null) {
44 48 // this is the last element,
45 49 // then try to update the tail
46 50 if (first != Interlocked.CompareExchange(ref m_last, null, first)) {
47 51 // this is the race condition
48 52 if (m_last == null)
49 53 // the queue is empty
50 54 return false;
51 55 // tail has been changed, we need to restart
52 56 continue;
53 57 }
54 58
55 59 // tail succesfully updated and first.next will never be changed
56 60 // other readers will fail due to inconsistency m_last != m_fist && m_first.next == null
57 61 // however the parallel writer may update the m_first since the m_last is null
58 62
59 63 // so we need to fix inconsistency by setting m_first to null or if it has been
60 64 // updated by the writer already then we should just to give up
61 65 Interlocked.CompareExchange(ref m_first, null, first);
62 66 break;
63 67
64 } else {
68 }
65 69 if (first == Interlocked.CompareExchange(ref m_first, next, first))
66 70 // head succesfully updated
67 71 break;
68 }
69 72 } while (true);
70 73
71 74 value = first.value;
72 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
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 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
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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
The requested commit or file is too big and content was truncated. Show full diff
1 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