Skip to content

Commit 21f56ec

Browse files
Overhaul part 2/2 (Refactored Language Client, Pipelines and unit tests!) (#248)
* Updated language client that mirrors the language server. Added input handler that uses the new pipelines for lower allocations of incomming data. Added validation tests for handler methods to ensure that all required methods exist when new handlers are added * Added global request timeout * fixed disposable when using named pipes * fixed the issue with notifications cancelling early when they shouldn't * Added forceful shutdown to the language server, and the ability to capture unhandled exceptions during input processing * Refactored options to share common code for on handlers and friends * fixed issue with parsing headers where sometimes the parsing thread would die * Moved cancellation from internal to the request to inside the scheduler * Shifted cancellation into input handler logic to allow for proper propogation of cancelled results back to the caller. Also fixed an issue where request cancellation was not working. Also request cancellation would not throw custom exceptions for anyone that was task like. * Added json specific OnNotification on and OnRequest handlers to ensure that the JToken is not sent through the serailzier (this ensure that the casing stays correct) * Added support for defining request options (currently just for serial/parallel) Fixed a bug where things would lock due to response tasks being set in the same thread.
1 parent bdec4c7 commit 21f56ec

File tree

408 files changed

+53576
-9380
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

408 files changed

+53576
-9380
lines changed

.editorconfig

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ indent_style = space
44
indent_size = 4
55
trim_trailing_whitespace = true
66
insert_final_newline = true
7+
max_line_length = 180
78

89
[*.xml]
910
indent_style = space

Directory.Build.targets

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<ItemGroup>
44
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
55
<PackageReference Include="Rocket.Surgery.MSBuild.SourceLink" Version="0.3.1" PrivateAssets="All"/>
6+
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
67
</ItemGroup>
78
<ItemGroup>
89
<PackageReference Update="GitVersion.Tool" Version="5.2.4"/>
@@ -33,12 +34,14 @@
3334
<PackageReference Update="coverlet.collector" Version="1.2.0"/>
3435
<PackageReference Update="coverlet.msbuild" Version="2.8.0"/>
3536
<PackageReference Update="System.Reactive" Version="4.4.1"/>
37+
<PackageReference Update="System.Collections.Immutable" Version="1.7.1"/>
3638
<PackageReference Update="Microsoft.Reactive.Testing" Version="4.3.2"/>
3739
<PackageReference Update="MediatR" Version="8.0.1"/>
3840
<PackageReference Update="MediatR.Extensions.Microsoft.DependencyInjection" Version="8.0.0"/>
3941
<PackageReference Update="Bogus" Version="29.0.1"/>
4042
<PackageReference Update="Snapper" Version="2.2.4"/>
4143
<PackageReference Update="Xunit.SkippableFact" Version="1.4.8" />
42-
<PackageReference Update="DynamicData" Version="6.14.18" />
44+
<PackageReference Update="System.IO.Pipelines" Version="4.7.1"/>
45+
<PackageReference Update="Nerdbank.Streams" Version="2.4.60" />
4346
</ItemGroup>
4447
</Project>

LSP.sln

+106-14
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,21 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = ".build", ".build\.build.csp
5757
EndProject
5858
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".build", ".build", "{26522B49-0743-4CBE-BA67-6D17FF65CAB9}"
5959
EndProject
60-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dap.Tests", "test\Dap.Tests\Dap.Tests.csproj", "{F3DF7917-6AD3-4EAA-A549-555032DDC4F8}"
60+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{954FB493-FA91-470B-8A78-FA32A7B05E97}"
61+
EndProject
62+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pipeline", "benchmarks\Pipeline\Pipeline.csproj", "{B781CDC6-34BB-4131-B18A-3B1294F53062}"
63+
EndProject
64+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "src\Shared\Shared.csproj", "{18FB2302-023B-4F6F-9F6D-099B47B69D9F}"
65+
EndProject
66+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dap.Tests", "test\Dap.Tests\Dap.Tests.csproj", "{6D9E5BF4-4666-476B-AC88-D108A80567F6}"
67+
EndProject
68+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dap.Client", "src\Dap.Client\Dap.Client.csproj", "{678A4DD2-A656-4DCC-AE78-F9940C82A6E6}"
69+
EndProject
70+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dap.Testing", "src\Dap.Testing\Dap.Testing.csproj", "{91919C54-3638-4A3C-963A-327D78368EE3}"
71+
EndProject
72+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testing", "src\Testing\Testing.csproj", "{A1EC39EE-AA1F-4EC9-9939-28C3532585C9}"
73+
EndProject
74+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonRpc.Testing", "src\JsonRpc.Testing\JsonRpc.Testing.csproj", "{202BA1AB-25DA-44ED-B962-FD82FCC74543}"
6175
EndProject
6276
Global
6377
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -195,18 +209,90 @@ Global
195209
{28B13787-A442-4D28-BF9A-3D65BF13AAEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
196210
{28B13787-A442-4D28-BF9A-3D65BF13AAEC}.Release|x64.ActiveCfg = Release|Any CPU
197211
{28B13787-A442-4D28-BF9A-3D65BF13AAEC}.Release|x86.ActiveCfg = Release|Any CPU
198-
{F3DF7917-6AD3-4EAA-A549-555032DDC4F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
199-
{F3DF7917-6AD3-4EAA-A549-555032DDC4F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
200-
{F3DF7917-6AD3-4EAA-A549-555032DDC4F8}.Debug|x64.ActiveCfg = Debug|Any CPU
201-
{F3DF7917-6AD3-4EAA-A549-555032DDC4F8}.Debug|x64.Build.0 = Debug|Any CPU
202-
{F3DF7917-6AD3-4EAA-A549-555032DDC4F8}.Debug|x86.ActiveCfg = Debug|Any CPU
203-
{F3DF7917-6AD3-4EAA-A549-555032DDC4F8}.Debug|x86.Build.0 = Debug|Any CPU
204-
{F3DF7917-6AD3-4EAA-A549-555032DDC4F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
205-
{F3DF7917-6AD3-4EAA-A549-555032DDC4F8}.Release|Any CPU.Build.0 = Release|Any CPU
206-
{F3DF7917-6AD3-4EAA-A549-555032DDC4F8}.Release|x64.ActiveCfg = Release|Any CPU
207-
{F3DF7917-6AD3-4EAA-A549-555032DDC4F8}.Release|x64.Build.0 = Release|Any CPU
208-
{F3DF7917-6AD3-4EAA-A549-555032DDC4F8}.Release|x86.ActiveCfg = Release|Any CPU
209-
{F3DF7917-6AD3-4EAA-A549-555032DDC4F8}.Release|x86.Build.0 = Release|Any CPU
212+
{B781CDC6-34BB-4131-B18A-3B1294F53062}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
213+
{B781CDC6-34BB-4131-B18A-3B1294F53062}.Debug|Any CPU.Build.0 = Debug|Any CPU
214+
{B781CDC6-34BB-4131-B18A-3B1294F53062}.Debug|x64.ActiveCfg = Debug|Any CPU
215+
{B781CDC6-34BB-4131-B18A-3B1294F53062}.Debug|x64.Build.0 = Debug|Any CPU
216+
{B781CDC6-34BB-4131-B18A-3B1294F53062}.Debug|x86.ActiveCfg = Debug|Any CPU
217+
{B781CDC6-34BB-4131-B18A-3B1294F53062}.Debug|x86.Build.0 = Debug|Any CPU
218+
{B781CDC6-34BB-4131-B18A-3B1294F53062}.Release|Any CPU.ActiveCfg = Release|Any CPU
219+
{B781CDC6-34BB-4131-B18A-3B1294F53062}.Release|Any CPU.Build.0 = Release|Any CPU
220+
{B781CDC6-34BB-4131-B18A-3B1294F53062}.Release|x64.ActiveCfg = Release|Any CPU
221+
{B781CDC6-34BB-4131-B18A-3B1294F53062}.Release|x64.Build.0 = Release|Any CPU
222+
{B781CDC6-34BB-4131-B18A-3B1294F53062}.Release|x86.ActiveCfg = Release|Any CPU
223+
{B781CDC6-34BB-4131-B18A-3B1294F53062}.Release|x86.Build.0 = Release|Any CPU
224+
{18FB2302-023B-4F6F-9F6D-099B47B69D9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
225+
{18FB2302-023B-4F6F-9F6D-099B47B69D9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
226+
{18FB2302-023B-4F6F-9F6D-099B47B69D9F}.Debug|x64.ActiveCfg = Debug|Any CPU
227+
{18FB2302-023B-4F6F-9F6D-099B47B69D9F}.Debug|x64.Build.0 = Debug|Any CPU
228+
{18FB2302-023B-4F6F-9F6D-099B47B69D9F}.Debug|x86.ActiveCfg = Debug|Any CPU
229+
{18FB2302-023B-4F6F-9F6D-099B47B69D9F}.Debug|x86.Build.0 = Debug|Any CPU
230+
{18FB2302-023B-4F6F-9F6D-099B47B69D9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
231+
{18FB2302-023B-4F6F-9F6D-099B47B69D9F}.Release|Any CPU.Build.0 = Release|Any CPU
232+
{18FB2302-023B-4F6F-9F6D-099B47B69D9F}.Release|x64.ActiveCfg = Release|Any CPU
233+
{18FB2302-023B-4F6F-9F6D-099B47B69D9F}.Release|x64.Build.0 = Release|Any CPU
234+
{18FB2302-023B-4F6F-9F6D-099B47B69D9F}.Release|x86.ActiveCfg = Release|Any CPU
235+
{18FB2302-023B-4F6F-9F6D-099B47B69D9F}.Release|x86.Build.0 = Release|Any CPU
236+
{6D9E5BF4-4666-476B-AC88-D108A80567F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
237+
{6D9E5BF4-4666-476B-AC88-D108A80567F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
238+
{6D9E5BF4-4666-476B-AC88-D108A80567F6}.Debug|x64.ActiveCfg = Debug|Any CPU
239+
{6D9E5BF4-4666-476B-AC88-D108A80567F6}.Debug|x64.Build.0 = Debug|Any CPU
240+
{6D9E5BF4-4666-476B-AC88-D108A80567F6}.Debug|x86.ActiveCfg = Debug|Any CPU
241+
{6D9E5BF4-4666-476B-AC88-D108A80567F6}.Debug|x86.Build.0 = Debug|Any CPU
242+
{6D9E5BF4-4666-476B-AC88-D108A80567F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
243+
{6D9E5BF4-4666-476B-AC88-D108A80567F6}.Release|Any CPU.Build.0 = Release|Any CPU
244+
{6D9E5BF4-4666-476B-AC88-D108A80567F6}.Release|x64.ActiveCfg = Release|Any CPU
245+
{6D9E5BF4-4666-476B-AC88-D108A80567F6}.Release|x64.Build.0 = Release|Any CPU
246+
{6D9E5BF4-4666-476B-AC88-D108A80567F6}.Release|x86.ActiveCfg = Release|Any CPU
247+
{6D9E5BF4-4666-476B-AC88-D108A80567F6}.Release|x86.Build.0 = Release|Any CPU
248+
{678A4DD2-A656-4DCC-AE78-F9940C82A6E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
249+
{678A4DD2-A656-4DCC-AE78-F9940C82A6E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
250+
{678A4DD2-A656-4DCC-AE78-F9940C82A6E6}.Debug|x64.ActiveCfg = Debug|Any CPU
251+
{678A4DD2-A656-4DCC-AE78-F9940C82A6E6}.Debug|x64.Build.0 = Debug|Any CPU
252+
{678A4DD2-A656-4DCC-AE78-F9940C82A6E6}.Debug|x86.ActiveCfg = Debug|Any CPU
253+
{678A4DD2-A656-4DCC-AE78-F9940C82A6E6}.Debug|x86.Build.0 = Debug|Any CPU
254+
{678A4DD2-A656-4DCC-AE78-F9940C82A6E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
255+
{678A4DD2-A656-4DCC-AE78-F9940C82A6E6}.Release|Any CPU.Build.0 = Release|Any CPU
256+
{678A4DD2-A656-4DCC-AE78-F9940C82A6E6}.Release|x64.ActiveCfg = Release|Any CPU
257+
{678A4DD2-A656-4DCC-AE78-F9940C82A6E6}.Release|x64.Build.0 = Release|Any CPU
258+
{678A4DD2-A656-4DCC-AE78-F9940C82A6E6}.Release|x86.ActiveCfg = Release|Any CPU
259+
{678A4DD2-A656-4DCC-AE78-F9940C82A6E6}.Release|x86.Build.0 = Release|Any CPU
260+
{91919C54-3638-4A3C-963A-327D78368EE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
261+
{91919C54-3638-4A3C-963A-327D78368EE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
262+
{91919C54-3638-4A3C-963A-327D78368EE3}.Debug|x64.ActiveCfg = Debug|Any CPU
263+
{91919C54-3638-4A3C-963A-327D78368EE3}.Debug|x64.Build.0 = Debug|Any CPU
264+
{91919C54-3638-4A3C-963A-327D78368EE3}.Debug|x86.ActiveCfg = Debug|Any CPU
265+
{91919C54-3638-4A3C-963A-327D78368EE3}.Debug|x86.Build.0 = Debug|Any CPU
266+
{91919C54-3638-4A3C-963A-327D78368EE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
267+
{91919C54-3638-4A3C-963A-327D78368EE3}.Release|Any CPU.Build.0 = Release|Any CPU
268+
{91919C54-3638-4A3C-963A-327D78368EE3}.Release|x64.ActiveCfg = Release|Any CPU
269+
{91919C54-3638-4A3C-963A-327D78368EE3}.Release|x64.Build.0 = Release|Any CPU
270+
{91919C54-3638-4A3C-963A-327D78368EE3}.Release|x86.ActiveCfg = Release|Any CPU
271+
{91919C54-3638-4A3C-963A-327D78368EE3}.Release|x86.Build.0 = Release|Any CPU
272+
{A1EC39EE-AA1F-4EC9-9939-28C3532585C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
273+
{A1EC39EE-AA1F-4EC9-9939-28C3532585C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
274+
{A1EC39EE-AA1F-4EC9-9939-28C3532585C9}.Debug|x64.ActiveCfg = Debug|Any CPU
275+
{A1EC39EE-AA1F-4EC9-9939-28C3532585C9}.Debug|x64.Build.0 = Debug|Any CPU
276+
{A1EC39EE-AA1F-4EC9-9939-28C3532585C9}.Debug|x86.ActiveCfg = Debug|Any CPU
277+
{A1EC39EE-AA1F-4EC9-9939-28C3532585C9}.Debug|x86.Build.0 = Debug|Any CPU
278+
{A1EC39EE-AA1F-4EC9-9939-28C3532585C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
279+
{A1EC39EE-AA1F-4EC9-9939-28C3532585C9}.Release|Any CPU.Build.0 = Release|Any CPU
280+
{A1EC39EE-AA1F-4EC9-9939-28C3532585C9}.Release|x64.ActiveCfg = Release|Any CPU
281+
{A1EC39EE-AA1F-4EC9-9939-28C3532585C9}.Release|x64.Build.0 = Release|Any CPU
282+
{A1EC39EE-AA1F-4EC9-9939-28C3532585C9}.Release|x86.ActiveCfg = Release|Any CPU
283+
{A1EC39EE-AA1F-4EC9-9939-28C3532585C9}.Release|x86.Build.0 = Release|Any CPU
284+
{202BA1AB-25DA-44ED-B962-FD82FCC74543}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
285+
{202BA1AB-25DA-44ED-B962-FD82FCC74543}.Debug|Any CPU.Build.0 = Debug|Any CPU
286+
{202BA1AB-25DA-44ED-B962-FD82FCC74543}.Debug|x64.ActiveCfg = Debug|Any CPU
287+
{202BA1AB-25DA-44ED-B962-FD82FCC74543}.Debug|x64.Build.0 = Debug|Any CPU
288+
{202BA1AB-25DA-44ED-B962-FD82FCC74543}.Debug|x86.ActiveCfg = Debug|Any CPU
289+
{202BA1AB-25DA-44ED-B962-FD82FCC74543}.Debug|x86.Build.0 = Debug|Any CPU
290+
{202BA1AB-25DA-44ED-B962-FD82FCC74543}.Release|Any CPU.ActiveCfg = Release|Any CPU
291+
{202BA1AB-25DA-44ED-B962-FD82FCC74543}.Release|Any CPU.Build.0 = Release|Any CPU
292+
{202BA1AB-25DA-44ED-B962-FD82FCC74543}.Release|x64.ActiveCfg = Release|Any CPU
293+
{202BA1AB-25DA-44ED-B962-FD82FCC74543}.Release|x64.Build.0 = Release|Any CPU
294+
{202BA1AB-25DA-44ED-B962-FD82FCC74543}.Release|x86.ActiveCfg = Release|Any CPU
295+
{202BA1AB-25DA-44ED-B962-FD82FCC74543}.Release|x86.Build.0 = Release|Any CPU
210296
EndGlobalSection
211297
GlobalSection(SolutionProperties) = preSolution
212298
HideSolutionNode = FALSE
@@ -223,7 +309,13 @@ Global
223309
{F2C9D555-118E-442B-A953-9A7B58A53F33} = {D764E024-3D3F-4112-B932-2DB722A1BACC}
224310
{E1A9123B-A236-4240-8C82-A61BD85C3BF4} = {D764E024-3D3F-4112-B932-2DB722A1BACC}
225311
{28B13787-A442-4D28-BF9A-3D65BF13AAEC} = {26522B49-0743-4CBE-BA67-6D17FF65CAB9}
226-
{F3DF7917-6AD3-4EAA-A549-555032DDC4F8} = {2F323ED5-EBF8-45E1-B9D3-C014561B3DDA}
312+
{B781CDC6-34BB-4131-B18A-3B1294F53062} = {954FB493-FA91-470B-8A78-FA32A7B05E97}
313+
{18FB2302-023B-4F6F-9F6D-099B47B69D9F} = {D764E024-3D3F-4112-B932-2DB722A1BACC}
314+
{6D9E5BF4-4666-476B-AC88-D108A80567F6} = {2F323ED5-EBF8-45E1-B9D3-C014561B3DDA}
315+
{678A4DD2-A656-4DCC-AE78-F9940C82A6E6} = {D764E024-3D3F-4112-B932-2DB722A1BACC}
316+
{91919C54-3638-4A3C-963A-327D78368EE3} = {D764E024-3D3F-4112-B932-2DB722A1BACC}
317+
{A1EC39EE-AA1F-4EC9-9939-28C3532585C9} = {D764E024-3D3F-4112-B932-2DB722A1BACC}
318+
{202BA1AB-25DA-44ED-B962-FD82FCC74543} = {D764E024-3D3F-4112-B932-2DB722A1BACC}
227319
EndGlobalSection
228320
GlobalSection(ExtensibilityGlobals) = postSolution
229321
SolutionGuid = {D38DD0EC-D095-4BCD-B8AF-2D788AF3B9AE}

benchmarks/Pipeline/Class1.cs

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using System.Buffers;
2+
using System.Text;
3+
using BenchmarkDotNet.Attributes;
4+
using BenchmarkDotNet.Running;
5+
using System.IO.Pipelines;
6+
using System.Linq;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
using BenchmarkDotNet.Jobs;
10+
11+
namespace Pipeline
12+
{
13+
[MemoryDiagnoser]
14+
// [MinColumn, MaxColumn, MeanColumn, MedianColumn]
15+
[SimpleJob(RuntimeMoniker.Net472)]
16+
[SimpleJob(RuntimeMoniker.NetCoreApp31)]
17+
[SimpleJob(RuntimeMoniker.NetCoreApp21)]
18+
public class ClassicVsPipelines
19+
{
20+
private const string sampleCommand =
21+
"Content-Length: 88\r\n\r\n{\"seq\":1,\"type\":\"response\",\"request_seq\":1,\"success\":true,\"command\":\"command\",\"body\":{}}";
22+
23+
private const string anotherPayload =
24+
"Content-Length: 894\r\n\r\n{\"edit\":{\"documentChanges\":[{\"textDocument\":{\"version\":1,\"uri\":\"file:///abc/123/d.cs\"},\"edits\":[{\"range\":{\"start\":{\"line\":1,\"character\":1},\"end\":{\"line\":2,\"character\":2}},\"newText\":\"new text\"},{\"range\":{\"start\":{\"line\":3,\"character\":3},\"end\":{\"line\":4,\"character\":4}},\"newText\":\"new text2\"}]},{\"textDocument\":{\"version\":1,\"uri\":\"file:///abc/123/b.cs\"},\"edits\":[{\"range\":{\"start\":{\"line\":1,\"character\":1},\"end\":{\"line\":2,\"character\":2}},\"newText\":\"new text2\"},{\"range\":{\"start\":{\"line\":3,\"character\":3},\"end\":{\"line\":4,\"character\":4}},\"newText\":\"new text3\"}]},{\"kind\":\"create\",\"uri\":\"file:///abc/123/b.cs\",\"options\":{\"overwrite\":true,\"ignoreIfExists\":true}},{\"kind\":\"rename\",\"oldUri\":\"file:///abc/123/b.cs\",\"newUri\":\"file:///abc/123/c.cs\",\"options\":{\"overwrite\":true,\"ignoreIfExists\":true}},{\"kind\":\"delete\",\"uri\":\"file:///abc/123/c.cs\",\"options\":{\"recursive\":false,\"ignoreIfNotExists\":true}}]}}";
25+
26+
27+
private ClassicHandler _classic;
28+
private PipelinesBased _pipelines;
29+
30+
public ClassicVsPipelines()
31+
{
32+
}
33+
34+
[Params(
35+
sampleCommand,
36+
anotherPayload
37+
)]
38+
public string Payload { get; set; }
39+
40+
[Params(
41+
// 10,
42+
100,
43+
1000
44+
)]
45+
public int Count { get; set; }
46+
47+
public byte[] Bytes { get; set; }
48+
49+
[GlobalSetup]
50+
public void SetupPipelines()
51+
{
52+
var bytes = Encoding.ASCII.GetBytes(Payload);
53+
Bytes = Enumerable.Range(0, Count).SelectMany(z => bytes).ToArray();
54+
}
55+
56+
[Benchmark]
57+
public Task Classic()
58+
{
59+
var pipe = new Pipe();
60+
pipe.Writer.Write(Bytes);
61+
pipe.Writer.Complete();
62+
_classic = new ClassicHandler(pipe.Reader.AsStream());
63+
return _classic.ProcessInputStream();
64+
}
65+
66+
[Benchmark]
67+
public Task Pipelines()
68+
{
69+
var pipe = new Pipe();
70+
pipe.Writer.Write(Bytes);
71+
pipe.Writer.Complete();
72+
_pipelines = new PipelinesBased(pipe.Reader);
73+
return _pipelines.ProcessInputStream(CancellationToken.None);
74+
}
75+
}
76+
77+
public class Program
78+
{
79+
public static void Main(string[] args)
80+
{
81+
var summary = BenchmarkRunner.Run<ClassicVsPipelines>();
82+
}
83+
}
84+
}

benchmarks/Pipeline/ClassicHandler.cs

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using System;
2+
using System.IO;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
6+
namespace Pipeline
7+
{
8+
public class ClassicHandler
9+
{
10+
private readonly Stream _input;
11+
public const char CR = '\r';
12+
public const char LF = '\n';
13+
public static char[] CRLF = {CR, LF};
14+
public static char[] HeaderKeys = {CR, LF, ':'};
15+
public const short MinBuffer = 21; // Minimum size of the buffer "Content-Length: X\r\n\r\n"
16+
17+
public ClassicHandler(Stream input)
18+
{
19+
_input = input;
20+
}
21+
22+
// don't be async: We already allocated a seperate thread for this.
23+
public Task ProcessInputStream()
24+
{
25+
// some time to attach a debugger
26+
// System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
27+
28+
// header is encoded in ASCII
29+
// "Content-Length: 0" counts bytes for the following content
30+
// content is encoded in UTF-8
31+
while (_input.CanRead)
32+
{
33+
var buffer = new byte[300];
34+
var current = _input.Read(buffer, 0, MinBuffer);
35+
if (current == 0) return Task.CompletedTask; // no more _input
36+
while (current < MinBuffer ||
37+
buffer[current - 4] != CR || buffer[current - 3] != LF ||
38+
buffer[current - 2] != CR || buffer[current - 1] != LF)
39+
{
40+
var n = _input.Read(buffer, current, 1);
41+
if (n == 0) return Task.CompletedTask; // no more _input, mitigates endless loop here.
42+
current += n;
43+
}
44+
45+
var headersContent = System.Text.Encoding.ASCII.GetString(buffer, 0, current);
46+
var headers = headersContent.Split(HeaderKeys, StringSplitOptions.RemoveEmptyEntries);
47+
long length = 0;
48+
for (var i = 1; i < headers.Length; i += 2)
49+
{
50+
// starting at i = 1 instead of 0 won't throw, if we have uneven headers' length
51+
var header = headers[i - 1];
52+
var value = headers[i].Trim();
53+
if (header.Equals("Content-Length", StringComparison.OrdinalIgnoreCase))
54+
{
55+
length = 0;
56+
long.TryParse(value, out length);
57+
}
58+
}
59+
60+
if (length == 0 || length >= int.MaxValue)
61+
{
62+
HandleRequest(string.Empty, CancellationToken.None);
63+
}
64+
else
65+
{
66+
var requestBuffer = new byte[length];
67+
var received = 0;
68+
while (received < length)
69+
{
70+
var n = _input.Read(requestBuffer, received, requestBuffer.Length - received);
71+
if (n == 0) return Task.CompletedTask; // no more _input
72+
received += n;
73+
}
74+
75+
// TODO sometimes: encoding should be based on the respective header (including the wrong "utf8" value)
76+
var payload = System.Text.Encoding.ASCII.GetString(requestBuffer);
77+
HandleRequest(payload, CancellationToken.None);
78+
}
79+
}
80+
81+
return Task.CompletedTask;
82+
}
83+
84+
private void HandleRequest(string request, CancellationToken cancellationToken)
85+
{
86+
}
87+
}
88+
}

benchmarks/Pipeline/Pipeline.csproj

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>net472;netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
5+
<OutputType>Exe</OutputType>
6+
<IsPackable>false</IsPackable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
11+
<PackageReference Include="System.IO.Pipelines" Version="4.7.1" />
12+
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
13+
<PackageReference Include="Nerdbank.Streams" Version="2.4.60" />
14+
</ItemGroup>
15+
16+
</Project>

0 commit comments

Comments
 (0)