##// 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
@@ -0,0 +1,202
1 using System;
2 using System.Threading;
3 using System.Diagnostics;
4
5 namespace Implab.Parallels {
6 /// <summary>
7 /// Implements a lightweight mechanism to aquire a shared or an exclusive lock.
8 /// </summary>
9 public class SharedLock {
10 readonly object m_lock = new object();
11 // the count of locks currently acquired by clients
12 int m_locks;
13 // the count of pending requests for upgrade
14 int m_upgrades;
15 bool m_exclusive;
16
17 public bool LockExclusive(int timeout) {
18 lock (m_lock) {
19 var dt = timeout;
20 if (m_locks > m_upgrades) {
21 var t1 = Environment.TickCount;
22 do {
23 if (!Monitor.Wait(m_lock, timeout))
24 return false;
25
26 if (m_locks == m_upgrades)
27 break;
28
29 if (timeout > 0) {
30 dt = timeout - Environment.TickCount + t1;
31 if (dt < 0)
32 return false;
33 }
34 } while(true);
35 }
36 m_exclusive = true;
37 m_locks ++;
38 return true;
39 }
40 }
41
42 public void LockExclusive() {
43 lock (m_lock) {
44
45 while (m_locks > m_upgrades)
46 Monitor.Wait(m_lock);
47
48 m_exclusive = true;
49 m_locks ++;
50 }
51 }
52
53 /// <summary>
54 /// Acquires a shared lock.
55 /// </summary>
56 /// <returns><c>true</c>, if the shared lock was acquired, <c>false</c> if the specified timeout was expired.</returns>
57 /// <param name="timeout">Timeout.</param>
58 public bool LockShared(int timeout) {
59 lock (m_lock) {
60 if (!m_exclusive) {
61 m_locks++;
62 return true;
63 }
64
65 if (m_locks == m_upgrades) {
66 m_exclusive = false;
67 m_locks = 1;
68 return true;
69 }
70
71 var t1 = Environment.TickCount;
72 var dt = timeout;
73 do {
74 if (!Monitor.Wait(m_lock, dt))
75 return false;
76
77 if (m_locks == m_upgrades || !m_exclusive)
78 break;
79
80 if (timeout >= 0) {
81 dt = timeout - Environment.TickCount + t1;
82 if (dt < 0)
83 return false;
84 }
85 } while(true);
86
87 m_locks ++;
88 m_exclusive = false;
89 return true;
90 }
91 }
92
93 /// <summary>
94 /// Acquires the shared lock.
95 /// </summary>
96 public void LockShared() {
97 lock (m_lock) {
98 if (!m_exclusive) {
99 m_locks++;
100 } else if (m_locks == m_upgrades) {
101 m_exclusive = false;
102 m_locks++;
103 } else {
104 while (m_exclusive && m_locks > m_upgrades)
105 Monitor.Wait(m_lock);
106
107 m_locks++;
108 m_exclusive = false;
109 }
110 }
111 }
112
113 /// <summary>
114 /// Upgrades the current lock to exclusive level.
115 /// </summary>
116 /// <remarks>If the current lock is exclusive already the method does nothing.</remarks>
117 public void Upgrade() {
118 lock (m_lock) {
119 if (!m_exclusive) {
120
121 if (m_locks <= m_upgrades)
122 throw new InvalidOperationException();
123
124 if (m_locks - m_upgrades == 1) {
125 m_exclusive = true;
126 } else {
127 m_upgrades++;
128
129 while (m_locks > m_upgrades)
130 Monitor.Wait(m_lock);
131
132 m_upgrades--;
133 m_exclusive = true;
134 }
135 }
136 }
137 }
138
139 /// <summary>
140 /// Upgrades the current lock to exclusive level.
141 /// </summary>
142 /// <param name="timeout">Timeout.</param>
143 /// <returns><c>true</c> if the current lock was updated, <c>false</c> the specified timeout was expired.</returns>
144 /// <remarks>If the current lock is exclusive already the method does nothing.</remarks>
145 public bool Upgrade(int timeout) {
146 lock (m_lock) {
147 if (m_exclusive)
148 return true;
149 if (m_locks <= m_upgrades)
150 throw new InvalidOperationException();
151
152 if (m_locks - m_upgrades == 1) {
153 m_exclusive = true;
154 } else {
155 var t1 = Environment.TickCount;
156 var dt = timeout;
157 m_upgrades++;
158 do {
159 if (!Monitor.Wait(m_lock, dt)) {
160 m_upgrades--;
161 return false;
162 }
163
164 // we may get there but the shared lock already aquired
165 if (m_locks == m_upgrades)
166 break;
167
168 if (timeout >= 0) {
169 dt = timeout - Environment.TickCount + t1;
170 if (dt < 0) {
171 m_upgrades--;
172 return false;
173 }
174 }
175 } while(true);
176 m_upgrades--;
177 m_exclusive = true;
178 }
179 return true;
180 }
181 }
182
183 /// <summary>
184 /// Downgrades this lock to shared level.
185 /// </summary>
186 public void Downgrade() {
187 lock (m_lock)
188 m_exclusive = false;
189 }
190
191 /// <summary>
192 /// Releases the current lock.
193 /// </summary>
194 public void Release() {
195 lock (m_lock)
196 // if no more running threads left
197 if (--m_locks == m_upgrades)
198 Monitor.PulseAll(m_lock);
199 }
200 }
201 }
202
@@ -0,0 +1,31
1 using System;
2 using System.Threading;
3
4 namespace Implab.Parallels {
5 /// <summary>
6 /// Implements a simple signalling logic using <see cref="Monitor.PulseAll(object)"/>.
7 /// </summary>
8 public class Signal {
9 readonly object m_lock = new object();
10 bool m_state;
11
12 public void Set() {
13 lock(m_lock) {
14 m_state = true;
15 Monitor.PulseAll(m_lock);
16 }
17 }
18
19 public void Wait() {
20 lock (m_lock)
21 if (!m_state)
22 Monitor.Wait(m_lock);
23 }
24
25 public bool Wait(int timeout) {
26 lock (m_lock)
27 return m_state || Monitor.Wait(m_lock, timeout);
28 }
29 }
30 }
31
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -13,3 +13,8 Implab.Fx.Test/obj/
13 _ReSharper.Implab/
13 _ReSharper.Implab/
14 Implab.Diagnostics.Interactive/bin/
14 Implab.Diagnostics.Interactive/bin/
15 Implab.Diagnostics.Interactive/obj/
15 Implab.Diagnostics.Interactive/obj/
16 MonoPlay/bin/
17 MonoPlay/obj/
18 Implab.Test/Implab.Format.Test/bin/
19 Implab.Test/Implab.Format.Test/obj/
20 *.suo
@@ -9,15 +9,15 using System.Windows.Forms;
9
9
10 namespace Implab.Diagnostics.Interactive
10 namespace Implab.Diagnostics.Interactive
11 {
11 {
12 public class InteractiveListener: TextListenerBase
12 public class InteractiveListener: ListenerBase
13 {
13 {
14 TraceForm m_form;
14 TraceForm m_form;
15
15
16 SynchronizationContext m_syncGuiThread;
16 SynchronizationContext m_syncGuiThread;
17 readonly Promise<object> m_guiStarted = new Promise<object>();
17 readonly Promise m_guiStarted = new Promise();
18
18
19 readonly IPromise m_guiFinished;
19 readonly IPromise m_guiFinished;
20 readonly IPromise m_workerFinished = new Promise<object>();
20 // readonly IPromise m_workerFinished = new Promise<object>();
21
21
22 readonly MTQueue<TraceViewItem> m_queue = new MTQueue<TraceViewItem>();
22 readonly MTQueue<TraceViewItem> m_queue = new MTQueue<TraceViewItem>();
23 readonly AutoResetEvent m_queueEvent = new AutoResetEvent(false);
23 readonly AutoResetEvent m_queueEvent = new AutoResetEvent(false);
@@ -29,9 +29,9 namespace Implab.Diagnostics.Interactive
29 bool m_paused;
29 bool m_paused;
30 readonly ManualResetEvent m_pauseEvent = new ManualResetEvent(true);
30 readonly ManualResetEvent m_pauseEvent = new ManualResetEvent(true);
31
31
32 public InteractiveListener(bool global) : base(global) {
32 public InteractiveListener() {
33 m_guiFinished = AsyncPool.InvokeNewThread(GuiThread);
33 m_guiFinished = AsyncPool.RunThread(GuiThread);
34 m_workerFinished = AsyncPool.InvokeNewThread(QueueThread);
34 /*m_workerFinished = */AsyncPool.RunThread(QueueThread);
35
35
36 m_guiStarted.Join();
36 m_guiStarted.Join();
37 }
37 }
@@ -107,12 +107,12 namespace Implab.Diagnostics.Interactive
107 base.Dispose(disposing);
107 base.Dispose(disposing);
108 }
108 }
109
109
110 protected override void WriteEntry(TraceContext context, EventText text, string channel) {
110 public override void Write(LogEventArgs args, object entry) {
111 var item = new TraceViewItem {
111 var item = new TraceViewItem {
112 Indent = text.indent,
112 Indent = args.Operation.Level,
113 Message = text.content,
113 Message = entry.ToString(),
114 Thread = context.ThreadId,
114 Thread = args.ThreadId,
115 Channel = channel,
115 Channel = args.ChannelName,
116 Timestamp = Environment.TickCount
116 Timestamp = Environment.TickCount
117 };
117 };
118
118
@@ -3,17 +3,17
3 <PropertyGroup>
3 <PropertyGroup>
4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 <ProductVersion>
6 <ProductVersion>8.0.30703</ProductVersion>
7 </ProductVersion>
8 <SchemaVersion>2.0</SchemaVersion>
7 <SchemaVersion>2.0</SchemaVersion>
9 <ProjectGuid>{2F31E405-E267-4195-A05D-574093C21209}</ProjectGuid>
8 <ProjectGuid>{2F31E405-E267-4195-A05D-574093C21209}</ProjectGuid>
10 <OutputType>Library</OutputType>
9 <OutputType>Library</OutputType>
11 <AppDesignerFolder>Properties</AppDesignerFolder>
10 <AppDesignerFolder>Properties</AppDesignerFolder>
12 <RootNamespace>Implab.Fx.Test</RootNamespace>
11 <RootNamespace>Implab.Fx.Test</RootNamespace>
13 <AssemblyName>Implab.Fx.Test</AssemblyName>
12 <AssemblyName>Implab.Fx.Test</AssemblyName>
14 <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
13 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
15 <FileAlignment>512</FileAlignment>
14 <FileAlignment>512</FileAlignment>
16 <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
15 <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
16 <TargetFrameworkProfile />
17 </PropertyGroup>
17 </PropertyGroup>
18 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
18 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
19 <DebugSymbols>true</DebugSymbols>
19 <DebugSymbols>true</DebugSymbols>
@@ -23,6 +23,7
23 <DefineConstants>DEBUG;TRACE</DefineConstants>
23 <DefineConstants>DEBUG;TRACE</DefineConstants>
24 <ErrorReport>prompt</ErrorReport>
24 <ErrorReport>prompt</ErrorReport>
25 <WarningLevel>4</WarningLevel>
25 <WarningLevel>4</WarningLevel>
26 <Prefer32Bit>false</Prefer32Bit>
26 </PropertyGroup>
27 </PropertyGroup>
27 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
28 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
28 <DebugType>pdbonly</DebugType>
29 <DebugType>pdbonly</DebugType>
@@ -31,6 +32,26
31 <DefineConstants>TRACE</DefineConstants>
32 <DefineConstants>TRACE</DefineConstants>
32 <ErrorReport>prompt</ErrorReport>
33 <ErrorReport>prompt</ErrorReport>
33 <WarningLevel>4</WarningLevel>
34 <WarningLevel>4</WarningLevel>
35 <Prefer32Bit>false</Prefer32Bit>
36 </PropertyGroup>
37 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
38 <DebugSymbols>true</DebugSymbols>
39 <DebugType>full</DebugType>
40 <Optimize>false</Optimize>
41 <OutputPath>bin\Debug\</OutputPath>
42 <DefineConstants>DEBUG;TRACE</DefineConstants>
43 <ErrorReport>prompt</ErrorReport>
44 <WarningLevel>4</WarningLevel>
45 <Prefer32Bit>false</Prefer32Bit>
46 </PropertyGroup>
47 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
48 <DebugType>pdbonly</DebugType>
49 <Optimize>true</Optimize>
50 <OutputPath>bin\Release\</OutputPath>
51 <DefineConstants>TRACE</DefineConstants>
52 <ErrorReport>prompt</ErrorReport>
53 <WarningLevel>4</WarningLevel>
54 <Prefer32Bit>false</Prefer32Bit>
34 </PropertyGroup>
55 </PropertyGroup>
35 <ItemGroup>
56 <ItemGroup>
36 <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
57 <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
@@ -45,11 +66,6
45 <Reference Include="WindowsBase" />
66 <Reference Include="WindowsBase" />
46 </ItemGroup>
67 </ItemGroup>
47 <ItemGroup>
68 <ItemGroup>
48 <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
49 <Visible>False</Visible>
50 </CodeAnalysisDependentAssemblyPaths>
51 </ItemGroup>
52 <ItemGroup>
53 <Compile Include="Properties\AssemblyInfo.cs" />
69 <Compile Include="Properties\AssemblyInfo.cs" />
54 <Compile Include="OverlayTest.cs" />
70 <Compile Include="OverlayTest.cs" />
55 <Compile Include="Sample\MainForm.cs">
71 <Compile Include="Sample\MainForm.cs">
@@ -68,9 +84,13
68 <ItemGroup>
84 <ItemGroup>
69 <EmbeddedResource Include="Sample\MainForm.resx">
85 <EmbeddedResource Include="Sample\MainForm.resx">
70 <DependentUpon>MainForm.cs</DependentUpon>
86 <DependentUpon>MainForm.cs</DependentUpon>
87 <LogicalName>
88 </LogicalName>
71 </EmbeddedResource>
89 </EmbeddedResource>
72 <EmbeddedResource Include="Sample\OverlayForm.resx">
90 <EmbeddedResource Include="Sample\OverlayForm.resx">
73 <DependentUpon>OverlayForm.cs</DependentUpon>
91 <DependentUpon>OverlayForm.cs</DependentUpon>
92 <LogicalName>
93 </LogicalName>
74 </EmbeddedResource>
94 </EmbeddedResource>
75 </ItemGroup>
95 </ItemGroup>
76 <ItemGroup>
96 <ItemGroup>
@@ -1,12 +1,18
1 using System;
1 using System.Windows.Forms;
2 using System.Text;
2 using Implab.Fx.Test.Sample;
3 using System.Collections.Generic;
3 using Implab.Fx;
4 using System.Linq;
4
5 #if MONO
6
7 using NUnit.Framework;
8 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
9 using TestMethod = NUnit.Framework.TestAttribute;
10
11 #else
12
5 using Microsoft.VisualStudio.TestTools.UnitTesting;
13 using Microsoft.VisualStudio.TestTools.UnitTesting;
6 using System.Windows.Forms;
14
7 using Implab.Fx.Test.Sample;
15 #endif
8 using System.Drawing;
9 using Implab.Fx;
10
16
11 namespace Implab.Fx.Test
17 namespace Implab.Fx.Test
12 {
18 {
@@ -21,11 +27,8 namespace Implab.Fx.Test
21 mainForm.ButtonEvent += (sender, args) =>
27 mainForm.ButtonEvent += (sender, args) =>
22 {
28 {
23 var overlay = new OverlayForm();
29 var overlay = new OverlayForm();
24 mainForm.OverlayFadeIn(overlay).Then(
30 mainForm.OverlayFadeIn(overlay).On(
25 o => o.ButtonEvent += (s2, args2) =>
31 o => o.ButtonEvent += (s2, args2) => o.CloseFadeOut()
26 {
27 o.CloseFadeOut();
28 }
29 );
32 );
30 };
33 };
31
34
@@ -42,7 +42,13 namespace Implab.Fx
42 {
42 {
43 var anim = ctl.AnimateTransparency(0);
43 var anim = ctl.AnimateTransparency(0);
44
44
45 return anim.Play().DispatchToControl(ctl).Then(frm => frm.Close());
45 return anim
46 .Play()
47 .DispatchToControl(ctl)
48 .Then(frm => {
49 frm.Close();
50 return frm;
51 });
46 }
52 }
47
53
48 public static IPromise<T> OverlayFadeIn<T>(this Form that, T overlay) where T : Form
54 public static IPromise<T> OverlayFadeIn<T>(this Form that, T overlay) where T : Form
@@ -10,8 +10,10
10 <AppDesignerFolder>Properties</AppDesignerFolder>
10 <AppDesignerFolder>Properties</AppDesignerFolder>
11 <RootNamespace>Implab.Fx</RootNamespace>
11 <RootNamespace>Implab.Fx</RootNamespace>
12 <AssemblyName>Implab.Fx</AssemblyName>
12 <AssemblyName>Implab.Fx</AssemblyName>
13 <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
13 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
14 <FileAlignment>512</FileAlignment>
14 <FileAlignment>512</FileAlignment>
15 <ReleaseVersion>0.2</ReleaseVersion>
16 <TargetFrameworkProfile />
15 </PropertyGroup>
17 </PropertyGroup>
16 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
18 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
17 <DebugSymbols>true</DebugSymbols>
19 <DebugSymbols>true</DebugSymbols>
@@ -21,6 +23,7
21 <DefineConstants>DEBUG;TRACE</DefineConstants>
23 <DefineConstants>DEBUG;TRACE</DefineConstants>
22 <ErrorReport>prompt</ErrorReport>
24 <ErrorReport>prompt</ErrorReport>
23 <WarningLevel>4</WarningLevel>
25 <WarningLevel>4</WarningLevel>
26 <Prefer32Bit>false</Prefer32Bit>
24 </PropertyGroup>
27 </PropertyGroup>
25 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
28 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
26 <DebugType>pdbonly</DebugType>
29 <DebugType>pdbonly</DebugType>
@@ -29,6 +32,26
29 <DefineConstants>TRACE</DefineConstants>
32 <DefineConstants>TRACE</DefineConstants>
30 <ErrorReport>prompt</ErrorReport>
33 <ErrorReport>prompt</ErrorReport>
31 <WarningLevel>4</WarningLevel>
34 <WarningLevel>4</WarningLevel>
35 <Prefer32Bit>false</Prefer32Bit>
36 </PropertyGroup>
37 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
38 <DebugSymbols>true</DebugSymbols>
39 <DebugType>full</DebugType>
40 <Optimize>false</Optimize>
41 <OutputPath>bin\Debug\</OutputPath>
42 <DefineConstants>DEBUG;TRACE</DefineConstants>
43 <ErrorReport>prompt</ErrorReport>
44 <WarningLevel>4</WarningLevel>
45 <Prefer32Bit>false</Prefer32Bit>
46 </PropertyGroup>
47 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
48 <DebugType>pdbonly</DebugType>
49 <Optimize>true</Optimize>
50 <OutputPath>bin\Release\</OutputPath>
51 <DefineConstants>TRACE</DefineConstants>
52 <ErrorReport>prompt</ErrorReport>
53 <WarningLevel>4</WarningLevel>
54 <Prefer32Bit>false</Prefer32Bit>
32 </PropertyGroup>
55 </PropertyGroup>
33 <ItemGroup>
56 <ItemGroup>
34 <Reference Include="System" />
57 <Reference Include="System" />
@@ -46,10 +69,11
46 <Compile Include="AnimationHelpers.cs" />
69 <Compile Include="AnimationHelpers.cs" />
47 <Compile Include="PromiseHelpers.cs" />
70 <Compile Include="PromiseHelpers.cs" />
48 <Compile Include="Properties\AssemblyInfo.cs" />
71 <Compile Include="Properties\AssemblyInfo.cs" />
72 <Compile Include="ControlBoundPromise.cs" />
49 </ItemGroup>
73 </ItemGroup>
50 <ItemGroup>
74 <ItemGroup>
51 <ProjectReference Include="..\Implab\Implab.csproj">
75 <ProjectReference Include="..\Implab\Implab.csproj">
52 <Project>{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}</Project>
76 <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
53 <Name>Implab</Name>
77 <Name>Implab</Name>
54 </ProjectReference>
78 </ProjectReference>
55 </ItemGroup>
79 </ItemGroup>
@@ -1,7 +1,4
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Windows.Forms;
2 using System.Windows.Forms;
6 using System.Threading;
3 using System.Threading;
7
4
@@ -20,76 +17,27 namespace Implab.Fx
20 /// <example>
17 /// <example>
21 /// client
18 /// client
22 /// .Get("description.txt") // returns a promise
19 /// .Get("description.txt") // returns a promise
23 /// .DirectToControl(m_ctl) // handle the promise in the thread of the control
20 /// .DispatchToControl(m_ctl) // handle the promise in the thread of the control
24 /// .Then(
21 /// .Then(
25 /// description => m_ctl.Text = description // now it's safe
22 /// description => m_ctl.Text = description // now it's safe
26 /// )
23 /// )
27 /// </example>
24 /// </example>
28 public static Promise<T> DispatchToControl<T>(this Promise<T> that, Control ctl)
25 public static IPromise<T> DispatchToControl<T>(this IPromise<T> that, Control ctl)
29 {
26 {
30 if (that == null)
27 Safe.ArgumentNotNull(that, "that");
31 throw new ArgumentNullException("that");
28 Safe.ArgumentNotNull(ctl, "ctl");
32 if (ctl == null)
33 throw new ArgumentNullException("ctl");
34
29
35 var directed = new Promise<T>();
30 var directed = new ControlBoundPromise<T>(ctl);
36
31
37 that.Then(
32 directed.On(that.Cancel, PromiseEventType.Cancelled);
38 res =>
33
39 {
34 that.On(
40 if (ctl.InvokeRequired)
35 directed.Resolve,
41 ctl.Invoke(new Action<T>(directed.Resolve), res);
36 directed.Reject,
42 else
37 directed.Cancel
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 }
52 );
38 );
53
39
54 return directed;
40 return directed;
55 }
41 }
56
57 /// <summary>
58 /// Направляет обработку обещания в текущий поток, если у него существует контекст синхронизации.
59 /// </summary>
60 /// <typeparam name="T">Тип результата обещания.</typeparam>
61 /// <param name="that">Обещание которое нужно обработать в текущем потоке.</param>
62 /// <returns>Перенаправленное обещание.</returns>
63 public static Promise<T> DispatchToCurrentThread<T>(this Promise<T> that)
64 {
65 var sync = SynchronizationContext.Current;
66 if (sync == null)
67 throw new InvalidOperationException("The current thread doesn't have a syncronization context");
68 return DispatchToSyncContext(that, sync);
69 }
70
71 /// <summary>
72 /// Направляет обработку обещания в указанный контекст синхронизации.
73 /// </summary>
74 /// <typeparam name="T">Тип результата обещания.</typeparam>
75 /// <param name="that">Обещание, которое требуется обработать в указанном контексте синхронизации.</param>
76 /// <param name="sync">Контекст синхронизации в который будет направлено обещание.</param>
77 /// <returns>Новое обещание, которое будет обрабатываться в указанном контексте.</returns>
78 public static Promise<T> DispatchToSyncContext<T>(this Promise<T> that, SynchronizationContext sync)
79 {
80 if (that == null)
81 throw new ArgumentNullException("that");
82 if (sync == null)
83 throw new ArgumentNullException("sync");
84
85 var d = new Promise<T>();
86
87 that.Then(
88 res => sync.Post(state => d.Resolve(res), null),
89 err => sync.Post(state => d.Reject(err), null)
90 );
91
92 return d;
93 }
94 }
42 }
95 }
43 }
@@ -32,5 +32,4 using System.Runtime.InteropServices;
32 // You can specify all the values or you can default the Build and Revision Numbers
32 // You can specify all the values or you can default the Build and Revision Numbers
33 // by using the '*' as shown below:
33 // by using the '*' as shown below:
34 // [assembly: AssemblyVersion("1.0.*")]
34 // [assembly: AssemblyVersion("1.0.*")]
35 [assembly: AssemblyVersion("1.0.0.0")]
35 [assembly: AssemblyVersion("2.0.*")]
36 [assembly: AssemblyFileVersion("1.0.0.0")]
This diff has been collapsed as it changes many lines, (1249 lines changed) Show them Hide them
@@ -1,386 +1,863
1 using System;
1 using System;
2 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using System.Reflection;
3 using System.Reflection;
3 using System.Threading;
4 using System.Threading;
4 using Implab.Parallels;
5 using Implab.Parallels;
5
6
6 #if MONO
7 namespace Implab.Test {
7
8 [TestClass]
8 using NUnit.Framework;
9 public class AsyncTests {
9 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
10 [TestMethod]
10 using TestMethodAttribute = NUnit.Framework.TestAttribute;
11 public void ResolveTest() {
11
12 int res = -1;
12 #else
13 var p = new Promise<int>();
13
14 p.Then(x => res = x);
14 using Microsoft.VisualStudio.TestTools.UnitTesting;
15 p.Resolve(100);
15
16
16 #endif
17 Assert.AreEqual(100, res);
17
18 }
18 namespace Implab.Test {
19
19 [TestClass]
20 [TestMethod]
20 public class AsyncTests {
21 public void RejectTest() {
21 [TestMethod]
22 int res = -1;
22 public void ResolveTest() {
23 Exception err = null;
23 int res = -1;
24
24 var p = new Promise<int>();
25 var p = new Promise<int>();
25 p.Then(x => res = x);
26 p.Then(x => res = x, e => err = e);
26 p.Resolve(100);
27 p.Reject(new ApplicationException("error"));
27
28
28 Assert.AreEqual(100, res);
29 Assert.AreEqual(res, -1);
29 }
30 Assert.AreEqual(err.Message, "error");
30
31
31 [TestMethod]
32 }
32 public void RejectTest() {
33
33 int res = -1;
34 [TestMethod]
34 Exception err = null;
35 public void JoinSuccessTest() {
35
36 var p = new Promise<int>();
36 var p = new Promise<int>();
37 p.Resolve(100);
37 p.Then(
38 Assert.AreEqual(p.Join(), 100);
38 x => res = x,
39 }
39 e => {
40
40 err = e;
41 [TestMethod]
41 return -2;
42 public void JoinFailTest() {
42 }
43 var p = new Promise<int>();
43 );
44 p.Reject(new ApplicationException("failed"));
44 p.Reject(new ApplicationException("error"));
45
45
46 try {
46 Assert.AreEqual(res, -1);
47 p.Join();
47 Assert.AreEqual(err.Message, "error");
48 throw new ApplicationException("WRONG!");
48
49 } catch (TargetInvocationException err) {
49 }
50 Assert.AreEqual(err.InnerException.Message, "failed");
50
51 } catch {
51 [TestMethod]
52 Assert.Fail("Got wrong excaption");
52 public void CancelExceptionTest() {
53 }
53 var p = new Promise<bool>();
54 }
54 p.CancelOperation(null);
55
55
56 [TestMethod]
56 var p2 = p.Then(x => x, null, reason => {
57 public void MapTest() {
57 throw new ApplicationException("CANCELLED");
58 var p = new Promise<int>();
58 });
59
59
60 var p2 = p.Map(x => x.ToString());
60 try {
61 p.Resolve(100);
61 p2.Join();
62
62 Assert.Fail();
63 Assert.AreEqual(p2.Join(), "100");
63 } catch (ApplicationException err) {
64 }
64 Assert.AreEqual("CANCELLED", err.InnerException.Message);
65
65 }
66 [TestMethod]
66
67 public void FixErrorTest() {
67 }
68 var p = new Promise<int>();
68
69
69 [TestMethod]
70 var p2 = p.Error(e => 101);
70 public void ContinueOnCancelTest() {
71
71 var p = new Promise<bool>();
72 p.Reject(new Exception());
72 p.CancelOperation(null);
73
73
74 Assert.AreEqual(p2.Join(), 101);
74 var p2 = p
75 }
75 .Then(x => x, null, reason => {
76
76 throw new ApplicationException("CANCELLED");
77 [TestMethod]
77 })
78 public void ChainTest() {
78 .Then(x => x, e => true);
79 var p1 = new Promise<int>();
79
80
80 Assert.AreEqual(true, p2.Join());
81 var p3 = p1.Chain(x => {
81 }
82 var p2 = new Promise<string>();
82
83 p2.Resolve(x.ToString());
83 [TestMethod]
84 return p2;
84 public void JoinSuccessTest() {
85 });
85 var p = new Promise<int>();
86
86 p.Resolve(100);
87 p1.Resolve(100);
87 Assert.AreEqual(p.Join(), 100);
88
88 }
89 Assert.AreEqual(p3.Join(), "100");
89
90 }
90 [TestMethod]
91
91 public void JoinFailTest() {
92 [TestMethod]
92 var p = new Promise<int>();
93 public void PoolTest() {
93 p.Reject(new ApplicationException("failed"));
94 var pid = Thread.CurrentThread.ManagedThreadId;
94
95 var p = AsyncPool.Invoke(() => Thread.CurrentThread.ManagedThreadId);
95 try {
96
96 p.Join();
97 Assert.AreNotEqual(pid, p.Join());
97 throw new ApplicationException("WRONG!");
98 }
98 } catch (TargetInvocationException err) {
99
99 Assert.AreEqual(err.InnerException.Message, "failed");
100 [TestMethod]
100 } catch {
101 public void WorkerPoolSizeTest() {
101 Assert.Fail("Got wrong excaption");
102 var pool = new WorkerPool(5, 10, 0);
102 }
103
103 }
104 Assert.AreEqual(5, pool.PoolSize);
104
105
105 [TestMethod]
106 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
106 public void MapTest() {
107 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
107 var p = new Promise<int>();
108 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
108
109
109 var p2 = p.Then(x => x.ToString());
110 Assert.AreEqual(5, pool.PoolSize);
110 p.Resolve(100);
111
111
112 for (int i = 0; i < 100; i++)
112 Assert.AreEqual(p2.Join(), "100");
113 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
113 }
114 Thread.Sleep(200);
114
115 Assert.AreEqual(10, pool.PoolSize);
115 [TestMethod]
116
116 public void FixErrorTest() {
117 pool.Dispose();
117 var p = new Promise<int>();
118 }
118
119
119 var p2 = p.Then(x => x, e => 101);
120 [TestMethod]
120
121 public void WorkerPoolCorrectTest() {
121 p.Reject(new Exception());
122 var pool = new WorkerPool(0,1000,100);
122
123
123 Assert.AreEqual(p2.Join(), 101);
124 int iterations = 1000;
124 }
125 int pending = iterations;
125
126 var stop = new ManualResetEvent(false);
126 [TestMethod]
127
127 public void ChainTest() {
128 var count = 0;
128 var p1 = new Promise<int>();
129 for (int i = 0; i < iterations; i++) {
129
130 pool
130 var p3 = p1.Chain(x => {
131 .Invoke(() => 1)
131 var p2 = new Promise<string>();
132 .Then(x => Interlocked.Add(ref count, x))
132 p2.Resolve(x.ToString());
133 .Then(x => Math.Log10(x))
133 return p2;
134 .Anyway(() => {
134 });
135 Interlocked.Decrement(ref pending);
135
136 if (pending == 0)
136 p1.Resolve(100);
137 stop.Set();
137
138 });
138 Assert.AreEqual(p3.Join(), "100");
139 }
139 }
140
140
141 stop.WaitOne();
141 [TestMethod]
142
142 public void ChainFailTest() {
143 Assert.AreEqual(iterations, count);
143 var p1 = new Promise<int>();
144 Console.WriteLine("Max threads: {0}", pool.MaxRunningThreads);
144
145 pool.Dispose();
145 var p3 = p1.Chain(x => {
146
146 var p2 = new Promise<string>();
147 }
147 p2.Reject(new Exception("DIE!!!"));
148
148 return p2;
149 [TestMethod]
149 });
150 public void WorkerPoolDisposeTest() {
150
151 var pool = new WorkerPool(5, 20);
151 p1.Resolve(100);
152 Assert.AreEqual(5, pool.PoolSize);
152
153 pool.Dispose();
153 Assert.IsTrue(p3.IsResolved);
154 Thread.Sleep(500);
154 }
155 Assert.AreEqual(0, pool.PoolSize);
155
156 pool.Dispose();
156 [TestMethod]
157 }
157 public void PoolTest() {
158
158 var pid = Thread.CurrentThread.ManagedThreadId;
159 [TestMethod]
159 var p = AsyncPool.Invoke(() => Thread.CurrentThread.ManagedThreadId);
160 public void MTQueueTest() {
160
161 var queue = new MTQueue<int>();
161 Assert.AreNotEqual(pid, p.Join());
162 int res;
162 }
163
163
164 queue.Enqueue(10);
164 [TestMethod]
165 Assert.IsTrue(queue.TryDequeue(out res));
165 public void WorkerPoolSizeTest() {
166 Assert.AreEqual(10, res);
166 var pool = new WorkerPool(5, 10, 1);
167 Assert.IsFalse(queue.TryDequeue(out res));
167
168
168 Assert.AreEqual(5, pool.PoolSize);
169 for (int i = 0; i < 1000; i++)
169
170 queue.Enqueue(i);
170 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
171
171 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
172 for (int i = 0; i < 1000; i++) {
172 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
173 queue.TryDequeue(out res);
173
174 Assert.AreEqual(i, res);
174 Assert.AreEqual(5, pool.PoolSize);
175 }
175
176
176 for (int i = 0; i < 100; i++)
177 int writers = 0;
177 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
178 int readers = 0;
178 Thread.Sleep(200);
179 var stop = new ManualResetEvent(false);
179 Assert.AreEqual(10, pool.PoolSize);
180 int total = 0;
180
181
181 pool.Dispose();
182 int itemsPerWriter = 1000;
182 }
183 int writersCount = 3;
183
184
184 [TestMethod]
185 for (int i = 0; i < writersCount; i++) {
185 public void WorkerPoolCorrectTest() {
186 Interlocked.Increment(ref writers);
186 var pool = new WorkerPool(0,1000,100);
187 var wn = i;
187
188 AsyncPool
188 const int iterations = 1000;
189 .InvokeNewThread(() => {
189 int pending = iterations;
190 for (int ii = 0; ii < itemsPerWriter; ii++) {
190 var stop = new ManualResetEvent(false);
191 queue.Enqueue(1);
191
192 }
192 var count = 0;
193 return 1;
193 for (int i = 0; i < iterations; i++) {
194 })
194 pool
195 .Anyway(() => Interlocked.Decrement(ref writers));
195 .Invoke(() => 1)
196 }
196 .Then(x => Interlocked.Add(ref count, x))
197
197 .Then(x => Math.Log10(x))
198 for (int i = 0; i < 10; i++) {
198 .On(() => {
199 Interlocked.Increment(ref readers);
199 Interlocked.Decrement(ref pending);
200 var wn = i;
200 if (pending == 0)
201 AsyncPool
201 stop.Set();
202 .InvokeNewThread(() => {
202 }, PromiseEventType.All);
203 int t;
203 }
204 do {
204
205 while (queue.TryDequeue(out t))
205 stop.WaitOne();
206 Interlocked.Add(ref total, t);
206
207 } while (writers > 0);
207 Assert.AreEqual(iterations, count);
208 return 1;
208 Console.WriteLine("Max threads: {0}", pool.MaxRunningThreads);
209 })
209 pool.Dispose();
210 .Anyway(() => {
210
211 Interlocked.Decrement(ref readers);
211 }
212 if (readers == 0)
212
213 stop.Set();
213 [TestMethod]
214 });
214 public void WorkerPoolDisposeTest() {
215 }
215 var pool = new WorkerPool(5, 20);
216
216 Assert.AreEqual(5, pool.PoolSize);
217 stop.WaitOne();
217 pool.Dispose();
218
218 Thread.Sleep(500);
219 Assert.AreEqual(itemsPerWriter * writersCount, total);
219 Assert.AreEqual(0, pool.PoolSize);
220 }
220 pool.Dispose();
221
221 }
222 [TestMethod]
222
223 public void ParallelMapTest() {
223 [TestMethod]
224
224 public void MTQueueTest() {
225 int count = 100000;
225 var queue = new MTQueue<int>();
226
226 int res;
227 double[] args = new double[count];
227
228 var rand = new Random();
228 queue.Enqueue(10);
229
229 Assert.IsTrue(queue.TryDequeue(out res));
230 for (int i = 0; i < count; i++)
230 Assert.AreEqual(10, res);
231 args[i] = rand.NextDouble();
231 Assert.IsFalse(queue.TryDequeue(out res));
232
232
233 var t = Environment.TickCount;
233 for (int i = 0; i < 1000; i++)
234 var res = args.ParallelMap(x => Math.Sin(x*x), 4).Join();
234 queue.Enqueue(i);
235
235
236 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
236 for (int i = 0; i < 1000; i++) {
237
237 queue.TryDequeue(out res);
238 t = Environment.TickCount;
238 Assert.AreEqual(i, res);
239 for (int i = 0; i < count; i++)
239 }
240 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
240
241 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
241 int writers = 0;
242 }
242 int readers = 0;
243
243 var stop = new ManualResetEvent(false);
244 [TestMethod]
244 int total = 0;
245 public void ChainedMapTest() {
245
246
246 const int itemsPerWriter = 10000;
247 using (var pool = new WorkerPool(0,100,100)) {
247 const int writersCount = 10;
248 int count = 10000;
248
249
249 for (int i = 0; i < writersCount; i++) {
250 double[] args = new double[count];
250 Interlocked.Increment(ref writers);
251 var rand = new Random();
251 AsyncPool
252
252 .RunThread(() => {
253 for (int i = 0; i < count; i++)
253 for (int ii = 0; ii < itemsPerWriter; ii++) {
254 args[i] = rand.NextDouble();
254 queue.Enqueue(1);
255
255 }
256 var t = Environment.TickCount;
256 return 1;
257 var res = args
257 })
258 .ChainedMap(
258 .On(() => Interlocked.Decrement(ref writers), PromiseEventType.All);
259 x => pool.Invoke(
259 }
260 () => Math.Sin(x * x)
260
261 ),
261 for (int i = 0; i < 10; i++) {
262 4
262 Interlocked.Increment(ref readers);
263 )
263 AsyncPool
264 .Join();
264 .RunThread(() => {
265
265 int t;
266 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
266 do {
267
267 while (queue.TryDequeue(out t))
268 t = Environment.TickCount;
268 Interlocked.Add(ref total, t);
269 for (int i = 0; i < count; i++)
269 } while (writers > 0);
270 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
270 return 1;
271 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
271 })
272 Console.WriteLine("Max workers: {0}", pool.MaxRunningThreads);
272 .On(() => {
273 }
273 Interlocked.Decrement(ref readers);
274 }
274 if (readers == 0)
275
275 stop.Set();
276 [TestMethod]
276 }, PromiseEventType.All);
277 public void ParallelForEachTest() {
277 }
278
278
279 int count = 100000;
279 stop.WaitOne();
280
280
281 int[] args = new int[count];
281 Assert.AreEqual(100000, total);
282 var rand = new Random();
282 }
283
283
284 for (int i = 0; i < count; i++)
284 [TestMethod]
285 args[i] = (int)(rand.NextDouble() * 100);
285 public void AsyncQueueTest() {
286
286 var queue = new AsyncQueue<int>();
287 int result = 0;
287 int res;
288
288
289 var t = Environment.TickCount;
289 queue.Enqueue(10);
290 args.ParallelForEach(x => Interlocked.Add(ref result, x), 4).Join();
290 Assert.IsTrue(queue.TryDequeue(out res));
291
291 Assert.AreEqual(10, res);
292 Console.WriteLine("Iteration complete in {0} ms, result: {1}", Environment.TickCount - t, result);
292 Assert.IsFalse(queue.TryDequeue(out res));
293
293
294 int result2 = 0;
294 for (int i = 0; i < 1000; i++)
295
295 queue.Enqueue(i);
296 t = Environment.TickCount;
296
297 for (int i = 0; i < count; i++)
297 for (int i = 0; i < 1000; i++) {
298 result2 += args[i];
298 queue.TryDequeue(out res);
299 Assert.AreEqual(result2, result);
299 Assert.AreEqual(i, res);
300 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
300 }
301 }
301
302
302 const int count = 10000000;
303 [TestMethod]
303
304 public void ComplexCase1Test() {
304 int res1 = 0, res2 = 0;
305 var flags = new bool[3];
305 var t1 = Environment.TickCount;
306
306
307 // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map)
307 AsyncPool.RunThread(
308
308 () => {
309 var p = PromiseHelper
309 for (var i = 0; i < count; i++)
310 .Sleep(200, "Alan")
310 queue.Enqueue(1);
311 .Cancelled(() => flags[0] = true)
311 Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1);
312 .Chain(x =>
312 },
313 PromiseHelper
313 () => {
314 .Sleep(200, "Hi, " + x)
314 for (var i = 0; i < count; i++)
315 .Map(y => y)
315 queue.Enqueue(2);
316 .Cancelled(() => flags[1] = true)
316 Console.WriteLine("done writer #2: {0} ms", Environment.TickCount - t1);
317 )
317 },
318 .Cancelled(() => flags[2] = true);
318 () => {
319 Thread.Sleep(300);
319 int temp;
320 p.Cancel();
320 int i = 0;
321 try {
321 while (i < count)
322 Assert.AreEqual(p.Join(), "Hi, Alan");
322 if (queue.TryDequeue(out temp)) {
323 Assert.Fail("Shouldn't get here");
323 i++;
324 } catch (OperationCanceledException) {
324 res1 += temp;
325 }
325 }
326
326 Console.WriteLine("done reader #1: {0} ms", Environment.TickCount - t1);
327 Assert.IsFalse(flags[0]);
327 },
328 Assert.IsTrue(flags[1]);
328 () => {
329 Assert.IsTrue(flags[2]);
329 int temp;
330 }
330 int i = 0;
331
331 while (i < count)
332 [TestMethod]
332 if (queue.TryDequeue(out temp)) {
333 public void ChainedCancel1Test() {
333 i++;
334 // при отмене сцепленной асинхронной операции все обещание должно
334 res2 += temp;
335 // завершаться ошибкой OperationCanceledException
335 }
336 var p = PromiseHelper
336 Console.WriteLine("done reader #2: {0} ms", Environment.TickCount - t1);
337 .Sleep(1, "Hi, HAL!")
337 }
338 .Chain(x => {
338 )
339 // запускаем две асинхронные операции
339 .Bundle()
340 var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!");
340 .Join();
341 // вторая операция отменяет первую до завершения
341
342 PromiseHelper
342 Assert.AreEqual(count * 3, res1 + res2);
343 .Sleep(100, "HAL, STOP!")
343
344 .Then(() => result.Cancel());
344 Console.WriteLine(
345 return result;
345 "done: {0} ms, summ#1: {1}, summ#2: {2}, total: {3}, count: {4}",
346 });
346 Environment.TickCount - t1,
347 try {
347 res1,
348 p.Join();
348 res2,
349 } catch (TargetInvocationException err) {
349 res1 + res2,
350 Assert.IsTrue(err.InnerException is OperationCanceledException);
350 count
351 }
351 );
352 }
352 }
353
353
354 [TestMethod]
354 [TestMethod]
355 public void ChainedCancel2Test() {
355 public void AsyncQueueBatchTest() {
356 // при отмене цепочки обещаний, вложенные операции также должны отменяться
356 var queue = new AsyncQueue<int>();
357 IPromiseBase p = null;
357
358 var pSurvive = new Promise<bool>();
358 const int wBatch = 29;
359 var hemStarted = new ManualResetEvent(false);
359 const int wCount = 400000;
360 p = PromiseHelper
360 const int total = wBatch * wCount * 2;
361 .Sleep(1, "Hi, HAL!")
361 const int summ = wBatch * wCount * 3;
362 .Chain(x => {
362
363 hemStarted.Set();
363 int r1 = 0, r2 = 0;
364 // запускаем две асинхронные операции
364 const int rBatch = 111;
365 var result = PromiseHelper
365 int read = 0;
366 .Sleep(1000, "HEM ENABLED!!!")
366
367 .Then(s => pSurvive.Resolve(false));
367 var t1 = Environment.TickCount;
368
368
369 result
369 AsyncPool.RunThread(
370 .Cancelled(() => pSurvive.Resolve(true));
370 () => {
371
371 var buffer = new int[wBatch];
372 return result;
372 for(int i = 0; i<wBatch; i++)
373 });
373 buffer[i] = 1;
374
374
375 hemStarted.WaitOne();
375 for(int i =0; i < wCount; i++)
376 p.Cancel();
376 queue.EnqueueRange(buffer,0,wBatch);
377
377 Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1);
378 try {
378 },
379 p.Join();
379 () => {
380 } catch (OperationCanceledException) {
380 var buffer = new int[wBatch];
381 Assert.IsTrue(pSurvive.Join());
381 for(int i = 0; i<wBatch; i++)
382 }
382 buffer[i] = 2;
383 }
383
384 }
384 for(int i =0; i < wCount; i++)
385 }
385 queue.EnqueueRange(buffer,0,wBatch);
386
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 );
609 }
610
611 [TestMethod]
612 public void ParallelMapTest() {
613
614 const int count = 100000;
615
616 var args = new double[count];
617 var rand = new Random();
618
619 for (int i = 0; i < count; i++)
620 args[i] = rand.NextDouble();
621
622 var t = Environment.TickCount;
623 var res = args.ParallelMap(x => Math.Sin(x*x), 4).Join();
624
625 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
626
627 t = Environment.TickCount;
628 for (int i = 0; i < count; i++)
629 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
630 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
631 }
632
633 [TestMethod]
634 public void ChainedMapTest() {
635
636 using (var pool = new WorkerPool()) {
637 const int count = 10000;
638
639 var args = new double[count];
640 var rand = new Random();
641
642 for (int i = 0; i < count; i++)
643 args[i] = rand.NextDouble();
644
645 var t = Environment.TickCount;
646 var res = args
647 .ChainedMap(
648 // Analysis disable once AccessToDisposedClosure
649 x => pool.Invoke(
650 () => Math.Sin(x * x)
651 ),
652 4
653 )
654 .Join();
655
656 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
657
658 t = Environment.TickCount;
659 for (int i = 0; i < count; i++)
660 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
661 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
662 Console.WriteLine("Max workers: {0}", pool.MaxRunningThreads);
663 }
664 }
665
666 [TestMethod]
667 public void ParallelForEachTest() {
668
669 const int count = 100000;
670
671 var args = new int[count];
672 var rand = new Random();
673
674 for (int i = 0; i < count; i++)
675 args[i] = (int)(rand.NextDouble() * 100);
676
677 int result = 0;
678
679 var t = Environment.TickCount;
680 args.ParallelForEach(x => Interlocked.Add(ref result, x), 4).Join();
681
682 Console.WriteLine("Iteration complete in {0} ms, result: {1}", Environment.TickCount - t, result);
683
684 int result2 = 0;
685
686 t = Environment.TickCount;
687 for (int i = 0; i < count; i++)
688 result2 += args[i];
689 Assert.AreEqual(result2, result);
690 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
691 }
692
693 [TestMethod]
694 public void ComplexCase1Test() {
695 var flags = new bool[3];
696
697 // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map)
698
699 var step1 = PromiseHelper
700 .Sleep(200, "Alan")
701 .On(() => flags[0] = true, PromiseEventType.Cancelled);
702 var p = step1
703 .Chain(x =>
704 PromiseHelper
705 .Sleep(200, "Hi, " + x)
706 .Then(y => y)
707 .On(() => flags[1] = true, PromiseEventType.Cancelled)
708 )
709 .On(() => flags[2] = true, PromiseEventType.Cancelled);
710 step1.Join();
711 p.Cancel();
712 try {
713 Assert.AreEqual(p.Join(), "Hi, Alan");
714 Assert.Fail("Shouldn't get here");
715 } catch (OperationCanceledException) {
716 }
717
718 Assert.IsFalse(flags[0]);
719 Assert.IsTrue(flags[1]);
720 Assert.IsTrue(flags[2]);
721 }
722
723 [TestMethod]
724 public void ChainedCancel1Test() {
725 // при отмене сцепленной асинхронной операции все обещание должно
726 // завершаться ошибкой OperationCanceledException
727 var p = PromiseHelper
728 .Sleep(1, "Hi, HAL!")
729 .Then(x => {
730 // запускаем две асинхронные операции
731 var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!");
732 // вторая операция отменяет первую до завершения
733 PromiseHelper
734 .Sleep(100, "HAL, STOP!")
735 .Then(result.Cancel);
736 return result;
737 });
738 try {
739 p.Join();
740 } catch (TargetInvocationException err) {
741 Assert.IsTrue(err.InnerException is OperationCanceledException);
742 }
743 }
744
745 [TestMethod]
746 public void ChainedCancel2Test() {
747 // при отмене цепочки обещаний, вложенные операции также должны отменяться
748 var pSurvive = new Promise<bool>();
749 var hemStarted = new Signal();
750 var p = PromiseHelper
751 .Sleep(1, "Hi, HAL!")
752 .Chain(() => {
753 hemStarted.Set();
754 // запускаем две асинхронные операции
755 var result = PromiseHelper
756 .Sleep(2000, "HEM ENABLED!!!")
757 .Then(() => pSurvive.Resolve(false));
758
759 result
760 .On(() => pSurvive.Resolve(true), PromiseEventType.Cancelled);
761
762 return result;
763 });
764
765 hemStarted.Wait();
766 p.Cancel();
767
768 try {
769 p.Join();
770 Assert.Fail();
771 } catch (OperationCanceledException) {
772 }
773 Assert.IsTrue(pSurvive.Join());
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");
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);
846 }
847 }
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
@@ -3,17 +3,17
3 <PropertyGroup>
3 <PropertyGroup>
4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 <ProductVersion>
6 <ProductVersion>8.0.30703</ProductVersion>
7 </ProductVersion>
8 <SchemaVersion>2.0</SchemaVersion>
7 <SchemaVersion>2.0</SchemaVersion>
9 <ProjectGuid>{63F92C0C-61BF-48C0-A377-8D67C3C661D0}</ProjectGuid>
8 <ProjectGuid>{63F92C0C-61BF-48C0-A377-8D67C3C661D0}</ProjectGuid>
10 <OutputType>Library</OutputType>
9 <OutputType>Library</OutputType>
11 <AppDesignerFolder>Properties</AppDesignerFolder>
10 <AppDesignerFolder>Properties</AppDesignerFolder>
12 <RootNamespace>Implab.Test</RootNamespace>
11 <RootNamespace>Implab.Test</RootNamespace>
13 <AssemblyName>Implab.Test</AssemblyName>
12 <AssemblyName>Implab.Test</AssemblyName>
14 <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
13 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
15 <FileAlignment>512</FileAlignment>
14 <FileAlignment>512</FileAlignment>
16 <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
15 <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
16 <TargetFrameworkProfile />
17 </PropertyGroup>
17 </PropertyGroup>
18 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
18 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
19 <DebugSymbols>true</DebugSymbols>
19 <DebugSymbols>true</DebugSymbols>
@@ -23,6 +23,7
23 <DefineConstants>DEBUG;TRACE</DefineConstants>
23 <DefineConstants>DEBUG;TRACE</DefineConstants>
24 <ErrorReport>prompt</ErrorReport>
24 <ErrorReport>prompt</ErrorReport>
25 <WarningLevel>4</WarningLevel>
25 <WarningLevel>4</WarningLevel>
26 <Prefer32Bit>false</Prefer32Bit>
26 </PropertyGroup>
27 </PropertyGroup>
27 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
28 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
28 <DebugType>pdbonly</DebugType>
29 <DebugType>pdbonly</DebugType>
@@ -31,6 +32,26
31 <DefineConstants>TRACE</DefineConstants>
32 <DefineConstants>TRACE</DefineConstants>
32 <ErrorReport>prompt</ErrorReport>
33 <ErrorReport>prompt</ErrorReport>
33 <WarningLevel>4</WarningLevel>
34 <WarningLevel>4</WarningLevel>
35 <Prefer32Bit>false</Prefer32Bit>
36 </PropertyGroup>
37 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
38 <DebugSymbols>true</DebugSymbols>
39 <DebugType>full</DebugType>
40 <Optimize>false</Optimize>
41 <OutputPath>bin\Debug\</OutputPath>
42 <DefineConstants>DEBUG;TRACE</DefineConstants>
43 <ErrorReport>prompt</ErrorReport>
44 <WarningLevel>4</WarningLevel>
45 <Prefer32Bit>false</Prefer32Bit>
46 </PropertyGroup>
47 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
48 <DebugType>pdbonly</DebugType>
49 <Optimize>true</Optimize>
50 <OutputPath>bin\Release\</OutputPath>
51 <DefineConstants>TRACE</DefineConstants>
52 <ErrorReport>prompt</ErrorReport>
53 <WarningLevel>4</WarningLevel>
54 <Prefer32Bit>false</Prefer32Bit>
34 </PropertyGroup>
55 </PropertyGroup>
35 <ItemGroup>
56 <ItemGroup>
36 <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
57 <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
@@ -40,14 +61,11
40 </Reference>
61 </Reference>
41 </ItemGroup>
62 </ItemGroup>
42 <ItemGroup>
63 <ItemGroup>
43 <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
44 <Visible>False</Visible>
45 </CodeAnalysisDependentAssemblyPaths>
46 </ItemGroup>
47 <ItemGroup>
48 <Compile Include="AsyncTests.cs" />
64 <Compile Include="AsyncTests.cs" />
65 <Compile Include="CancelationTests.cs" />
49 <Compile Include="PromiseHelper.cs" />
66 <Compile Include="PromiseHelper.cs" />
50 <Compile Include="Properties\AssemblyInfo.cs" />
67 <Compile Include="Properties\AssemblyInfo.cs" />
68 <Compile Include="RunnableComponentTests.cs" />
51 </ItemGroup>
69 </ItemGroup>
52 <ItemGroup>
70 <ItemGroup>
53 <ProjectReference Include="..\Implab\Implab.csproj">
71 <ProjectReference Include="..\Implab\Implab.csproj">
@@ -1,14 +1,11
1 using Implab.Parallels;
1 using Implab.Parallels;
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Text;
6 using System.Threading;
2 using System.Threading;
7
3
8 namespace Implab.Test {
4 namespace Implab.Test {
9 class PromiseHelper {
5 static class PromiseHelper {
10 public static Promise<T> Sleep<T>(int timeout, T retVal) {
6 public static IPromise<T> Sleep<T>(int timeout, T retVal) {
11 return AsyncPool.Invoke(() => {
7 return AsyncPool.Invoke((ct) => {
8 ct.CancellationRequested(ct.CancelOperation);
12 Thread.Sleep(timeout);
9 Thread.Sleep(timeout);
13 return retVal;
10 return retVal;
14 });
11 });
@@ -31,5 +31,4 using System.Runtime.InteropServices;
31 //
31 //
32 // You can specify all the values or you can default the Build and Revision Numbers
32 // You can specify all the values or you can default the Build and Revision Numbers
33 // by using the '*' as shown below:
33 // by using the '*' as shown below:
34 [assembly: AssemblyVersion("1.0.0.0")]
34 [assembly: AssemblyVersion("0.0.*")]
35 [assembly: AssemblyFileVersion("1.0.0.0")]
@@ -17,35 +17,254 EndProject
17 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx.Test", "Implab.Fx.Test\Implab.Fx.Test.csproj", "{2F31E405-E267-4195-A05D-574093C21209}"
17 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx.Test", "Implab.Fx.Test\Implab.Fx.Test.csproj", "{2F31E405-E267-4195-A05D-574093C21209}"
18 EndProject
18 EndProject
19 Global
19 Global
20 GlobalSection(TestCaseManagementSettings) = postSolution
21 CategoryFile = Implab.vsmdi
22 EndGlobalSection
23 GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 Debug|Any CPU = Debug|Any CPU
21 Debug|Any CPU = Debug|Any CPU
25 Release|Any CPU = Release|Any CPU
22 Release|Any CPU = Release|Any CPU
23 Debug 4.5|Any CPU = Debug 4.5|Any CPU
24 Release 4.5|Any CPU = Release 4.5|Any CPU
26 EndGlobalSection
25 EndGlobalSection
27 GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
29 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
30 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU
30 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
32 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
33 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
34 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.Build.0 = Release|Any CPU
35 {2F31E405-E267-4195-A05D-574093C21209}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
36 {2F31E405-E267-4195-A05D-574093C21209}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
37 {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.Build.0 = Debug|Any CPU
39 {2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
40 {2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
41 {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.ActiveCfg = Release|Any CPU
42 {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.Build.0 = Release|Any CPU
43 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
44 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
32 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
47 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
48 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
34 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
49 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.Build.0 = Release|Any CPU
50 {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.Build.0 = Release|Any CPU
36 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU
37 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU
52 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU
38 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
53 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.Build.0 = Release|Any CPU
54 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU
41 {2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.Build.0 = Debug|Any CPU
56 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU
42 {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.ActiveCfg = Release|Any CPU
57 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.Build.0 = Release|Any CPU
58 {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU
59 EndGlobalSection
60 GlobalSection(NestedProjects) = preSolution
61 EndGlobalSection
62 GlobalSection(MonoDevelopProperties) = preSolution
63 StartupItem = Implab\Implab.csproj
64 Policies = $0
65 $0.CSharpFormattingPolicy = $1
66 $1.IndentSwitchBody = True
67 $1.NamespaceBraceStyle = EndOfLine
68 $1.ClassBraceStyle = EndOfLine
69 $1.InterfaceBraceStyle = EndOfLine
70 $1.StructBraceStyle = EndOfLine
71 $1.EnumBraceStyle = EndOfLine
72 $1.MethodBraceStyle = EndOfLine
73 $1.ConstructorBraceStyle = EndOfLine
74 $1.DestructorBraceStyle = EndOfLine
75 $1.BeforeMethodDeclarationParentheses = False
76 $1.BeforeMethodCallParentheses = False
77 $1.BeforeConstructorDeclarationParentheses = False
78 $1.NewLineBeforeConstructorInitializerColon = NewLine
79 $1.NewLineAfterConstructorInitializerColon = SameLine
80 $1.BeforeIndexerDeclarationBracket = False
81 $1.BeforeDelegateDeclarationParentheses = False
82 $1.NewParentheses = False
83 $1.SpacesBeforeBrackets = False
84 $1.inheritsSet = Mono
85 $1.inheritsScope = text/x-csharp
86 $1.scope = text/x-csharp
87 $0.TextStylePolicy = $2
88 $2.FileWidth = 120
89 $2.EolMarker = Unix
90 $2.inheritsSet = VisualStudio
91 $2.inheritsScope = text/plain
92 $2.scope = text/x-csharp
93 $0.DotNetNamingPolicy = $3
94 $3.DirectoryNamespaceAssociation = PrefixedHierarchical
95 $3.ResourceNamePolicy = MSBuild
96 $0.TextStylePolicy = $4
97 $4.FileWidth = 120
98 $4.TabsToSpaces = False
99 $4.inheritsSet = VisualStudio
100 $4.inheritsScope = text/plain
101 $4.scope = application/xml
102 $0.XmlFormattingPolicy = $5
103 $5.inheritsSet = Mono
104 $5.inheritsScope = application/xml
105 $5.scope = application/xml
106 $0.TextStylePolicy = $6
107 $6.FileWidth = 120
108 $6.TabsToSpaces = False
109 $6.inheritsSet = VisualStudio
110 $6.inheritsScope = text/plain
111 $6.scope = text/plain
112 $0.NameConventionPolicy = $7
113 $7.Rules = $8
114 $8.NamingRule = $9
115 $9.Name = Namespaces
116 $9.AffectedEntity = Namespace
117 $9.VisibilityMask = VisibilityMask
118 $9.NamingStyle = PascalCase
119 $9.IncludeInstanceMembers = True
120 $9.IncludeStaticEntities = True
121 $8.NamingRule = $10
122 $10.Name = Types
123 $10.AffectedEntity = Class, Struct, Enum, Delegate
124 $10.VisibilityMask = VisibilityMask
125 $10.NamingStyle = PascalCase
126 $10.IncludeInstanceMembers = True
127 $10.IncludeStaticEntities = True
128 $8.NamingRule = $11
129 $11.Name = Interfaces
130 $11.RequiredPrefixes = $12
131 $12.String = I
132 $11.AffectedEntity = Interface
133 $11.VisibilityMask = VisibilityMask
134 $11.NamingStyle = PascalCase
135 $11.IncludeInstanceMembers = True
136 $11.IncludeStaticEntities = True
137 $8.NamingRule = $13
138 $13.Name = Attributes
139 $13.RequiredSuffixes = $14
140 $14.String = Attribute
141 $13.AffectedEntity = CustomAttributes
142 $13.VisibilityMask = VisibilityMask
143 $13.NamingStyle = PascalCase
144 $13.IncludeInstanceMembers = True
145 $13.IncludeStaticEntities = True
146 $8.NamingRule = $15
147 $15.Name = Event Arguments
148 $15.RequiredSuffixes = $16
149 $16.String = EventArgs
150 $15.AffectedEntity = CustomEventArgs
151 $15.VisibilityMask = VisibilityMask
152 $15.NamingStyle = PascalCase
153 $15.IncludeInstanceMembers = True
154 $15.IncludeStaticEntities = True
155 $8.NamingRule = $17
156 $17.Name = Exceptions
157 $17.RequiredSuffixes = $18
158 $18.String = Exception
159 $17.AffectedEntity = CustomExceptions
160 $17.VisibilityMask = VisibilityMask
161 $17.NamingStyle = PascalCase
162 $17.IncludeInstanceMembers = True
163 $17.IncludeStaticEntities = True
164 $8.NamingRule = $19
165 $19.Name = Methods
166 $19.AffectedEntity = Methods
167 $19.VisibilityMask = VisibilityMask
168 $19.NamingStyle = PascalCase
169 $19.IncludeInstanceMembers = True
170 $19.IncludeStaticEntities = True
171 $8.NamingRule = $20
172 $20.Name = Static Readonly Fields
173 $20.AffectedEntity = ReadonlyField
174 $20.VisibilityMask = Internal, Protected, Public
175 $20.NamingStyle = CamelCase
176 $20.IncludeInstanceMembers = False
177 $20.IncludeStaticEntities = True
178 $8.NamingRule = $21
179 $21.Name = Fields (Non Private)
180 $21.AffectedEntity = Field
181 $21.VisibilityMask = Internal, Public
182 $21.NamingStyle = CamelCase
183 $21.IncludeInstanceMembers = True
184 $21.IncludeStaticEntities = True
185 $8.NamingRule = $22
186 $22.Name = ReadOnly Fields (Non Private)
187 $22.AffectedEntity = ReadonlyField
188 $22.VisibilityMask = Internal, Public
189 $22.NamingStyle = CamelCase
190 $22.IncludeInstanceMembers = True
191 $22.IncludeStaticEntities = False
192 $8.NamingRule = $23
193 $23.Name = Fields (Private)
194 $23.RequiredPrefixes = $24
195 $24.String = m_
196 $23.AffectedEntity = Field, ReadonlyField
197 $23.VisibilityMask = Private, Protected
198 $23.NamingStyle = CamelCase
199 $23.IncludeInstanceMembers = True
200 $23.IncludeStaticEntities = False
201 $8.NamingRule = $25
202 $25.Name = Static Fields (Private)
203 $25.RequiredPrefixes = $26
204 $26.String = _
205 $25.AffectedEntity = Field
206 $25.VisibilityMask = Private
207 $25.NamingStyle = CamelCase
208 $25.IncludeInstanceMembers = False
209 $25.IncludeStaticEntities = True
210 $8.NamingRule = $27
211 $27.Name = ReadOnly Fields (Private)
212 $27.RequiredPrefixes = $28
213 $28.String = m_
214 $27.AffectedEntity = ReadonlyField
215 $27.VisibilityMask = Private, Protected
216 $27.NamingStyle = CamelCase
217 $27.IncludeInstanceMembers = True
218 $27.IncludeStaticEntities = False
219 $8.NamingRule = $29
220 $29.Name = Constant Fields
221 $29.AffectedEntity = ConstantField
222 $29.VisibilityMask = VisibilityMask
223 $29.NamingStyle = AllUpper
224 $29.IncludeInstanceMembers = True
225 $29.IncludeStaticEntities = True
226 $8.NamingRule = $30
227 $30.Name = Properties
228 $30.AffectedEntity = Property
229 $30.VisibilityMask = VisibilityMask
230 $30.NamingStyle = PascalCase
231 $30.IncludeInstanceMembers = True
232 $30.IncludeStaticEntities = True
233 $8.NamingRule = $31
234 $31.Name = Events
235 $31.AffectedEntity = Event
236 $31.VisibilityMask = VisibilityMask
237 $31.NamingStyle = PascalCase
238 $31.IncludeInstanceMembers = True
239 $31.IncludeStaticEntities = True
240 $8.NamingRule = $32
241 $32.Name = Enum Members
242 $32.AffectedEntity = EnumMember
243 $32.VisibilityMask = VisibilityMask
244 $32.NamingStyle = PascalCase
245 $32.IncludeInstanceMembers = True
246 $32.IncludeStaticEntities = True
247 $8.NamingRule = $33
248 $33.Name = Parameters
249 $33.AffectedEntity = Parameter, LocalVariable
250 $33.VisibilityMask = VisibilityMask
251 $33.NamingStyle = CamelCase
252 $33.IncludeInstanceMembers = True
253 $33.IncludeStaticEntities = True
254 $8.NamingRule = $34
255 $34.Name = Type Parameters
256 $34.RequiredPrefixes = $35
257 $35.String = T
258 $34.AffectedEntity = TypeParameter
259 $34.VisibilityMask = VisibilityMask
260 $34.NamingStyle = PascalCase
261 $34.IncludeInstanceMembers = True
262 $34.IncludeStaticEntities = True
263 EndGlobalSection
264 GlobalSection(TestCaseManagementSettings) = postSolution
265 CategoryFile = Implab.vsmdi
44 EndGlobalSection
266 EndGlobalSection
45 GlobalSection(SolutionProperties) = preSolution
267 GlobalSection(SolutionProperties) = preSolution
46 HideSolutionNode = FALSE
268 HideSolutionNode = FALSE
47 EndGlobalSection
269 EndGlobalSection
48 GlobalSection(MonoDevelopProperties) = preSolution
49 StartupItem = Implab\Implab.csproj
50 EndGlobalSection
51 EndGlobal
270 EndGlobal
@@ -1,33 +1,21
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
2 using System.Text;
5
3
6 namespace Implab.Diagnostics {
4 namespace Implab.Diagnostics {
7 public class ConsoleTraceListener: TextListenerBase {
5 public class ConsoleTraceListener: ListenerBase {
8
6
9 static readonly object _consoleLock = new object();
7 static readonly object _consoleLock = new object();
10
8
11 public ConsoleTraceListener()
9 public override void Write(LogEventArgs args, object entry) {
12 : base(true) {
13
14 }
15
16 public ConsoleTraceListener(bool global)
17 : base(global) {
18
19 }
20
21 protected override void WriteEntry(TraceContext context, EventText text, string channel) {
22 var msg = new StringBuilder();
10 var msg = new StringBuilder();
23
11
24 for (int i = 0; i < text.indent; i++)
12 for (int i = 0; i < args.Operation.Level; i++)
25 msg.Append(" ");
13 msg.Append(" ");
26 msg.AppendFormat("[{0}]:{1}: {2}", context.ThreadId, channel, text.content);
14 msg.AppendFormat("[{0}]: {1}", args.ThreadId, entry);
27
15
28 lock (_consoleLock) {
16 lock (_consoleLock) {
29 Console.ForegroundColor = (ConsoleColor)(context.ThreadId % 15 + 1);
17 Console.ForegroundColor = (ConsoleColor)(args.ThreadId % 15 + 1);
30 Console.WriteLine(msg.ToString());
18 Console.WriteLine(msg);
31 }
19 }
32 }
20 }
33 }
21 }
@@ -27,8 +27,8 namespace Implab.Diagnostics {
27 /// <summary>
27 /// <summary>
28 /// Событие появление новой записи в журнале, на это событие подписываются слушатели.
28 /// Событие появление новой записи в журнале, на это событие подписываются слушатели.
29 /// </summary>
29 /// </summary>
30 public event EventHandler<ValueEventArgs<TEvent>> Events;
30 public event EventHandler<LogEventArgs<TEvent>> Events;
31
31
32 /// <summary>
32 /// <summary>
33 /// Имя канала, полезно для отображения в журнале
33 /// Имя канала, полезно для отображения в журнале
34 /// </summary>
34 /// </summary>
@@ -63,19 +63,19 namespace Implab.Diagnostics {
63 /// </remarks>
63 /// </remarks>
64 public void LogEvent(TEvent data) {
64 public void LogEvent(TEvent data) {
65 var t = Events;
65 var t = Events;
66 if (t!= null)
66 if (t != null) {
67 t(TraceContext.Current,new ValueEventArgs<TEvent>(data));
67 var traceContext = TraceContext.Instance;
68 }
68 t(
69
69 this,
70 /// <summary>
70 new LogEventArgs<TEvent>(
71 /// Отправляет запись журнала через канал подписчикам.
71 data,
72 /// </summary>
72 Name,
73 /// <param name="data">Запись журнала.</param>
73 traceContext.ThreadId,
74 /// <param name="context">Контекст трассировки от которого рассылается сообщение/</param>
74 traceContext.CurrentOperation,
75 public void LogEvent(TraceContext context,TEvent data) {
75 traceContext.CurrentOperation.Duration
76 var t = Events;
76 )
77 if (t != null)
77 );
78 t(context, new ValueEventArgs<TEvent>(data));
78 }
79 }
79 }
80 }
80 }
81 }
81 }
@@ -1,11 +1,9
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
2
7 namespace Implab.Diagnostics {
3 namespace Implab.Diagnostics {
8 public class LogicalOperation {
4 public class LogicalOperation {
5 public static readonly LogicalOperation EMPTY = new LogicalOperation("__EMPTY__", null);
6
9 readonly LogicalOperation m_parent;
7 readonly LogicalOperation m_parent;
10 readonly string m_name;
8 readonly string m_name;
11 readonly int m_level;
9 readonly int m_level;
@@ -1,37 +1,36
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
2 using System.IO;
4 using System.Linq;
5 using System.Text;
3 using System.Text;
6
4
7 namespace Implab.Diagnostics {
5 namespace Implab.Diagnostics {
8 public class TextFileListener: TextListenerBase {
6 public class TextFileListener: ListenerBase {
9 readonly TextWriter m_textWriter;
7 readonly TextWriter m_textWriter;
10
8
11 public TextFileListener(string fileName, bool global)
9 public TextFileListener(string fileName) {
12 : base(global) {
13 m_textWriter = File.CreateText(fileName);
10 m_textWriter = File.CreateText(fileName);
14
11
15 m_textWriter.WriteLine("LOG {0}", DateTime.Now);
12 m_textWriter.WriteLine("LOG {0}", DateTime.Now);
16 Register(this);
17 }
13 }
18
14
19 protected override void WriteEntry(TraceContext context, EventText text, string channel) {
15 #region implemented abstract members of ListenerBase
16
17 public override void Write(LogEventArgs args, object entry) {
20 var msg = new StringBuilder();
18 var msg = new StringBuilder();
21 for (int i = 0; i < text.indent; i++)
19 for (int i = 0; i < args.Operation.Level; i++)
22 msg.Append(" ");
20 msg.Append(" ");
23 msg.AppendFormat("[{0}]:{1}: {2}", context.ThreadId, channel, text.content);
21 msg.AppendFormat("[{0}]:{1}: {2}", args.ThreadId, args.ChannelName, entry);
24
22
25 lock (m_textWriter) {
23 lock (m_textWriter) {
26 if (!IsDisposed) {
24 if (!IsDisposed) {
27 // тут гарантировано еще не освобожден m_textWriter
25 // тут гарантировано еще не освобожден m_textWriter
28 m_textWriter.WriteLine(msg.ToString());
26 m_textWriter.WriteLine(msg);
29 m_textWriter.Flush();
27 m_textWriter.Flush();
30 }
28 }
31 }
29 }
32 }
30 }
33
31
34
32 #endregion
33
35 protected override void Dispose(bool disposing) {
34 protected override void Dispose(bool disposing) {
36 base.Dispose(disposing);
35 base.Dispose(disposing);
37 if (disposing) {
36 if (disposing) {
@@ -1,238 +1,83
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System.Threading;
4 using System.Text;
4
5 using System.Threading;
5 namespace Implab.Diagnostics {
6 using System.Threading.Tasks;
6 /// <summary>
7
7 /// Trace context is bound to the specific thread, each thread has it's own ThreadContext.
8 namespace Implab.Diagnostics {
8 /// </summary>
9 /// <summary>
9 /// <remarks>
10 /// Контекст трассировки, привязывается к потоку и содержит в себе информацию о стеке логических операций.
10 /// ThreadContext manages relations between logical operations and threads.
11 /// </summary>
11 /// </remarks>
12 /// <remarks>
12 public class TraceContext {
13 /// Контекст трассировки передается слушателям событий для определения места, где возникло событие.
13
14 /// </remarks>
14 [ThreadStatic]
15 public class TraceContext {
15 static TraceContext _instance;
16 LogicalOperation m_currentOperation;
16
17 readonly LogicalOperation m_bound;
17 OperationContext m_current = OperationContext.EMPTY;
18 readonly int m_threadId;
18 readonly Stack<OperationContext> m_stack = new Stack<OperationContext>();
19
19 readonly int m_threadId;
20 [ThreadStatic]
20
21 static TraceContext _current;
21 public static TraceContext Instance {
22
22 get {
23 /// <summary>
23 if (_instance == null)
24 /// Текущий контекст трассировки для потока, создается астоматически при первом обращении.
24 _instance = new TraceContext();
25 /// </summary>
25 return _instance;
26 public static TraceContext Current {
26 }
27 get {
27 }
28 if (_current == null) {
28
29 _current = new TraceContext();
29 public TraceContext() {
30 _current.LogEvent(TraceEventType.Created,"[{0}]", _current.ThreadId);
30 m_threadId = Thread.CurrentThread.ManagedThreadId;
31 }
31 }
32 return _current;
32
33 }
33 public int ThreadId {
34 }
34 get { return m_threadId; }
35
35 }
36 TraceContext(TraceContext context)
36
37 : this(context, false) {
37 public LogicalOperation CurrentOperation {
38 }
38 get {
39
39 return m_current.CurrentOperation;
40 TraceContext(TraceContext context, bool attach) {
40 }
41 if (context == null)
41 }
42 throw new ArgumentNullException("context");
42
43
43 public void EnterLogicalOperation(LogicalOperation operation, bool takeOwnership) {
44 m_currentOperation = context.CurrentOperation;
44 //var prev = CurrentOperation;
45 m_bound = attach ? context.BoundOperation : context.CurrentOperation;
45 //LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(takeOwnership ? TraceEventType.Attach : TraceEventType.Enter, String.Format("{0} -> {1}",prev.Name, operation.Name)));
46 m_threadId = Thread.CurrentThread.ManagedThreadId;
46 m_stack.Push(m_current);
47 }
47 m_current = new OperationContext(operation, takeOwnership);
48
48 }
49 TraceContext() {
49
50 m_currentOperation = new LogicalOperation();
50 public void StartLogicalOperation(string name) {
51 m_bound = m_currentOperation;
51 LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(TraceEventType.OperationStarted, name));
52 m_threadId = Thread.CurrentThread.ManagedThreadId;
52 m_current.BeginLogicalOperation(name);
53 }
53 }
54
54
55 /// <summary>
55 public void StartLogicalOperation() {
56 /// При необходимости копирует состояние контекста трассивровки в текущий поток.
56 StartLogicalOperation(String.Empty);
57 /// </summary>
57 }
58 /// <param name="from">Исходный контекст трассировки, который передается.</param>
58
59 /// <remarks>
59 public void EndLogicalOperation() {
60 /// <para>
60 var op = m_current.EndLogicalOperation();
61 /// Копирование происходит за счет создания нового контекста трассировки и заполнением его
61 LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(TraceEventType.OperationCompleted, String.Format("-{0} : {1}ms",op.Name, op.Duration)));
62 /// состояния из переданного контекста. При этом копируется стек операций, однако в новом
62 }
63 /// контексте ранее начатые логические операции не могут быть завершены.
63
64 /// </para>
64 public LogicalOperation DetachLogicalOperation() {
65 /// <para>
65 var prev = m_current.DetachLogicalOperation();
66 /// Если передача состояния состоялась, то вызывается событие трассировки <see cref="TraceEventType.Fork"/>.
66 //LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(TraceEventType.Detach, String.Format("{0} -> {1}",prev.Name, CurrentOperation.Name)));
67 /// </para>
67 return prev;
68 /// </remarks>
68 }
69 public static void Fork(TraceContext from) {
69
70 if (_current == from)
70 public void Leave() {
71 return;
71 if (m_stack.Count > 0) {
72 if (from != null) {
72 m_current.Leave();
73 var context = new TraceContext(from);
73 //var prev = CurrentOperation;
74 context.LogEvent(TraceEventType.Fork, "[{0}]-->[{1}]",from.ThreadId, context.ThreadId);
74 m_current = m_stack.Pop();
75 _current = context;
75 //LogChannel<TraceEvent>.Default.LogEvent(new TraceEvent(TraceEventType.Leave, String.Format("{0} -> {1}", prev.Name, CurrentOperation.Name)));
76 } else {
76 } else {
77 _current = new TraceContext();
77 TraceLog.TraceWarning("Attempt to leave the last operation context");
78 }
78 m_current = OperationContext.EMPTY;
79 }
79 }
80
80 }
81 /// <summary>
81 }
82 /// Задает текущему потоку указанный контекст, текущей поток может заканчивать ранее начатые
82 }
83 /// логические операции в указанном контексте.
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();
95 }
96 }
97
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;
112 }
113
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);
120 }
121
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;
137 }
138 }
139
140 /// <summary>
141 /// Текущая логическая операция.
142 /// </summary>
143 public LogicalOperation CurrentOperation {
144 get {
145 return m_currentOperation;
146 }
147 }
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 }
165 }
166
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,7 +1,4
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
2
6 namespace Implab.Diagnostics {
3 namespace Implab.Diagnostics {
7 public class TraceEvent {
4 public class TraceEvent {
@@ -21,10 +18,8 namespace Implab.Diagnostics {
21 }
18 }
22
19
23 public override string ToString() {
20 public override string ToString() {
24 if (EventType == TraceEventType.Information)
21 /*return EventType == TraceEventType.Information ? Message : String.Format("{0}: {1}", EventType, Message);*/
25 return Message;
22 return Message;
26 else
27 return String.Format("{0}: {1}", EventType, Message);
28 }
23 }
29
24
30 public static TraceEvent Create(TraceEventType type, string format, params object[] args) {
25 public static TraceEvent Create(TraceEventType type, string format, params object[] args) {
@@ -11,9 +11,9 namespace Implab.Diagnostics {
11 Error,
11 Error,
12 OperationStarted,
12 OperationStarted,
13 OperationCompleted,
13 OperationCompleted,
14 Fork,
15 Attach,
14 Attach,
16 Detach,
15 Detach,
17 Created
16 Enter,
17 Leave
18 }
18 }
19 }
19 }
@@ -14,22 +14,17 namespace Implab.Diagnostics {
14 public static class TraceLog {
14 public static class TraceLog {
15 [Conditional("TRACE")]
15 [Conditional("TRACE")]
16 public static void StartLogicalOperation() {
16 public static void StartLogicalOperation() {
17 TraceContext.Current.StartLogicalOperation();
17 TraceContext.Instance.StartLogicalOperation();
18 }
18 }
19
19
20 [Conditional("TRACE")]
20 [Conditional("TRACE")]
21 public static void StartLogicalOperation(string name) {
21 public static void StartLogicalOperation(string name) {
22 TraceContext.Current.StartLogicalOperation(name);
22 TraceContext.Instance.StartLogicalOperation(name);
23 }
23 }
24
24
25 [Conditional("TRACE")]
25 [Conditional("TRACE")]
26 public static void EndLogicalOperation() {
26 public static void EndLogicalOperation() {
27 TraceContext.Current.EndLogicalOperation();
27 TraceContext.Instance.EndLogicalOperation();
28 }
29
30 [Conditional("TRACE")]
31 public static void BindLogicalOperationToPromise(IPromise promise) {
32 TraceContext.Current.BindLogicalOperationToPromise(promise);
33 }
28 }
34
29
35 [Conditional("TRACE")]
30 [Conditional("TRACE")]
@@ -1,10 +1,8
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
2
6 namespace Implab {
3 namespace Implab {
7 public interface ICancellable {
4 public interface ICancellable {
8 bool Cancel();
5 void Cancel();
6 void Cancel(Exception reason);
9 }
7 }
10 }
8 }
@@ -5,32 +5,61 using System.Text;
5
5
6 namespace Implab {
6 namespace Implab {
7 public interface IPromise: ICancellable {
7 public interface IPromise: ICancellable {
8 /// <summary>
9 /// Check whereather the promise has no more than one dependent promise.
10 /// </summary>
11 bool IsExclusive {
12 get;
13 }
14
8
15 /// <summary>
9 /// <summary>
16 /// Тип результата, получаемого через данное обещание.
10 /// Тип результата, получаемого через данное обещание.
17 /// </summary>
11 /// </summary>
18 Type PromiseType { get; }
12 Type PromiseType { get; }
19
13
14 /// <summary>
15 /// Обещание является выполненым, либо успешно, либо с ошибкой, либо отменено.
16 /// </summary>
20 bool IsResolved { get; }
17 bool IsResolved { get; }
21
18
19 /// <summary>
20 /// Обещание было отменено.
21 /// </summary>
22 bool IsCancelled { get; }
22 bool IsCancelled { get; }
23
23
24 IPromise Then(Action success,ErrorHandler error);
24 /// <summary>
25 IPromise Then(Action success);
25 /// Исключение возникшее в результате выполнения обещания, либо причина отмены.
26 IPromise Error(ErrorHandler error);
26 /// </summary>
27 IPromise Anyway(Action handler);
27 Exception Error { get; }
28 IPromise Finally(Action handler);
29 IPromise Cancelled(Action handler);
30
28
29 /// <summary>
30 /// Adds specified listeners to the current promise.
31 /// </summary>
32 /// <param name="success">The handler called on the successful promise completion.</param>
33 /// <param name="error">The handler is called if an error while completing the promise occurred.</param>
34 /// <param name="cancel">The handler is called in case of promise cancellation.</param>
35 /// <returns>The current promise.</returns>
36 IPromise On(Action success, Action<Exception> error, Action<Exception> cancel);
37 IPromise On(Action success, Action<Exception> error);
38 IPromise On(Action success);
39
40 /// <summary>
41 /// Adds specified listeners to the current promise.
42 /// </summary>
43 /// <param name="handler">The handler called on the specified events.</param>
44 /// <param name = "events">The combination of flags denoting the events for which the
45 /// handler shoud be called.</param>
46 /// <returns>The current promise.</returns>
47 IPromise On(Action handler, PromiseEventType events);
48
49 /// <summary>
50 /// Преобразует результат обещания к заданному типу и возвращает новое обещание.
51 /// </summary>
31 IPromise<T> Cast<T>();
52 IPromise<T> Cast<T>();
32
53
54 /// <summary>
55 /// Синхронизирует текущий поток с обещанием.
56 /// </summary>
33 void Join();
57 void Join();
58 /// <summary>
59 /// Синхронизирует текущий поток с обещанием.
60 /// </summary>
61 /// <param name="timeout">Время ожидания, по его истечению возникнет исключение.</param>
62 /// <exception cref="TimeoutException">Превышено время ожидания.</exception>
34 void Join(int timeout);
63 void Join(int timeout);
35
64
36 }
65 }
@@ -1,31 +1,25
1 using System;
1 using System;
2 using System.Collections.Generic;
2
3 using System.Linq;
3 namespace Implab {
4 using System.Text;
4 public interface IPromise<out T> : IPromise {
5
5
6 namespace Implab
6 IPromise<T> On(Action<T> success, Action<Exception> error, Action<Exception> cancel);
7 {
7
8 public interface IPromise<T>: IPromise
8 IPromise<T> On(Action<T> success, Action<Exception> error);
9 {
9
10 IPromise<T> On(Action<T> success);
10
11
11 new T Join();
12 new T Join();
13
12 new T Join(int timeout);
14 new T Join(int timeout);
13
15
14 IPromise<T> Then(ResultHandler<T> success, ErrorHandler error);
16 new IPromise<T> On(Action success, Action<Exception> error, Action<Exception> cancel);
15 IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error);
17
16 IPromise<T> Then(ResultHandler<T> success);
18 new IPromise<T> On(Action success, Action<Exception> error);
17 new IPromise<T> Error(ErrorHandler error);
18 IPromise<T> Error(ErrorHandler<T> error);
19
19
20 IPromise<T2> Map<T2>(ResultMapper<T,T2> mapper, ErrorHandler error);
20 new IPromise<T> On(Action success);
21 IPromise<T2> Map<T2>(ResultMapper<T, T2> mapper);
22
21
23 IPromise<T2> Chain<T2>(ChainedOperation<T, T2> chained, ErrorHandler error);
22 new IPromise<T> On(Action handler, PromiseEventType events);
24 IPromise<T2> Chain<T2>(ChainedOperation<T, T2> chained);
25
26 new IPromise<T> Cancelled(Action handler);
27 new IPromise<T> Finally(Action handler);
28 new IPromise<T> Anyway(Action handler);
29
23
30 }
24 }
31 }
25 }
@@ -1,7 +1,4
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
2
6 namespace Implab {
3 namespace Implab {
7 public interface ITaskController: IProgressHandler, ICancellable {
4 public interface ITaskController: IProgressHandler, ICancellable {
@@ -7,6 +7,10
7 <OutputType>Library</OutputType>
7 <OutputType>Library</OutputType>
8 <RootNamespace>Implab</RootNamespace>
8 <RootNamespace>Implab</RootNamespace>
9 <AssemblyName>Implab</AssemblyName>
9 <AssemblyName>Implab</AssemblyName>
10 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
11 <ReleaseVersion>0.2</ReleaseVersion>
12 <ProductVersion>8.0.30703</ProductVersion>
13 <SchemaVersion>2.0</SchemaVersion>
10 </PropertyGroup>
14 </PropertyGroup>
11 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
15 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
12 <DebugSymbols>true</DebugSymbols>
16 <DebugSymbols>true</DebugSymbols>
@@ -27,25 +31,59
27 <WarningLevel>4</WarningLevel>
31 <WarningLevel>4</WarningLevel>
28 <ConsolePause>false</ConsolePause>
32 <ConsolePause>false</ConsolePause>
29 </PropertyGroup>
33 </PropertyGroup>
34 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
35 <DebugSymbols>true</DebugSymbols>
36 <DebugType>full</DebugType>
37 <Optimize>false</Optimize>
38 <OutputPath>bin\Debug</OutputPath>
39 <DefineConstants>TRACE;DEBUG;NET_4_5</DefineConstants>
40 <ErrorReport>prompt</ErrorReport>
41 <WarningLevel>4</WarningLevel>
42 <RunCodeAnalysis>true</RunCodeAnalysis>
43 <ConsolePause>false</ConsolePause>
44 </PropertyGroup>
45 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
46 <Optimize>true</Optimize>
47 <OutputPath>bin\Release</OutputPath>
48 <ErrorReport>prompt</ErrorReport>
49 <WarningLevel>4</WarningLevel>
50 <ConsolePause>false</ConsolePause>
51 <DefineConstants>NET_4_5</DefineConstants>
52 </PropertyGroup>
53 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugMono|AnyCPU' ">
54 <DebugSymbols>true</DebugSymbols>
55 <DebugType>full</DebugType>
56 <Optimize>false</Optimize>
57 <OutputPath>bin\Debug</OutputPath>
58 <DefineConstants>TRACE;DEBUG;NET_4_5;MONO</DefineConstants>
59 <ErrorReport>prompt</ErrorReport>
60 <WarningLevel>4</WarningLevel>
61 <RunCodeAnalysis>true</RunCodeAnalysis>
62 <ConsolePause>false</ConsolePause>
63 </PropertyGroup>
64 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseMono|AnyCPU' ">
65 <Optimize>true</Optimize>
66 <OutputPath>bin\Release</OutputPath>
67 <DefineConstants>NET_4_5;MONO;</DefineConstants>
68 <ErrorReport>prompt</ErrorReport>
69 <WarningLevel>4</WarningLevel>
70 <ConsolePause>false</ConsolePause>
71 </PropertyGroup>
30 <ItemGroup>
72 <ItemGroup>
31 <Reference Include="System" />
73 <Reference Include="System" />
32 <Reference Include="System.Xml" />
74 <Reference Include="System.Xml" />
75 <Reference Include="mscorlib" />
33 </ItemGroup>
76 </ItemGroup>
34 <ItemGroup>
77 <ItemGroup>
35 <Compile Include="Component.cs" />
36 <Compile Include="CustomEqualityComparer.cs" />
78 <Compile Include="CustomEqualityComparer.cs" />
37 <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
79 <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
38 <Compile Include="Diagnostics\EventText.cs" />
80 <Compile Include="Diagnostics\EventText.cs" />
39 <Compile Include="Diagnostics\IEventTextFormatter.cs" />
40 <Compile Include="Diagnostics\LogChannel.cs" />
81 <Compile Include="Diagnostics\LogChannel.cs" />
41 <Compile Include="Diagnostics\LogicalOperation.cs" />
82 <Compile Include="Diagnostics\LogicalOperation.cs" />
42 <Compile Include="Diagnostics\TextFileListener.cs" />
83 <Compile Include="Diagnostics\TextFileListener.cs" />
43 <Compile Include="Diagnostics\TextListenerBase.cs" />
44 <Compile Include="Diagnostics\TraceLog.cs" />
84 <Compile Include="Diagnostics\TraceLog.cs" />
45 <Compile Include="Diagnostics\TraceContext.cs" />
46 <Compile Include="Diagnostics\TraceEvent.cs" />
85 <Compile Include="Diagnostics\TraceEvent.cs" />
47 <Compile Include="Diagnostics\TraceEventType.cs" />
86 <Compile Include="Diagnostics\TraceEventType.cs" />
48 <Compile Include="Disposable.cs" />
49 <Compile Include="ICancellable.cs" />
87 <Compile Include="ICancellable.cs" />
50 <Compile Include="IProgressHandler.cs" />
88 <Compile Include="IProgressHandler.cs" />
51 <Compile Include="IProgressNotifier.cs" />
89 <Compile Include="IProgressNotifier.cs" />
@@ -53,53 +91,186
53 <Compile Include="IPromise.cs" />
91 <Compile Include="IPromise.cs" />
54 <Compile Include="IServiceLocator.cs" />
92 <Compile Include="IServiceLocator.cs" />
55 <Compile Include="ITaskController.cs" />
93 <Compile Include="ITaskController.cs" />
56 <Compile Include="JSON\JSONElementContext.cs" />
57 <Compile Include="JSON\JSONElementType.cs" />
58 <Compile Include="JSON\JSONGrammar.cs" />
59 <Compile Include="JSON\JSONParser.cs" />
60 <Compile Include="JSON\JSONScanner.cs" />
61 <Compile Include="JSON\JsonTokenType.cs" />
62 <Compile Include="JSON\JSONWriter.cs" />
63 <Compile Include="JSON\JSONXmlReader.cs" />
64 <Compile Include="JSON\JSONXmlReaderOptions.cs" />
65 <Compile Include="JSON\StringTranslator.cs" />
66 <Compile Include="Parallels\DispatchPool.cs" />
94 <Compile Include="Parallels\DispatchPool.cs" />
67 <Compile Include="Parallels\ArrayTraits.cs" />
95 <Compile Include="Parallels\ArrayTraits.cs" />
68 <Compile Include="Parallels\MTQueue.cs" />
96 <Compile Include="Parallels\MTQueue.cs" />
69 <Compile Include="Parallels\WorkerPool.cs" />
97 <Compile Include="Parallels\WorkerPool.cs" />
70 <Compile Include="Parsing\Alphabet.cs" />
71 <Compile Include="Parsing\AlphabetBase.cs" />
72 <Compile Include="Parsing\AltToken.cs" />
73 <Compile Include="Parsing\BinaryToken.cs" />
74 <Compile Include="Parsing\CatToken.cs" />
75 <Compile Include="Parsing\CDFADefinition.cs" />
76 <Compile Include="Parsing\DFABuilder.cs" />
77 <Compile Include="Parsing\DFADefinitionBase.cs" />
78 <Compile Include="Parsing\DFAStateDescriptor.cs" />
79 <Compile Include="Parsing\DFAutomaton.cs" />
80 <Compile Include="Parsing\EDFADefinition.cs" />
81 <Compile Include="Parsing\EmptyToken.cs" />
82 <Compile Include="Parsing\EndToken.cs" />
83 <Compile Include="Parsing\EnumAlphabet.cs" />
84 <Compile Include="Parsing\Grammar.cs" />
85 <Compile Include="Parsing\IAlphabet.cs" />
86 <Compile Include="Parsing\IDFADefinition.cs" />
87 <Compile Include="Parsing\IVisitor.cs" />
88 <Compile Include="Parsing\ParserException.cs" />
89 <Compile Include="Parsing\Scanner.cs" />
90 <Compile Include="Parsing\StarToken.cs" />
91 <Compile Include="Parsing\SymbolToken.cs" />
92 <Compile Include="Parsing\Token.cs" />
93 <Compile Include="SafePool.cs" />
94 <Compile Include="ServiceLocator.cs" />
95 <Compile Include="TaskController.cs" />
96 <Compile Include="ProgressInitEventArgs.cs" />
98 <Compile Include="ProgressInitEventArgs.cs" />
97 <Compile Include="Properties\AssemblyInfo.cs" />
99 <Compile Include="Properties\AssemblyInfo.cs" />
98 <Compile Include="Promise.cs" />
99 <Compile Include="Parallels\AsyncPool.cs" />
100 <Compile Include="Parallels\AsyncPool.cs" />
100 <Compile Include="Safe.cs" />
101 <Compile Include="Safe.cs" />
101 <Compile Include="ValueEventArgs.cs" />
102 <Compile Include="ValueEventArgs.cs" />
103 <Compile Include="PromiseExtensions.cs" />
104 <Compile Include="SyncContextPromise.cs" />
105 <Compile Include="Diagnostics\OperationContext.cs" />
106 <Compile Include="Diagnostics\TraceContext.cs" />
107 <Compile Include="Diagnostics\LogEventArgs.cs" />
108 <Compile Include="Diagnostics\LogEventArgsT.cs" />
109 <Compile Include="Diagnostics\Extensions.cs" />
110 <Compile Include="PromiseEventType.cs" />
111 <Compile Include="Parallels\AsyncQueue.cs" />
112 <Compile Include="PromiseT.cs" />
113 <Compile Include="IDeferred.cs" />
114 <Compile Include="IDeferredT.cs" />
115 <Compile Include="Promise.cs" />
116 <Compile Include="PromiseTransientException.cs" />
117 <Compile Include="Parallels\Signal.cs" />
118 <Compile Include="Parallels\SharedLock.cs" />
119 <Compile Include="Diagnostics\ILogWriter.cs" />
120 <Compile Include="Diagnostics\ListenerBase.cs" />
121 <Compile Include="Parallels\BlockingQueue.cs" />
122 <Compile Include="AbstractEvent.cs" />
123 <Compile Include="AbstractPromise.cs" />
124 <Compile Include="AbstractPromiseT.cs" />
125 <Compile Include="FuncTask.cs" />
126 <Compile Include="FuncTaskBase.cs" />
127 <Compile Include="FuncTaskT.cs" />
128 <Compile Include="ActionChainTaskBase.cs" />
129 <Compile Include="ActionChainTask.cs" />
130 <Compile Include="ActionChainTaskT.cs" />
131 <Compile Include="FuncChainTaskBase.cs" />
132 <Compile Include="FuncChainTask.cs" />
133 <Compile Include="FuncChainTaskT.cs" />
134 <Compile Include="ActionTaskBase.cs" />
135 <Compile Include="ActionTask.cs" />
136 <Compile Include="ActionTaskT.cs" />
137 <Compile Include="ICancellationToken.cs" />
138 <Compile Include="SuccessPromise.cs" />
139 <Compile Include="SuccessPromiseT.cs" />
140 <Compile Include="PromiseAwaiterT.cs" />
141 <Compile Include="PromiseAwaiter.cs" />
142 <Compile Include="Components\ComponentContainer.cs" />
143 <Compile Include="Components\Disposable.cs" />
144 <Compile Include="Components\DisposablePool.cs" />
145 <Compile Include="Components\ObjectPool.cs" />
146 <Compile Include="Components\ServiceLocator.cs" />
147 <Compile Include="Components\IInitializable.cs" />
148 <Compile Include="TaskController.cs" />
149 <Compile Include="Components\App.cs" />
150 <Compile Include="Components\IRunnable.cs" />
151 <Compile Include="Components\ExecutionState.cs" />
152 <Compile Include="Components\RunnableComponent.cs" />
153 <Compile Include="Components\IFactory.cs" />
154 <Compile Include="Automaton\IAlphabet.cs" />
155 <Compile Include="Automaton\ParserException.cs" />
156 <Compile Include="Automaton\IndexedAlphabetBase.cs" />
157 <Compile Include="Automaton\IAlphabetBuilder.cs" />
158 <Compile Include="Automaton\RegularExpressions\AltToken.cs" />
159 <Compile Include="Automaton\RegularExpressions\BinaryToken.cs" />
160 <Compile Include="Automaton\RegularExpressions\CatToken.cs" />
161 <Compile Include="Automaton\RegularExpressions\StarToken.cs" />
162 <Compile Include="Automaton\RegularExpressions\SymbolToken.cs" />
163 <Compile Include="Automaton\RegularExpressions\EmptyToken.cs" />
164 <Compile Include="Automaton\RegularExpressions\Token.cs" />
165 <Compile Include="Automaton\RegularExpressions\IVisitor.cs" />
166 <Compile Include="Automaton\AutomatonTransition.cs" />
167 <Compile Include="Formats\JSON\JSONElementContext.cs" />
168 <Compile Include="Formats\JSON\JSONElementType.cs" />
169 <Compile Include="Formats\JSON\JSONGrammar.cs" />
170 <Compile Include="Formats\JSON\JSONParser.cs" />
171 <Compile Include="Formats\JSON\JSONScanner.cs" />
172 <Compile Include="Formats\JSON\JsonTokenType.cs" />
173 <Compile Include="Formats\JSON\JSONWriter.cs" />
174 <Compile Include="Formats\JSON\JSONXmlReader.cs" />
175 <Compile Include="Formats\JSON\JSONXmlReaderOptions.cs" />
176 <Compile Include="Formats\JSON\StringTranslator.cs" />
177 <Compile Include="Automaton\MapAlphabet.cs" />
178 <Compile Include="Formats\CharAlphabet.cs" />
179 <Compile Include="Formats\ByteAlphabet.cs" />
180 <Compile Include="Automaton\IDFATable.cs" />
181 <Compile Include="Automaton\IDFATableBuilder.cs" />
182 <Compile Include="Automaton\DFATable.cs" />
183 <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitor.cs" />
184 <Compile Include="Automaton\RegularExpressions\ITaggedDFABuilder.cs" />
185 <Compile Include="Formats\TextScanner.cs" />
186 <Compile Include="Formats\StringScanner.cs" />
187 <Compile Include="Formats\ReaderScanner.cs" />
188 <Compile Include="Formats\ScannerContext.cs" />
189 <Compile Include="Formats\Grammar.cs" />
190 <Compile Include="Automaton\RegularExpressions\EndTokenT.cs" />
191 <Compile Include="Automaton\RegularExpressions\EndToken.cs" />
192 <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitorT.cs" />
193 <Compile Include="Automaton\AutomatonConst.cs" />
194 <Compile Include="Automaton\RegularExpressions\RegularDFA.cs" />
195 <Compile Include="Components\LazyAndWeak.cs" />
196 <Compile Include="AbstractTask.cs" />
197 <Compile Include="AbstractTaskT.cs" />
102 </ItemGroup>
198 </ItemGroup>
103 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
199 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
104 <ItemGroup />
200 <ItemGroup />
201 <ProjectExtensions>
202 <MonoDevelop>
203 <Properties>
204 <Policies>
205 <CSharpFormattingPolicy IndentSwitchBody="True" NamespaceBraceStyle="EndOfLine" ClassBraceStyle="EndOfLine" InterfaceBraceStyle="EndOfLine" StructBraceStyle="EndOfLine" EnumBraceStyle="EndOfLine" MethodBraceStyle="EndOfLine" ConstructorBraceStyle="EndOfLine" DestructorBraceStyle="EndOfLine" BeforeMethodDeclarationParentheses="False" BeforeMethodCallParentheses="False" BeforeConstructorDeclarationParentheses="False" NewLineBeforeConstructorInitializerColon="NewLine" NewLineAfterConstructorInitializerColon="SameLine" BeforeIndexerDeclarationBracket="False" BeforeDelegateDeclarationParentheses="False" NewParentheses="False" SpacesBeforeBrackets="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
206 <TextStylePolicy FileWidth="120" EolMarker="Unix" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
207 <DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" />
208 <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="application/xml" />
209 <XmlFormattingPolicy inheritsSet="Mono" inheritsScope="application/xml" scope="application/xml" />
210 <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/plain" />
211 <NameConventionPolicy>
212 <Rules>
213 <NamingRule Name="Namespaces" AffectedEntity="Namespace" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
214 <NamingRule Name="Types" AffectedEntity="Class, Struct, Enum, Delegate" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
215 <NamingRule Name="Interfaces" AffectedEntity="Interface" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
216 <RequiredPrefixes>
217 <String>I</String>
218 </RequiredPrefixes>
219 </NamingRule>
220 <NamingRule Name="Attributes" AffectedEntity="CustomAttributes" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
221 <RequiredSuffixes>
222 <String>Attribute</String>
223 </RequiredSuffixes>
224 </NamingRule>
225 <NamingRule Name="Event Arguments" AffectedEntity="CustomEventArgs" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
226 <RequiredSuffixes>
227 <String>EventArgs</String>
228 </RequiredSuffixes>
229 </NamingRule>
230 <NamingRule Name="Exceptions" AffectedEntity="CustomExceptions" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
231 <RequiredSuffixes>
232 <String>Exception</String>
233 </RequiredSuffixes>
234 </NamingRule>
235 <NamingRule Name="Methods" AffectedEntity="Methods" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
236 <NamingRule Name="Static Readonly Fields" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Protected, Public" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True" />
237 <NamingRule Name="Fields (Non Private)" AffectedEntity="Field" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
238 <NamingRule Name="ReadOnly Fields (Non Private)" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False" />
239 <NamingRule Name="Fields (Private)" AffectedEntity="Field, ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
240 <RequiredPrefixes>
241 <String>m_</String>
242 </RequiredPrefixes>
243 </NamingRule>
244 <NamingRule Name="Static Fields (Private)" AffectedEntity="Field" VisibilityMask="Private" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True">
245 <RequiredPrefixes>
246 <String>_</String>
247 </RequiredPrefixes>
248 </NamingRule>
249 <NamingRule Name="ReadOnly Fields (Private)" AffectedEntity="ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
250 <RequiredPrefixes>
251 <String>m_</String>
252 </RequiredPrefixes>
253 </NamingRule>
254 <NamingRule Name="Constant Fields" AffectedEntity="ConstantField" VisibilityMask="VisibilityMask" NamingStyle="AllUpper" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
255 <NamingRule Name="Properties" AffectedEntity="Property" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
256 <NamingRule Name="Events" AffectedEntity="Event" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
257 <NamingRule Name="Enum Members" AffectedEntity="EnumMember" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
258 <NamingRule Name="Parameters" AffectedEntity="Parameter, LocalVariable" VisibilityMask="VisibilityMask" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
259 <NamingRule Name="Type Parameters" AffectedEntity="TypeParameter" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
260 <RequiredPrefixes>
261 <String>T</String>
262 </RequiredPrefixes>
263 </NamingRule>
264 </Rules>
265 </NameConventionPolicy>
266 </Policies>
267 </Properties>
268 </MonoDevelop>
269 </ProjectExtensions>
270 <ItemGroup>
271 <Folder Include="Components\" />
272 <Folder Include="Automaton\RegularExpressions\" />
273 <Folder Include="Formats\" />
274 <Folder Include="Formats\JSON\" />
275 </ItemGroup>
105 </Project> No newline at end of file
276 </Project>
@@ -1,9 +1,6
1 using Implab.Diagnostics;
1 using Implab.Diagnostics;
2 using System;
2 using System;
3 using System.Collections.Generic;
4 using System.Diagnostics;
3 using System.Diagnostics;
5 using System.Linq;
6 using System.Text;
7 using System.Threading;
4 using System.Threading;
8
5
9 namespace Implab.Parallels {
6 namespace Implab.Parallels {
@@ -12,7 +9,7 namespace Implab.Parallels {
12 readonly Action<TSrc> m_action;
9 readonly Action<TSrc> m_action;
13 readonly TSrc[] m_source;
10 readonly TSrc[] m_source;
14 readonly Promise<int> m_promise = new Promise<int>();
11 readonly Promise<int> m_promise = new Promise<int>();
15 readonly TraceContext m_traceContext;
12 readonly LogicalOperation m_logicalOperation;
16
13
17 int m_pending;
14 int m_pending;
18 int m_next;
15 int m_next;
@@ -23,14 +20,13 namespace Implab.Parallels {
23 Debug.Assert(source != null);
20 Debug.Assert(source != null);
24 Debug.Assert(action != null);
21 Debug.Assert(action != null);
25
22
26 m_traceContext = TraceContext.Snapshot();
23 m_logicalOperation = TraceContext.Instance.CurrentOperation;
27 m_next = 0;
24 m_next = 0;
28 m_source = source;
25 m_source = source;
29 m_pending = source.Length;
26 m_pending = source.Length;
30 m_action = action;
27 m_action = action;
31
28
32 m_promise.Anyway(() => Dispose());
29 m_promise.On(Dispose, PromiseEventType.All);
33 m_promise.Cancelled(() => Dispose());
34
30
35 InitPool();
31 InitPool();
36 }
32 }
@@ -42,13 +38,17 namespace Implab.Parallels {
42 }
38 }
43
39
44 protected override void Worker() {
40 protected override void Worker() {
45 TraceContext.Fork(m_traceContext);
41 TraceContext.Instance.EnterLogicalOperation(m_logicalOperation, false);
46 base.Worker();
42 try {
43 base.Worker();
44 } finally {
45 TraceContext.Instance.Leave();
46 }
47 }
47 }
48
48
49 protected override bool TryDequeue(out int unit) {
49 protected override bool TryDequeue(out int unit) {
50 unit = Interlocked.Increment(ref m_next) - 1;
50 unit = Interlocked.Increment(ref m_next) - 1;
51 return unit >= m_source.Length ? false : true;
51 return unit < m_source.Length;
52 }
52 }
53
53
54 protected override void InvokeUnit(int unit) {
54 protected override void InvokeUnit(int unit) {
@@ -68,7 +68,7 namespace Implab.Parallels {
68 readonly TSrc[] m_source;
68 readonly TSrc[] m_source;
69 readonly TDst[] m_dest;
69 readonly TDst[] m_dest;
70 readonly Promise<TDst[]> m_promise = new Promise<TDst[]>();
70 readonly Promise<TDst[]> m_promise = new Promise<TDst[]>();
71 readonly TraceContext m_traceContext;
71 readonly LogicalOperation m_logicalOperation;
72
72
73 int m_pending;
73 int m_pending;
74 int m_next;
74 int m_next;
@@ -84,10 +84,9 namespace Implab.Parallels {
84 m_dest = new TDst[source.Length];
84 m_dest = new TDst[source.Length];
85 m_pending = source.Length;
85 m_pending = source.Length;
86 m_transform = transform;
86 m_transform = transform;
87 m_traceContext = TraceContext.Snapshot();
87 m_logicalOperation = TraceContext.Instance.CurrentOperation;
88
88
89 m_promise.Anyway(() => Dispose());
89 m_promise.On(Dispose, PromiseEventType.All);
90 m_promise.Cancelled(() => Dispose());
91
90
92 InitPool();
91 InitPool();
93 }
92 }
@@ -99,13 +98,17 namespace Implab.Parallels {
99 }
98 }
100
99
101 protected override void Worker() {
100 protected override void Worker() {
102 TraceContext.Fork(m_traceContext);
101 TraceContext.Instance.EnterLogicalOperation(m_logicalOperation,false);
103 base.Worker();
102 try {
103 base.Worker();
104 } finally {
105 TraceContext.Instance.Leave();
106 }
104 }
107 }
105
108
106 protected override bool TryDequeue(out int unit) {
109 protected override bool TryDequeue(out int unit) {
107 unit = Interlocked.Increment(ref m_next) - 1;
110 unit = Interlocked.Increment(ref m_next) - 1;
108 return unit >= m_source.Length ? false : true;
111 return unit < m_source.Length;
109 }
112 }
110
113
111 protected override void InvokeUnit(int unit) {
114 protected override void InvokeUnit(int unit) {
@@ -140,42 +143,55 namespace Implab.Parallels {
140 return iter.Promise;
143 return iter.Promise;
141 }
144 }
142
145
143 public static IPromise<TDst[]> ChainedMap<TSrc, TDst>(this TSrc[] source, ChainedOperation<TSrc, TDst> transform, int threads) {
146 public static IPromise<TDst[]> ChainedMap<TSrc, TDst>(this TSrc[] source, Func<TSrc, IPromise<TDst>> transform, int threads) {
144 if (source == null)
147 if (source == null)
145 throw new ArgumentNullException("source");
148 throw new ArgumentNullException("source");
146 if (transform == null)
149 if (transform == null)
147 throw new ArgumentNullException("transform");
150 throw new ArgumentNullException("transform");
148 if (threads <= 0)
151 if (threads <= 0)
149 throw new ArgumentOutOfRangeException("Threads number must be greater then zero");
152 throw new ArgumentOutOfRangeException("threads","Threads number must be greater then zero");
150
153
151 if (source.Length == 0)
154 if (source.Length == 0)
152 return Promise<TDst[]>.ResultToPromise(new TDst[0]);
155 return Promise<TDst[]>.FromResult(new TDst[0]);
153
156
154 var promise = new Promise<TDst[]>();
157 var promise = new Promise<TDst[]>();
155 var res = new TDst[source.Length];
158 var res = new TDst[source.Length];
156 var pending = source.Length;
159 var pending = source.Length;
157
160
158 var semaphore = new Semaphore(threads, threads);
161 object locker = new object();
162 int slots = threads;
159
163
160 AsyncPool.InvokeNewThread(() => {
164 // Analysis disable AccessToDisposedClosure
165 AsyncPool.RunThread<int>(() => {
161 for (int i = 0; i < source.Length; i++) {
166 for (int i = 0; i < source.Length; i++) {
162 if(promise.IsResolved)
167 if(promise.IsResolved)
163 break; // stop processing in case of error or cancellation
168 break; // stop processing in case of error or cancellation
164 var idx = i;
169 var idx = i;
165 semaphore.WaitOne();
170
171 if (Interlocked.Decrement(ref slots) < 0) {
172 lock(locker) {
173 while(slots < 0)
174 Monitor.Wait(locker);
175 }
176 }
177
166 try {
178 try {
167 var p1 = transform(source[i]);
179 transform(source[i])
168 p1.Anyway(() => semaphore.Release());
180 .On( x => {
169 p1.Cancelled(() => semaphore.Release());
181 Interlocked.Increment(ref slots);
170 p1.Then(
182 lock (locker) {
171 x => {
183 Monitor.Pulse(locker);
172 res[idx] = x;
184 }
173 var left = Interlocked.Decrement(ref pending);
185 })
174 if (left == 0)
186 .On(
175 promise.Resolve(res);
187 x => {
176 },
188 res[idx] = x;
177 e => promise.Reject(e)
189 var left = Interlocked.Decrement(ref pending);
178 );
190 if (left == 0)
191 promise.Resolve(res);
192 },
193 promise.Reject
194 );
179
195
180 } catch (Exception e) {
196 } catch (Exception e) {
181 promise.Reject(e);
197 promise.Reject(e);
@@ -184,7 +200,7 namespace Implab.Parallels {
184 return 0;
200 return 0;
185 });
201 });
186
202
187 return promise.Anyway(() => semaphore.Dispose());
203 return promise;
188 }
204 }
189
205
190 }
206 }
@@ -1,6 +1,7
1 using Implab.Diagnostics;
1 using Implab.Diagnostics;
2 using System;
2 using System;
3 using System.Threading;
3 using System.Threading;
4 using System.Linq;
4
5
5 namespace Implab.Parallels {
6 namespace Implab.Parallels {
6 /// <summary>
7 /// <summary>
@@ -14,31 +15,74 namespace Implab.Parallels {
14
15
15 public static IPromise<T> Invoke<T>(Func<T> func) {
16 public static IPromise<T> Invoke<T>(Func<T> func) {
16 var p = new Promise<T>();
17 var p = new Promise<T>();
17 var caller = TraceContext.Snapshot();
18 var caller = TraceContext.Instance.CurrentOperation;
18
19
19 ThreadPool.QueueUserWorkItem(param => {
20 ThreadPool.QueueUserWorkItem(param => {
20 TraceContext.Fork(caller);
21 TraceContext.Instance.EnterLogicalOperation(caller,false);
21 try {
22 try {
22 p.Resolve(func());
23 p.Resolve(func());
23 } catch(Exception e) {
24 } catch(Exception e) {
24 p.Reject(e);
25 p.Reject(e);
25 }
26 } finally {
27 TraceContext.Instance.Leave();
28 }
26 });
29 });
27
30
28 return p;
31 return p;
29 }
32 }
30
33
31 public static IPromise<T> InvokeNewThread<T>(Func<T> func) {
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();
46 }
47 });
48
49 return p;
50 }
51
52 public static IPromise<T> RunThread<T>(Func<T> func) {
32 var p = new Promise<T>();
53 var p = new Promise<T>();
33
54
34 var caller = TraceContext.Snapshot();
55 var caller = TraceContext.Instance.CurrentOperation;
35
56
36 var worker = new Thread(() => {
57 var worker = new Thread(() => {
37 TraceContext.Fork(caller);
58 TraceContext.Instance.EnterLogicalOperation(caller,false);
38 try {
59 try {
39 p.Resolve(func());
60 p.Resolve(func());
40 } catch (Exception e) {
61 } catch (Exception e) {
41 p.Reject(e);
62 p.Reject(e);
63 } finally {
64 TraceContext.Instance.Leave();
65 }
66 });
67 worker.IsBackground = true;
68 worker.Start();
69
70 return p;
71 }
72
73 public static IPromise<T> RunThread<T>(Func<ICancellationToken, T> func) {
74 var p = new Promise<T>();
75
76 var caller = TraceContext.Instance.CurrentOperation;
77
78 var worker = new Thread(() => {
79 TraceContext.Instance.EnterLogicalOperation(caller,false);
80 try {
81 p.Resolve(func(p));
82 } catch (Exception e) {
83 p.Reject(e);
84 } finally {
85 TraceContext.Instance.Leave();
42 }
86 }
43 });
87 });
44 worker.IsBackground = true;
88 worker.IsBackground = true;
@@ -48,18 +92,20 namespace Implab.Parallels {
48 }
92 }
49
93
50
94
51 public static IPromise InvokeNewThread(Action func) {
95 public static IPromise RunThread(Action func) {
52 var p = new Promise<object>();
96 var p = new Promise();
53
97
54 var caller = TraceContext.Snapshot();
98 var caller = TraceContext.Instance.CurrentOperation;
55
99
56 var worker = new Thread(() => {
100 var worker = new Thread(() => {
57 TraceContext.Fork(caller);
101 TraceContext.Instance.EnterLogicalOperation(caller,false);
58 try {
102 try {
59 func();
103 func();
60 p.Resolve();
104 p.Resolve();
61 } catch (Exception e) {
105 } catch (Exception e) {
62 p.Reject(e);
106 p.Reject(e);
107 } finally {
108 TraceContext.Instance.Leave();
63 }
109 }
64 });
110 });
65 worker.IsBackground = true;
111 worker.IsBackground = true;
@@ -67,5 +113,43 namespace Implab.Parallels {
67
113
68 return p;
114 return p;
69 }
115 }
116
117 public static IPromise RunThread(Action<ICancellationToken> func) {
118 var p = new Promise();
119
120 var caller = TraceContext.Instance.CurrentOperation;
121
122 var worker = new Thread(() => {
123 TraceContext.Instance.EnterLogicalOperation(caller,false);
124 try {
125 func(p);
126 p.Resolve();
127 } catch (Exception e) {
128 p.Reject(e);
129 } finally {
130 TraceContext.Instance.Leave();
131 }
132 });
133 worker.IsBackground = true;
134 worker.Start();
135
136 return p;
137 }
138
139 public static IPromise[] RunThread(params Action[] func) {
140 return func.Select(f => RunThread(f)).ToArray();
141 }
142
143 public static IPromise[] RunThread(params Action<ICancellationToken>[] func) {
144 return func.Select(f => RunThread(f)).ToArray();
145 }
146
147 public static IPromise<T>[] RunThread<T>(params Func<T>[] func) {
148 return func.Select(f => RunThread(f)).ToArray();
149 }
150
151 public static IPromise<T>[] RunThread<T>(params Func<ICancellationToken, T>[] func) {
152 return func.Select(f => RunThread(f)).ToArray();
153 }
70 }
154 }
71 }
155 }
@@ -1,24 +1,17
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading;
2 using System.Threading;
6 using System.Diagnostics;
7
3
8 namespace Implab.Parallels {
4 namespace Implab.Parallels {
9 public abstract class DispatchPool<TUnit> : IDisposable {
5 public abstract class DispatchPool<TUnit> : IDisposable {
10 readonly int m_minThreads;
6 readonly int m_minThreadsLimit;
11 readonly int m_maxThreads;
7 readonly int m_maxThreadsLimit;
8 readonly int m_releaseTimeout = 1000; // the timeout while the working thread will wait for the new tasks before exit
12
9
13 int m_createdThreads = 0; // the current size of the pool
10 int m_threads; // the current size of the pool
14 int m_activeThreads = 0; // the count of threads which are active
11 int m_maxRunningThreads; // the meximum reached size of the pool
15 int m_sleepingThreads = 0; // the count of currently inactive threads
12 int m_exit; // the pool is going to shutdown, all unused workers are released
16 int m_maxRunningThreads = 0; // the meximum reached size of the pool
13
17 int m_exitRequired = 0; // the pool is going to shutdown, all unused workers are released
14 readonly object m_signal = new object(); // used to pulse waiting threads
18 int m_releaseTimeout = 100; // the timeout while the working thread will wait for the new tasks before exit
19 int m_wakeEvents = 0; // the count of wake events
20
21 AutoResetEvent m_hasTasks = new AutoResetEvent(false);
22
15
23 protected DispatchPool(int min, int max) {
16 protected DispatchPool(int min, int max) {
24 if (min < 0)
17 if (min < 0)
@@ -28,8 +21,8 namespace Implab.Parallels {
28
21
29 if (min > max)
22 if (min > max)
30 min = max;
23 min = max;
31 m_minThreads = min;
24 m_minThreadsLimit = min;
32 m_maxThreads = max;
25 m_maxThreadsLimit = max;
33 }
26 }
34
27
35 protected DispatchPool(int threads)
28 protected DispatchPool(int threads)
@@ -37,177 +30,74 namespace Implab.Parallels {
37 }
30 }
38
31
39 protected DispatchPool() {
32 protected DispatchPool() {
40 int maxThreads, maxCP;
41 ThreadPool.GetMaxThreads(out maxThreads, out maxCP);
42
33
43 m_minThreads = 0;
34 m_minThreadsLimit = 0;
44 m_maxThreads = maxThreads;
35 m_maxThreadsLimit = Environment.ProcessorCount;
45 }
36 }
46
37
47 protected void InitPool() {
38 protected void InitPool() {
48 for (int i = 0; i < m_minThreads; i++)
39 for (int i = 0; i < m_minThreadsLimit; i++)
49 StartWorker();
40 StartWorker();
50 }
41 }
51
42
52 public int PoolSize {
43 public int PoolSize {
53 get {
44 get {
54 return m_createdThreads;
45 Thread.MemoryBarrier();
46 return m_threads;
55 }
47 }
56 }
48 }
57
49
58 public int ActiveThreads {
59 get {
60 return m_activeThreads;
61 }
62 }
63
64 public int MaxRunningThreads {
50 public int MaxRunningThreads {
65 get {
51 get {
52 Thread.MemoryBarrier();
66 return m_maxRunningThreads;
53 return m_maxRunningThreads;
67 }
54 }
68 }
55 }
69
56
70 protected bool IsDisposed {
57 protected bool IsDisposed {
71 get {
58 get {
72 return m_exitRequired != 0;
59 Thread.MemoryBarrier();
60 return m_exit == 1;
73 }
61 }
74 }
62 }
75
63
76 protected abstract bool TryDequeue(out TUnit unit);
64 protected abstract bool TryDequeue(out TUnit unit);
77
65
78 #region thread execution traits
66 bool Dequeue(out TUnit unit, int timeout) {
79 int SignalThread() {
67 int ts = Environment.TickCount;
80 var signals = Interlocked.Increment(ref m_wakeEvents);
68 if (TryDequeue(out unit))
81 if(signals == 1)
69 return true;
82 m_hasTasks.Set();
70 lock (m_signal) {
83 return signals;
71 while (!TryDequeue(out unit) && m_exit == 0)
84 }
72 if(!Monitor.Wait(m_signal, Math.Max(0, ts + timeout - Environment.TickCount))) {
85
73 // timeout
86 bool FetchSignalOrWait(int timeout) {
74 return false;
87 var start = Environment.TickCount;
75 }
88
76 // queue item or terminate
89 // означает, что поток владеет блокировкой и при успешном получении сигнала должен
77 Monitor.Pulse(m_signal);
90 // ее вернуть, чтобы другой ожидающий поток смог
78 if (m_exit == 1)
91 bool hasLock = false;
79 return false;
92 do {
80 }
93 int signals;
81 return true;
94 do {
95 signals = m_wakeEvents;
96 if (signals == 0)
97 break;
98 } while (Interlocked.CompareExchange(ref m_wakeEvents, signals - 1, signals) != signals);
99
100 if (signals >= 1) {
101 if (signals > 1 && hasLock)
102 m_hasTasks.Set();
103 return true;
104 }
105
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 }
82 }
117
83
118 bool Sleep(int timeout) {
84 protected void SignalThread() {
119 Interlocked.Increment(ref m_sleepingThreads);
85 lock (m_signal) {
120 if (FetchSignalOrWait(timeout)) {
86 Monitor.Pulse(m_signal);
121 Interlocked.Decrement(ref m_sleepingThreads);
122 return true;
123 } else {
124 Interlocked.Decrement(ref m_sleepingThreads);
125 return false;
126 }
127 }
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 }
87 }
156 }
88 }
157
89
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 }
199
200 #region thread slots traits
90 #region thread slots traits
201
91
202 bool AllocateThreadSlot() {
92 bool AllocateThreadSlot() {
203 int current;
93 int current;
204 // use spins to allocate slot for the new thread
94 // use spins to allocate slot for the new thread
205 do {
95 do {
206 current = m_createdThreads;
96 current = m_threads;
207 if (current >= m_maxThreads || m_exitRequired != 0)
97 if (current >= m_maxThreadsLimit || m_exit == 1)
208 // no more slots left or the pool has been disposed
98 // no more slots left or the pool has been disposed
209 return false;
99 return false;
210 } while (current != Interlocked.CompareExchange(ref m_createdThreads, current + 1, current));
100 } while (current != Interlocked.CompareExchange(ref m_threads, current + 1, current));
211
101
212 UpdateMaxThreads(current + 1);
102 UpdateMaxThreads(current + 1);
213
103
@@ -215,7 +105,7 namespace Implab.Parallels {
215 }
105 }
216
106
217 bool AllocateThreadSlot(int desired) {
107 bool AllocateThreadSlot(int desired) {
218 if (desired - 1 != Interlocked.CompareExchange(ref m_createdThreads, desired, desired - 1))
108 if (desired - 1 != Interlocked.CompareExchange(ref m_threads, desired, desired - 1))
219 return false;
109 return false;
220
110
221 UpdateMaxThreads(desired);
111 UpdateMaxThreads(desired);
@@ -227,27 +117,19 namespace Implab.Parallels {
227 last = false;
117 last = false;
228 int current;
118 int current;
229 // use spins to release slot for the new thread
119 // use spins to release slot for the new thread
120 Thread.MemoryBarrier();
230 do {
121 do {
231 current = m_createdThreads;
122 current = m_threads;
232 if (current <= m_minThreads && m_exitRequired == 0)
123 if (current <= m_minThreadsLimit && m_exit == 0)
233 // the thread is reserved
124 // the thread is reserved
234 return false;
125 return false;
235 } while (current != Interlocked.CompareExchange(ref m_createdThreads, current - 1, current));
126 } while (current != Interlocked.CompareExchange(ref m_threads, current - 1, current));
236
127
237 last = (current == 1);
128 last = (current == 1);
238
129
239 return true;
130 return true;
240 }
131 }
241
132
242 /// <summary>
243 /// releases thread slot unconditionally, used during cleanup
244 /// </summary>
245 /// <returns>true - no more threads left</returns>
246 bool ReleaseThreadSlotAnyway() {
247 var left = Interlocked.Decrement(ref m_createdThreads);
248 return left == 0;
249 }
250
251 void UpdateMaxThreads(int count) {
133 void UpdateMaxThreads(int count) {
252 int max;
134 int max;
253 do {
135 do {
@@ -259,65 +141,46 namespace Implab.Parallels {
259
141
260 #endregion
142 #endregion
261
143
262 bool StartWorker() {
144 protected bool StartWorker() {
263 if (AllocateThreadSlot()) {
145 if (AllocateThreadSlot()) {
264 // slot successfully allocated
146 // slot successfully allocated
265 var worker = new Thread(this.Worker);
147 var worker = new Thread(Worker);
266 worker.IsBackground = true;
148 worker.IsBackground = true;
267 worker.Start();
149 worker.Start();
268
150
269 return true;
151 return true;
270 } else {
271 return false;
272 }
152 }
153 return false;
273 }
154 }
274
155
275 protected abstract void InvokeUnit(TUnit unit);
156 protected abstract void InvokeUnit(TUnit unit);
276
157
277 protected virtual void Worker() {
158 protected virtual void Worker() {
278 TUnit unit;
159 TUnit unit;
279 //Console.WriteLine("{0}: Active", Thread.CurrentThread.ManagedThreadId);
160 bool last;
280 Interlocked.Increment(ref m_activeThreads);
281 do {
161 do {
282 // exit if requested
162 while (Dequeue(out unit, m_releaseTimeout)) {
283 if (m_exitRequired != 0) {
163 InvokeUnit(unit);
284 // release the thread slot
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
290 break;
291 }
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 }
174 break;
175 } while(true);
176 }
292
177
293 // fetch task
294 if (TryDequeue(out unit)) {
295 InvokeUnit(unit);
296 continue;
297 }
298 Interlocked.Decrement(ref m_activeThreads);
299
300 // entering suspend state
301 // keep this thread and wait
302 if (!Suspend())
303 break;
304 //Console.WriteLine("{0}: Awake", Thread.CurrentThread.ManagedThreadId);
305 Interlocked.Increment(ref m_activeThreads);
306 } while (true);
307 //Console.WriteLine("{0}: Exited", Thread.CurrentThread.ManagedThreadId);
308 }
309
178
310 protected virtual void Dispose(bool disposing) {
179 protected virtual void Dispose(bool disposing) {
311 if (disposing) {
180 if (disposing) {
312 if (m_exitRequired == 0) {
181 if (0 == Interlocked.CompareExchange(ref m_exit, 1, 0)) { // implies memory barrier
313 if (Interlocked.CompareExchange(ref m_exitRequired, 1, 0) != 0)
314 return;
315
316 // wake sleeping threads
182 // wake sleeping threads
317 if (m_createdThreads > 0)
183 SignalThread();
318 SignalThread();
319 else
320 m_hasTasks.Dispose();
321 GC.SuppressFinalize(this);
184 GC.SuppressFinalize(this);
322 }
185 }
323 }
186 }
@@ -1,11 +1,10
1 using System;
1 using System.Threading;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System;
4 using System.Text;
4 using System.Collections;
5 using System.Threading;
6
5
7 namespace Implab.Parallels {
6 namespace Implab.Parallels {
8 public class MTQueue<T> {
7 public class MTQueue<T> : IEnumerable<T> {
9 class Node {
8 class Node {
10 public Node(T value) {
9 public Node(T value) {
11 this.value = value;
10 this.value = value;
@@ -18,9 +17,13 namespace Implab.Parallels {
18 Node m_last;
17 Node m_last;
19
18
20 public void Enqueue(T value) {
19 public void Enqueue(T value) {
20 Thread.MemoryBarrier();
21
21 var last = m_last;
22 var last = m_last;
22 var next = new Node(value);
23 var next = new Node(value);
23
24
25 // Interlocaked.CompareExchange implies Thread.MemoryBarrier();
26 // to ensure that the next node is completely constructed
24 while (last != Interlocked.CompareExchange(ref m_last, next, last))
27 while (last != Interlocked.CompareExchange(ref m_last, next, last))
25 last = m_last;
28 last = m_last;
26
29
@@ -32,9 +35,10 namespace Implab.Parallels {
32
35
33 public bool TryDequeue(out T value) {
36 public bool TryDequeue(out T value) {
34 Node first;
37 Node first;
35 Node next = null;
38 Node next;
36 value = default(T);
39 value = default(T);
37
40
41 Thread.MemoryBarrier();
38 do {
42 do {
39 first = m_first;
43 first = m_first;
40 if (first == null)
44 if (first == null)
@@ -61,15 +65,79 namespace Implab.Parallels {
61 Interlocked.CompareExchange(ref m_first, null, first);
65 Interlocked.CompareExchange(ref m_first, null, first);
62 break;
66 break;
63
67
64 } else {
65 if (first == Interlocked.CompareExchange(ref m_first, next, first))
66 // head succesfully updated
67 break;
68 }
68 }
69 if (first == Interlocked.CompareExchange(ref m_first, next, first))
70 // head succesfully updated
71 break;
69 } while (true);
72 } while (true);
70
73
71 value = first.value;
74 value = first.value;
72 return true;
75 return true;
73 }
76 }
77
78 #region IEnumerable implementation
79
80 class Enumerator : IEnumerator<T> {
81 Node m_current;
82 Node m_first;
83
84 public Enumerator(Node first) {
85 m_first = first;
86 }
87
88 #region IEnumerator implementation
89
90 public bool MoveNext() {
91 m_current = m_current == null ? m_first : m_current.next;
92 return m_current != null;
93 }
94
95 public void Reset() {
96 m_current = null;
97 }
98
99 object IEnumerator.Current {
100 get {
101 if (m_current == null)
102 throw new InvalidOperationException();
103 return m_current.value;
104 }
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
74 }
142 }
75 }
143 }
@@ -1,7 +1,4
1 using System;
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading;
2 using System.Threading;
6 using System.Diagnostics;
3 using System.Diagnostics;
7 using Implab.Diagnostics;
4 using Implab.Diagnostics;
@@ -9,8 +6,8 using Implab.Diagnostics;
9 namespace Implab.Parallels {
6 namespace Implab.Parallels {
10 public class WorkerPool : DispatchPool<Action> {
7 public class WorkerPool : DispatchPool<Action> {
11
8
12 MTQueue<Action> m_queue = new MTQueue<Action>();
9 AsyncQueue<Action> m_queue = new AsyncQueue<Action>();
13 int m_queueLength = 0;
10 int m_queueLength;
14 readonly int m_threshold = 1;
11 readonly int m_threshold = 1;
15
12
16 public WorkerPool(int minThreads, int maxThreads, int threshold)
13 public WorkerPool(int minThreads, int maxThreads, int threshold)
@@ -29,12 +26,53 namespace Implab.Parallels {
29 InitPool();
26 InitPool();
30 }
27 }
31
28
32 public WorkerPool()
29 public WorkerPool() {
33 : base() {
34 InitPool();
30 InitPool();
35 }
31 }
36
32
37 public Promise<T> Invoke<T>(Func<T> task) {
33 public IPromise<T> Invoke<T>(Func<T> task) {
34 if (task == null)
35 throw new ArgumentNullException("task");
36 if (IsDisposed)
37 throw new ObjectDisposedException(ToString());
38
39 var promise = new FuncTask<T>(task, null, null, true);
40
41 var lop = TraceContext.Instance.CurrentOperation;
42
43 EnqueueTask(delegate {
44 TraceContext.Instance.EnterLogicalOperation(lop, false);
45
46 promise.Resolve();
47
48 TraceContext.Instance.Leave();
49 });
50
51 return promise;
52 }
53
54 public IPromise Invoke(Action task) {
55 if (task == null)
56 throw new ArgumentNullException("task");
57 if (IsDisposed)
58 throw new ObjectDisposedException(ToString());
59
60 var promise = new ActionTask(task, null, null, true);
61
62 var lop = TraceContext.Instance.CurrentOperation;
63
64 EnqueueTask(delegate {
65 TraceContext.Instance.EnterLogicalOperation(lop, false);
66
67 promise.Resolve();
68
69 TraceContext.Instance.Leave();
70 });
71
72 return promise;
73 }
74
75 public IPromise<T> Invoke<T>(Func<ICancellationToken, T> task) {
38 if (task == null)
76 if (task == null)
39 throw new ArgumentNullException("task");
77 throw new ArgumentNullException("task");
40 if (IsDisposed)
78 if (IsDisposed)
@@ -42,16 +80,45 namespace Implab.Parallels {
42
80
43 var promise = new Promise<T>();
81 var promise = new Promise<T>();
44
82
45 var caller = TraceContext.Snapshot();
83 var lop = TraceContext.Instance.CurrentOperation;
84
85 EnqueueTask(delegate {
86 TraceContext.Instance.EnterLogicalOperation(lop, false);
87 try {
88 if (!promise.CancelOperationIfRequested())
89 promise.Resolve(task(promise));
90 } catch (Exception e) {
91 promise.Reject(e);
92 } finally {
93 TraceContext.Instance.Leave();
94 }
95 });
96
97 return promise;
98 }
46
99
47 EnqueueTask(delegate() {
100 public IPromise Invoke<T>(Action<ICancellationToken> task) {
48 caller.Invoke(delegate() {
101 if (task == null)
49 try {
102 throw new ArgumentNullException("task");
50 promise.Resolve(task());
103 if (IsDisposed)
51 } catch (Exception e) {
104 throw new ObjectDisposedException(ToString());
52 promise.Reject(e);
105
106 var promise = new Promise();
107
108 var lop = TraceContext.Instance.CurrentOperation;
109
110 EnqueueTask(delegate {
111 TraceContext.Instance.EnterLogicalOperation(lop, false);
112 try {
113 if (!promise.CancelOperationIfRequested()) {
114 task(promise);
115 promise.Resolve();
53 }
116 }
54 });
117 } catch (Exception e) {
118 promise.Reject(e);
119 } finally {
120 TraceContext.Instance.Leave();
121 }
55 });
122 });
56
123
57 return promise;
124 return promise;
@@ -62,8 +129,11 namespace Implab.Parallels {
62 var len = Interlocked.Increment(ref m_queueLength);
129 var len = Interlocked.Increment(ref m_queueLength);
63 m_queue.Enqueue(unit);
130 m_queue.Enqueue(unit);
64
131
65 if (len > m_threshold*ActiveThreads)
132 if (len > m_threshold * PoolSize) {
66 GrowPool();
133 StartWorker();
134 }
135
136 SignalThread();
67 }
137 }
68
138
69 protected override bool TryDequeue(out Action unit) {
139 protected override bool TryDequeue(out Action unit) {
@@ -74,22 +144,6 namespace Implab.Parallels {
74 return false;
144 return false;
75 }
145 }
76
146
77 protected override bool Suspend() {
78 // This override solves race condition
79 // WORKER CLIENT
80 // ---------------------------------------
81 // TryDeque == false
82 // Enqueue(unit), queueLen++
83 // GrowPool? == NO
84 // ActiveThreads--
85 // Suspend
86 // queueLength > 0
87 // continue
88 if (m_queueLength > 0)
89 return true;
90 return base.Suspend();
91 }
92
93 protected override void InvokeUnit(Action unit) {
147 protected override void InvokeUnit(Action unit) {
94 unit();
148 unit();
95 }
149 }
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now