21 Commits
Author SHA1 Message Date
micialware dce374035f Caching singleton proxies 2026-07-07 22:39:25 +03:00
micialware a4c05af5c8 Fix udp bug 2026-07-07 22:30:46 +03:00
micialware 1ecca099d8 fixing reading message crash 2026-06-05 09:24:44 +03:00
micialware 5cc76ddbcc Refactoring 2026-06-05 09:02:25 +03:00
micialware 84f9840024 Codegen in modidier support 2026-01-29 23:15:39 +03:00
micialware b4280cd06d Bug found, temporal solution made 2025-09-24 00:18:09 +03:00
micialware aae30af664 Some refactoring 2025-09-24 00:11:10 +03:00
micialware dca21339db Update LICENSE 2025-09-12 00:53:04 +03:00
micialware dfad8b9141 Prepare for zero-copy sending 2025-08-16 19:51:35 +03:00
micialware 46d741a287 Changing references 2025-08-16 18:30:05 +03:00
micialware 24d346795d Benchmark kaput 2025-08-16 12:14:03 +03:00
micialware 8c137115f4 Add logging for deserialization 2025-08-09 13:16:12 +03:00
micialware cc7d2d478c Remove old option for distribution 2025-08-09 13:09:38 +03:00
micialware b409e5ef08 Abstract distribution model 2025-08-09 13:03:20 +03:00
micialware c29db73a0e Benchmark for execution test written 2025-08-07 23:39:42 +03:00
micialware 0f355b9df3 Flat COI binary representation 2025-08-07 19:14:17 +03:00
micialware 89a2bee8d2 Renaming and cleanup. Begin to use own small arena allocator 2025-08-07 18:52:31 +03:00
micialware 37f5d8a198 Merge pull request #7 from YaslePoy/stick-fix
Stick fix
2025-08-06 19:19:39 +03:00
micialware e7c9bec4cd Merge pull request #6 from YaslePoy/lock-free
Lock free
2025-08-06 19:19:03 +03:00
micialware fd971cdecc Clone serialization toolkit for each user 2025-08-06 19:16:10 +03:00
micialware 304811c964 Prepare for many parsers 2025-08-06 14:32:48 +03:00
76 changed files with 1772 additions and 358 deletions
+2 -2
View File
@@ -7,14 +7,14 @@
<Nullable>enable</Nullable>
<LangVersion>9</LangVersion>
<LangVersion>12</LangVersion>
<PublishAot>true</PublishAot>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DefineConstants>TRACE</DefineConstants>
<PlatformTarget>x64</PlatformTarget>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+1 -1
View File
@@ -11,7 +11,7 @@ namespace Example.Backend
{
private readonly List<IPrinter> _printers = new();
public IPrinter Create(string printerName)
public IPrinter Create(in string printerName)
{
Console.WriteLine("Creating printer");
return new Printer { Name = printerName };
+5 -7
View File
@@ -22,12 +22,12 @@ class Program
builder.Services.AddSingleton<IGatewayModule, NetworkGatewayModule>();
builder.Services.AddSingleton<IUntrustedGateway, UdpGateway>();
builder.Services.AddSingleton<IConnectionHub, ConnectionHub>();
builder.Services.AddOptions();
var listening = new IPEndPoint(IPAddress.Any, 4567);
builder.Services.Configure<GatewayOptions>(options => options.Endpoint = listening);
builder.Services.Configure<DistributionOptions>(o => o.DistributionType = EDistributionType.ExtractorFirst);
builder.Services.AddSingleton<IDistributionModule, ExtractorFirstDistributionModule>();
builder.Services.Configure<SerializationBufferOffset>(options => options.Offset = 0);
builder.Services.AddSingleton<HubRequestExtractor>();
builder.Services.AddSingleton<IExecuteModule, BasicExecutionModule>();
builder.Services.AddSingleton<IRepresentationModuleProducer, CreativeRepresentationModuleProducer>();
@@ -40,7 +40,6 @@ class Program
return repo;
}));
builder.Services.AddSingleton<IMethodRepository>(p =>
{
var methodRepo = new CollectableMethodRepository();
@@ -48,13 +47,12 @@ class Program
return methodRepo;
});
builder.Services.AddSingleton<ICallIndexProvider, GeneratedCallIndexProvider>();
builder.Services.AddSingleton<ICancellationRepository, CancellationRepository>();
builder.Services.Configure<DistributionOptions>(o => o.DistributionType = EDistributionType.ExtractorFirst);
var host = builder.Build();
//
new RemoteTypeBinder();
//
_ = host.Services.GetService<IUntrustedGateway>()!.Start();
var gateway = host.Services.GetService<IGatewayModule>();
gateway.Run();
+1 -1
View File
@@ -31,7 +31,7 @@ namespace Example.Frontend
public string GetName()
{
Console.WriteLine("ClientBasedPrinter called from server!!!!!!!!!!! Vova likes that:)");
Console.WriteLine("ClientBasedPrinter called from server!!!!!!!!!!!");
DemoCheck.BackwardCall = true;
return "ClientBasedPrinter from mroa";
+1 -1
View File
@@ -6,7 +6,7 @@
<Nullable>enable</Nullable>
<LangVersion>9</LangVersion>
<LangVersion>12</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+13 -5
View File
@@ -16,7 +16,6 @@ using mROA.Implementation;
using mROA.Implementation.Backend;
using mROA.Implementation.Frontend;
class Program
{
public static async Task Main(string[] args)
@@ -46,6 +45,7 @@ class Program
builder.Services.AddOptions();
builder.Services.Configure<GatewayOptions>(options => options.Endpoint = serverEndPoint);
builder.Services.Configure<DistributionOptions>(o => o.DistributionType = EDistributionType.Channeled);
builder.Services.Configure<SerializationBufferOffset>(options => options.Offset = 0);
builder.Services.AddSingleton<IRepresentationModuleProducer, StaticRepresentationModuleProducer>();
builder.Services.AddSingleton<IRequestExtractor, RequestExtractor>();
@@ -76,7 +76,14 @@ class Program
using (var disposingPrinter = factory.Create("Test"))
{
disposingPrinter.SetFingerPrint(new[] { 1, 2, 3 }).ContinueWith(r => { Console.WriteLine(r.Status); });
disposingPrinter.SetFingerPrint(new[] { 1, 2, 3 }).ContinueWith(r =>
{
Console.WriteLine(r.Status);
if (r.Status == TaskStatus.Faulted)
{
Console.WriteLine(r.Exception);
}
});
await disposingPrinter.IntTest(new MyData { Id = 5, Score = 7, Name = "Test" });
DemoCheck.CreatingPrinter = true;
@@ -87,7 +94,7 @@ class Program
};
Console.WriteLine("Printer created");
frontendBridge.Obstacle();
// frontendBridge.Obstacle();
var name = disposingPrinter.GetName();
DemoCheck.BasicNonParamsCall = true;
Console.WriteLine("Printer name : {0}", name);
@@ -140,13 +147,14 @@ class Program
var token = cts.Token;
var t = Task.Run(async () => await loadSingleton!.AsyncTest(token));
Thread.Sleep(5000);
Console.WriteLine("Waiting for timer");
Thread.Sleep(2000);
cts.Cancel();
Console.WriteLine($"Token state {cts.Token.IsCancellationRequested}");
DemoCheck.TaskCancelation = true;
#endif
const int iterations = 10_000;
const int iterations = 5;
var timer = Stopwatch.StartNew();
var x = 0;
for (int i = 0; i < iterations; i++)
+1 -1
View File
@@ -10,7 +10,7 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<PlatformTarget>x64</PlatformTarget>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<ItemGroup>
+20 -6
View File
@@ -1,4 +1,6 @@
using System.Net;
using System.Diagnostics;
using System.Net;
using System.Runtime;
using Example.Shared;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
@@ -9,14 +11,13 @@ using mROA.Implementation;
using mROA.Implementation.Backend;
using mROA.Implementation.Frontend;
const int C = 100;
var time = TimeSpan.FromSeconds(10);
Console.WriteLine($"Starting bench for {time} from {C} connections");
var cts = new CancellationTokenSource();
new RemoteTypeBinder();
var eps = await GetLoadEndpoints(C);
var tasks = new Task<int>[C];
var tasks = new Task<(int, double[])>[C];
for (int i = 0; i < C; i++)
{
tasks[i] = Requests(cts.Token, i, eps[i]);
@@ -28,9 +29,17 @@ Console.WriteLine("Start waiting");
await Task.WhenAll(tasks);
Console.WriteLine("End waiting");
var totalRequests = tasks.Sum(i => i.Result);
var totalRequests = tasks.Sum(i => i.Result.Item1);
var totalLatency = tasks.SelectMany(i => i.Result.Item2).ToList();
totalLatency.Sort();
var n = totalLatency.Count;
var p50 = totalLatency[(int)(n * 50f / 100f)];
var p95 = totalLatency[(int)(n * 95f / 100f)];
var p99 = totalLatency[(int)(n * 99f / 100f)];
Console.WriteLine($"Total requests: {totalRequests:N0}");
Console.WriteLine($"Results: {totalRequests / time.TotalSeconds:N} RPS");
Console.WriteLine("Latency (µs): p50={0} p95={1} p99={2}", p50, p95, p99);
File.AppendAllText("results.txt", $"[FAST ID] {totalRequests}\r\n");
async Task<List<ILoadTest>> GetLoadEndpoints(int count)
@@ -94,10 +103,12 @@ async Task<List<ILoadTest>> GetLoadEndpoints(int count)
}
}
async Task<int> Requests(CancellationToken token, int id, ILoadTest load)
async Task<(int, double[])> Requests(CancellationToken token, int id, ILoadTest load)
{
try
{
var latencyList = new List<double>();
var sw = new Stopwatch();
int count = 0;
while (true)
{
@@ -106,11 +117,14 @@ async Task<int> Requests(CancellationToken token, int id, ILoadTest load)
break;
}
sw.Restart();
await load.Next(2);
sw.Stop();
latencyList.Add(sw.Elapsed.TotalMicroseconds);
count++;
}
return count;
return (count, latencyList.ToArray());
}
catch (Exception e)
{
-2
View File
@@ -10,12 +10,10 @@
<ProjectReference Include="..\mROA.Cbor\mROA.Cbor.csproj" />
<ProjectReference Include="..\mROA\mROA.csproj"/>
<ProjectReference Include="..\mROA.Codegen\mROA.Codegen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
<PackageReference Include="mROA.Codegen" Version="2.0.5" />
</ItemGroup>
</Project>
+2 -1
View File
@@ -12,7 +12,8 @@ namespace Example.Shared
{
double Resource { get; set; }
string GetName();
Task<IPage> Print(string text, bool someParameter, RequestContext context, CancellationToken cancellationToken);
Task<IPage> Print(string text, bool someParameter,
RequestContext context, CancellationToken cancellationToken);
event Action<IPage, RequestContext> OnPrint;
[Untrusted]
+1 -1
View File
@@ -6,7 +6,7 @@ namespace Example.Shared
[SharedObjectInterface]
public interface IPrinterFactory : IShared
{
IPrinter Create(string printerName);
IPrinter Create(in string printerName);
void Register(IPrinter printer);
IPrinter GetPrinterByName(string printerName);
IPrinter GetFirstPrinter();
+197 -17
View File
@@ -1,21 +1,201 @@
MIT License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2025 YaslePoy
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. Definitions.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2025 Mitrofanov M. M.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+50
View File
@@ -0,0 +1,50 @@
using System.Formats.Cbor;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using mROA.Implementation;
namespace mROA.Benchmark;
public static class CborExtensions
{
public static unsafe void WriteToCbor(this RequestId id, CborWriter writer)
{
Span<byte> span = stackalloc byte[16];
MemoryMarshal.Write(span, ref id);
writer.WriteByteString(span);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void WriteToCborInline(this RequestId id, CborWriter writer)
{
Span<byte> span = stackalloc byte[16];
MemoryMarshal.Write(span, ref id);
writer.WriteByteString(span);
}
public static void WriteToDest(this RequestId id, Span<byte> destination)
{
MemoryMarshal.Write(destination, ref id);
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static unsafe void WriteToCborOpt(this RequestId id, CborWriter writer)
{
Span<byte> span = stackalloc byte[16];
MemoryMarshal.Write(span, ref id);
writer.WriteByteString(span);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RequestId ReadFromTrueCbor(CborReader reader)
{
var enc = reader.ReadEncodedValue(true);
return MemoryMarshal.Read<RequestId>(enc.Span[1..]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RequestId ReadFromCbor(CborReader reader)
{
var enc = reader.ReadEncodedValue();
return MemoryMarshal.Read<RequestId>(enc.Span[1..]);
}
}
+150
View File
@@ -0,0 +1,150 @@
using System.Formats.Cbor;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using mROA.Implementation;
namespace mROA.Benchmark;
[MemoryDiagnoser]
public class ConcurrentAlloc
{
private readonly CircularMemoryManager _cmm = new(1024);
private readonly FastCircularMemoryManager _fmm = new();
private CborWriter _writer = new();
[GlobalSetup]
public void Setup()
{
_writer.WriteStartArray(2);
_writer.WriteByteString([1, 2, 3, 4, 5, 6, 7, 8]);
_writer.WriteStartArray(2);
_writer.WriteInt32(12);
_writer.WriteTextString("tralala 7EBC1458-BB53-49EA-84C9-EFECC0FC08FD");
_writer.WriteEndArray();
_writer.WriteEndArray();
}
[Benchmark(Baseline = true)]
public int CircularAllocate()
{
var writer = _writer;
var buffer = _cmm.AllocSlice(writer.BytesWritten);
writer.Encode(buffer);
return buffer.Length;
}
[Benchmark]
public int CircularMemoryAllocate()
{
var writer = _writer;
var buffer = _cmm.AllocMemory(writer.BytesWritten);
writer.Encode(buffer.Span);
return buffer.Length;
}
[Benchmark]
public int FastCircularAllocate()
{
var writer = _writer;
var buffer = _fmm.Alloc(writer.BytesWritten);
writer.Encode(buffer);
return buffer.Length;
}
[Benchmark]
public int FastMemoryCircularAllocate()
{
var writer = _writer;
var buffer = _fmm.AllocMem(writer.BytesWritten);
writer.Encode(buffer.Span);
return buffer.Length;
}
[Benchmark]
public int HeapAllocate()
{
var writer = _writer;
var encoded = writer.Encode();
return encoded.Length;
}
}
public class FastCircularMemoryManager
{
private readonly byte[] _buffer;
private long _offset; // atomic offset (in bytes)
private readonly int _mask; // если размер степени двойки — можно использовать маску
public FastCircularMemoryManager(int size = 4096) // 4KB buffer
{
if (!IsPowerOfTwo(size))
throw new ArgumentException("Size should be power of two for performance.", nameof(size));
_buffer = new byte[size];
_offset = 0;
_mask = size - 1; // для быстрого циклического сдвига: (offset & _mask)
}
private static bool IsPowerOfTwo(int x) => x > 0 && (x & (x - 1)) == 0;
public Span<byte> Alloc(int size)
{
if (size > _buffer.Length)
return new byte[size]; // fallback
long oldOffset, newOffset;
int start;
// Atomic "bump pointer" с циклическим переполнением
do
{
oldOffset = Volatile.Read(ref _offset);
start = (int)(oldOffset & _mask);
// Проверяем, не пересекает ли выделение границу буфера
if (start + size > _buffer.Length)
{
// Переполнение — обнуляем (циклический буфер)
newOffset = size; // сбрасываем на начало + size
start = 0;
}
else
{
newOffset = oldOffset + size;
}
} while (Interlocked.CompareExchange(ref _offset, newOffset, oldOffset) != oldOffset);
return _buffer.AsSpan(start, size);
}
public Memory<byte> AllocMem(int size)
{
if (size > _buffer.Length)
return new byte[size]; // fallback
long oldOffset, newOffset;
int start;
// Atomic "bump pointer" с циклическим переполнением
do
{
oldOffset = Volatile.Read(ref _offset);
start = (int)(oldOffset & _mask);
// Проверяем, не пересекает ли выделение границу буфера
if (start + size > _buffer.Length)
{
// Переполнение — обнуляем (циклический буфер)
newOffset = size; // сбрасываем на начало + size
start = 0;
}
else
{
newOffset = oldOffset + size;
}
} while (Interlocked.CompareExchange(ref _offset, newOffset, oldOffset) != oldOffset);
return _buffer.AsMemory(start, size);
}
}
+15
View File
@@ -0,0 +1,15 @@
using mROA.Abstract;
using mROA.Implementation.Attributes;
namespace mROA.Benchmark
{
[SharedObjectInterface]
public interface ILoadTest : IShared
{
Task<int> Next(int last);
int Last(int next);
void C();
void A();
Task AsyncTest(CancellationToken token = default);
}
}
+11
View File
@@ -0,0 +1,11 @@
using mROA.Abstract;
using mROA.Implementation.Attributes;
namespace mROA.Benchmark
{
[SharedObjectInterface]
public interface IPage : IShared
{
byte[] GetData();
}
}
+51
View File
@@ -0,0 +1,51 @@
using mROA.Abstract;
using mROA.Implementation;
using mROA.Implementation.Attributes;
namespace mROA.Benchmark
{
[SharedObjectInterface]
public partial interface IPrinter : IDisposable, IShared
{
double Resource { get; set; }
string GetName();
Task<IPage> Print(string text, bool someParameter, RequestContext context, CancellationToken cancellationToken);
event Action<IPage, RequestContext> OnPrint;
[Untrusted]
Task SomeoneIsApproaching(string humanName);
Task SetFingerPrint(int[] fingerPrint);
Task IntTest(MyData data);
}
public class MyData
{
public int Id { get; set; }
public string Name { get; set; }
public double Score { get; set; }
public override string ToString()
{
return $"{{{nameof(Id)}: {Id}, {nameof(Name)}: {Name}, {nameof(Score)}: {Score}}}";
}
protected bool Equals(MyData other)
{
return Id == other.Id && Name == other.Name && Score.Equals(other.Score);
}
public override bool Equals(object obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((MyData)obj);
}
public override int GetHashCode()
{
return HashCode.Combine(Id, Name, Score);
}
}
}
+15
View File
@@ -0,0 +1,15 @@
using mROA.Abstract;
using mROA.Implementation.Attributes;
namespace mROA.Benchmark
{
[SharedObjectInterface]
public interface IPrinterFactory : IShared
{
IPrinter Create(string printerName);
void Register(IPrinter printer);
IPrinter GetPrinterByName(string printerName);
IPrinter GetFirstPrinter();
string[] CollectAllNames();
}
}
+37
View File
@@ -0,0 +1,37 @@
using BenchmarkDotNet.Attributes;
using mROA.Implementation;
namespace mROA.Benchmark;
public class IdGeneration
{
public static int X = 0;
[Benchmark]
public Guid GuidGeneration()
{
return Guid.NewGuid();
}
[Benchmark(Baseline = true)]
public RequestId ReqIdGeneration()
{
return RequestId.Generate();
}
[Benchmark]
public RequestId ReqIdIfGeneration()
{
var reqId = RequestId.Generate();
if (++X % 2 == 0)
{
reqId.P0 = 0;
}
else
{
reqId.P1 = 0;
}
return reqId;
}
}
+30
View File
@@ -0,0 +1,30 @@
using BenchmarkDotNet.Attributes;
namespace mROA.Benchmark;
[MemoryDiagnoser]
[DisassemblyDiagnoser]
public class InvokePerformance
{
public Func<int, int> A = x =>
{
var result = x * 5 + x * x / 5;
return result;
};
public SomeMath B = x => x * 5 + x * x / 5;
[Benchmark(Baseline = true)]
public int ActionInvoke()
{
return A(132);
}
[Benchmark]
public int DelegateInvoke()
{
return A(132);
}
}
public delegate int SomeMath(int x);
+133
View File
@@ -0,0 +1,133 @@
using BenchmarkDotNet.Attributes;
using mROA.Abstract;
using mROA.Codegen;
using mROA.Implementation;
namespace mROA.Benchmark;
[MemoryDiagnoser]
// [DisassemblyDiagnoser]
public class MethodAccess
{
private CollectableMethodRepository _current = new();
private FastMethodRepository _fast = new();
private readonly int _count = new GeneratedInvokersCollection().Count;
[GlobalSetup]
public void SetupMethodAccess()
{
_current.AppendInvokers(new GeneratedInvokersCollection());
_fast.AppendInvokers(new GeneratedInvokersCollection());
}
// [Benchmark(Baseline = true)]
// public IMethodInvoker CurrentSingle()
// {
// return _current.GetMethod(0);
// }
//
// [Benchmark]
// public IMethodInvoker CurrentDispose()
// {
// return _current.GetMethod(-1);
// }
//
// [Benchmark]
// public IMethodInvoker FastSingle()
// {
// return _fast.GetMethod(0);
// }
//
// [Benchmark]
// public IMethodInvoker FastDispose()
// {
// return _fast.GetMethod(-1);
// }
// [Benchmark]
// public IMethodInvoker FastSingleBaked()
// {
// return _fast.GetMethodBaked(0);
// }
//
// [Benchmark]
// public IMethodInvoker FastDisposeBaked()
// {
// return _fast.GetMethodBaked(-1);
// }
[Benchmark(Baseline = true)]
public IMethodInvoker CurrentAll()
{
IMethodInvoker inv = null;
for (int i = -1; i < _count; i++)
{
inv = _current.GetMethod(i);
}
return inv;
}
[Benchmark]
public IMethodInvoker FastAll()
{
IMethodInvoker inv = null;
for (int i = -1; i < _count; i++)
{
inv = _fast.GetMethod(i);
}
return inv;
}
[Benchmark]
public IMethodInvoker FastAllBaked()
{
IMethodInvoker inv = null;
for (int i = -1; i < _count; i++)
{
inv = _fast.GetMethodBaked(i);
}
return inv;
}
[Benchmark]
public IMethodInvoker FastAllBakedPreicrement()
{
IMethodInvoker inv = null;
for (int i = -1; i < _count; i++)
{
inv = _fast.GetMethodBakedPreicrement(i);
}
return inv;
}
}
internal class FastMethodRepository : IMethodRepository
{
private readonly List<IMethodInvoker> _methods = [MethodInvoker.Dispose];
private IMethodInvoker[] _baked = [];
public void AppendInvokers(IEnumerable<IMethodInvoker> methodInvokers)
{
_methods.AddRange(methodInvokers);
_baked = _methods.ToArray();
}
public IMethodInvoker GetMethod(int id)
{
return _methods[id + 1];
}
public IMethodInvoker GetMethodBaked(int id)
{
return _baked[id + 1];
}
public IMethodInvoker GetMethodBakedPreicrement(int id)
{
return _baked[++id];
}
}
+2 -38
View File
@@ -1,44 +1,8 @@
// See https://aka.ms/new-console-template for more information
using System.Formats.Cbor;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Running;
using mROA.Benchmark;
using mROA.Implementation;
Console.WriteLine("Hello, World!");
var test = new CborTest();
test.ArrayWrite();
BenchmarkRunner.Run<VirtualOverhead>();
Console.WriteLine("Hello, Performance!");
public static class CborExtensions
{
public static unsafe void WriteToCbor(this RequestId id, CborWriter writer)
{
Span<byte> span = stackalloc byte[16];
MemoryMarshal.Write(span, ref id);
writer.WriteByteString(span);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void WriteToCborInline(this RequestId id, CborWriter writer)
{
Span<byte> span = stackalloc byte[16];
MemoryMarshal.Write(span, ref id);
writer.WriteByteString(span);
}
public static void WriteToDest(this RequestId id, Span<byte> destination)
{
MemoryMarshal.Write(destination, ref id);
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static unsafe void WriteToCborOpt(this RequestId id, CborWriter writer)
{
Span<byte> span = stackalloc byte[16];
MemoryMarshal.Write(span, ref id);
writer.WriteByteString(span);
}
}
BenchmarkRunner.Run<MethodAccess>();
+38
View File
@@ -0,0 +1,38 @@
using System.Formats.Cbor;
using BenchmarkDotNet.Attributes;
using mROA.Implementation;
namespace mROA.Benchmark;
[MemoryDiagnoser]
[DisassemblyDiagnoser]
public class RequestReader
{
private ReadOnlyMemory<byte> _data;
public RequestReader()
{
_data = new ReadOnlyMemory<byte>([80, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
}
[Benchmark(Baseline = true)]
public ulong DefaultRead()
{
var reader = new CborReader(_data);
return new RequestId(reader.ReadByteString()).P0;
}
[Benchmark]
public ulong MemoryRead()
{
var reader = new CborReader(_data);
return CborExtensions.ReadFromCbor(reader).P0;
}
[Benchmark]
public ulong MemoryTrueRead()
{
var reader = new CborReader(_data);
return CborExtensions.ReadFromTrueCbor(reader).P0;
}
}
+4 -3
View File
@@ -2,12 +2,13 @@ using System.Formats.Cbor;
using BenchmarkDotNet.Attributes;
using mROA.Implementation;
namespace mROA.Benchmark;
[MemoryDiagnoser]
public class RequestWriter
{
private const int N = 1000;
public RequestId Id = RequestId.Generate();
private CborWriter _writer;
public RequestId Id;
private readonly CborWriter _writer;
public RequestWriter()
{
+73
View File
@@ -0,0 +1,73 @@
using BenchmarkDotNet.Attributes;
using mROA.Implementation;
namespace mROA.Benchmark;
public class StackOperations
{
private Stack<StackFrame> stack = new();
public StackOperations()
{
}
[Benchmark]
public void Stack()
{
}
private readonly struct StackFrame
{
public StackFrame(
CborMajorType? type,
int frameOffset,
int? definiteLength,
int itemsWritten,
int? currentKeyOffset,
int? currentValueOffset,
List<int>? keyValuePairEncodingRanges,
HashSet<(int Offset, int Length)>? keyEncodingRanges)
{
MajorType = type;
FrameOffset = frameOffset;
DefiniteLength = definiteLength;
ItemsWritten = itemsWritten;
CurrentKeyOffset = currentKeyOffset;
CurrentValueOffset = currentValueOffset;
KeyValuePairEncodingRanges = keyValuePairEncodingRanges;
KeyEncodingRanges = keyEncodingRanges;
}
public CborMajorType? MajorType { get; }
public int FrameOffset { get; }
public int? DefiniteLength { get; }
public int ItemsWritten { get; }
public int? CurrentKeyOffset { get; }
public int? CurrentValueOffset { get; }
public List<int>? KeyValuePairEncodingRanges { get; }
public HashSet<(int Offset, int Length)>? KeyEncodingRanges { get; }
}
}
internal enum CborMajorType
{
Unknown,
Int8,
Int16,
Int32,
Int64,
UInt8,
UInt16,
UInt32,
UInt64,
Float32,
Float64,
Double,
Double2,
Double3,
}
+133
View File
@@ -0,0 +1,133 @@
using BenchmarkDotNet.Attributes;
using mROA.Abstract;
using mROA.Cbor;
using mROA.Implementation;
using mROA.Implementation.Attributes;
using mROA.Implementation.Backend;
using mROA.Implementation.CommandExecution;
namespace mROA.Benchmark;
public class TaskWaiting
{
private readonly BasicExecutionModule _executionModule;
private readonly CallRequest _syncRequest;
private readonly CallRequest _asyncRequest;
private readonly InstanceRepository _instanceRepo;
private readonly EndPointContext _endPointContext;
private readonly FastRepresentationModule _representationModule;
public TaskWaiting()
{
_executionModule = new BasicExecutionModule(new CancellationRepository(), new TestMethodRepo(), new CborSerializationToolkit(null));
_syncRequest = new CallRequest{CommandId = 0, Parameters = null, Id = RequestId.Generate(), ObjectId = new ComplexObjectIdentifier(-1, 0)};
_asyncRequest = new CallRequest{CommandId = 1, Parameters = null, Id = RequestId.Generate(), ObjectId = new ComplexObjectIdentifier(-1, 0)};
_instanceRepo = new InstanceRepository(null);
_instanceRepo.FillSingletons(typeof(TaskWaiting).Assembly);
_endPointContext = new EndPointContext(_instanceRepo, null)
{
OwnerId = 0
};
_representationModule = new FastRepresentationModule();
}
[Benchmark(Baseline = true)]
public int DefaultJob()
{
return (int)((FinalCommandExecution<object>)_executionModule.Execute(_syncRequest, _instanceRepo, _representationModule, _endPointContext)).Result;
}
[Benchmark]
public async Task<int> DefaultJobAsync()
{
_representationModule.Signal = new TaskCompletionSource<int>();
var task = _representationModule.Signal.Task;
_ = _executionModule.Execute(_asyncRequest, _instanceRepo, _representationModule, _endPointContext);
_ = await task;
return (int)((FinalCommandExecution<object>)_representationModule.Result).Result;
}
}
public class TestMethodRepo : IMethodRepository
{
public IMethodInvoker GetMethod(int id)
{
if (id == 0)
return new MethodInvoker
{
IsVoid = false,
IsTrusted = true,
ReturnType = typeof(int),
ParameterTypes = Type.EmptyTypes,
SuitableType = typeof(IJobClass),
Invoking = (i, _, _) => (i as IJobClass).A()
};
return new AsyncMethodInvoker
{
IsVoid = false,
IsTrusted = true,
ReturnType = typeof(int),
ParameterTypes = Type.EmptyTypes,
SuitableType = typeof(IJobClass),
Invoking = (i, _, _, post) => (i as IJobClass).B().ContinueWith(task => post(task.Result))
};
}
}
[SharedObjectInterface]
public interface IJobClass
{
int A();
Task<int> B();
}
[SharedObjectSingleton]
public class JobClass : IJobClass
{
private readonly RequestWriter _requestWriter = new();
public int A()
{
return _requestWriter.DefaultCbor();
}
public Task<int> B()
{
return Task.FromResult(_requestWriter.DefaultCbor());
}
}
public class FastRepresentationModule : IRepresentationModule
{
public TaskCompletionSource<int> Signal = new();
public object Result;
public int Id { get; }
public IEndPointContext Context { get; }
public Task<(object? Deserialized, EMessageType MessageType)> GetSingle(Predicate<NetworkMessage> rule, IEndPointContext? context, CancellationToken token = default, params Func<NetworkMessage, Type?>[] converter)
{
throw new NotImplementedException();
}
public IAsyncEnumerable<(object parced, EMessageType originalType)> GetStream(Predicate<NetworkMessage> rule, IEndPointContext? context, CancellationToken token = default,
params Func<NetworkMessage, Type?>[] converter)
{
throw new NotImplementedException();
}
public Task PostCallMessageAsync<T>(RequestId id, EMessageType eMessageType, T payload, IEndPointContext? context) where T : notnull
{
Result = payload;
Signal.SetResult(0);
return Task.CompletedTask;
}
public void PostCallMessage<T>(RequestId id, EMessageType eMessageType, T payload, IEndPointContext? context) where T : notnull
{
throw new NotImplementedException();
}
public Task PostCallMessageUntrustedAsync<T>(RequestId id, EMessageType eMessageType, T payload, IEndPointContext? context) where T : notnull
{
throw new NotImplementedException();
}
}
+39 -18
View File
@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using mROA.Implementation;
@@ -5,17 +6,15 @@ namespace mROA.Benchmark;
public class VirtualOverhead
{
private ICallRequest _request;
private CallRequest _directRequest;
private RawCallRequest _rawRequest;
public VirtualOverhead()
{
_request = new CallRequest
[GlobalSetup]
public void Setup() {
_directRequest = new CallRequest
{
CommandId = 5, Id = new RequestId(), ObjectId = ComplexObjectIdentifier.Null, Parameters = null
};
_directRequest = (CallRequest)_request;
_rawRequest = new RawCallRequest
{
CommandId = _directRequest.CommandId, Id = _directRequest.Id, ObjectId = _directRequest.ObjectId,
@@ -24,18 +23,6 @@ public class VirtualOverhead
}
[Benchmark(Baseline = true)]
public long VirtualUsage()
{
var req = _request;
long acc = 0;
acc += req.CommandId;
acc += (long)(req.Id.P1 + req.Id.P0);
acc += req.ObjectId.ContextId + req.ObjectId.OwnerId;
acc += (req.Parameters ?? []).Length;
return acc;
}
[Benchmark]
public long DirectUsage()
{
var req = _directRequest;
@@ -58,6 +45,40 @@ public class VirtualOverhead
acc += (req.Parameters ?? []).Length;
return acc;
}
[Benchmark]
public long ParamUsage()
{
return Call(_directRequest);
}
[Benchmark]
public long ParamInUsage()
{
return CallIn(in _directRequest);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public long Call(CallRequest req)
{
long acc = 0;
acc += req.CommandId;
acc += (long)(req.Id.P1 + req.Id.P0);
acc += req.ObjectId.ContextId + req.ObjectId.OwnerId;
acc += (req.Parameters ?? []).Length;
return acc;
}
[MethodImpl(MethodImplOptions.NoInlining)]
public long CallIn(in CallRequest req)
{
long acc = 0;
acc += req.CommandId;
acc += (long)(req.Id.P1 + req.Id.P0);
acc += req.ObjectId.ContextId + req.ObjectId.OwnerId;
acc += (req.Parameters ?? []).Length;
return acc;
}
}
public struct RawCallRequest
+5 -8
View File
@@ -9,17 +9,14 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\mROA\mROA.csproj" />
<PackageReference Include="BenchmarkDotNet" Version="0.15.2"/>
<PackageReference Include="System.Formats.Cbor" Version="9.0.8"/>
</ItemGroup>
<ItemGroup>
<Reference Include="System.Formats.Cbor">
<HintPath>..\..\..\..\.nuget\packages\system.formats.cbor\9.0.7\lib\net9.0\System.Formats.Cbor.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.2" />
<ProjectReference Include="..\mROA.Cbor\mROA.Cbor.csproj"/>
<ProjectReference Include="..\mROA.Codegen\mROA.Codegen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
<ProjectReference Include="..\mROA\mROA.csproj"/>
</ItemGroup>
</Project>
+91 -39
View File
@@ -1,10 +1,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Formats.Cbor;
using System.Linq;
using System.Reflection;
using System.Threading;
using Microsoft.Extensions.Options;
using mROA.Abstract;
using mROA.Implementation;
using mROA.Implementation.Attributes;
@@ -14,7 +15,13 @@ namespace mROA.Cbor
{
public class CborSerializationToolkit : IContextualSerializationToolKit
{
private readonly CborWriter _writer = new(initialCapacity: 512);
private readonly ThreadLocal<CborWriter> _writer = new(() => new CborWriter(initialCapacity: 2048));
private readonly int _offset;
public CborSerializationToolkit(IOptions<SerializationBufferOffset> offsetOptions) : this(offsetOptions.Value
.Offset)
{
}
private readonly IOrdinaryStructureParser[] _parsers =
{
@@ -23,6 +30,12 @@ namespace mROA.Cbor
};
private readonly Dictionary<Type, List<PropertyInfo>> _propertiesCache = new();
public CborSerializationToolkit(int offset)
{
_offset = offset;
}
public static TimeSpan SerializationTime = TimeSpan.Zero;
private bool FindParser(Type t, out IOrdinaryStructureParser parser)
@@ -51,25 +64,24 @@ namespace mROA.Cbor
public byte[] Serialize(object objectToSerialize, IEndPointContext context)
{
byte[] result;
lock (_writer)
{
_writer.Reset();
WriteData(objectToSerialize, _writer, context);
result = _writer.Encode();
}
var writer = _writer.Value;
writer.Reset();
WriteData(objectToSerialize, writer, context);
var result = new byte[_offset + writer.BytesWritten];
var span = result.AsSpan();
writer.Encode(span[_offset..]);
return result;
}
public int Serialize(object objectToSerialize, Span<byte> destination, IEndPointContext context)
{
lock (_writer)
{
_writer.Reset();
WriteData(objectToSerialize, _writer, context);
return _writer.Encode(destination);
}
var writer = _writer.Value;
writer.Reset();
WriteData(objectToSerialize, writer, context);
return writer.Encode(destination);
}
public T Deserialize<T>(byte[] rawData, IEndPointContext? context)
@@ -89,13 +101,17 @@ namespace mROA.Cbor
public object? Deserialize(ReadOnlyMemory<byte> rawMemory, Type type, IEndPointContext? context)
{
var reader = new CborReader(rawMemory);
return ReadData(reader, type, context);
}
public T Cast<T>(object nonCasted, IEndPointContext? context)
{
return (T)Cast(nonCasted, typeof(T), context);
try
{
var reader = new CborReader(rawMemory);
return ReadData(reader, type, context);
}
catch (Exception)
{
Console.WriteLine(
$"Bad deserialization for type {type}. Bytes: {BitConverter.ToString(rawMemory.ToArray())}");
throw;
}
}
public object? Cast(object? nonCasted, Type type, IEndPointContext? context)
@@ -115,9 +131,19 @@ namespace mROA.Cbor
return new RequestId((byte[])nonCasted);
}
if (type.IsInterface)
{
return ReadSharedShell(type, context, (ulong)nonCasted);
}
return Convert.ChangeType(nonCasted, type);
}
public IContextualSerializationToolKit Clone()
{
return new CborSerializationToolkit(_offset);
}
public void WriteData(object? obj, CborWriter writer, IEndPointContext? context)
{
if (obj is not null && FindParser(obj.GetType(), out var parser))
@@ -177,9 +203,20 @@ namespace mROA.Cbor
WriteObject(sharedObject, writer, context);
break;
default:
if (obj.GetType().IsEnum)
var objType = obj.GetType();
if (objType.IsEnum)
{
writer.WriteInt32((int)obj);
var basicType = Enum.GetUnderlyingType(obj.GetType());
if (basicType == typeof(byte))
{
writer.WriteInt32((byte)obj);
}
else if (basicType == typeof(int))
{
writer.WriteInt32((byte)obj);
}
break;
}
@@ -230,9 +267,7 @@ namespace mROA.Cbor
Activator.CreateInstance(sharedShell, obj, context) as
ISharedObjectShell;
writer.WriteStartArray(1);
writer.WriteUInt64(so.Identifier.Flat);
writer.WriteEndArray();
return;
}
@@ -262,6 +297,10 @@ namespace mROA.Cbor
return reader.ReadInt64();
if (type == typeof(uint))
return reader.ReadUInt32();
if (type.IsInterface)
{
return ReadSharedShell(type, context, reader.ReadUInt64());
}
return reader.ReadUInt64();
case CborReaderState.ByteString:
@@ -373,19 +412,9 @@ namespace mROA.Cbor
if (type.IsInterface)
{
var sharedShell = typeof(SharedObjectShellShell<>).MakeGenericType(type);
var so =
Activator.CreateInstance(sharedShell) as
ISharedObjectShell;
if (context != null)
so.EndPointContext = context;
reader.ReadStartArray();
var identifier = reader.ReadUInt64();
reader.ReadEndArray();
so.Identifier = ComplexObjectIdentifier.FromFlat(identifier);
return so.UniversalValue;
return ReadSharedShell(type, context, identifier);
}
var instance = Activator.CreateInstance(type)!;
@@ -395,6 +424,20 @@ namespace mROA.Cbor
return instance;
}
private static object ReadSharedShell(Type type, IEndPointContext? context, ulong identifier)
{
var sharedShell = typeof(SharedObjectShellShell<>).MakeGenericType(type);
var so =
Activator.CreateInstance(sharedShell) as
ISharedObjectShell;
if (context != null)
so.EndPointContext = context;
so.Identifier = ComplexObjectIdentifier.FromFlat(identifier);
return so.UniversalValue;
}
private ISharedObjectShell ReadSharedObject(CborReader reader, Type type, IEndPointContext? context)
{
var sharedObject = (Activator.CreateInstance(type) as ISharedObjectShell)!;
@@ -422,6 +465,15 @@ namespace mROA.Cbor
{
var property = properties[index];
var value = ReadData(reader, property.PropertyType, context);
if (property.PropertyType.IsEnum)
{
if (property.PropertyType.GetEnumUnderlyingType() == typeof(byte))
{
value = Convert.ChangeType(value, typeof(byte));
}
}
property.SetValue(obj, value);
}
+16 -15
View File
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Formats.Cbor;
using System.Runtime.InteropServices;
using mROA.Abstract;
using mROA.Implementation;
using mROA.Implementation.CommandExecution;
@@ -14,16 +16,15 @@ namespace mROA.Cbor
public class CallRequestParser : IOrdinaryStructureParser
{
public void Write(CborWriter writer, object value, IEndPointContext context, CborSerializationToolkit serialization)
public void Write(CborWriter writer, object value, IEndPointContext context,
CborSerializationToolkit serialization)
{
var v = (CallRequest)value;
writer.WriteStartArray(4);
v.Id.WriteToCborInline(writer);
// writer.WriteByteString(v.Id.ToByteArray());
writer.WriteInt32(v.CommandId);
writer.WriteStartArray(1);
writer.WriteUInt64(v.ObjectId.Flat);
writer.WriteEndArray();
serialization.WriteData(v.Parameters, writer, context);
writer.WriteEndArray();
}
@@ -35,7 +36,8 @@ namespace mROA.Cbor
{
Id = new RequestId(reader.ReadByteString()),
CommandId = reader.ReadInt32(),
ObjectId = (ComplexObjectIdentifier)ComplexObjectIdentifierParser.Instance.Read(reader, context, serialization),
ObjectId = (ComplexObjectIdentifier)ComplexObjectIdentifierParser.Instance.Read(reader, context,
serialization),
Parameters = serialization.ReadData(reader, typeof(object[]), context) as object[]
};
reader.ReadEndArray();
@@ -46,25 +48,24 @@ namespace mROA.Cbor
public class ComplexObjectIdentifierParser : IOrdinaryStructureParser
{
public static readonly ComplexObjectIdentifierParser Instance = new();
public void Write(CborWriter writer, object value, IEndPointContext context, CborSerializationToolkit serialization)
public void Write(CborWriter writer, object value, IEndPointContext context,
CborSerializationToolkit serialization)
{
writer.WriteStartArray(1);
writer.WriteUInt64(((ComplexObjectIdentifier)value).Flat);
writer.WriteEndArray();
}
public object Read(CborReader reader, IEndPointContext context, CborSerializationToolkit serialization)
{
reader.ReadStartArray();
var value = new ComplexObjectIdentifier { Flat = reader.ReadUInt64() };
reader.ReadEndArray();
return value;
}
}
public class FinalCommandExecutionParser : IOrdinaryStructureParser
{
public void Write(CborWriter writer, object value, IEndPointContext context, CborSerializationToolkit serialization)
public void Write(CborWriter writer, object value, IEndPointContext context,
CborSerializationToolkit serialization)
{
var v = (FinalCommandExecution<object>)value;
writer.WriteStartArray(2);
@@ -82,18 +83,18 @@ namespace mROA.Cbor
Id = new RequestId(reader.ReadByteString()),
Result = serialization.ReadData(reader, typeof(object), context),
};
reader.ReadEndArray();
return result;
reader.ReadEndArray();
return result;
}
}
public class FinalCommandExecutionResultlessParser : IOrdinaryStructureParser
{
public void Write(CborWriter writer, object value, IEndPointContext context, CborSerializationToolkit serialization)
public void Write(CborWriter writer, object value, IEndPointContext context,
CborSerializationToolkit serialization)
{
var v = (FinalCommandExecution)value;
writer.WriteStartArray(1);
// writer.WriteByteString(v.Id.ToByteArray());
v.Id.WriteToCborInline(writer);
writer.WriteEndArray();
}
+2 -2
View File
@@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.0.7</Version>
<Version>3.0.4</Version>
<LangVersion>9</LangVersion>
<PackageIcon>mroaLogo.png</PackageIcon>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -31,6 +31,6 @@
<ItemGroup>
<PackageReference Include="System.Formats.Cbor" Version="9.0.7" />
<PackageReference Include="System.Formats.Cbor" Version="9.0.8" />
</ItemGroup>
</Project>
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+1
View File
@@ -23,6 +23,7 @@ namespace mROA.Codegen
ReturnType = typeof(<!L returnType>),
ParameterTypes = new Type[] { <!L parametersType> },
SuitableType = typeof(<!L suitableType>),
RequireCancellation = <!L cancellation>,
Invoking = (i, parameters, special, post) => <!L funcInvoking>,
}<!T>
<!T syncInvoker>
-1
View File
@@ -26,7 +26,6 @@ namespace mROA.Codegen
<!T eventBinderTemplate>
(instance as <!L type>).<!L eventName> += (<!L parametersDeclaration>) =>
{
Console.WriteLine($"Try to send to {ownerId} with hash code {context.GetHashCode()}");
<!I callFilter>
Console.WriteLine("Sending event...");
var request = new CallRequest
+1 -1
View File
@@ -7,7 +7,7 @@ namespace mROA.Codegen
{
public static TemplateDocument CloneInnerTemplate(this TemplateDocument template, string tag)
{
var innerTemplate = template[tag] as InnerTemplateSection
var innerTemplate = template[tag] as InnerTemplateSection
?? throw new InvalidOperationException();
var clone = innerTemplate.InnerTemplate.Clone();
return (TemplateDocument)clone;
@@ -10,34 +10,36 @@ namespace mROA.Codegen.Templates
private const string ParametersDeclarationTag = "parametersDeclaration";
private const string CommandIdTag = "commandId";
private const string TransferParametersTag = "transferParameters";
public EventBinderTemplate(TemplateDocument template) : base(template) { }
public EventBinderTemplate(TemplateDocument template) : base(template)
{
}
public void DefineCallFilter(string value)
{
Define(CallFilterTag, value);
}
public void DefineType(string value)
{
Define(TypeTag, value);
}
public void DefineEventName(string value)
{
Define(EventNameTag, value);
}
public void DefineParametersDeclaration(string value)
{
Define(ParametersDeclarationTag, value);
}
public void DefineCommandIdTag(string value)
{
Define(CommandIdTag, value);
}
public void DefineTransferParameters(string value)
{
Define(TransferParametersTag, value);
@@ -3,25 +3,27 @@ namespace mROA.Codegen.Templates
public class IndexProviderTemplate : TemplateView
{
private const string TemplateFile = "IndexProvider.cstmpl";
private const string NamespaceTag = "namespace";
private const string LevelTag = "level";
private const string LenTag = "len";
private const string IndexSpanTag = "indexSpan";
private const string RemoteTypePairTag = "remoteTypePair";
public IndexProviderTemplate() : base(TemplateFile) { }
public IndexProviderTemplate() : base(TemplateFile)
{
}
public void DefineNamespace(string value)
{
Define(NamespaceTag, value);
}
public void DefineLevel(string value)
{
Define(LevelTag, value);
}
public void DefineLen(string value)
{
Define(LenTag, value);
@@ -31,7 +33,7 @@ namespace mROA.Codegen.Templates
{
Insert(IndexSpanTag, value);
}
public void InsertRemoteTypePair(string value)
{
Insert(RemoteTypePairTag, value);
+14 -6
View File
@@ -9,38 +9,46 @@ namespace mROA.Codegen.Templates
private const string ParametersTypeTag = "parametersType";
private const string SuitableTypeTag = "suitableType";
private const string FuncInvokingTag = "funcInvoking";
private const string CancellationTag = "cancellation";
private const string IsTrustedTag = "isTrusted";
public InvokerTemplate(TemplateDocument template) : base(template) { }
public InvokerTemplate(TemplateDocument template) : base(template)
{
}
public void DefineIsVoid(string value)
{
Define(IsVoidTag, value);
}
public void DefineReturnType(string value)
{
Define(ReturnTypeTag, value);
}
public void DefineParametersType(string value)
{
Define(ParametersTypeTag, value);
}
public void DefineSuitableType(string value)
{
Define(SuitableTypeTag, value);
}
public void DefineFuncInvoking(string value)
{
Define(FuncInvokingTag, value);
}
public void DefineIsTrusted(string value)
{
Define(IsTrustedTag, value);
}
public void DefineCancellation(string value)
{
Define(CancellationTag, value);
}
}
}
+7 -5
View File
@@ -3,25 +3,27 @@ namespace mROA.Codegen.Templates
public class MethodRepoTemplate : TemplateView
{
private const string TemplateFile = "MethodRepo.cstmpl";
private const string SyncInvokerTemplate = "syncInvoker";
private const string AsyncInvokerTemplate = "asyncInvoker";
private const string InvokerTag = "invoker";
public MethodRepoTemplate() : base(TemplateFile) { }
public MethodRepoTemplate() : base(TemplateFile)
{
}
public void InsertInvoke(string value)
{
Insert(InvokerTag, value);
}
public InvokerTemplate CloneInnerSyncInvoker()
{
var template = CloneInner(SyncInvokerTemplate);
return new InvokerTemplate(template);
}
public InvokerTemplate CloneInnerAsyncInvoker()
{
var template = CloneInner(AsyncInvokerTemplate);
@@ -5,11 +5,13 @@ namespace mROA.Codegen.Templates
public partial class ObjectBinderTemplate : TemplateView
{
private const string EventBinderTemplateTag = "eventBinderTemplate";
private const string TypeTag = "type";
private const string EventBinderTag = "eventBinder";
public ObjectBinderTemplate(TemplateDocument template) : base(template) { }
public ObjectBinderTemplate(TemplateDocument template) : base(template)
{
}
public void DefineType(string value)
{
@@ -20,7 +22,7 @@ namespace mROA.Codegen.Templates
{
Insert(EventBinderTag, value);
}
public EventBinderTemplate CloneInnerEventBinder()
{
var template = CloneInner(EventBinderTemplateTag);
@@ -3,18 +3,20 @@ namespace mROA.Codegen.Templates
public class PartialInterfaceTemplate : TemplateView
{
private const string TemplateFile = "PartialInterface.cstmpl";
private const string NameTag = "name";
private const string NamespaceTag = "namespace";
private const string SignatureTag = "signature";
public PartialInterfaceTemplate() : base(TemplateFile) { }
public PartialInterfaceTemplate() : base(TemplateFile)
{
}
public void DefineName(string value)
{
Define(NameTag, value);
}
public void DefineNamespace(string value)
{
Define(NamespaceTag, value);
+8 -6
View File
@@ -1,26 +1,28 @@
namespace mROA.Codegen.Templates
{
{
public class ProxyTemplate : TemplateView
{
private const string TemplateFile = "Proxy.cstmpl";
private const string ClassNameTag = "className";
private const string OriginalNameTag = "originalName";
private const string NamespaceNameTag= "namespaceName";
private const string NamespaceNameTag = "namespaceName";
private const string MethodsTag = "methods";
public ProxyTemplate() : base(TemplateFile) { }
public ProxyTemplate() : base(TemplateFile)
{
}
public void DefineClassName(string value)
{
Define(ClassNameTag, value);
}
public void DefineOriginalName(string value)
{
Define(OriginalNameTag, value);
}
public void DefineNamespaceName(string value)
{
Define(NamespaceNameTag, value);
@@ -3,18 +3,20 @@ namespace mROA.Codegen.Templates
public class RemoteTypeBinderTemplate : TemplateView
{
private const string TemplateFile = "RemoteTypeBinder.cstmpl";
private const string ObjectBinderTemplateTag = "objectBinderTemplate";
private const string EventBinderTag = "eventBinder";
public RemoteTypeBinderTemplate() : base(TemplateFile) { }
public RemoteTypeBinderTemplate() : base(TemplateFile)
{
}
public ObjectBinderTemplate CloneInnerObjectBinder()
{
var template = CloneInner(ObjectBinderTemplateTag);
return new ObjectBinderTemplate(template);
}
public void InsertEventBinder(string value)
{
Insert(EventBinderTag, value);
+2 -2
View File
@@ -6,12 +6,12 @@ namespace mROA.Codegen.Templates
public abstract class TemplateView
{
private readonly TemplateDocument _template;
protected TemplateView(string templateFile)
{
_template = TemplateReader.FromEmbeddedResource(templateFile);
}
protected TemplateView(TemplateDocument template)
{
_template = template;
+14 -6
View File
@@ -17,10 +17,18 @@
<RepositoryUrl>https://github.com/YaslePoy/mROA</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Version>2.0.7</Version>
<Version>3.0.3</Version>
<PackageIcon>mroaLogo.png</PackageIcon>
</PropertyGroup>
<PropertyGroup>
<PackageLicenseFile>LICENSE-2.0.txt</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<None Include="LICENSE-2.0.txt" Pack="true" PackagePath="$(PackageLicenseFile)"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
<PrivateAssets>all</PrivateAssets>
@@ -32,8 +40,8 @@
<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Update="mroaLogo.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
@@ -49,9 +57,9 @@
<ItemGroup>
<None Remove="IndexProvider.cstmpl" />
<EmbeddedResource Include="IndexProvider.cstmpl" />
<None Remove="IndexProvider.cstmpl" />
<EmbeddedResource Include="IndexProvider.cstmpl" />
</ItemGroup>
</Project>
</Project>
+40 -20
View File
@@ -30,7 +30,7 @@ namespace mROA.Codegen
private static readonly Predicate<ITypeSymbol> ParameterFilterForType =
i => i.Name is "CancellationToken" or "RequestContext";
private int _currentInternalCallIndex;
public void Initialize(IncrementalGeneratorInitializationContext context)
@@ -71,7 +71,7 @@ namespace mROA.Codegen
if (compilation.AssemblyName == null)
throw new InvalidOperationException();
indexProviderTemplate.DefineNamespace(compilation.AssemblyName);
var invokers = new List<string>();
var declarations = classes.ToList();
@@ -137,15 +137,18 @@ namespace mROA.Codegen
// declaredMethods.Add(impl);
break;
case IEventSymbol eventSymbol:
proxyTemplate.InsertMethods($"public event {eventSymbol.Type.ToUnityString()}? {eventSymbol.Name};");
proxyTemplate.InsertMethods(
$"public event {eventSymbol.Type.ToUnityString()}? {eventSymbol.Name};");
// declaredMethods.Add(
// $"public event {eventSymbol.Type.ToDisplayString()}? {eventSymbol.Name};");
break;
}
GenerateEventImplementation(proxyTemplate, methodRepoTemplate, typeBinder, classSymbol, invokers, context);
GenerateEventImplementation(proxyTemplate, methodRepoTemplate, typeBinder, classSymbol, invokers,
context);
var endInvokers = invokers.Count;
indexProviderTemplate.InsertIndexSpan($"{{ typeof({originalName}), new[] {{ {CodegenUtilities.JoinWithComa(Enumerable.Range(startInvokers, endInvokers - startInvokers).Select(i => i.ToString()))} }} }},");
indexProviderTemplate.InsertIndexSpan(
$"{{ typeof({originalName}), new[] {{ {CodegenUtilities.JoinWithComa(Enumerable.Range(startInvokers, endInvokers - startInvokers).Select(i => i.ToString()))} }} }},");
proxyTemplate.DefineClassName(className);
proxyTemplate.DefineOriginalName(originalName);
proxyTemplate.DefineNamespaceName(namespaceName);
@@ -157,7 +160,8 @@ namespace mROA.Codegen
#if !DONT_ADD
context.AddSource($"{className}.g.cs", SourceText.From(code, Encoding.UTF8));
#endif
indexProviderTemplate.InsertRemoteTypePair($"{{ typeof({classSymbol.ToUnityString()}), (id, r, c, indices) => new {namespaceName}.{className}(id, r, c, indices) }}");
indexProviderTemplate.InsertRemoteTypePair(
$"{{ typeof({classSymbol.ToUnityString()}), (id, r, c, indices) => new {namespaceName}.{className}(id, r, c, indices) }}");
}
if (totalMethods.Count != 0)
@@ -184,7 +188,8 @@ namespace mROA.Codegen
}
}
private void GenerateEventImplementation(ProxyTemplate proxyTemplate, MethodRepoTemplate methodRepoTemplate, RemoteTypeBinderTemplate remoteTypeBinder, INamedTypeSymbol classSymbol,
private void GenerateEventImplementation(ProxyTemplate proxyTemplate, MethodRepoTemplate methodRepoTemplate,
RemoteTypeBinderTemplate remoteTypeBinder, INamedTypeSymbol classSymbol,
List<string> invokers, SourceProductionContext context)
{
var events = classSymbol.AllInterfaces.Add(classSymbol).SelectMany(i => i.GetMembers())
@@ -195,7 +200,7 @@ namespace mROA.Codegen
var partialInterfaceTemplate = new PartialInterfaceTemplate();
partialInterfaceTemplate.DefineName(classSymbol.Name);
partialInterfaceTemplate.DefineNamespace(classSymbol.ContainingNamespace.ToDisplayString());
var objectBinderTemplate = remoteTypeBinder.CloneInnerObjectBinder();
foreach (var currentEvent in events)
{
@@ -207,7 +212,7 @@ namespace mROA.Codegen
GenerateEventCode(methodRepoTemplate, currentEvent, invokers, classSymbol);
GenerateBinderCode(objectBinderTemplate, currentEvent, classSymbol);
}
objectBinderTemplate.DefineType(classSymbol.ToUnityString());
var partialInterface = partialInterfaceTemplate.Compile();
var binder = objectBinderTemplate.Compile();
@@ -235,7 +240,8 @@ namespace mROA.Codegen
return caller;
}
private void GenerateDeclaredMethod(ProxyTemplate proxyTemplate, MethodRepoTemplate methodRepoTemplate, IMethodSymbol method, List<string> invokers,
private void GenerateDeclaredMethod(ProxyTemplate proxyTemplate, MethodRepoTemplate methodRepoTemplate,
IMethodSymbol method, List<string> invokers,
INamedTypeSymbol baseInterface)
{
var sb = new StringBuilder();
@@ -316,11 +322,14 @@ namespace mROA.Codegen
var parametersInsertList = new List<string>();
var useCancellationToken = false;
foreach (var parameter in method.Parameters)
switch (parameter.Type.Name)
{
case "CancellationToken":
parametersInsertList.Add("(CancellationToken)special[1]");
useCancellationToken = true;
break;
case "RequestContext":
parametersInsertList.Add("(RequestContext)special[0]");
@@ -359,6 +368,7 @@ namespace mROA.Codegen
invokerTemplate.DefineSuitableType(baseInterface.ToUnityString());
invokerTemplate.DefineFuncInvoking(funcInvoking);
invokerTemplate.DefineIsTrusted((!isUntrusted).ToString().ToLower());
invokerTemplate.DefineCancellation(useCancellationToken.ToString().ToLower());
backend = invokerTemplate.Compile();
}
else
@@ -370,6 +380,7 @@ namespace mROA.Codegen
invokerTemplate.DefineSuitableType(baseInterface.ToUnityString());
invokerTemplate.DefineFuncInvoking(funcInvoking);
invokerTemplate.DefineIsTrusted((!isUntrusted).ToString().ToLower());
backend = invokerTemplate.Compile();
}
@@ -378,7 +389,8 @@ namespace mROA.Codegen
invokers.Add(backend);
}
private void GenerateBinderCode(ObjectBinderTemplate objectBinderTemplate, IEventSymbol eventSymbol, INamedTypeSymbol baseType)
private void GenerateBinderCode(ObjectBinderTemplate objectBinderTemplate, IEventSymbol eventSymbol,
INamedTypeSymbol baseType)
{
var eventBinderTemplate = objectBinderTemplate.CloneInnerEventBinder();
@@ -392,7 +404,7 @@ namespace mROA.Codegen
var transferParameters = CodegenUtilities.JoinWithComa(parameters.Select(i => (i, pi++))
.Where(i => !ParameterFilterForType(i.i))
.Select(i => "p" + i.Item2));
var requestIndex = parameters.FindIndex(i => i.Name == "RequestContext");
if (requestIndex != -1)
{
@@ -403,13 +415,15 @@ namespace mROA.Codegen
eventBinderTemplate.DefineType(baseType.ToUnityString());
eventBinderTemplate.DefineEventName(eventSymbol.Name);
eventBinderTemplate.DefineParametersDeclaration(parametersDeclaration);
eventBinderTemplate.DefineCommandIdTag($"context.CallIndexProvider.GetIndices(typeof({baseType.ToUnityString()}))[{index}]");
eventBinderTemplate.DefineCommandIdTag(
$"context.CallIndexProvider.GetIndices(typeof({baseType.ToUnityString()}))[{index}]");
eventBinderTemplate.DefineTransferParameters(transferParameters);
var eventBinderCode = eventBinderTemplate.Compile();
objectBinderTemplate.InsertEventBinder(eventBinderCode);
}
private void GenerateEventCode(MethodRepoTemplate methodRepoTemplate, IEventSymbol eventSymbol, List<string> invokers, ITypeSymbol baseInterface)
private void GenerateEventCode(MethodRepoTemplate methodRepoTemplate, IEventSymbol eventSymbol,
List<string> invokers, ITypeSymbol baseInterface)
{
var level = "\t\t\t";
@@ -482,7 +496,8 @@ namespace mROA.Codegen
invokerTemplate.DefineReturnType(method.ReturnType.ToUnityString());
invokerTemplate.DefineParametersType(parameterTypes);
invokerTemplate.DefineSuitableType(baseInterface.ToUnityString());
invokerTemplate.DefineFuncInvoking($"(i as {method.ContainingType.ToUnityString()})[{parameterInserts}]");
invokerTemplate.DefineFuncInvoking(
$"(i as {method.ContainingType.ToUnityString()})[{parameterInserts}]");
invokerTemplate.DefineIsTrusted("true");
backend = invokerTemplate.Compile();
@@ -494,7 +509,8 @@ namespace mROA.Codegen
invokerTemplate.DefineReturnType(method.ReturnType.ToUnityString());
invokerTemplate.DefineParametersType("");
invokerTemplate.DefineSuitableType(baseInterface.ToUnityString());
invokerTemplate.DefineFuncInvoking($"(i as {method.ContainingType.ToUnityString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name}");
invokerTemplate.DefineFuncInvoking(
$"(i as {method.ContainingType.ToUnityString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name}");
invokerTemplate.DefineIsTrusted("true");
backend = invokerTemplate.Compile();
@@ -518,13 +534,14 @@ namespace mROA.Codegen
var valueInsert = CodegenUtilities.Caster(method.Parameters.Last().Type,
"parameters[" + (method.Parameters.Length - 1) + "]");
var invokerTemplate = methodRepoTemplate.CloneInnerSyncInvoker();
invokerTemplate.DefineIsVoid("true");
invokerTemplate.DefineReturnType(method.ReturnType.ToUnityString());
invokerTemplate.DefineParametersType(parameterTypes);
invokerTemplate.DefineSuitableType(baseInterface.ToUnityString());
invokerTemplate.DefineFuncInvoking($"(i as {method.ContainingType.ToUnityString()})[{parameterInserts}] = {valueInsert}");
invokerTemplate.DefineFuncInvoking(
$"(i as {method.ContainingType.ToUnityString()})[{parameterInserts}] = {valueInsert}");
invokerTemplate.DefineIsTrusted("true");
backend = invokerTemplate.Compile();
@@ -536,7 +553,8 @@ namespace mROA.Codegen
invokerTemplate.DefineReturnType(method.ReturnType.ToUnityString());
invokerTemplate.DefineParametersType($"typeof({method.Parameters.First().Type.ToUnityString()})");
invokerTemplate.DefineSuitableType(baseInterface.ToUnityString());
invokerTemplate.DefineFuncInvoking($"(i as {method.ContainingType.ToUnityString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name} = {CodegenUtilities.Caster((method.AssociatedSymbol as IPropertySymbol)!.Type, "parameters[0]")}");
invokerTemplate.DefineFuncInvoking(
$"(i as {method.ContainingType.ToUnityString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name} = {CodegenUtilities.Caster((method.AssociatedSymbol as IPropertySymbol)!.Type, "parameters[0]")}");
invokerTemplate.DefineIsTrusted("true");
backend = invokerTemplate.Compile();
@@ -575,7 +593,9 @@ namespace mROA.Codegen
public static string ToFullString(IParameterSymbol parameter)
{
return parameter.Type.ToUnityString() + " " + parameter.Name;
var coreString = $"{parameter.Type.ToUnityString()} {parameter.Name}";
if (parameter.RefKind == RefKind.In) coreString = $"in {coreString}";
return coreString;
}
public static string ToFullString(ITypeSymbol type)
+22
View File
@@ -0,0 +1,22 @@
using mROA.Benchmark;
namespace mROA.Test;
[TestFixture]
public class BenchmarkTest
{
[Test]
public void Benchmark()
{
var bench = new TaskWaiting();
var x = bench.DefaultJob();
var y = bench.DefaultJobAsync();
y.Wait();
if (x == y.Result)
{
Assert.Pass();
}
Assert.Fail();
}
}
+23
View File
@@ -0,0 +1,23 @@
using System;
using System.Threading.Tasks;
using mROA.Implementation;
namespace mROA.Test;
[TestFixture]
public class ConcurrentTest
{
private CircularMemoryManager _cmm;
[SetUp]
public void Setup()
{
_cmm = new CircularMemoryManager(100);
}
[Test]
public void ParallelAlloc()
{
Assert.Fail();
}
}
+1
View File
@@ -27,6 +27,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\mROA.Benchmark\mROA.Benchmark.csproj" />
<ProjectReference Include="..\mROA.Cbor\mROA.Cbor.csproj" />
<ProjectReference Include="..\mROA\mROA.csproj" />
</ItemGroup>
-10
View File
@@ -25,10 +25,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mROA.CodegenTools", "mROA.C
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Load", "Example.Load\Example.Load.csproj", "{930B236B-BDAA-4B8C-8054-5B992BAE6622}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Functionality.Shared", "Functionality.Shared\Functionality.Shared.csproj", "{D9D28596-E10C-4A98-A2AA-573219467506}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{8C20901F-B416-4ABC-8AA4-9059646B081B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mROA.Benchmark", "mROA.Benchmark\mROA.Benchmark.csproj", "{E021E8B3-56C2-400E-A05E-523CF7831189}"
EndProject
Global
@@ -73,10 +69,6 @@ Global
{930B236B-BDAA-4B8C-8054-5B992BAE6622}.Debug|Any CPU.Build.0 = Debug|Any CPU
{930B236B-BDAA-4B8C-8054-5B992BAE6622}.Release|Any CPU.ActiveCfg = Release|Any CPU
{930B236B-BDAA-4B8C-8054-5B992BAE6622}.Release|Any CPU.Build.0 = Release|Any CPU
{D9D28596-E10C-4A98-A2AA-573219467506}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D9D28596-E10C-4A98-A2AA-573219467506}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9D28596-E10C-4A98-A2AA-573219467506}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9D28596-E10C-4A98-A2AA-573219467506}.Release|Any CPU.Build.0 = Release|Any CPU
{E021E8B3-56C2-400E-A05E-523CF7831189}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E021E8B3-56C2-400E-A05E-523CF7831189}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E021E8B3-56C2-400E-A05E-523CF7831189}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -91,7 +83,5 @@ Global
{9BD25A13-3165-47C0-9EAA-5C59EC490E32} = {FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E}
{E7703F5B-F2A6-4A88-AA57-EBB1E38D533E} = {FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E}
{930B236B-BDAA-4B8C-8054-5B992BAE6622} = {FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E}
{8C20901F-B416-4ABC-8AA4-9059646B081B} = {EAE92F5A-664C-41AB-8811-5885524B5347}
{D9D28596-E10C-4A98-A2AA-573219467506} = {8C20901F-B416-4ABC-8AA4-9059646B081B}
EndGlobalSection
EndGlobal
@@ -10,5 +10,6 @@ namespace mROA.Abstract
object? Deserialize(byte[] rawData, Type type, IEndPointContext? context);
T Deserialize<T>(ReadOnlyMemory<byte> rawMemory, IEndPointContext? context);
object? Cast(object? nonCasted, Type type, IEndPointContext? context);
IContextualSerializationToolKit Clone();
}
}
+10
View File
@@ -0,0 +1,10 @@
using System;
using mROA.Implementation;
namespace mROA.Abstract
{
public interface IDistributionModule
{
Action<NetworkMessage> GetDistributionAction(int clientId);
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ namespace mROA.Abstract
{
ICommandExecution? Execute(CallRequest command, IInstanceRepository instanceRepository,
IRepresentationModule representationModule, IEndPointContext context);
ICommandExecution Cancel(CancelRequest command);
ICommandExecution Cancel(CancelRequest command);
}
}
+2 -1
View File
@@ -25,7 +25,8 @@ namespace mROA.Abstract
void PostCallMessage<T>(RequestId id, EMessageType eMessageType, T payload, IEndPointContext? context)
where T : notnull;
Task PostCallMessageUntrustedAsync<T>(RequestId id, EMessageType eMessageType, T payload, IEndPointContext? context)
Task PostCallMessageUntrustedAsync<T>(RequestId id, EMessageType eMessageType, T payload,
IEndPointContext? context)
where T : notnull;
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ namespace mROA.Abstract
public interface IRequestExtractor
{
Task StartExtraction();
void PushMessage(object parced, EMessageType originalType);
void PushMessage(object parsed, EMessageType originalType);
Predicate<NetworkMessage> Rule { get; }
Func<NetworkMessage, Type?>[] Converters { get; }
}
@@ -10,7 +10,6 @@ namespace mROA.Implementation.Backend
private readonly ICancellationRepository _cancellationRepo;
private readonly IMethodRepository _methodRepo;
private readonly IContextualSerializationToolKit _serialization;
public BasicExecutionModule(ICancellationRepository cancellationRepo, IMethodRepository methodRepo,
IContextualSerializationToolKit serialization)
{
@@ -18,11 +17,9 @@ namespace mROA.Implementation.Backend
_methodRepo = methodRepo;
_serialization = serialization;
}
public ICommandExecution? Execute(CallRequest command, IInstanceRepository instanceRepository,
IRepresentationModule representationModule, IEndPointContext endPointContext)
{
// _logger.LogInformation("Executing {0}", command.Id);
try
{
var invoker = _methodRepo.GetMethod(command.CommandId);
@@ -57,7 +54,6 @@ namespace mROA.Implementation.Backend
};
}
}
private ICommandExecution? ExecuteRequest(CallRequest command, IInstanceRepository instanceRepository,
IRepresentationModule representationModule, IEndPointContext endPointContext, IMethodInvoker invoker,
object context, object?[]? castedParams, RequestContext execContext)
@@ -81,7 +77,6 @@ namespace mROA.Implementation.Backend
return result;
}
}
private static object GetInstance(CallRequest command, IInstanceRepository instanceRepository,
IMethodInvoker invoker, IEndPointContext endPointContext)
{
@@ -91,7 +86,6 @@ namespace mROA.Implementation.Backend
return context;
}
private object?[] CastedParams(CallRequest command, IMethodInvoker invoker, IEndPointContext context)
{
object?[] castedParams = new object[invoker.ParameterTypes.Length];
@@ -102,7 +96,6 @@ namespace mROA.Implementation.Backend
return castedParams;
}
public ICommandExecution Cancel(CancelRequest command)
{
var cts = _cancellationRepo.GetCancellation(command.Id);
@@ -116,7 +109,6 @@ namespace mROA.Implementation.Backend
Id = command.Id
};
}
private static ICommandExecution? Execute(MethodInvoker invoker, object instance, object?[] parameter,
CallRequest command, RequestContext executionContext)
{
@@ -141,25 +133,33 @@ namespace mROA.Implementation.Backend
Id = command.Id
};
}
private ICommandExecution? ExecuteAsync(AsyncMethodInvoker invoker, object instance, object?[]? parameters,
CallRequest command, ICancellationRepository cancellationRepository,
IRepresentationModule representationModule, RequestContext executionContext, IEndPointContext context)
{
var tokenSource = new CancellationTokenSource();
cancellationRepository.RegisterCancellation(command.Id, tokenSource);
var token = tokenSource.Token;
CancellationToken? token = null;
if (invoker.RequireCancellation)
{
var tokenSource = new CancellationTokenSource();
cancellationRepository.RegisterCancellation(command.Id, tokenSource);
token = tokenSource.Token;
}
invoker.Invoke(instance, parameters, new object[] { executionContext, token }, _ =>
{
if (token.IsCancellationRequested)
return;
if (invoker.RequireCancellation)
{
_cancellationRepo.FreeCancellation(command.Id);
if (token.Value.IsCancellationRequested)
return;
}
var payload = new FinalCommandExecution
{
Id = command.Id
};
_cancellationRepo.FreeCancellation(command.Id);
if (invoker.IsTrusted)
@@ -170,25 +170,35 @@ namespace mROA.Implementation.Backend
return null;
}
private ICommandExecution? TypedExecuteAsync(AsyncMethodInvoker invoker, object instance, object?[]? parameters,
CallRequest command, ICancellationRepository cancellationRepository,
IRepresentationModule representationModule, RequestContext executionContext, IEndPointContext context)
{
var tokenSource = new CancellationTokenSource();
cancellationRepository.RegisterCancellation(command.Id, tokenSource);
CancellationToken? token = null;
if (invoker.RequireCancellation)
{
var tokenSource = new CancellationTokenSource();
cancellationRepository.RegisterCancellation(command.Id, tokenSource);
var token = tokenSource.Token;
token = tokenSource.Token;
}
invoker.Invoke(instance, parameters, new object[] { executionContext, token },
finalResult =>
{
if (invoker.RequireCancellation)
{
_cancellationRepo.FreeCancellation(command.Id);
if (token.Value.IsCancellationRequested)
return;
}
var payload = new FinalCommandExecution<object>
{
Id = command.Id,
Result = finalResult
};
_cancellationRepo.FreeCancellation(command.Id);
representationModule.PostCallMessageAsync(command.Id, EMessageType.FinishedCommandExecution,
payload, context);
@@ -25,15 +25,15 @@ namespace mROA.Implementation.Backend
public IRequestExtractor this[int id] => _producedExtractors[id];
public IRequestExtractor HubOnOnConnected(IRepresentationModule interaction)
public IRequestExtractor HubOnOnConnected(IRepresentationModule representationModule)
{
var extractor = CreateExtractor(interaction);
var extractor = CreateExtractor(representationModule);
if (_mode.DistributionType == EDistributionType.Channeled)
{
extractor.StartExtraction().ContinueWith(_ => OnDisconnected(interaction));
extractor.StartExtraction().ContinueWith(_ => OnDisconnected(representationModule));
}
_producedExtractors[interaction.Id] = extractor;
_producedExtractors[representationModule.Id] = extractor;
return extractor;
}
@@ -14,15 +14,14 @@ namespace mROA.Implementation.Backend
private readonly TcpListener _tcpListener;
private readonly IConnectionHub _hub;
private readonly HubRequestExtractor _hre;
private readonly DistributionOptions _distribution;
private readonly IDistributionModule _distribution;
private readonly IContextualSerializationToolKit _serialization;
private readonly Dictionary<int, CancellationTokenSource> _extractorsTokenSources = new();
private readonly ICallIndexProvider _callIndexProvider;
private readonly IIdentityGenerator _identityGenerator;
public NetworkGatewayModule(IOptions<GatewayOptions> options, IIdentityGenerator identityGenerator,
IContextualSerializationToolKit serialization, ICallIndexProvider callIndexProvider, IConnectionHub hub,
IOptions<DistributionOptions> distribution, HubRequestExtractor hre)
IContextualSerializationToolKit serialization, ICallIndexProvider callIndexProvider, IConnectionHub hub, HubRequestExtractor hre, IDistributionModule distribution)
{
_tcpListener = new(options.Value.Endpoint);
_identityGenerator = identityGenerator;
@@ -30,7 +29,7 @@ namespace mROA.Implementation.Backend
_callIndexProvider = callIndexProvider;
_hub = hub;
_hre = hre;
_distribution = distribution.Value;
_distribution = distribution;
}
public void Run()
@@ -58,6 +57,7 @@ namespace mROA.Implementation.Backend
private async Task HandleConnection(TcpClient client)
{
client.NoDelay = true;
Console.WriteLine($"Client connected from {client.Client.RemoteEndPoint}");
var interaction = new ChannelInteractionModule(_serialization, _identityGenerator);
@@ -79,7 +79,7 @@ namespace mROA.Implementation.Backend
switch (connectionRequest.MessageType)
{
case EMessageType.ClientConnect:
HandleNewClient(context, interaction, streamExtractor, cts, connectionRequest);
break;
case EMessageType.ClientRecovery:
@@ -94,7 +94,8 @@ namespace mROA.Implementation.Backend
}
private void HandleNewClient(EndPointContext context, ChannelInteractionModule interaction,
ChannelInteractionModule.StreamExtractor streamExtractor, CancellationTokenSource cts, NetworkMessage connection)
ChannelInteractionModule.StreamExtractor streamExtractor, CancellationTokenSource cts,
NetworkMessage connection)
{
context.HostId = 0;
context.OwnerId = -interaction.ConnectionId;
@@ -106,12 +107,10 @@ namespace mROA.Implementation.Backend
_extractorsTokenSources[interaction.ConnectionId] = cts;
_hub.RegisterInteraction(interaction);
var requestExtractor = _hre.HubOnOnConnected(new RepresentationModule(interaction, _serialization));
_hre.HubOnOnConnected(new RepresentationModule(interaction, _serialization.Clone()));
streamExtractor.MessageReceived = _distribution.GetDistributionAction(interaction.ConnectionId);
if (_distribution.DistributionType != EDistributionType.Channeled)
{
BindRequestFirstDistribution(context, interaction, streamExtractor, requestExtractor);
}
}
private void BindRequestFirstDistribution(IEndPointContext context, IChannelInteractionModule interaction,
@@ -126,8 +125,8 @@ namespace mROA.Implementation.Backend
{
var func = converters[i];
if (func(message) is not { } t) continue;
var deserialized = _serialization.Deserialize(message.Data, t, context);
var deserialized = _serialization.Deserialize(message.Data, t, context)!;
Task.Run(() => requestExtractor.PushMessage(deserialized, message.MessageType));
break;
}
@@ -155,12 +154,8 @@ namespace mROA.Implementation.Backend
};
_ = streamExtractor.SendFromChannel(recoveryInteraction.TrustedPostChanel, cts.Token);
if (_distribution.DistributionType == EDistributionType.ExtractorFirst)
{
BindRequestFirstDistribution(recoveryInteraction.Context, recoveryInteraction, streamExtractor,
_hre[recoveryInteraction.ConnectionId]);
}
streamExtractor.MessageReceived = _distribution.GetDistributionAction(recoveryInteraction.ConnectionId);
Task.Run(async () => await streamExtractor.LoopedReceive(cts.Token).ConfigureAwait(false));
recoveryInteraction.Restart(false);
+8 -7
View File
@@ -14,15 +14,17 @@ namespace mROA.Implementation.Backend
{
private readonly IConnectionHub _hub;
private readonly UdpClient _client;
private readonly Dictionary<IPEndPoint, int> _reservedPorts = new();
private readonly Dictionary<IPEndPoint, Action<NetworkMessage>> _distributionActions = new();
private readonly CancellationTokenSource _tokenSource = new();
private readonly IContextualSerializationToolKit _serializationToolkit;
private readonly IDistributionModule _distribution;
public UdpGateway(IOptions<GatewayOptions> options, IConnectionHub hub,
IContextualSerializationToolKit serializationToolkit)
IContextualSerializationToolKit serializationToolkit, IDistributionModule distribution)
{
_hub = hub;
_serializationToolkit = serializationToolkit;
_distribution = distribution;
_client = new UdpClient(options.Value.Endpoint);
}
@@ -49,13 +51,12 @@ namespace mROA.Implementation.Backend
{
case UntrustedConnect:
channelId = BitConverter.ToInt32(parsed.Data);
_reservedPorts[incoming.RemoteEndPoint] = channelId;
_distributionActions[incoming.RemoteEndPoint] = _distribution.GetDistributionAction(channelId);
_ = UntrustedSend(_hub.GetInteraction(channelId), incoming.RemoteEndPoint);
break;
default:
channelId = _reservedPorts[incoming.RemoteEndPoint];
var interaction = _hub.GetInteraction(channelId);
await interaction.ReceiveChanel.Writer.WriteAsync(parsed, token);
_distributionActions[incoming.RemoteEndPoint].Invoke(parsed);
break;
}
}
-2
View File
@@ -2,8 +2,6 @@
namespace mROA.Implementation
{
public struct CallRequest
{
public RequestId Id { get; set; }
+17 -23
View File
@@ -143,7 +143,6 @@ namespace mROA.Implementation
public class StreamExtractor
{
private const int BufferSize = ushort.MaxValue + 19;
private readonly Stream _ioStream;
private readonly Memory<byte> _buffer = new byte[BufferSize];
@@ -153,27 +152,25 @@ namespace mROA.Implementation
}
public Action<NetworkMessage> MessageReceived = _ => { };
public async Task SingleReceive(CancellationToken token = default)
{
var firstRead = await _ioStream.ReadAsync(_buffer, token);
var meta = MemoryMarshal.Read<NetworkMessage.NetworkMessageMeta>(_buffer.Span);
var len = meta.BodyLength;
var readLen = firstRead - 19;
if (readLen != len)
{
var lastPart = _buffer[firstRead..(len + 19)];
await _ioStream.ReadExactlyAsync(lastPart, cancellationToken: token);
}
var message = meta.ToMessage(_buffer.Span);
#if TRACE
Console.WriteLine("RECV " + message);
#endif
MessageReceived(message);
_ = await _ioStream.ReadExactlyAsync(_buffer[..19], token);
var meta = MemoryMarshal.Read<NetworkMessage.NetworkMessageMeta>(_buffer.Span);
var len = meta.BodyLength;
var range = 19..(len + 19);
// Console.WriteLine($"{range} {_buffer.Length}");
var lastPart = _buffer[range];
await _ioStream.ReadExactlyAsync(lastPart, cancellationToken: token);
var message = meta.ToMessage(_buffer.Span);
// Console.WriteLine("RECV " + message);
MessageReceived(message);
}
public async Task LoopedReceive(CancellationToken token = default)
@@ -190,11 +187,8 @@ namespace mROA.Implementation
MemoryMarshal.Write(_buffer.Span, ref meta);
message.Data.CopyTo(_buffer.Span[19..]);
var sendingSpan = _buffer[..(19 + meta.BodyLength)];
// Console.WriteLine("SEND " + message);
await _ioStream.WriteAsync(sendingSpan, token);
#if TRACE
Console.WriteLine("SEND " + message);
#endif
// _logger.LogTrace("SEND {0}", message.ToString());
}
public async Task SendFromChannel(ChannelReader<NetworkMessage> channel,
@@ -0,0 +1,60 @@
using System;
using System.Threading;
namespace mROA.Implementation
{
public class CircularMemoryManager
{
private readonly Memory<byte> _buffer;
private Memory<byte> _current;
private SpinLock _spinLock = new(false);
public CircularMemoryManager(int size)
{
_buffer = new Memory<byte>(new byte[size]);
_current = _buffer;
}
public Span<byte> AllocSlice(int size)
{
Span<byte> order;
var lockTaken = false;
try
{
_spinLock.Enter(ref lockTaken);
if (_current.Length < size)
_current = _buffer;
order = _current.Span[..size];
_current = _current[size..];
}
finally
{
if (lockTaken) _spinLock.Exit(false);
}
return order;
}
public Memory<byte> AllocMemory(int size)
{
Memory<byte> order;
var lockTaken = false;
try
{
_spinLock.Enter(ref lockTaken);
if (_current.Length < size)
_current = _buffer;
order = _current[..size];
_current = _current[size..];
}
finally
{
if (lockTaken) _spinLock.Exit(false);
}
return order;
}
}
}
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using mROA.Abstract;
@@ -5,16 +6,16 @@ namespace mROA.Implementation
{
public class CollectableMethodRepository : IMethodRepository
{
private readonly List<IMethodInvoker> _methods = new();
private readonly List<IMethodInvoker> _methods = new() { MethodInvoker.Dispose };
private IMethodInvoker[] _baked = Array.Empty<IMethodInvoker>();
public void AppendInvokers(IEnumerable<IMethodInvoker> methodInvokers)
{
_methods.AddRange(methodInvokers);
_baked = _methods.ToArray();
}
public IMethodInvoker GetMethod(int id)
{
return id == -1 ? MethodInvoker.Dispose : _methods[id];
return _baked[++id];
}
}
}
@@ -0,0 +1,11 @@
using System;
using mROA.Abstract;
namespace mROA.Implementation.CommandExecution
{
public class AsyncCommandExecution : ICommandExecution
{
public RequestId Id { get; set; }
public EMessageType MessageType => EMessageType.Unknown;
}
}
@@ -13,12 +13,11 @@ namespace mROA.Implementation
_serialization = serialization;
}
public IRepresentationModule Produce(int id)
{
var interaction = _hub.GetInteraction(id);
var produced = new RepresentationModule(interaction, _serialization);
var produced = new RepresentationModule(interaction, _serialization.Clone());
return produced;
}
@@ -0,0 +1,70 @@
using System;
using System.Threading.Tasks;
using mROA.Abstract;
using mROA.Implementation.Backend;
namespace mROA.Implementation
{
public class ChannelDistributionModule : IDistributionModule
{
private readonly IConnectionHub _hub;
public ChannelDistributionModule(IConnectionHub hub)
{
_hub = hub;
}
public Action<NetworkMessage> GetDistributionAction(int clientId)
{
var writer = _hub.GetInteraction(clientId).ReceiveChanel.Writer;
return message =>
{
writer.TryWrite(message);
};
}
}
public class ExtractorFirstDistributionModule : IDistributionModule
{
private readonly IConnectionHub _hub;
private readonly IContextualSerializationToolKit _serialization;
private readonly HubRequestExtractor _extractorHub;
public ExtractorFirstDistributionModule(HubRequestExtractor extractorHub, IConnectionHub hub, IContextualSerializationToolKit serialization)
{
_extractorHub = extractorHub;
_hub = hub;
_serialization = serialization;
}
public Action<NetworkMessage> GetDistributionAction(int clientId)
{
var interaction = _hub.GetInteraction(clientId);
var writer = interaction.ReceiveChanel.Writer;
var context = interaction.Context;
var requestExtractor = _extractorHub[clientId];
var converters = requestExtractor.Converters;
var serialization = _serialization.Clone();
return message =>
{
if (requestExtractor.Rule(message))
{
for (var i = 0; i < converters.Length; i++)
{
var func = converters[i];
if (func(message) is not { } t) continue;
var deserialized = serialization.Deserialize(message.Data, t, context)!;
Task.Run(() => requestExtractor.PushMessage(deserialized, message.MessageType));
break;
}
return;
}
writer.WriteAsync(message).ConfigureAwait(false);
};
}
}
}
@@ -50,8 +50,7 @@ namespace mROA.Implementation.Frontend
throw new Exception(
$"Incorrect message type. Must be IdAssigning, current : {idMessage.MessageType.ToString()}");
}
_ = Task.Run(async () => await _currentExtractor.LoopedReceive(_rawExtractorCancellation.Token));
var assignment = _serialization.Deserialize<IdAssignment>(idMessage.Data, _context);
@@ -36,20 +36,20 @@ namespace mROA.Implementation.Frontend
}
}
public void PushMessage(object parced, EMessageType originalType)
public void PushMessage(object parsed, EMessageType originalType)
{
switch (originalType)
{
case EMessageType.CallRequest:
HandleCallRequest((CallRequest)parced);
HandleCallRequest((CallRequest)parsed);
break;
case EMessageType.ClientDisconnect:
return;
case EMessageType.EventRequest:
HandleEventRequest((CallRequest)parced);
HandleEventRequest((CallRequest)parsed);
break;
case EMessageType.CancelRequest:
HandleCancelRequest((CancelRequest)parced);
HandleCancelRequest((CancelRequest)parsed);
break;
default:
throw new ArgumentOutOfRangeException();
@@ -76,7 +76,7 @@ namespace mROA.Implementation.Frontend
private void HandleCallRequest(CallRequest request)
{
var result = _executeModule.Execute(request, _context.RealRepository, _representationModule, _context);
if (result is null)
{
return;
@@ -55,7 +55,7 @@ namespace mROA.Implementation.Frontend
var initMessage = new NetworkMessage
{
MessageType = EMessageType.UntrustedConnect, Id = RequestId.Generate(),
Data = BitConverter.GetBytes(_channelInteractionModule.ConnectionId)
Data = BitConverter.GetBytes(-_channelInteractionModule.ConnectionId)
};
var initParsed = _serializationToolkit.Serialize(initMessage, _context);
+1 -1
View File
@@ -37,7 +37,7 @@ namespace mROA.Implementation
public Type[] ParameterTypes { get; set; } = Type.EmptyTypes;
public Type? ReturnType { get; set; }
public Type SuitableType { get; set; } = typeof(object);
public bool RequireCancellation { get; set; } = true;
public Action<object, object?[]?, object[], Action<object?>> Invoking { get; set; } =
(_, _, _, post) => { post.Invoke(null); };
+2 -3
View File
@@ -35,8 +35,7 @@ namespace mROA.Implementation
public EMessageType MessageType { get; set; }
public byte[] Data { get; set; }
public object Serialized { get; set; }
public IEndPointContext Context { get; set; }
public override string ToString()
{
return $" {Id}:{MessageType} [{Data.Length}]";
@@ -51,7 +50,7 @@ namespace mROA.Implementation
Id = Id
};
}
public struct NetworkMessageMeta
{
public RequestId Id;
@@ -8,6 +8,7 @@ namespace mROA.Implementation
public class RemoteInstanceRepository : IInstanceRepository
{
private readonly List<RemoteObjectBase> _producedProxies = new();
private readonly List<RemoteObjectBase> _producedSingletonProxies = new();
private readonly ICallIndexProvider _callIndexProvider;
private readonly IRepresentationModuleProducer _representationProducer;
@@ -32,9 +33,9 @@ namespace mROA.Implementation
public T GetObject<T>(ComplexObjectIdentifier id, IEndPointContext context) where T : class
{
var index = _producedProxies.Find(i => i.Identifier.Equals(id));
if (index is not null)
return (T)(index as object);
var existing = _producedProxies.Find(i => i.Identifier.Equals(id));
if (existing is not null)
return (T)(existing as object);
if (!_callIndexProvider.Activators.TryGetValue(typeof(T), out var remoteType))
throw new NotSupportedException();
@@ -55,6 +56,10 @@ namespace mROA.Implementation
public object GetSingletonObject(Type type, IEndPointContext context)
{
var existing = _producedSingletonProxies.Find(i => i.GetType() == type);
if (existing is not null)
return existing;
var representationModule =
_representationProducer.Produce(context.OwnerId);
@@ -62,6 +67,7 @@ namespace mROA.Implementation
_callIndexProvider.GetIndices(type))!;
_producedProxies.Add(instance);
_producedSingletonProxies.Add(instance);
return _producedProxies.Last();
}
+1 -2
View File
@@ -33,8 +33,7 @@ namespace mROA.Implementation
{
var writer = _interaction.ReceiveChanel.Writer;
var reader = _interaction.ReceiveChanel.Reader;
await foreach (var message in reader.ReadAllAsync(token))
{
if (!rule(message))
@@ -0,0 +1,7 @@
namespace mROA.Implementation
{
public class SerializationBufferOffset
{
public int Offset { get; set; } = 0;
}
}
+2 -1
View File
@@ -9,7 +9,8 @@ namespace mROA
{
public static async ValueTask<int> ReadExactlyAsync(this Stream stream, byte[] buffer, int offset, int count)
{
return await stream.ReadAtLeastAsyncCore(buffer.AsMemory(offset, count), count, true, CancellationToken.None);
return await stream.ReadAtLeastAsyncCore(buffer.AsMemory(offset, count), count, true,
CancellationToken.None);
}
public static ValueTask<int> ReadExactlyAsync(this Stream stream, Memory<byte> buffer,
+1 -8
View File
@@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<Title>mROA</Title>
<Version>2.0.8</Version>
<Version>3.0.4</Version>
<Authors>YaslePoy</Authors>
<Description>Fast and easy RPC with contex</Description>
<RepositoryUrl>https://github.com/YaslePoy/mROA</RepositoryUrl>
@@ -38,11 +38,4 @@
<PackagePath></PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.Logging.Abstractions">
<HintPath>..\..\..\..\.nuget\packages\microsoft.extensions.logging.abstractions\9.0.7\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
</Reference>
</ItemGroup>
</Project>