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 37e768d6e8 Remove waiting from demo 2025-08-06 15:03:46 +03:00
micialware 5ece345a3f Add logging 2025-08-06 14:50:10 +03:00
micialware 304811c964 Prepare for many parsers 2025-08-06 14:32:48 +03:00
micialware 2cbcc6b201 Remove ICallRequest 2025-08-03 00:32:32 +03:00
micialware 0818ed6a5a remove additional logging from load test 2025-07-30 23:38:03 +03:00
micialware 3aa723c2ca remove AsyncCommandExecution 2025-07-29 17:03:27 +03:00
micialware dea464d251 Remove logging 2025-07-28 13:51:19 +03:00
micialware 4dca13b85b Merge pull request #4 from YaslePoy/codegen-refactoring
Codegen refactoring
2025-07-27 23:49:16 +03:00
micialware 9425f25081 Merge pull request #3 from YaslePoy/header-remake
Header remake
2025-07-27 23:47:18 +03:00
micialware 965c8eb823 serialization improves and benchmarks 2025-07-27 23:46:54 +03:00
micialware 6c55567d3f fast new message header 2025-07-27 11:52:16 +03:00
micialware bb3c4887df Works, but slow for many connections 2025-07-27 00:14:15 +03:00
micialware be12f1da65 Preparing for new request id 2025-07-26 20:44:19 +03:00
micialware 31d4102dac Using cbor writer without releasing memory 2025-07-26 19:51:54 +03:00
micialware 14ec6abd8d Adding configure option on client connect 2025-07-21 23:46:04 +03:00
micialware f8998178ff Global refactoring with ReSharper 2025-07-20 00:20:05 +03:00
micialware 5cbfb4d2b1 Merge remote-tracking branch 'origin/Distribution' 2025-07-19 21:15:53 +03:00
micialware 83391bd55e Load test header update 2025-07-19 21:15:03 +03:00
micialware e4d6709a77 Backward call with ExtractorFirst option works 2025-07-19 21:08:25 +03:00
micialware c7c5b1637c Direct to exe mode works faster, but has restrictions 2025-07-19 17:18:46 +03:00
micialware 79d0e4b5b1 Base of synced distribution model written 2025-07-19 16:01:41 +03:00
micialware 0c648f0af0 Refactor Network gateway module 2025-07-18 14:08:29 +03:00
micialware 85cf1bc48a Refactoring and configure await false 2025-07-18 13:49:36 +03:00
micialware 153272a1fa Rewrite for own cbor lib 2025-07-15 17:10:12 +03:00
micialware 17a6d3aad6 New distribution model base 2025-07-14 21:03:12 +03:00
109 changed files with 2668 additions and 1054 deletions
+2 -2
View File
@@ -7,14 +7,14 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>9</LangVersion> <LangVersion>12</LangVersion>
<PublishAot>true</PublishAot> <PublishAot>true</PublishAot>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+1 -1
View File
@@ -11,7 +11,7 @@ namespace Example.Backend
{ {
private readonly List<IPrinter> _printers = new(); private readonly List<IPrinter> _printers = new();
public IPrinter Create(string printerName) public IPrinter Create(in string printerName)
{ {
Console.WriteLine("Creating printer"); Console.WriteLine("Creating printer");
return new Printer { Name = printerName }; return new Printer { Name = printerName };
+11 -16
View File
@@ -1,5 +1,4 @@
using System; using System;
using System.Linq;
using System.Net; using System.Net;
using Example.Backend; using Example.Backend;
using Example.Shared; using Example.Shared;
@@ -10,24 +9,26 @@ using mROA.Implementation;
using mROA.Implementation.Backend; using mROA.Implementation.Backend;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
class Program class Program
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
var builder = Host.CreateApplicationBuilder(); var builder = Host.CreateApplicationBuilder();
builder.Services.AddLogging(l => l.AddConsole());
builder.Services.AddSingleton<IContextualSerializationToolKit, CborSerializationToolkit>(); builder.Services.AddSingleton<IContextualSerializationToolKit, CborSerializationToolkit>();
builder.Services.AddSingleton<IIdentityGenerator, BackendIdentityGenerator>(); builder.Services.AddSingleton<IIdentityGenerator, BackendIdentityGenerator>();
builder.Services.AddSingleton<IGatewayModule, NetworkGatewayModule>(); builder.Services.AddSingleton<IGatewayModule, NetworkGatewayModule>();
builder.Services.AddSingleton<IUntrustedGateway, UdpGateway>(); builder.Services.AddSingleton<IUntrustedGateway, UdpGateway>();
builder.Services.AddSingleton<IConnectionHub, ConnectionHub>(); builder.Services.AddSingleton<IConnectionHub, ConnectionHub>();
builder.Services.AddOptions(); builder.Services.AddOptions();
var listening = new IPEndPoint(IPAddress.Any, 4567); var listening = new IPEndPoint(IPAddress.Any, 4567);
builder.Services.Configure<GatewayOptions>(options => options.Endpoint = listening); builder.Services.Configure<GatewayOptions>(options => options.Endpoint = listening);
builder.Services.AddSingleton<IDistributionModule, ExtractorFirstDistributionModule>();
builder.Services.Configure<SerializationBufferOffset>(options => options.Offset = 0);
builder.Services.AddSingleton<HubRequestExtractor>(); builder.Services.AddSingleton<HubRequestExtractor>();
builder.Services.AddSingleton<IExecuteModule, BasicExecutionModule>(); builder.Services.AddSingleton<IExecuteModule, BasicExecutionModule>();
builder.Services.AddSingleton<IRepresentationModuleProducer, CreativeRepresentationModuleProducer>(); builder.Services.AddSingleton<IRepresentationModuleProducer, CreativeRepresentationModuleProducer>();
builder.Services.AddSingleton<IInstanceRepository, RemoteInstanceRepository>(); builder.Services.AddSingleton<IInstanceRepository, RemoteInstanceRepository>();
@@ -39,7 +40,6 @@ class Program
return repo; return repo;
})); }));
builder.Services.AddSingleton<IMethodRepository>(p => builder.Services.AddSingleton<IMethodRepository>(p =>
{ {
var methodRepo = new CollectableMethodRepository(); var methodRepo = new CollectableMethodRepository();
@@ -47,21 +47,16 @@ class Program
return methodRepo; return methodRepo;
}); });
builder.Services.AddSingleton<ICallIndexProvider, GeneratedCallIndexProvider>(); builder.Services.AddSingleton<ICallIndexProvider, GeneratedCallIndexProvider>();
builder.Services.AddSingleton<ICancellationRepository, CancellationRepository>(); builder.Services.AddSingleton<ICancellationRepository, CancellationRepository>();
builder.Services.Configure<DistributionOptions>(o => o.DistributionType = EDistributionType.ExtractorFirst);
var host = builder.Build(); var host = builder.Build();
host.Services.GetService<HubRequestExtractor>(); new RemoteTypeBinder();
//
// builder.Build();
new RemoteTypeBinder();
//
//
_ = host.Services.GetService<IUntrustedGateway>()!.Start();
var gateway = host.Services.GetService<IGatewayModule>();
gateway.Run();
Console.ReadLine(); _ = host.Services.GetService<IUntrustedGateway>()!.Start();
var gateway = host.Services.GetService<IGatewayModule>();
gateway.Run();
Console.ReadLine();
} }
} }
+1 -1
View File
@@ -31,7 +31,7 @@ namespace Example.Frontend
public string GetName() public string GetName()
{ {
Console.WriteLine("ClientBasedPrinter called from server!!!!!!!!!!! Vova likes that:)"); Console.WriteLine("ClientBasedPrinter called from server!!!!!!!!!!!");
DemoCheck.BackwardCall = true; DemoCheck.BackwardCall = true;
return "ClientBasedPrinter from mroa"; return "ClientBasedPrinter from mroa";
+1 -1
View File
@@ -6,7 +6,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>9</LangVersion> <LangVersion>12</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+19 -10
View File
@@ -8,6 +8,7 @@ using Example.Frontend;
using Example.Shared; using Example.Shared;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using mROA.Abstract; using mROA.Abstract;
using mROA.Cbor; using mROA.Cbor;
using mROA.Codegen; using mROA.Codegen;
@@ -15,7 +16,6 @@ using mROA.Implementation;
using mROA.Implementation.Backend; using mROA.Implementation.Backend;
using mROA.Implementation.Frontend; using mROA.Implementation.Frontend;
class Program class Program
{ {
public static async Task Main(string[] args) public static async Task Main(string[] args)
@@ -23,6 +23,8 @@ class Program
new RemoteTypeBinder(); new RemoteTypeBinder();
var builder = Host.CreateApplicationBuilder(new HostApplicationBuilderSettings { DisableDefaults = true }); var builder = Host.CreateApplicationBuilder(new HostApplicationBuilderSettings { DisableDefaults = true });
builder.Services.AddLogging(l => l.SetMinimumLevel(LogLevel.Trace).AddConsole());
builder.Services.AddSingleton<IContextualSerializationToolKit, CborSerializationToolkit>(); builder.Services.AddSingleton<IContextualSerializationToolKit, CborSerializationToolkit>();
builder.Services.AddSingleton<IEndPointContext, EndPointContext>(); builder.Services.AddSingleton<IEndPointContext, EndPointContext>();
builder.Services.AddSingleton<IRealStoreInstanceRepository, InstanceRepository>(provider => builder.Services.AddSingleton<IRealStoreInstanceRepository, InstanceRepository>(provider =>
@@ -39,8 +41,12 @@ class Program
builder.Services.AddSingleton<IRepresentationModule, RepresentationModule>(); builder.Services.AddSingleton<IRepresentationModule, RepresentationModule>();
var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 4567); var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 4567);
builder.Services.AddSingleton<IFrontendBridge, NetworkFrontendBridge>(); builder.Services.AddSingleton<IFrontendBridge, NetworkFrontendBridge>();
builder.Services.AddOptions(); builder.Services.AddOptions();
builder.Services.Configure<GatewayOptions>(options => options.Endpoint = serverEndPoint); 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<IRepresentationModuleProducer, StaticRepresentationModuleProducer>();
builder.Services.AddSingleton<IRequestExtractor, RequestExtractor>(); builder.Services.AddSingleton<IRequestExtractor, RequestExtractor>();
builder.Services.AddSingleton<IExecuteModule, BasicExecutionModule>(); builder.Services.AddSingleton<IExecuteModule, BasicExecutionModule>();
@@ -70,7 +76,14 @@ class Program
using (var disposingPrinter = factory.Create("Test")) 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" }); await disposingPrinter.IntTest(new MyData { Id = 5, Score = 7, Name = "Test" });
DemoCheck.CreatingPrinter = true; DemoCheck.CreatingPrinter = true;
@@ -80,14 +93,12 @@ class Program
DemoCheck.EventCallback = true; DemoCheck.EventCallback = true;
}; };
Console.WriteLine("Printer created"); Console.WriteLine("Printer created");
Thread.Sleep(100);
frontendBridge.Obstacle(); // frontendBridge.Obstacle();
var name = disposingPrinter.GetName(); var name = disposingPrinter.GetName();
DemoCheck.BasicNonParamsCall = true; DemoCheck.BasicNonParamsCall = true;
Console.WriteLine("Printer name : {0}", name); Console.WriteLine("Printer name : {0}", name);
Thread.Sleep(100);
disposingPrinter.SomeoneIsApproaching("Mikhail"); disposingPrinter.SomeoneIsApproaching("Mikhail");
Console.WriteLine("Approaching detected"); Console.WriteLine("Approaching detected");
@@ -96,17 +107,14 @@ class Program
factory.Register(disposingPrinter); factory.Register(disposingPrinter);
DemoCheck.ClientBasedImplementation = true; DemoCheck.ClientBasedImplementation = true;
Console.WriteLine("Registered printer"); Console.WriteLine("Registered printer");
Thread.Sleep(100);
var registered = factory.GetFirstPrinter(); var registered = factory.GetFirstPrinter();
Console.WriteLine("First printer"); Console.WriteLine("First printer");
Thread.Sleep(100);
Console.WriteLine(registered); Console.WriteLine(registered);
Console.WriteLine("Collecting all printers"); Console.WriteLine("Collecting all printers");
var names = factory.CollectAllNames(); var names = factory.CollectAllNames();
Thread.Sleep(100);
Console.WriteLine("Names: " + string.Join(", ", names)); Console.WriteLine("Names: " + string.Join(", ", names));
@@ -139,13 +147,14 @@ class Program
var token = cts.Token; var token = cts.Token;
var t = Task.Run(async () => await loadSingleton!.AsyncTest(token)); var t = Task.Run(async () => await loadSingleton!.AsyncTest(token));
Thread.Sleep(5000); Console.WriteLine("Waiting for timer");
Thread.Sleep(2000);
cts.Cancel(); cts.Cancel();
Console.WriteLine($"Token state {cts.Token.IsCancellationRequested}"); Console.WriteLine($"Token state {cts.Token.IsCancellationRequested}");
DemoCheck.TaskCancelation = true; DemoCheck.TaskCancelation = true;
#endif #endif
const int iterations = 10_000; const int iterations = 5;
var timer = Stopwatch.StartNew(); var timer = Stopwatch.StartNew();
var x = 0; var x = 0;
for (int i = 0; i < iterations; i++) for (int i = 0; i < iterations; i++)
+1 -1
View File
@@ -10,7 +10,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
+89 -70
View File
@@ -1,6 +1,7 @@
using System.Net; using System.Diagnostics;
using System.Net;
using System.Runtime;
using Example.Shared; using Example.Shared;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using mROA.Abstract; using mROA.Abstract;
@@ -10,14 +11,13 @@ using mROA.Implementation;
using mROA.Implementation.Backend; using mROA.Implementation.Backend;
using mROA.Implementation.Frontend; using mROA.Implementation.Frontend;
const int C = 100; const int C = 100;
var time = TimeSpan.FromSeconds(10); var time = TimeSpan.FromSeconds(10);
Console.WriteLine($"Starting bench for {time} from {C} connections"); Console.WriteLine($"Starting bench for {time} from {C} connections");
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
new RemoteTypeBinder(); new RemoteTypeBinder();
var eps = await GetLoadEndpoints(C); 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++) for (int i = 0; i < C; i++)
{ {
tasks[i] = Requests(cts.Token, i, eps[i]); tasks[i] = Requests(cts.Token, i, eps[i]);
@@ -29,83 +29,102 @@ Console.WriteLine("Start waiting");
await Task.WhenAll(tasks); await Task.WhenAll(tasks);
Console.WriteLine("End waiting"); 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($"Total requests: {totalRequests:N0}");
Console.WriteLine($"Results: {totalRequests / time.TotalSeconds:N} RPS"); 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) async Task<List<ILoadTest>> GetLoadEndpoints(int count)
{
var loads = new List<ILoadTest>();
for (int i = 0; i < count; i++)
{
var builder = Host.CreateApplicationBuilder(new HostApplicationBuilderSettings { DisableDefaults = true });
builder.Services.AddSingleton<IContextualSerializationToolKit, CborSerializationToolkit>();
builder.Services.AddSingleton<IEndPointContext, EndPointContext>();
builder.Services.AddSingleton<IRealStoreInstanceRepository, InstanceRepository>(provider =>
{
var repo = new InstanceRepository(provider.GetService<IRepresentationModuleProducer>());
repo.FillSingletons(typeof(Program).Assembly);
return repo;
});
builder.Services.AddSingleton<IInstanceRepository, RemoteInstanceRepository>();
builder.Services.AddSingleton<IChannelInteractionModule, ChannelInteractionModule>();
// builder.Services.AddSingleton<IUntrustedInteractionModule, UdpUntrustedInteraction>();
builder.Services.AddSingleton<IRepresentationModule, RepresentationModule>();
var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 4567);
builder.Services.AddSingleton<IFrontendBridge, NetworkFrontendBridge>();
builder.Services.AddOptions();
builder.Services.Configure<GatewayOptions>(options => options.Endpoint = serverEndPoint);
builder.Services.AddSingleton<IRepresentationModuleProducer, StaticRepresentationModuleProducer>();
// builder.Services.AddSingleton<IRequestExtractor, RequestExtractor>();
builder.Services.AddSingleton<IExecuteModule, BasicExecutionModule>();
builder.Services.AddSingleton<IMethodRepository, CollectableMethodRepository>(p =>
{
var methodRepo = new CollectableMethodRepository();
methodRepo.AppendInvokers(new GeneratedInvokersCollection());
return methodRepo;
});
builder.Services.AddSingleton<ICallIndexProvider, GeneratedCallIndexProvider>();
builder.Services.AddSingleton<ICancellationRepository, CancellationRepository>();
var app = builder.Build();
var frontendBridge = app.Services.GetService<IFrontendBridge>()!;
await frontendBridge.Connect();
// _ = app.Services.GetService<IRequestExtractor>()!.StartExtraction();
// _ = app.Services.GetService<IUntrustedInteractionModule>().Start(serverEndPoint);
var context = app.Services.GetService<IInstanceRepository>();
var singletonObject =
context.GetSingletonObject<ILoadTest>(
app.Services.GetService<IEndPointContext>());
loads.Add(singletonObject);
}
return loads;
}
async Task<int> Requests(CancellationToken token, int id, ILoadTest load)
{ {
try try
{ {
var loads = new List<ILoadTest>();
for (int i = 0; i < count; i++)
int count = 0; {
while (true){ var builder = Host.CreateApplicationBuilder(new HostApplicationBuilderSettings { DisableDefaults = true });
if (token.IsCancellationRequested) builder.Services.AddSingleton<IContextualSerializationToolKit, CborSerializationToolkit>();
builder.Services.AddSingleton<IEndPointContext, EndPointContext>();
builder.Services.AddSingleton<IRealStoreInstanceRepository, InstanceRepository>(provider =>
{ {
break; var repo = new InstanceRepository(provider.GetService<IRepresentationModuleProducer>());
} repo.FillSingletons(typeof(Program).Assembly);
return repo;
});
await load.Next(2); builder.Services.AddSingleton<IInstanceRepository, RemoteInstanceRepository>();
count++; builder.Services.AddSingleton<IChannelInteractionModule, ChannelInteractionModule>();
// builder.Services.AddSingleton<IUntrustedInteractionModule, UdpUntrustedInteraction>();
builder.Services.AddSingleton<IRepresentationModule, RepresentationModule>();
var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 4567);
builder.Services.AddSingleton<IFrontendBridge, NetworkFrontendBridge>();
builder.Services.AddOptions();
builder.Services.Configure<GatewayOptions>(options => options.Endpoint = serverEndPoint);
builder.Services.AddSingleton<IRepresentationModuleProducer, StaticRepresentationModuleProducer>();
// builder.Services.AddSingleton<IRequestExtractor, RequestExtractor>();
builder.Services.AddSingleton<IExecuteModule, BasicExecutionModule>();
builder.Services.AddSingleton<IMethodRepository, CollectableMethodRepository>(p =>
{
var methodRepo = new CollectableMethodRepository();
methodRepo.AppendInvokers(new GeneratedInvokersCollection());
return methodRepo;
});
builder.Services.AddSingleton<ICallIndexProvider, GeneratedCallIndexProvider>();
builder.Services.AddSingleton<ICancellationRepository, CancellationRepository>();
var app = builder.Build();
var frontendBridge = app.Services.GetService<IFrontendBridge>()!;
await frontendBridge.Connect();
// _ = app.Services.GetService<IRequestExtractor>()!.StartExtraction();
// _ = app.Services.GetService<IUntrustedInteractionModule>().Start(serverEndPoint);
var context = app.Services.GetService<IInstanceRepository>();
var singletonObject =
context.GetSingletonObject<ILoadTest>(
app.Services.GetService<IEndPointContext>());
loads.Add(singletonObject);
} }
Console.WriteLine(id); return loads;
return count; }
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
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)
{
if (token.IsCancellationRequested)
{
break;
}
sw.Restart();
await load.Next(2);
sw.Stop();
latencyList.Add(sw.Elapsed.TotalMicroseconds);
count++;
}
return (count, latencyList.ToArray());
} }
catch (Exception e) catch (Exception e)
{ {
-2
View File
@@ -10,12 +10,10 @@
<ProjectReference Include="..\mROA.Cbor\mROA.Cbor.csproj" /> <ProjectReference Include="..\mROA.Cbor\mROA.Cbor.csproj" />
<ProjectReference Include="..\mROA\mROA.csproj"/> <ProjectReference Include="..\mROA\mROA.csproj"/>
<ProjectReference Include="..\mROA.Codegen\mROA.Codegen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/> <ProjectReference Include="..\mROA.Codegen\mROA.Codegen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.7" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
<PackageReference Include="mROA.Codegen" Version="2.0.5" />
</ItemGroup> </ItemGroup>
</Project> </Project>
-1
View File
@@ -1,7 +1,6 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using mROA.Abstract; using mROA.Abstract;
using mROA.Implementation;
using mROA.Implementation.Attributes; using mROA.Implementation.Attributes;
namespace Example.Shared namespace Example.Shared
-1
View File
@@ -1,5 +1,4 @@
using mROA.Abstract; using mROA.Abstract;
using mROA.Implementation;
using mROA.Implementation.Attributes; using mROA.Implementation.Attributes;
namespace Example.Shared namespace Example.Shared
+2 -1
View File
@@ -12,7 +12,8 @@ namespace Example.Shared
{ {
double Resource { get; set; } double Resource { get; set; }
string GetName(); 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; event Action<IPage, RequestContext> OnPrint;
[Untrusted] [Untrusted]
+1 -2
View File
@@ -1,5 +1,4 @@
using mROA.Abstract; using mROA.Abstract;
using mROA.Implementation;
using mROA.Implementation.Attributes; using mROA.Implementation.Attributes;
namespace Example.Shared namespace Example.Shared
@@ -7,7 +6,7 @@ namespace Example.Shared
[SharedObjectInterface] [SharedObjectInterface]
public interface IPrinterFactory : IShared public interface IPrinterFactory : IShared
{ {
IPrinter Create(string printerName); IPrinter Create(in string printerName);
void Register(IPrinter printer); void Register(IPrinter printer);
IPrinter GetPrinterByName(string printerName); IPrinter GetPrinterByName(string printerName);
IPrinter GetFirstPrinter(); 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 1. Definitions.
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:
The above copyright notice and this permission notice shall be included in all "License" shall mean the terms and conditions for use, reproduction,
copies or substantial portions of the Software. 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 "Licensor" shall mean the copyright owner or entity authorized by
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, the copyright owner that is granting the License.
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER "Legal Entity" shall mean the union of the acting entity and all
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, other entities that control, are controlled by, or are under common
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE control with that entity. For the purposes of this definition,
SOFTWARE. "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..]);
}
}
+93
View File
@@ -0,0 +1,93 @@
using System.Formats.Cbor;
using BenchmarkDotNet.Attributes;
namespace mROA.Benchmark;
[MemoryDiagnoser]
public class CborTest
{
private CborWriter _writer;
private CborReader _reader;
private Memory<byte> FlatEncoded;
private Memory<byte> ArrayEncoded;
public CborTest()
{
_writer = new CborWriter(initialCapacity:512);
_writer.WriteStartArray(2);
_writer.WriteByteString([1, 2, 3, 4, 5, 6, 7, 8]);
_writer.WriteStartArray(2);
_writer.WriteInt32(12);
_writer.WriteTextString("tralala");
_writer.WriteEndArray();
_writer.WriteEndArray();
ArrayEncoded = _writer.Encode();
_writer.Reset();
_writer.WriteStartArray(3);
_writer.WriteByteString([1, 2, 3, 4, 5, 6, 7, 8]);
_writer.WriteInt32(12);
_writer.WriteTextString("tralala");
_writer.WriteEndArray();
FlatEncoded = _writer.Encode();
}
[Benchmark]
public int FlatWrite()
{
_writer.Reset();
_writer.WriteStartArray(3);
_writer.WriteByteString([1, 2, 3, 4, 5, 6, 7, 8]);
_writer.WriteInt32(12);
_writer.WriteTextString("tralala");
_writer.WriteEndArray();
var len = _writer.Encode(FlatEncoded.Span);
return len;
}
[Benchmark]
public int ArrayWrite()
{
_writer.Reset();
_writer.WriteStartArray(2);
_writer.WriteByteString([1, 2, 3, 4, 5, 6, 7, 8]);
_writer.WriteStartArray(2);
_writer.WriteInt32(12);
_writer.WriteTextString("tralala");
_writer.WriteEndArray();
_writer.WriteEndArray();
var len = _writer.Encode(ArrayEncoded.Span);
return len;
}
[Benchmark]
public int FlatRead()
{
var reader = new CborReader(FlatEncoded);
reader.ReadStartArray();
var arr = reader.ReadByteString();
var i = reader.ReadInt32();
var text = reader.ReadTextString();
reader.ReadEndArray();
return arr.Length;
}
[Benchmark]
public int ArrayRead()
{
var reader = new CborReader(ArrayEncoded);
reader.ReadStartArray();
var arr = reader.ReadByteString();
reader.ReadStartArray();
var i = reader.ReadInt32();
var text = reader.ReadTextString();
reader.ReadEndArray();
reader.ReadEndArray();
return arr.Length;
}
}
+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];
}
}
+5 -48
View File
@@ -1,51 +1,8 @@
using System.Collections.Generic; // See https://aka.ms/new-console-template for more information
using System.Linq;
using BenchmarkDotNet.Attributes;
namespace mROA.Benchmark using BenchmarkDotNet.Running;
{ using mROA.Benchmark;
class Program
{
static void Main(string[] args)
{
// Console.WriteLine("Hello, World!");
// var summary = BenchmarkRunner.Run<CollectionsSpeed>();
}
}
public class CollectionsSpeed Console.WriteLine("Hello, Performance!");
{
private const int N = 1000;
private readonly List<int> _immutable; BenchmarkRunner.Run<MethodAccess>();
private readonly int[] _array;
public CollectionsSpeed()
{
_array = Enumerable.Range(0, N).ToArray();
// _immutable = [.._array];
}
[Benchmark]
public int DefaultArray()
{
var sum = 0;
for (int i = 0; i < N; i++)
{
sum += _array[i];
}
return sum;
}
[Benchmark]
public int ImmutableArray()
{
var sum = 0;
for (int i = 0; i < N; i++)
{
sum += _immutable[i];
}
return sum;
}
}
}
+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;
}
}
+58
View File
@@ -0,0 +1,58 @@
using System.Formats.Cbor;
using BenchmarkDotNet.Attributes;
using mROA.Implementation;
namespace mROA.Benchmark;
[MemoryDiagnoser]
public class RequestWriter
{
public RequestId Id;
private readonly CborWriter _writer;
public RequestWriter()
{
_writer = new CborWriter(initialCapacity: 512);
}
[Benchmark(Baseline = true)]
public int DefaultCbor()
{
_writer.Reset();
_writer.WriteByteString(Id.ToByteArray());
return _writer.BytesWritten;
}
[Benchmark]
public int DirectCbor()
{
_writer.Reset();
Id.WriteToCbor(_writer);
return _writer.BytesWritten;
}
[Benchmark]
public int Stackalloc()
{
_writer.Reset();
Span<byte> span = stackalloc byte[16];
Id.WriteToDest(span);
_writer.WriteByteString(span);
return _writer.BytesWritten;
}
[Benchmark]
public int DirectCborInline()
{
_writer.Reset();
Id.WriteToCborInline(_writer);
return _writer.BytesWritten;
}
[Benchmark]
public int DirectCborOpt()
{
_writer.Reset();
Id.WriteToCborOpt(_writer);
return _writer.BytesWritten;
}
}
+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();
}
}
+96
View File
@@ -0,0 +1,96 @@
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using mROA.Implementation;
namespace mROA.Benchmark;
public class VirtualOverhead
{
private CallRequest _directRequest;
private RawCallRequest _rawRequest;
[GlobalSetup]
public void Setup() {
_directRequest = new CallRequest
{
CommandId = 5, Id = new RequestId(), ObjectId = ComplexObjectIdentifier.Null, Parameters = null
};
_rawRequest = new RawCallRequest
{
CommandId = _directRequest.CommandId, Id = _directRequest.Id, ObjectId = _directRequest.ObjectId,
Parameters = null
};
}
[Benchmark(Baseline = true)]
public long DirectUsage()
{
var req = _directRequest;
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 RawUsage()
{
var req = _rawRequest;
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 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
{
public RequestId Id;
public int CommandId;
public ComplexObjectIdentifier ObjectId;
public object?[]? Parameters;
public override string ToString()
{
return $"Call request {{ Id : {Id}, CommandId : {CommandId}, ObjectId : {ObjectId} }}";
}
}
+11 -3
View File
@@ -2,13 +2,21 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" /> <PackageReference Include="BenchmarkDotNet" Version="0.15.2"/>
<PackageReference Include="System.Formats.Cbor" Version="9.0.8"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\mROA.Cbor\mROA.Cbor.csproj"/>
<ProjectReference Include="..\mROA.Codegen\mROA.Codegen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
<ProjectReference Include="..\mROA\mROA.csproj"/>
</ItemGroup> </ItemGroup>
</Project> </Project>
+19
View File
@@ -0,0 +1,19 @@
using System;
using System.Formats.Cbor;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using mROA.Implementation;
namespace mROA.Cbor
{
public static class CborExtensions
{
[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);
}
}
}
+106 -95
View File
@@ -1,10 +1,11 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Formats.Cbor; using System.Formats.Cbor;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading;
using Microsoft.Extensions.Options;
using mROA.Abstract; using mROA.Abstract;
using mROA.Implementation; using mROA.Implementation;
using mROA.Implementation.Attributes; using mROA.Implementation.Attributes;
@@ -14,59 +15,73 @@ namespace mROA.Cbor
{ {
public class CborSerializationToolkit : IContextualSerializationToolKit public class CborSerializationToolkit : IContextualSerializationToolKit
{ {
private readonly IOrdinaryStructureParser[] _parsers = { private readonly ThreadLocal<CborWriter> _writer = new(() => new CborWriter(initialCapacity: 2048));
new NetworkMessageHeaderParser(), new DefaultCallRequestParser(), new FinalCommandExecutionParser(), private readonly int _offset;
public CborSerializationToolkit(IOptions<SerializationBufferOffset> offsetOptions) : this(offsetOptions.Value
.Offset)
{
}
private readonly IOrdinaryStructureParser[] _parsers =
{
new CallRequestParser(), new FinalCommandExecutionParser(),
new FinalCommandExecutionResultlessParser() new FinalCommandExecutionResultlessParser()
}; };
// private Dictionary<Type, IOrdinaryStructureParser> _parsers = new(){{typeof(NetworkMessageHeader), new NetworkMessageHeaderParser()}, {typeof(DefaultCallRequest), new DefaultCallRequestParser()}, {typeof(FinalCommandExecution<object>), new FinalCommandExecutionParser()}, {typeof(FinalCommandExecution), new FinalCommandExecutionResultlessParser()}}; //
private Dictionary<Type, List<PropertyInfo>> _propertiesCache = new(); private readonly Dictionary<Type, List<PropertyInfo>> _propertiesCache = new();
public CborSerializationToolkit(int offset)
{
_offset = offset;
}
public static TimeSpan SerializationTime = TimeSpan.Zero; public static TimeSpan SerializationTime = TimeSpan.Zero;
private bool FindParser(Type t, out IOrdinaryStructureParser parser) private bool FindParser(Type t, out IOrdinaryStructureParser parser)
{ {
if (t == typeof(NetworkMessageHeader)) if (t == typeof(CallRequest))
{ {
parser = _parsers[0]; parser = _parsers[0];
return true; return true;
} }
if (t == typeof(DefaultCallRequest)) if (t == typeof(FinalCommandExecution<object>))
{ {
parser = _parsers[1]; parser = _parsers[1];
return true; return true;
} }
if (t == typeof(FinalCommandExecution<object>))
{
parser = _parsers[2];
return true;
}
if (t == typeof(FinalCommandExecution)) if (t == typeof(FinalCommandExecution))
{ {
parser = _parsers[3]; parser = _parsers[2];
return true; return true;
} }
parser = null; parser = null;
return false; return false;
} }
public byte[] Serialize(object objectToSerialize, IEndPointContext context) public byte[] Serialize(object objectToSerialize, IEndPointContext context)
{ {
var sw = Stopwatch.StartNew(); var writer = _writer.Value;
var writer = new CborWriter(initialCapacity:64); writer.Reset();
WriteData(objectToSerialize, writer, context); WriteData(objectToSerialize, writer, context);
var result = writer.Encode(); var result = new byte[_offset + writer.BytesWritten];
sw.Stop(); var span = result.AsSpan();
SerializationTime = SerializationTime.Add(sw.Elapsed); writer.Encode(span[_offset..]);
return result; return result;
} }
public int Serialize(object objectToSerialize, Span<byte> destination, IEndPointContext context) public int Serialize(object objectToSerialize, Span<byte> destination, IEndPointContext context)
{ {
var writer = new CborWriter(initialCapacity:64); var writer = _writer.Value;
WriteData(objectToSerialize, writer, context); writer.Reset();
return writer.Encode(destination); WriteData(objectToSerialize, writer, context);
return writer.Encode(destination);
} }
public T Deserialize<T>(byte[] rawData, IEndPointContext? context) public T Deserialize<T>(byte[] rawData, IEndPointContext? context)
@@ -86,13 +101,17 @@ namespace mROA.Cbor
public object? Deserialize(ReadOnlyMemory<byte> rawMemory, Type type, IEndPointContext? context) public object? Deserialize(ReadOnlyMemory<byte> rawMemory, Type type, IEndPointContext? context)
{ {
var reader = new CborReader(rawMemory); try
return ReadData(reader, type, context); {
} var reader = new CborReader(rawMemory);
return ReadData(reader, type, context);
public T Cast<T>(object nonCasted, IEndPointContext? context) }
{ catch (Exception)
return (T)Cast(nonCasted, typeof(T), context); {
Console.WriteLine(
$"Bad deserialization for type {type}. Bytes: {BitConverter.ToString(rawMemory.ToArray())}");
throw;
}
} }
public object? Cast(object? nonCasted, Type type, IEndPointContext? context) public object? Cast(object? nonCasted, Type type, IEndPointContext? context)
@@ -107,56 +126,22 @@ namespace mROA.Cbor
return preParsed.ToObject(type, context); return preParsed.ToObject(type, context);
if (type == typeof(Guid)) if (type == typeof(RequestId))
{ {
return new Guid((byte[])nonCasted); return new RequestId((byte[])nonCasted);
}
if (type.IsInterface)
{
return ReadSharedShell(type, context, (ulong)nonCasted);
} }
return Convert.ChangeType(nonCasted, type); return Convert.ChangeType(nonCasted, type);
} }
public void Inject(object dependency) public IContextualSerializationToolKit Clone()
{ {
} return new CborSerializationToolkit(_offset);
public byte[] Serialize<T>(T objectToSerialize)
{
return Serialize(objectToSerialize, typeof(T));
}
public byte[] Serialize(object objectToSerialize, Type type)
{
return Serialize(objectToSerialize, context: null);
}
public T Deserialize<T>(byte[] rawData)
{
return Deserialize<T>(rawData: rawData, context: null);
}
public object? Deserialize(byte[] rawData, Type type)
{
return Deserialize(rawData: rawData, type, context: null);
}
public T Deserialize<T>(Span<byte> rawData)
{
return Deserialize<T>(rawData.ToArray().AsMemory(), context: null);
}
public object? Deserialize(Span<byte> rawData, Type type)
{
return Deserialize(rawData: rawData.ToArray(), type: type);
}
public T Cast<T>(object nonCasted)
{
return Cast<T>(nonCasted: nonCasted, context: null);
}
public object Cast(object nonCasted, Type type)
{
return Cast(nonCasted: nonCasted, type: type, context: null);
} }
public void WriteData(object? obj, CborWriter writer, IEndPointContext? context) public void WriteData(object? obj, CborWriter writer, IEndPointContext? context)
@@ -199,8 +184,9 @@ namespace mROA.Cbor
case DateTimeOffset dto: case DateTimeOffset dto:
writer.WriteDateTimeOffset(dto); writer.WriteDateTimeOffset(dto);
break; break;
case Guid g: case RequestId g:
writer.WriteByteString(g.ToByteArray()); // writer.WriteByteString(g.ToByteArray());
g.WriteToCborInline(writer);
break; break;
case byte[] bytes: case byte[] bytes:
writer.WriteByteString(bytes); writer.WriteByteString(bytes);
@@ -217,9 +203,20 @@ namespace mROA.Cbor
WriteObject(sharedObject, writer, context); WriteObject(sharedObject, writer, context);
break; break;
default: 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; break;
} }
@@ -270,9 +267,7 @@ namespace mROA.Cbor
Activator.CreateInstance(sharedShell, obj, context) as Activator.CreateInstance(sharedShell, obj, context) as
ISharedObjectShell; ISharedObjectShell;
writer.WriteStartArray(1);
writer.WriteUInt64(so.Identifier.Flat); writer.WriteUInt64(so.Identifier.Flat);
writer.WriteEndArray();
return; return;
} }
@@ -288,6 +283,7 @@ namespace mROA.Cbor
{ {
return parser.Read(reader, context, this); return parser.Read(reader, context, this);
} }
var state = reader.PeekState(); var state = reader.PeekState();
switch (state) switch (state)
{ {
@@ -301,13 +297,15 @@ namespace mROA.Cbor
return reader.ReadInt64(); return reader.ReadInt64();
if (type == typeof(uint)) if (type == typeof(uint))
return reader.ReadUInt32(); return reader.ReadUInt32();
if (type == typeof(ulong)) if (type.IsInterface)
return reader.ReadUInt64(); {
return ReadSharedShell(type, context, reader.ReadUInt64());
}
return reader.ReadUInt64(); return reader.ReadUInt64();
case CborReaderState.ByteString: case CborReaderState.ByteString:
if (type == typeof(Guid)) if (type == typeof(RequestId))
return new Guid(reader.ReadByteString()); return new RequestId(reader.ReadByteString());
return reader.ReadByteString(); return reader.ReadByteString();
case CborReaderState.TextString: case CborReaderState.TextString:
return reader.ReadTextString(); return reader.ReadTextString();
@@ -414,19 +412,9 @@ namespace mROA.Cbor
if (type.IsInterface) 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(); var identifier = reader.ReadUInt64();
reader.ReadEndArray();
so.Identifier = ComplexObjectIdentifier.FromFlat(identifier); return ReadSharedShell(type, context, identifier);
return so.UniversalValue;
} }
var instance = Activator.CreateInstance(type)!; var instance = Activator.CreateInstance(type)!;
@@ -436,6 +424,20 @@ namespace mROA.Cbor
return instance; 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) private ISharedObjectShell ReadSharedObject(CborReader reader, Type type, IEndPointContext? context)
{ {
var sharedObject = (Activator.CreateInstance(type) as ISharedObjectShell)!; var sharedObject = (Activator.CreateInstance(type) as ISharedObjectShell)!;
@@ -463,6 +465,15 @@ namespace mROA.Cbor
{ {
var property = properties[index]; var property = properties[index];
var value = ReadData(reader, property.PropertyType, context); 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); property.SetValue(obj, value);
} }
@@ -485,11 +496,11 @@ namespace mROA.Cbor
for (int i = 0; i < properties.Length; i++) for (int i = 0; i < properties.Length; i++)
{ {
var property = properties[i]; var property = properties[i];
if (property is not { CanRead: true, CanWrite: true } || property.GetCustomAttribute<SerializationIgnoreAttribute>() != null) if (property is not { CanRead: true, CanWrite: true } ||
property.GetCustomAttribute<SerializationIgnoreAttribute>() != null)
continue; continue;
finalProperties.Add(property); finalProperties.Add(property);
} }
return finalProperties; return finalProperties;
+26 -47
View File
@@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Formats.Cbor; using System.Formats.Cbor;
using System.Runtime.InteropServices;
using mROA.Abstract; using mROA.Abstract;
using mROA.Implementation; using mROA.Implementation;
using mROA.Implementation.CommandExecution; using mROA.Implementation.CommandExecution;
@@ -12,42 +14,17 @@ namespace mROA.Cbor
object Read(CborReader reader, IEndPointContext context, CborSerializationToolkit serialization); object Read(CborReader reader, IEndPointContext context, CborSerializationToolkit serialization);
} }
public class NetworkMessageHeaderParser : IOrdinaryStructureParser 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 = value as NetworkMessageHeader; var v = (CallRequest)value;
writer.WriteStartArray(3);
writer.WriteByteString(v.Id.ToByteArray());
writer.WriteInt32((int)v.MessageType);
writer.WriteByteString(v.Data);
writer.WriteEndArray();
}
public object Read(CborReader reader, IEndPointContext context, CborSerializationToolkit serialization)
{
reader.ReadStartArray();
var value = new NetworkMessageHeader
{
Id = new Guid(reader.ReadByteString()),
MessageType = (EMessageType)reader.ReadInt32(),
Data = reader.ReadByteString()
};
return value;
}
}
public class DefaultCallRequestParser : IOrdinaryStructureParser
{
public void Write(CborWriter writer, object value, IEndPointContext context, CborSerializationToolkit serialization)
{
var v = (DefaultCallRequest)value;
writer.WriteStartArray(4); writer.WriteStartArray(4);
writer.WriteByteString(v.Id.ToByteArray()); v.Id.WriteToCborInline(writer);
// writer.WriteByteString(v.Id.ToByteArray());
writer.WriteInt32(v.CommandId); writer.WriteInt32(v.CommandId);
writer.WriteStartArray(1);
writer.WriteUInt64(v.ObjectId.Flat); writer.WriteUInt64(v.ObjectId.Flat);
writer.WriteEndArray();
serialization.WriteData(v.Parameters, writer, context); serialization.WriteData(v.Parameters, writer, context);
writer.WriteEndArray(); writer.WriteEndArray();
} }
@@ -55,11 +32,12 @@ namespace mROA.Cbor
public object Read(CborReader reader, IEndPointContext context, CborSerializationToolkit serialization) public object Read(CborReader reader, IEndPointContext context, CborSerializationToolkit serialization)
{ {
reader.ReadStartArray(); reader.ReadStartArray();
var value = new DefaultCallRequest var value = new CallRequest
{ {
Id = new Guid(reader.ReadByteString()), Id = new RequestId(reader.ReadByteString()),
CommandId = reader.ReadInt32(), 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[] Parameters = serialization.ReadData(reader, typeof(object[]), context) as object[]
}; };
reader.ReadEndArray(); reader.ReadEndArray();
@@ -70,29 +48,29 @@ namespace mROA.Cbor
public class ComplexObjectIdentifierParser : IOrdinaryStructureParser public class ComplexObjectIdentifierParser : IOrdinaryStructureParser
{ {
public static readonly ComplexObjectIdentifierParser Instance = new(); 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.WriteUInt64(((ComplexObjectIdentifier)value).Flat);
writer.WriteEndArray();
} }
public object Read(CborReader reader, IEndPointContext context, CborSerializationToolkit serialization) public object Read(CborReader reader, IEndPointContext context, CborSerializationToolkit serialization)
{ {
reader.ReadStartArray();
var value = new ComplexObjectIdentifier { Flat = reader.ReadUInt64() }; var value = new ComplexObjectIdentifier { Flat = reader.ReadUInt64() };
reader.ReadEndArray();
return value; return value;
} }
} }
public class FinalCommandExecutionParser : IOrdinaryStructureParser 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; var v = (FinalCommandExecution<object>)value;
writer.WriteStartArray(2); writer.WriteStartArray(2);
writer.WriteByteString(v.Id.ToByteArray()); // writer.WriteByteString(v.Id.ToByteArray());
v.Id.WriteToCborInline(writer);
serialization.WriteData(v.Result, writer, context); serialization.WriteData(v.Result, writer, context);
writer.WriteEndArray(); writer.WriteEndArray();
} }
@@ -102,21 +80,22 @@ namespace mROA.Cbor
reader.ReadStartArray(); reader.ReadStartArray();
var result = new FinalCommandExecution<object> var result = new FinalCommandExecution<object>
{ {
Id = new Guid(reader.ReadByteString()), Id = new RequestId(reader.ReadByteString()),
Result = serialization.ReadData(reader, typeof(object), context), Result = serialization.ReadData(reader, typeof(object), context),
}; };
reader.ReadEndArray(); reader.ReadEndArray();
return result; return result;
} }
} }
public class FinalCommandExecutionResultlessParser : IOrdinaryStructureParser 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; var v = (FinalCommandExecution)value;
writer.WriteStartArray(1); writer.WriteStartArray(1);
writer.WriteByteString(v.Id.ToByteArray()); v.Id.WriteToCborInline(writer);
writer.WriteEndArray(); writer.WriteEndArray();
} }
@@ -125,7 +104,7 @@ namespace mROA.Cbor
reader.ReadStartArray(); reader.ReadStartArray();
var result = new FinalCommandExecution var result = new FinalCommandExecution
{ {
Id = new Guid(reader.ReadByteString()) Id = new RequestId(reader.ReadByteString())
}; };
reader.ReadEndArray(); reader.ReadEndArray();
return result; return result;
+6 -5
View File
@@ -4,9 +4,10 @@
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.0.7</Version> <Version>3.0.4</Version>
<LangVersion>9</LangVersion> <LangVersion>9</LangVersion>
<PackageIcon>mroaLogo.png</PackageIcon> <PackageIcon>mroaLogo.png</PackageIcon>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
@@ -21,10 +22,6 @@
<ProjectReference Include="..\mROA\mROA.csproj" /> <ProjectReference Include="..\mROA\mROA.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="System.Formats.Cbor" Version="9.0.2" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="mroaLogo.png"> <None Update="mroaLogo.png">
<Pack>True</Pack> <Pack>True</Pack>
@@ -32,4 +29,8 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="System.Formats.Cbor" Version="9.0.8" />
</ItemGroup>
</Project> </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>), ReturnType = typeof(<!L returnType>),
ParameterTypes = new Type[] { <!L parametersType> }, ParameterTypes = new Type[] { <!L parametersType> },
SuitableType = typeof(<!L suitableType>), SuitableType = typeof(<!L suitableType>),
RequireCancellation = <!L cancellation>,
Invoking = (i, parameters, special, post) => <!L funcInvoking>, Invoking = (i, parameters, special, post) => <!L funcInvoking>,
}<!T> }<!T>
<!T syncInvoker> <!T syncInvoker>
+2 -3
View File
@@ -26,12 +26,11 @@ namespace mROA.Codegen
<!T eventBinderTemplate> <!T eventBinderTemplate>
(instance as <!L type>).<!L eventName> += (<!L parametersDeclaration>) => (instance as <!L type>).<!L eventName> += (<!L parametersDeclaration>) =>
{ {
Console.WriteLine($"Try to send to {ownerId} with hash code {context.GetHashCode()}");
<!I callFilter> <!I callFilter>
Console.WriteLine("Sending event..."); Console.WriteLine("Sending event...");
var request = new DefaultCallRequest var request = new CallRequest
{ {
Id = Guid.NewGuid(), Id = RequestId.Generate(),
CommandId = <!L commandId>, CommandId = <!L commandId>,
ObjectId = new ComplexObjectIdentifier(index, ownerId), ObjectId = new ComplexObjectIdentifier(index, ownerId),
Parameters = new object[] { <!L transferParameters> } Parameters = new object[] { <!L transferParameters> }
@@ -11,7 +11,9 @@ namespace mROA.Codegen.Templates
private const string CommandIdTag = "commandId"; private const string CommandIdTag = "commandId";
private const string TransferParametersTag = "transferParameters"; private const string TransferParametersTag = "transferParameters";
public EventBinderTemplate(TemplateDocument template) : base(template) { } public EventBinderTemplate(TemplateDocument template) : base(template)
{
}
public void DefineCallFilter(string value) public void DefineCallFilter(string value)
{ {
@@ -10,7 +10,9 @@ namespace mROA.Codegen.Templates
private const string IndexSpanTag = "indexSpan"; private const string IndexSpanTag = "indexSpan";
private const string RemoteTypePairTag = "remoteTypePair"; private const string RemoteTypePairTag = "remoteTypePair";
public IndexProviderTemplate() : base(TemplateFile) { } public IndexProviderTemplate() : base(TemplateFile)
{
}
public void DefineNamespace(string value) public void DefineNamespace(string value)
{ {
+9 -1
View File
@@ -9,9 +9,12 @@ namespace mROA.Codegen.Templates
private const string ParametersTypeTag = "parametersType"; private const string ParametersTypeTag = "parametersType";
private const string SuitableTypeTag = "suitableType"; private const string SuitableTypeTag = "suitableType";
private const string FuncInvokingTag = "funcInvoking"; private const string FuncInvokingTag = "funcInvoking";
private const string CancellationTag = "cancellation";
private const string IsTrustedTag = "isTrusted"; private const string IsTrustedTag = "isTrusted";
public InvokerTemplate(TemplateDocument template) : base(template) { } public InvokerTemplate(TemplateDocument template) : base(template)
{
}
public void DefineIsVoid(string value) public void DefineIsVoid(string value)
{ {
@@ -42,5 +45,10 @@ namespace mROA.Codegen.Templates
{ {
Define(IsTrustedTag, value); Define(IsTrustedTag, value);
} }
public void DefineCancellation(string value)
{
Define(CancellationTag, value);
}
} }
} }
+3 -1
View File
@@ -9,7 +9,9 @@ namespace mROA.Codegen.Templates
private const string InvokerTag = "invoker"; private const string InvokerTag = "invoker";
public MethodRepoTemplate() : base(TemplateFile) { } public MethodRepoTemplate() : base(TemplateFile)
{
}
public void InsertInvoke(string value) public void InsertInvoke(string value)
{ {
@@ -9,7 +9,9 @@ namespace mROA.Codegen.Templates
private const string TypeTag = "type"; private const string TypeTag = "type";
private const string EventBinderTag = "eventBinder"; private const string EventBinderTag = "eventBinder";
public ObjectBinderTemplate(TemplateDocument template) : base(template) { } public ObjectBinderTemplate(TemplateDocument template) : base(template)
{
}
public void DefineType(string value) public void DefineType(string value)
{ {
@@ -8,7 +8,9 @@ namespace mROA.Codegen.Templates
private const string NamespaceTag = "namespace"; private const string NamespaceTag = "namespace";
private const string SignatureTag = "signature"; private const string SignatureTag = "signature";
public PartialInterfaceTemplate() : base(TemplateFile) { } public PartialInterfaceTemplate() : base(TemplateFile)
{
}
public void DefineName(string value) public void DefineName(string value)
{ {
+4 -2
View File
@@ -6,10 +6,12 @@ namespace mROA.Codegen.Templates
private const string ClassNameTag = "className"; private const string ClassNameTag = "className";
private const string OriginalNameTag = "originalName"; private const string OriginalNameTag = "originalName";
private const string NamespaceNameTag= "namespaceName"; private const string NamespaceNameTag = "namespaceName";
private const string MethodsTag = "methods"; private const string MethodsTag = "methods";
public ProxyTemplate() : base(TemplateFile) { } public ProxyTemplate() : base(TemplateFile)
{
}
public void DefineClassName(string value) public void DefineClassName(string value)
{ {
@@ -7,7 +7,9 @@ namespace mROA.Codegen.Templates
private const string ObjectBinderTemplateTag = "objectBinderTemplate"; private const string ObjectBinderTemplateTag = "objectBinderTemplate";
private const string EventBinderTag = "eventBinder"; private const string EventBinderTag = "eventBinder";
public RemoteTypeBinderTemplate() : base(TemplateFile) { } public RemoteTypeBinderTemplate() : base(TemplateFile)
{
}
public ObjectBinderTemplate CloneInnerObjectBinder() public ObjectBinderTemplate CloneInnerObjectBinder()
{ {
+13 -5
View File
@@ -17,10 +17,18 @@
<RepositoryUrl>https://github.com/YaslePoy/mROA</RepositoryUrl> <RepositoryUrl>https://github.com/YaslePoy/mROA</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Version>2.0.7</Version> <Version>3.0.3</Version>
<PackageIcon>mroaLogo.png</PackageIcon> <PackageIcon>mroaLogo.png</PackageIcon>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<PackageLicenseFile>LICENSE-2.0.txt</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<None Include="LICENSE-2.0.txt" Pack="true" PackagePath="$(PackageLicenseFile)"/>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4"> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
@@ -32,8 +40,8 @@
<ItemGroup> <ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" /> <None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Update="mroaLogo.png"> <None Update="mroaLogo.png">
<Pack>True</Pack> <Pack>True</Pack>
<PackagePath></PackagePath> <PackagePath></PackagePath>
</None> </None>
</ItemGroup> </ItemGroup>
@@ -49,8 +57,8 @@
<ItemGroup> <ItemGroup>
<None Remove="IndexProvider.cstmpl" /> <None Remove="IndexProvider.cstmpl" />
<EmbeddedResource Include="IndexProvider.cstmpl" /> <EmbeddedResource Include="IndexProvider.cstmpl" />
</ItemGroup> </ItemGroup>
+36 -17
View File
@@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
using mROA.Codegen.Templates; using mROA.Codegen.Templates;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
namespace mROA.Codegen namespace mROA.Codegen
{ {
@@ -138,15 +137,18 @@ namespace mROA.Codegen
// declaredMethods.Add(impl); // declaredMethods.Add(impl);
break; break;
case IEventSymbol eventSymbol: case IEventSymbol eventSymbol:
proxyTemplate.InsertMethods($"public event {eventSymbol.Type.ToUnityString()}? {eventSymbol.Name};"); proxyTemplate.InsertMethods(
$"public event {eventSymbol.Type.ToUnityString()}? {eventSymbol.Name};");
// declaredMethods.Add( // declaredMethods.Add(
// $"public event {eventSymbol.Type.ToDisplayString()}? {eventSymbol.Name};"); // $"public event {eventSymbol.Type.ToDisplayString()}? {eventSymbol.Name};");
break; break;
} }
GenerateEventImplementation(proxyTemplate, methodRepoTemplate, typeBinder, classSymbol, invokers, context); GenerateEventImplementation(proxyTemplate, methodRepoTemplate, typeBinder, classSymbol, invokers,
context);
var endInvokers = invokers.Count; 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.DefineClassName(className);
proxyTemplate.DefineOriginalName(originalName); proxyTemplate.DefineOriginalName(originalName);
proxyTemplate.DefineNamespaceName(namespaceName); proxyTemplate.DefineNamespaceName(namespaceName);
@@ -158,7 +160,8 @@ namespace mROA.Codegen
#if !DONT_ADD #if !DONT_ADD
context.AddSource($"{className}.g.cs", SourceText.From(code, Encoding.UTF8)); context.AddSource($"{className}.g.cs", SourceText.From(code, Encoding.UTF8));
#endif #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) if (totalMethods.Count != 0)
@@ -185,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) List<string> invokers, SourceProductionContext context)
{ {
var events = classSymbol.AllInterfaces.Add(classSymbol).SelectMany(i => i.GetMembers()) var events = classSymbol.AllInterfaces.Add(classSymbol).SelectMany(i => i.GetMembers())
@@ -236,7 +240,8 @@ namespace mROA.Codegen
return caller; 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) INamedTypeSymbol baseInterface)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
@@ -317,14 +322,17 @@ namespace mROA.Codegen
var parametersInsertList = new List<string>(); var parametersInsertList = new List<string>();
var useCancellationToken = false;
foreach (var parameter in method.Parameters) foreach (var parameter in method.Parameters)
switch (parameter.Type.Name) switch (parameter.Type.Name)
{ {
case "CancellationToken": case "CancellationToken":
parametersInsertList.Add("(CancellationToken)special[1]"); parametersInsertList.Add("(CancellationToken)special[1]");
useCancellationToken = true;
break; break;
case "RequestContext": case "RequestContext":
parametersInsertList.Add("special[0] as RequestContext"); parametersInsertList.Add("(RequestContext)special[0]");
break; break;
default: default:
parametersInsertList.Add(CodegenUtilities.Caster(parameter.Type, parametersInsertList.Add(CodegenUtilities.Caster(parameter.Type,
@@ -360,6 +368,7 @@ namespace mROA.Codegen
invokerTemplate.DefineSuitableType(baseInterface.ToUnityString()); invokerTemplate.DefineSuitableType(baseInterface.ToUnityString());
invokerTemplate.DefineFuncInvoking(funcInvoking); invokerTemplate.DefineFuncInvoking(funcInvoking);
invokerTemplate.DefineIsTrusted((!isUntrusted).ToString().ToLower()); invokerTemplate.DefineIsTrusted((!isUntrusted).ToString().ToLower());
invokerTemplate.DefineCancellation(useCancellationToken.ToString().ToLower());
backend = invokerTemplate.Compile(); backend = invokerTemplate.Compile();
} }
else else
@@ -371,6 +380,7 @@ namespace mROA.Codegen
invokerTemplate.DefineSuitableType(baseInterface.ToUnityString()); invokerTemplate.DefineSuitableType(baseInterface.ToUnityString());
invokerTemplate.DefineFuncInvoking(funcInvoking); invokerTemplate.DefineFuncInvoking(funcInvoking);
invokerTemplate.DefineIsTrusted((!isUntrusted).ToString().ToLower()); invokerTemplate.DefineIsTrusted((!isUntrusted).ToString().ToLower());
backend = invokerTemplate.Compile(); backend = invokerTemplate.Compile();
} }
@@ -379,7 +389,8 @@ namespace mROA.Codegen
invokers.Add(backend); 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(); var eventBinderTemplate = objectBinderTemplate.CloneInnerEventBinder();
@@ -404,13 +415,15 @@ namespace mROA.Codegen
eventBinderTemplate.DefineType(baseType.ToUnityString()); eventBinderTemplate.DefineType(baseType.ToUnityString());
eventBinderTemplate.DefineEventName(eventSymbol.Name); eventBinderTemplate.DefineEventName(eventSymbol.Name);
eventBinderTemplate.DefineParametersDeclaration(parametersDeclaration); eventBinderTemplate.DefineParametersDeclaration(parametersDeclaration);
eventBinderTemplate.DefineCommandIdTag($"context.CallIndexProvider.GetIndices(typeof({baseType.ToUnityString()}))[{index}]"); eventBinderTemplate.DefineCommandIdTag(
$"context.CallIndexProvider.GetIndices(typeof({baseType.ToUnityString()}))[{index}]");
eventBinderTemplate.DefineTransferParameters(transferParameters); eventBinderTemplate.DefineTransferParameters(transferParameters);
var eventBinderCode = eventBinderTemplate.Compile(); var eventBinderCode = eventBinderTemplate.Compile();
objectBinderTemplate.InsertEventBinder(eventBinderCode); 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"; var level = "\t\t\t";
@@ -430,7 +443,7 @@ namespace mROA.Codegen
parametersInsertList.Add("(CancellationToken)special[1]"); parametersInsertList.Add("(CancellationToken)special[1]");
break; break;
case "RequestContext": case "RequestContext":
parametersInsertList.Add("special[0] as RequestContext"); parametersInsertList.Add("(RequestContext)special[0]");
break; break;
default: default:
parametersInsertList.Add(CodegenUtilities.Caster(parameter.i, parametersInsertList.Add(CodegenUtilities.Caster(parameter.i,
@@ -483,7 +496,8 @@ namespace mROA.Codegen
invokerTemplate.DefineReturnType(method.ReturnType.ToUnityString()); invokerTemplate.DefineReturnType(method.ReturnType.ToUnityString());
invokerTemplate.DefineParametersType(parameterTypes); invokerTemplate.DefineParametersType(parameterTypes);
invokerTemplate.DefineSuitableType(baseInterface.ToUnityString()); invokerTemplate.DefineSuitableType(baseInterface.ToUnityString());
invokerTemplate.DefineFuncInvoking($"(i as {method.ContainingType.ToUnityString()})[{parameterInserts}]"); invokerTemplate.DefineFuncInvoking(
$"(i as {method.ContainingType.ToUnityString()})[{parameterInserts}]");
invokerTemplate.DefineIsTrusted("true"); invokerTemplate.DefineIsTrusted("true");
backend = invokerTemplate.Compile(); backend = invokerTemplate.Compile();
@@ -495,7 +509,8 @@ namespace mROA.Codegen
invokerTemplate.DefineReturnType(method.ReturnType.ToUnityString()); invokerTemplate.DefineReturnType(method.ReturnType.ToUnityString());
invokerTemplate.DefineParametersType(""); invokerTemplate.DefineParametersType("");
invokerTemplate.DefineSuitableType(baseInterface.ToUnityString()); 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"); invokerTemplate.DefineIsTrusted("true");
backend = invokerTemplate.Compile(); backend = invokerTemplate.Compile();
@@ -525,7 +540,8 @@ namespace mROA.Codegen
invokerTemplate.DefineReturnType(method.ReturnType.ToUnityString()); invokerTemplate.DefineReturnType(method.ReturnType.ToUnityString());
invokerTemplate.DefineParametersType(parameterTypes); invokerTemplate.DefineParametersType(parameterTypes);
invokerTemplate.DefineSuitableType(baseInterface.ToUnityString()); 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"); invokerTemplate.DefineIsTrusted("true");
backend = invokerTemplate.Compile(); backend = invokerTemplate.Compile();
@@ -537,7 +553,8 @@ namespace mROA.Codegen
invokerTemplate.DefineReturnType(method.ReturnType.ToUnityString()); invokerTemplate.DefineReturnType(method.ReturnType.ToUnityString());
invokerTemplate.DefineParametersType($"typeof({method.Parameters.First().Type.ToUnityString()})"); invokerTemplate.DefineParametersType($"typeof({method.Parameters.First().Type.ToUnityString()})");
invokerTemplate.DefineSuitableType(baseInterface.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"); invokerTemplate.DefineIsTrusted("true");
backend = invokerTemplate.Compile(); backend = invokerTemplate.Compile();
@@ -576,7 +593,9 @@ namespace mROA.Codegen
public static string ToFullString(IParameterSymbol parameter) 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) 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();
}
}
+24
View File
@@ -20,4 +20,28 @@ public class Identifier
Assert.Fail(); Assert.Fail();
} }
} }
[Test]
public void RequestIdTest()
{
var id = RequestId.Generate();
Assert.Pass(id.ToString());
}
[Test]
public void EqualsTest()
{
var id = RequestId.Generate();
var id2 = new RequestId { P0 = id.P0, P1 = id.P1 };
Assert.That(id2, Is.EqualTo(id));
}
[Test]
public void ByteString()
{
var id = RequestId.Generate();
var binary = id.ToByteArray();
var reverced = new RequestId(binary);
Assert.That(reverced, Is.EqualTo(id));
}
} }
+1
View File
@@ -27,6 +27,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\mROA.Benchmark\mROA.Benchmark.csproj" />
<ProjectReference Include="..\mROA.Cbor\mROA.Cbor.csproj" /> <ProjectReference Include="..\mROA.Cbor\mROA.Cbor.csproj" />
<ProjectReference Include="..\mROA\mROA.csproj" /> <ProjectReference Include="..\mROA\mROA.csproj" />
</ItemGroup> </ItemGroup>
+5 -15
View File
@@ -17,8 +17,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Shared", "Example.S
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Frontend", "Example.Frontend\Example.Frontend.csproj", "{9BD25A13-3165-47C0-9EAA-5C59EC490E32}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Frontend", "Example.Frontend\Example.Frontend.csproj", "{9BD25A13-3165-47C0-9EAA-5C59EC490E32}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mROA.Benchmark", "mROA.Benchmark\mROA.Benchmark.csproj", "{6868F42B-E30D-4040-AD4A-BC2A2E76D03A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mROA.Cbor", "mROA.Cbor\mROA.Cbor.csproj", "{6211E4EA-13FD-4EB1-8E6A-C0173DD0784A}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mROA.Cbor", "mROA.Cbor\mROA.Cbor.csproj", "{6211E4EA-13FD-4EB1-8E6A-C0173DD0784A}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TotalDemo", "TotalDemo", "{FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TotalDemo", "TotalDemo", "{FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E}"
@@ -27,9 +25,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mROA.CodegenTools", "mROA.C
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Load", "Example.Load\Example.Load.csproj", "{930B236B-BDAA-4B8C-8054-5B992BAE6622}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Load", "Example.Load\Example.Load.csproj", "{930B236B-BDAA-4B8C-8054-5B992BAE6622}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Functionality.Shared", "Functionality.Shared\Functionality.Shared.csproj", "{D9D28596-E10C-4A98-A2AA-573219467506}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mROA.Benchmark", "mROA.Benchmark\mROA.Benchmark.csproj", "{E021E8B3-56C2-400E-A05E-523CF7831189}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{8C20901F-B416-4ABC-8AA4-9059646B081B}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -61,10 +57,6 @@ Global
{9BD25A13-3165-47C0-9EAA-5C59EC490E32}.Debug|Any CPU.Build.0 = Debug|Any CPU {9BD25A13-3165-47C0-9EAA-5C59EC490E32}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9BD25A13-3165-47C0-9EAA-5C59EC490E32}.Release|Any CPU.ActiveCfg = Release|Any CPU {9BD25A13-3165-47C0-9EAA-5C59EC490E32}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9BD25A13-3165-47C0-9EAA-5C59EC490E32}.Release|Any CPU.Build.0 = Release|Any CPU {9BD25A13-3165-47C0-9EAA-5C59EC490E32}.Release|Any CPU.Build.0 = Release|Any CPU
{6868F42B-E30D-4040-AD4A-BC2A2E76D03A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6868F42B-E30D-4040-AD4A-BC2A2E76D03A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6868F42B-E30D-4040-AD4A-BC2A2E76D03A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6868F42B-E30D-4040-AD4A-BC2A2E76D03A}.Release|Any CPU.Build.0 = Release|Any CPU
{6211E4EA-13FD-4EB1-8E6A-C0173DD0784A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6211E4EA-13FD-4EB1-8E6A-C0173DD0784A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6211E4EA-13FD-4EB1-8E6A-C0173DD0784A}.Debug|Any CPU.Build.0 = Debug|Any CPU {6211E4EA-13FD-4EB1-8E6A-C0173DD0784A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6211E4EA-13FD-4EB1-8E6A-C0173DD0784A}.Release|Any CPU.ActiveCfg = Release|Any CPU {6211E4EA-13FD-4EB1-8E6A-C0173DD0784A}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -77,10 +69,10 @@ Global
{930B236B-BDAA-4B8C-8054-5B992BAE6622}.Debug|Any CPU.Build.0 = Debug|Any CPU {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.ActiveCfg = Release|Any CPU
{930B236B-BDAA-4B8C-8054-5B992BAE6622}.Release|Any CPU.Build.0 = 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 {E021E8B3-56C2-400E-A05E-523CF7831189}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D9D28596-E10C-4A98-A2AA-573219467506}.Debug|Any CPU.Build.0 = Debug|Any CPU {E021E8B3-56C2-400E-A05E-523CF7831189}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9D28596-E10C-4A98-A2AA-573219467506}.Release|Any CPU.ActiveCfg = Release|Any CPU {E021E8B3-56C2-400E-A05E-523CF7831189}.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}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -91,7 +83,5 @@ Global
{9BD25A13-3165-47C0-9EAA-5C59EC490E32} = {FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E} {9BD25A13-3165-47C0-9EAA-5C59EC490E32} = {FC4FA752-10A7-4D78-A7C2-9BCD8A81FB5E}
{E7703F5B-F2A6-4A88-AA57-EBB1E38D533E} = {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} {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 EndGlobalSection
EndGlobal EndGlobal
+4 -3
View File
@@ -1,12 +1,13 @@
using System; using System;
using System.Threading; using System.Threading;
using mROA.Implementation;
namespace mROA.Abstract namespace mROA.Abstract
{ {
public interface ICancellationRepository public interface ICancellationRepository
{ {
void RegisterCancellation(Guid id, CancellationTokenSource cts); void RegisterCancellation(RequestId id, CancellationTokenSource cts);
CancellationTokenSource? GetCancellation(Guid id); CancellationTokenSource? GetCancellation(RequestId id);
void FreeCancelation(Guid id); void FreeCancellation(RequestId id);
} }
} }
+6 -6
View File
@@ -9,13 +9,13 @@ namespace mROA.Abstract
{ {
int ConnectionId { get; set; } int ConnectionId { get; set; }
IEndPointContext Context { get; set; } IEndPointContext Context { get; set; }
Channel<NetworkMessageHeader> ReceiveChanel { get; } Channel<NetworkMessage> ReceiveChanel { get; }
ChannelReader<NetworkMessageHeader> TrustedPostChanel { get; } ChannelReader<NetworkMessage> TrustedPostChanel { get; }
ChannelReader<NetworkMessageHeader> UntrustedPostChanel { get; } ChannelReader<NetworkMessage> UntrustedPostChanel { get; }
Func<bool> IsConnected { get; set; } Func<bool> IsConnected { get; set; }
ValueTask<NetworkMessageHeader> GetNextMessageReceiving(bool infinite = true); ValueTask<NetworkMessage> GetNextMessageReceiving();
Task PostMessageAsync(NetworkMessageHeader messageHeader); Task PostMessageAsync(NetworkMessage message);
Task PostMessageUntrustedAsync(NetworkMessageHeader messageHeader); Task PostMessageUntrustedAsync(NetworkMessage message);
event Action<int> OnDisconnected; event Action<int> OnDisconnected;
Task Restart(bool sendRecovery); Task Restart(bool sendRecovery);
void PassReconnection(); void PassReconnection();
+1 -1
View File
@@ -5,6 +5,6 @@ namespace mROA.Abstract
{ {
public interface ICommandExecution : INetworkMessage public interface ICommandExecution : INetworkMessage
{ {
Guid Id { get; set; } RequestId Id { get; set; }
} }
} }
-6
View File
@@ -1,14 +1,8 @@
namespace mROA.Abstract namespace mROA.Abstract
{ {
public delegate void ConnectionHandler(IRepresentationModule representationModule);
public delegate void DisconnectionHandler(IRepresentationModule representationModule);
public interface IConnectionHub public interface IConnectionHub
{ {
void RegisterInteraction(IChannelInteractionModule interaction); void RegisterInteraction(IChannelInteractionModule interaction);
IChannelInteractionModule GetInteraction(int id); IChannelInteractionModule GetInteraction(int id);
event ConnectionHandler? OnConnected;
event DisconnectionHandler? OnDisconnected;
} }
} }
@@ -9,8 +9,7 @@ namespace mROA.Abstract
T Deserialize<T>(byte[] rawData, IEndPointContext? context); T Deserialize<T>(byte[] rawData, IEndPointContext? context);
object? Deserialize(byte[] rawData, Type type, IEndPointContext? context); object? Deserialize(byte[] rawData, Type type, IEndPointContext? context);
T Deserialize<T>(ReadOnlyMemory<byte> rawMemory, IEndPointContext? context); T Deserialize<T>(ReadOnlyMemory<byte> rawMemory, IEndPointContext? context);
object? Deserialize(ReadOnlyMemory<byte> rawMemory, Type type, IEndPointContext? context);
T Cast<T>(object nonCasted, IEndPointContext? context);
object? Cast(object? nonCasted, Type type, 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
@@ -1,6 +1,6 @@
namespace mROA.Abstract namespace mROA.Abstract
{ {
public interface IEventBinder<T> : IEventBinder public interface IEventBinder<in T> : IEventBinder
{ {
public void BindEvents(T source, IEndPointContext context, public void BindEvents(T source, IEndPointContext context,
IRepresentationModuleProducer representationModuleProducer, int index); IRepresentationModuleProducer representationModuleProducer, int index);
+4 -1
View File
@@ -1,10 +1,13 @@
using mROA.Implementation; using mROA.Implementation;
using mROA.Implementation.CommandExecution;
namespace mROA.Abstract namespace mROA.Abstract
{ {
public interface IExecuteModule public interface IExecuteModule
{ {
ICommandExecution Execute(ICallRequest command, IInstanceRepository instanceRepository, ICommandExecution? Execute(CallRequest command, IInstanceRepository instanceRepository,
IRepresentationModule representationModule, IEndPointContext context); IRepresentationModule representationModule, IEndPointContext context);
ICommandExecution Cancel(CancelRequest command);
} }
} }
-8
View File
@@ -1,8 +0,0 @@
namespace mROA.Abstract
{
public interface IOwnershipRepository
{
int GetOwnershipId();
int GetHostOwnershipId();
}
}
@@ -2,6 +2,5 @@ namespace mROA.Abstract
{ {
public interface IRealStoreInstanceRepository : IInstanceRepository public interface IRealStoreInstanceRepository : IInstanceRepository
{ {
} }
} }
-9
View File
@@ -1,9 +0,0 @@
using mROA.Implementation;
namespace mROA.Abstract
{
public interface IRemoteObjectFactory
{
T Produce<T>(ComplexObjectIdentifier id, IEndPointContext context);
}
}
+8 -7
View File
@@ -11,21 +11,22 @@ namespace mROA.Abstract
int Id { get; } int Id { get; }
IEndPointContext Context { get; } IEndPointContext Context { get; }
Task<(object? Deserialized, EMessageType MessageType)> GetSingle(Predicate<NetworkMessageHeader> rule, Task<(object? Deserialized, EMessageType MessageType)> GetSingle(Predicate<NetworkMessage> rule,
IEndPointContext? context, CancellationToken token = default, IEndPointContext? context, CancellationToken token = default,
params Func<NetworkMessageHeader, Type?>[] converter); params Func<NetworkMessage, Type?>[] converter);
IAsyncEnumerable<(object parced, EMessageType originalType)> GetStream(Predicate<NetworkMessageHeader> rule, IAsyncEnumerable<(object parced, EMessageType originalType)> GetStream(Predicate<NetworkMessage> rule,
IEndPointContext? context, CancellationToken token = default, IEndPointContext? context, CancellationToken token = default,
params Func<NetworkMessageHeader, Type?>[] converter); params Func<NetworkMessage, Type?>[] converter);
Task PostCallMessageAsync<T>(Guid id, EMessageType eMessageType, T payload, IEndPointContext? context) Task PostCallMessageAsync<T>(RequestId id, EMessageType eMessageType, T payload, IEndPointContext? context)
where T : notnull; where T : notnull;
void PostCallMessage<T>(Guid id, EMessageType eMessageType, T payload, IEndPointContext? context) void PostCallMessage<T>(RequestId id, EMessageType eMessageType, T payload, IEndPointContext? context)
where T : notnull; where T : notnull;
Task PostCallMessageUntrustedAsync<T>(Guid id, EMessageType eMessageType, T payload, IEndPointContext? context) Task PostCallMessageUntrustedAsync<T>(RequestId id, EMessageType eMessageType, T payload,
IEndPointContext? context)
where T : notnull; where T : notnull;
} }
} }
+5
View File
@@ -1,9 +1,14 @@
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using mROA.Implementation;
namespace mROA.Abstract namespace mROA.Abstract
{ {
public interface IRequestExtractor public interface IRequestExtractor
{ {
Task StartExtraction(); Task StartExtraction();
void PushMessage(object parsed, EMessageType originalType);
Predicate<NetworkMessage> Rule { get; }
Func<NetworkMessage, Type?>[] Converters { get; }
} }
} }
@@ -10,32 +10,25 @@ namespace mROA.Implementation.Backend
private readonly ICancellationRepository _cancellationRepo; private readonly ICancellationRepository _cancellationRepo;
private readonly IMethodRepository _methodRepo; private readonly IMethodRepository _methodRepo;
private readonly IContextualSerializationToolKit _serialization; private readonly IContextualSerializationToolKit _serialization;
public BasicExecutionModule(ICancellationRepository cancellationRepo, IMethodRepository methodRepo,
public BasicExecutionModule(ICancellationRepository cancellationRepo, IMethodRepository methodRepo, IContextualSerializationToolKit serialization) IContextualSerializationToolKit serialization)
{ {
_cancellationRepo = cancellationRepo; _cancellationRepo = cancellationRepo;
_methodRepo = methodRepo; _methodRepo = methodRepo;
_serialization = serialization; _serialization = serialization;
} }
public ICommandExecution? Execute(CallRequest command, IInstanceRepository instanceRepository,
public ICommandExecution Execute(ICallRequest command, IInstanceRepository instanceRepository,
IRepresentationModule representationModule, IEndPointContext endPointContext) IRepresentationModule representationModule, IEndPointContext endPointContext)
{ {
try try
{ {
ThrowIfNotInjected(instanceRepository);
if (command is CancelRequest)
{
return CancelExecution(command);
}
var invoker = _methodRepo.GetMethod(command.CommandId); var invoker = _methodRepo.GetMethod(command.CommandId);
if (invoker == null) if (invoker == null)
throw new Exception($"Command {command.CommandId} not found"); throw new Exception($"Command {command.CommandId} not found");
var context = GetContext(command, instanceRepository, invoker, endPointContext); var instance = GetInstance(command, instanceRepository, invoker, endPointContext);
if (context == null) if (instance is null)
throw new NullReferenceException("Instance can't be null"); throw new NullReferenceException("Instance can't be null");
@@ -47,24 +40,10 @@ namespace mROA.Implementation.Backend
var execContext = new RequestContext(command.Id, representationModule.Id); var execContext = new RequestContext(command.Id, representationModule.Id);
switch (invoker) var executionResult = ExecuteRequest(command, instanceRepository, representationModule, endPointContext,
{ invoker,
case AsyncMethodInvoker { IsVoid: false } asyncNonVoidMethodInvoker: instance, castedParams, execContext);
return TypedExecuteAsync(asyncNonVoidMethodInvoker, context, castedParams, command, return executionResult;
_cancellationRepo,
representationModule, execContext, endPointContext);
case AsyncMethodInvoker asyncMethodInvoker:
return ExecuteAsync(asyncMethodInvoker, context, castedParams, command, _cancellationRepo,
representationModule, execContext, endPointContext);
default:
var result = Execute((invoker as MethodInvoker)!, context, castedParams!, command, execContext);
if (command.CommandId == -1)
{
instanceRepository.ClearObject(command.ObjectId, endPointContext);
}
return result;
}
} }
catch (Exception e) catch (Exception e)
{ {
@@ -75,17 +54,39 @@ namespace mROA.Implementation.Backend
}; };
} }
} }
private ICommandExecution? ExecuteRequest(CallRequest command, IInstanceRepository instanceRepository,
IRepresentationModule representationModule, IEndPointContext endPointContext, IMethodInvoker invoker,
object context, object?[]? castedParams, RequestContext execContext)
{
switch (invoker)
{
case AsyncMethodInvoker { IsVoid: false } asyncNonVoidMethodInvoker:
return TypedExecuteAsync(asyncNonVoidMethodInvoker, context, castedParams, command,
_cancellationRepo,
representationModule, execContext, endPointContext);
case AsyncMethodInvoker asyncMethodInvoker:
return ExecuteAsync(asyncMethodInvoker, context, castedParams, command, _cancellationRepo,
representationModule, execContext, endPointContext);
default:
var result = Execute((invoker as MethodInvoker)!, context, castedParams!, command, execContext);
if (command.CommandId == -1)
{
instanceRepository.ClearObject(command.ObjectId, endPointContext);
}
private static object GetContext(ICallRequest command, IInstanceRepository instanceRepository, return result;
}
}
private static object GetInstance(CallRequest command, IInstanceRepository instanceRepository,
IMethodInvoker invoker, IEndPointContext endPointContext) IMethodInvoker invoker, IEndPointContext endPointContext)
{ {
var context = command.ObjectId.ContextId != -1 var context = command.ObjectId.ContextId != -1
? instanceRepository.GetObject<object>(command.ObjectId, endPointContext) ? instanceRepository.GetObject<object>(command.ObjectId, endPointContext)
: instanceRepository.GetSingletonObject(invoker.SuitableType, endPointContext); : instanceRepository.GetSingletonObject(invoker.SuitableType, endPointContext);
return context; return context;
} }
private object?[] CastedParams(CallRequest command, IMethodInvoker invoker, IEndPointContext context)
private object?[] CastedParams(ICallRequest command, IMethodInvoker invoker, IEndPointContext context)
{ {
object?[] castedParams = new object[invoker.ParameterTypes.Length]; object?[] castedParams = new object[invoker.ParameterTypes.Length];
for (var i = 0; i < castedParams.Length; i++) for (var i = 0; i < castedParams.Length; i++)
@@ -95,158 +96,115 @@ namespace mROA.Implementation.Backend
return castedParams; return castedParams;
} }
public ICommandExecution Cancel(CancelRequest command)
private void ThrowIfNotInjected(IInstanceRepository instanceRepository)
{
if (_cancellationRepo is null)
throw new NullReferenceException("Method repository was not defined");
if (_methodRepo is null)
throw new NullReferenceException("Method repository was not defined");
if (instanceRepository is null)
throw new NullReferenceException("Context repository was not defined");
}
private FinalCommandExecution CancelExecution(ICallRequest command)
{ {
var cts = _cancellationRepo.GetCancellation(command.Id); var cts = _cancellationRepo.GetCancellation(command.Id);
if (cts == null) if (cts == null)
throw new NullReferenceException("Can't find cancellation for this request"); throw new NullReferenceException("Can't find cancellation for this request");
cts.Cancel(); cts.Cancel();
_cancellationRepo.FreeCancelation(command.Id); _cancellationRepo.FreeCancellation(command.Id);
return new FinalCommandExecution return new FinalCommandExecution
{ {
Id = command.Id Id = command.Id
}; };
} }
private static ICommandExecution? Execute(MethodInvoker invoker, object instance, object?[] parameter,
private static ICommandExecution Execute(MethodInvoker invoker, object instance, object?[] parameter, CallRequest command, RequestContext executionContext)
ICallRequest command, RequestContext executionContext)
{ {
try var finalResult = invoker.Invoke(instance, parameter, new object[] { executionContext });
if (!invoker.IsTrusted)
{ {
var finalResult = invoker.Invoke(instance, parameter, new object[] { executionContext }); return null;
if (!invoker.IsTrusted)
{
return new AsyncCommandExecution();
}
if (invoker.IsVoid)
{
return new FinalCommandExecution
{
Id = command.Id
};
}
return new FinalCommandExecution<object>
{
Result = finalResult,
Id = command.Id
};
} }
catch (Exception e)
if (invoker.IsVoid)
{ {
if (invoker.IsTrusted) return new FinalCommandExecution
return new ExceptionCommandExecution
{
Id = command.Id,
Exception = e.ToString()
};
return new AsyncCommandExecution
{ {
Id = command.Id Id = command.Id
}; };
} }
return new FinalCommandExecution<object>
{
Result = finalResult,
Id = command.Id
};
} }
private ICommandExecution? ExecuteAsync(AsyncMethodInvoker invoker, object instance, object?[]? parameters,
private ICommandExecution ExecuteAsync(AsyncMethodInvoker invoker, object instance, object?[]? parameters, CallRequest command, ICancellationRepository cancellationRepository,
ICallRequest command, ICancellationRepository cancellationRepository,
IRepresentationModule representationModule, RequestContext executionContext, IEndPointContext context) IRepresentationModule representationModule, RequestContext executionContext, IEndPointContext context)
{ {
var tokenSource = new CancellationTokenSource(); CancellationToken? token = null;
cancellationRepository.RegisterCancellation(command.Id, tokenSource); if (invoker.RequireCancellation)
var token = tokenSource.Token;
try
{ {
invoker.Invoke(instance, parameters, new object[] { executionContext, token }, _ => var tokenSource = new CancellationTokenSource();
cancellationRepository.RegisterCancellation(command.Id, tokenSource);
token = tokenSource.Token;
}
invoker.Invoke(instance, parameters, new object[] { executionContext, token }, _ =>
{
if (invoker.RequireCancellation)
{ {
if (token.IsCancellationRequested) _cancellationRepo.FreeCancellation(command.Id);
if (token.Value.IsCancellationRequested)
return; return;
}
var payload = new FinalCommandExecution var payload = new FinalCommandExecution
{
Id = command.Id
};
if (invoker.IsTrusted)
representationModule.PostCallMessageAsync(command.Id, EMessageType.FinishedCommandExecution,
payload, context);
});
return null;
}
private ICommandExecution? TypedExecuteAsync(AsyncMethodInvoker invoker, object instance, object?[]? parameters,
CallRequest command, ICancellationRepository cancellationRepository,
IRepresentationModule representationModule, RequestContext executionContext, IEndPointContext context)
{
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 },
finalResult =>
{
if (invoker.RequireCancellation)
{ {
Id = command.Id _cancellationRepo.FreeCancellation(command.Id);
if (token.Value.IsCancellationRequested)
return;
}
var payload = new FinalCommandExecution<object>
{
Id = command.Id,
Result = finalResult
}; };
_cancellationRepo?.FreeCancelation(command.Id);
representationModule.PostCallMessageAsync(command.Id, EMessageType.FinishedCommandExecution,
if (invoker.IsTrusted) payload, context);
representationModule.PostCallMessage(command.Id, EMessageType.FinishedCommandExecution,
payload, context);
}); });
return new AsyncCommandExecution return null;
{
Id = command.Id
};
}
catch (Exception e)
{
if (invoker.IsTrusted)
return new ExceptionCommandExecution
{
Id = command.Id,
Exception = e.ToString()
};
return new AsyncCommandExecution
{
Id = command.Id
};
}
}
private ICommandExecution TypedExecuteAsync(AsyncMethodInvoker invoker, object instance, object?[]? parameters,
ICallRequest command, ICancellationRepository cancellationRepository,
IRepresentationModule representationModule, RequestContext executionContext, IEndPointContext context)
{
var tokenSource = new CancellationTokenSource();
cancellationRepository.RegisterCancellation(command.Id, tokenSource);
var token = tokenSource.Token;
try
{
invoker.Invoke(instance, parameters, new object[] { executionContext, token },
finalResult =>
{
var payload = new FinalCommandExecution<object>
{
Id = command.Id,
Result = finalResult
};
_cancellationRepo.FreeCancelation(command.Id);
representationModule.PostCallMessage(command.Id, EMessageType.FinishedCommandExecution,
payload, context);
});
return new AsyncCommandExecution
{
Id = command.Id
};
}
catch (Exception e)
{
return new ExceptionCommandExecution
{
Id = command.Id,
Exception = e.ToString()
};
}
} }
} }
} }
@@ -7,18 +7,10 @@ namespace mROA.Implementation.Backend
public class ConnectionHub : IConnectionHub public class ConnectionHub : IConnectionHub
{ {
private readonly Dictionary<int, IChannelInteractionModule> _connections = new(); private readonly Dictionary<int, IChannelInteractionModule> _connections = new();
private readonly IContextualSerializationToolKit _serializationToolkit;
public ConnectionHub(IContextualSerializationToolKit serializationToolkit)
{
_serializationToolkit = serializationToolkit;
}
public void RegisterInteraction(IChannelInteractionModule interaction) public void RegisterInteraction(IChannelInteractionModule interaction)
{ {
_connections.Add(interaction.ConnectionId, interaction); _connections.Add(interaction.ConnectionId, interaction);
var module = new RepresentationModule(interaction, _serializationToolkit);
OnConnected?.Invoke(module);
} }
public IChannelInteractionModule GetInteraction(int id) public IChannelInteractionModule GetInteraction(int id)
@@ -26,8 +18,5 @@ namespace mROA.Implementation.Backend
return _connections!.GetValueOrDefault(id, null) ?? _connections!.GetValueOrDefault(-id, null) ?? return _connections!.GetValueOrDefault(id, null) ?? _connections!.GetValueOrDefault(-id, null) ??
throw new Exception("No connection found"); throw new Exception("No connection found");
} }
public event ConnectionHandler? OnConnected;
public event DisconnectionHandler? OnDisconnected;
} }
} }
@@ -1,3 +1,5 @@
using System.Collections.Generic;
using Microsoft.Extensions.Options;
using mROA.Abstract; using mROA.Abstract;
using mROA.Implementation.Frontend; using mROA.Implementation.Frontend;
@@ -5,28 +7,34 @@ namespace mROA.Implementation.Backend
{ {
public class HubRequestExtractor public class HubRequestExtractor
{ {
private IRealStoreInstanceRepository _contextRepository; private readonly IRealStoreInstanceRepository _contextRepository;
private IInstanceRepository _remoteContextRepository; private readonly IInstanceRepository _remoteContextRepository;
private IMethodRepository _methodRepository; private readonly IExecuteModule _executeModule;
private IContextualSerializationToolKit _serializationToolkit; private readonly DistributionOptions _mode;
private IExecuteModule _executeModule; private readonly Dictionary<int, IRequestExtractor> _producedExtractors = new();
public HubRequestExtractor(IConnectionHub hub, IRealStoreInstanceRepository contextRepository, public HubRequestExtractor(IRealStoreInstanceRepository contextRepository,
IInstanceRepository remoteContextRepository, IMethodRepository methodRepository, IInstanceRepository remoteContextRepository, IExecuteModule executeModule,
IContextualSerializationToolKit serializationToolkit, IExecuteModule executeModule) IOptions<DistributionOptions> mode)
{ {
hub.OnConnected += HubOnOnConnected;
_contextRepository = contextRepository; _contextRepository = contextRepository;
_remoteContextRepository = remoteContextRepository; _remoteContextRepository = remoteContextRepository;
_methodRepository = methodRepository;
_serializationToolkit = serializationToolkit;
_executeModule = executeModule; _executeModule = executeModule;
_mode = mode.Value;
} }
private void HubOnOnConnected(IRepresentationModule interaction) public IRequestExtractor this[int id] => _producedExtractors[id];
public IRequestExtractor HubOnOnConnected(IRepresentationModule representationModule)
{ {
var extractor = CreateExtractor(interaction); var extractor = CreateExtractor(representationModule);
extractor.StartExtraction().ContinueWith(_ => OnDisconnected(interaction)); if (_mode.DistributionType == EDistributionType.Channeled)
{
extractor.StartExtraction().ContinueWith(_ => OnDisconnected(representationModule));
}
_producedExtractors[representationModule.Id] = extractor;
return extractor;
} }
private void OnDisconnected(IRepresentationModule representationModule) private void OnDisconnected(IRepresentationModule representationModule)
@@ -37,7 +45,7 @@ namespace mROA.Implementation.Backend
private IRequestExtractor CreateExtractor(IRepresentationModule interaction) private IRequestExtractor CreateExtractor(IRepresentationModule interaction)
{ {
var extractor = new RequestExtractor(_executeModule, _methodRepository, interaction, _serializationToolkit, interaction.Context); var extractor = new RequestExtractor(_executeModule, interaction, interaction.Context);
var context = interaction.Context; var context = interaction.Context;
if (_contextRepository is IContextRepositoryHub contextHub) if (_contextRepository is IContextRepositoryHub contextHub)
context.RealRepository = contextHub.GetRepository(interaction.Id); context.RealRepository = contextHub.GetRepository(interaction.Id);
@@ -11,7 +11,7 @@ namespace mROA.Implementation.Backend
{ {
public static object[] EventBinders = { }; public static object[] EventBinders = { };
private IRepresentationModuleProducer _representationModuleProducer; private readonly IRepresentationModuleProducer _representationModuleProducer;
private Dictionary<int, object?> _singletons = new(); private Dictionary<int, object?> _singletons = new();
private readonly IStorage<object> _storage; private readonly IStorage<object> _storage;
@@ -22,8 +22,6 @@ namespace mROA.Implementation.Backend
_storage = new ExtensibleStorage<object>(); _storage = new ExtensibleStorage<object>();
} }
public int HostId { get; set; }
public int ResisterObject<T>(object o, IEndPointContext context) public int ResisterObject<T>(object o, IEndPointContext context)
{ {
var last = _storage.Place(o); var last = _storage.Place(o);
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using mROA.Abstract; using mROA.Abstract;
namespace mROA.Implementation.Backend namespace mROA.Implementation.Backend
@@ -14,8 +15,6 @@ namespace mROA.Implementation.Backend
_produceRepository = produceRepository; _produceRepository = produceRepository;
} }
public int HostId { get; set; }
public int ResisterObject<T>(object o, IEndPointContext context) public int ResisterObject<T>(object o, IEndPointContext context)
{ {
var repository = GetRepositoryByClientId(context.OwnerId); var repository = GetRepositoryByClientId(context.OwnerId);
@@ -11,21 +11,25 @@ namespace mROA.Implementation.Backend
{ {
public class NetworkGatewayModule : IGatewayModule public class NetworkGatewayModule : IGatewayModule
{ {
private readonly IServiceProvider _serviceProvider;
private readonly TcpListener _tcpListener; private readonly TcpListener _tcpListener;
private readonly IConnectionHub _hub; private readonly IConnectionHub _hub;
private readonly HubRequestExtractor _hre;
private readonly IDistributionModule _distribution;
private readonly IContextualSerializationToolKit _serialization; private readonly IContextualSerializationToolKit _serialization;
private readonly Dictionary<int, CancellationTokenSource> _extractorsCTS = new(); private readonly Dictionary<int, CancellationTokenSource> _extractorsTokenSources = new();
private ICallIndexProvider _callIndexProvider; private readonly ICallIndexProvider _callIndexProvider;
private readonly IIdentityGenerator _identityGenerator; private readonly IIdentityGenerator _identityGenerator;
public NetworkGatewayModule(IOptions<GatewayOptions> options, IServiceProvider service, IIdentityGenerator identityGenerator, IContextualSerializationToolKit serialization, ICallIndexProvider callIndexProvider, IConnectionHub hub)
public NetworkGatewayModule(IOptions<GatewayOptions> options, IIdentityGenerator identityGenerator,
IContextualSerializationToolKit serialization, ICallIndexProvider callIndexProvider, IConnectionHub hub, HubRequestExtractor hre, IDistributionModule distribution)
{ {
_tcpListener = new(options.Value.Endpoint); _tcpListener = new(options.Value.Endpoint);
_serviceProvider = service;
_identityGenerator = identityGenerator; _identityGenerator = identityGenerator;
_serialization = serialization; _serialization = serialization;
_callIndexProvider = callIndexProvider; _callIndexProvider = callIndexProvider;
_hub = hub; _hub = hub;
_hre = hre;
_distribution = distribution;
} }
public void Run() public void Run()
@@ -47,67 +51,119 @@ namespace mROA.Implementation.Backend
while (true) while (true)
{ {
var client = await _tcpListener.AcceptTcpClientAsync(); var client = await _tcpListener.AcceptTcpClientAsync();
Console.WriteLine($"Client connected from {client.Client.RemoteEndPoint}"); _ = HandleConnection(client);
var interaction = new ChannelInteractionModule(_serialization, _identityGenerator); }
}
var context = new EndPointContext(null, null); private async Task HandleConnection(TcpClient client)
context.CallIndexProvider = _callIndexProvider; {
var streamExtractor = client.NoDelay = true;
new ChannelInteractionModule.StreamExtractor(client.GetStream(), _serialization, context); Console.WriteLine($"Client connected from {client.Client.RemoteEndPoint}");
interaction.IsConnected = () => streamExtractor.IsConnected; var interaction = new ChannelInteractionModule(_serialization, _identityGenerator);
streamExtractor.MessageReceived = async message =>
{
await interaction.ReceiveChanel.Writer.WriteAsync(message);
};
_ = Task.Run(() => streamExtractor.SingleReceive());
var connectionRequest = await interaction.ReceiveChanel.Reader.ReadAsync();
var cts = new CancellationTokenSource();
switch (connectionRequest.MessageType) var context = new EndPointContext(null, null)
{
CallIndexProvider = _callIndexProvider
};
var streamExtractor =
new ChannelInteractionModule.StreamExtractor(client.GetStream());
interaction.IsConnected = () => streamExtractor.IsConnected;
streamExtractor.MessageReceived = async message =>
{
await interaction.ReceiveChanel.Writer.WriteAsync(message).ConfigureAwait(false);
};
_ = Task.Run(() => streamExtractor.SingleReceive());
var connectionRequest = await interaction.ReceiveChanel.Reader.ReadAsync();
var cts = new CancellationTokenSource();
switch (connectionRequest.MessageType)
{
case EMessageType.ClientConnect:
HandleNewClient(context, interaction, streamExtractor, cts, connectionRequest);
break;
case EMessageType.ClientRecovery:
{ {
case EMessageType.ClientConnect: RecoverDisconnectedClient(connectionRequest, streamExtractor, cts);
context.HostId = 0; break;
context.OwnerId = -interaction.ConnectionId; }
interaction.Context = context; default:
Task.Run(async () => await streamExtractor.LoopedReceive(cts.Token)); client.Close();
_ = streamExtractor.SendFromChannel(interaction.TrustedPostChanel, cts.Token); break;
interaction.PostMessageAsync(new NetworkMessageHeader(_serialization!, }
new IdAssignment { Id = interaction.ConnectionId }, null)); }
_extractorsCTS[interaction.ConnectionId] = cts;
_hub!.RegisterInteraction(interaction); private void HandleNewClient(EndPointContext context, ChannelInteractionModule interaction,
Console.WriteLine("Client registered"); ChannelInteractionModule.StreamExtractor streamExtractor, CancellationTokenSource cts,
break; NetworkMessage connection)
case EMessageType.ClientRecovery: {
context.HostId = 0;
context.OwnerId = -interaction.ConnectionId;
interaction.Context = context;
Task.Run(async () => await streamExtractor.LoopedReceive(cts.Token));
_ = streamExtractor.SendFromChannel(interaction.TrustedPostChanel, cts.Token);
interaction.PostMessageAsync(new NetworkMessage(_serialization,
new IdAssignment { Id = interaction.ConnectionId }, null));
_extractorsTokenSources[interaction.ConnectionId] = cts;
_hub.RegisterInteraction(interaction);
_hre.HubOnOnConnected(new RepresentationModule(interaction, _serialization.Clone()));
streamExtractor.MessageReceived = _distribution.GetDistributionAction(interaction.ConnectionId);
}
private void BindRequestFirstDistribution(IEndPointContext context, IChannelInteractionModule interaction,
ChannelInteractionModule.StreamExtractor streamExtractor, IRequestExtractor requestExtractor)
{
var converters = requestExtractor.Converters;
streamExtractor.MessageReceived = message =>
{
if (requestExtractor.Rule(message))
{
for (var i = 0; i < converters.Length; i++)
{ {
var recoveryRequest = _serialization!.Deserialize<ClientRecovery>(connectionRequest.Data, null); var func = converters[i];
var recoveryInteraction = _hub.GetInteraction(recoveryRequest.Id); if (func(message) is not { } t) continue;
_extractorsCTS[-recoveryRequest.Id].Cancel(); var deserialized = _serialization.Deserialize(message.Data, t, context)!;
Task.Run(() => requestExtractor.PushMessage(deserialized, message.MessageType));
recoveryInteraction.IsConnected = () => streamExtractor.IsConnected;
streamExtractor.MessageReceived = message =>
{
recoveryInteraction.ReceiveChanel.Writer.WriteAsync(message);
};
_ = streamExtractor.SendFromChannel(recoveryInteraction.TrustedPostChanel, cts.Token);
Task.Run(async () => await streamExtractor.LoopedReceive(cts.Token));
recoveryInteraction.Restart(false);
break; break;
} }
default:
client.Close(); return;
break;
} }
}
interaction.ReceiveChanel.Writer.WriteAsync(message).ConfigureAwait(false);
};
}
private void RecoverDisconnectedClient(NetworkMessage connectionRequest,
ChannelInteractionModule.StreamExtractor streamExtractor,
CancellationTokenSource cts)
{
var recoveryRequest = _serialization.Deserialize<ClientRecovery>(connectionRequest.Data, null);
var recoveryInteraction = _hub.GetInteraction(recoveryRequest.Id);
_extractorsTokenSources[-recoveryRequest.Id].Cancel();
recoveryInteraction.IsConnected = () => streamExtractor.IsConnected;
streamExtractor.MessageReceived = message =>
{
recoveryInteraction.ReceiveChanel.Writer.WriteAsync(message).ConfigureAwait(false);
};
_ = streamExtractor.SendFromChannel(recoveryInteraction.TrustedPostChanel, cts.Token);
streamExtractor.MessageReceived = _distribution.GetDistributionAction(recoveryInteraction.ConnectionId);
Task.Run(async () => await streamExtractor.LoopedReceive(cts.Token).ConfigureAwait(false));
recoveryInteraction.Restart(false);
} }
} }
public class GatewayOptions public class GatewayOptions
{ {
public IPEndPoint Endpoint { get; set; } public IPEndPoint Endpoint { get; set; }
public Type InteractionModuleType { get; set; }
} }
} }
+14 -12
View File
@@ -12,16 +12,19 @@ namespace mROA.Implementation.Backend
{ {
public class UdpGateway : IUntrustedGateway public class UdpGateway : IUntrustedGateway
{ {
private IConnectionHub _hub; private readonly IConnectionHub _hub;
private UdpClient _client; private readonly UdpClient _client;
private Dictionary<IPEndPoint, int> _reservedPorts = new(); private readonly Dictionary<IPEndPoint, Action<NetworkMessage>> _distributionActions = new();
private CancellationTokenSource _tokenSource = new(); private readonly CancellationTokenSource _tokenSource = new();
private IContextualSerializationToolKit _serializationToolkit; private readonly IContextualSerializationToolKit _serializationToolkit;
private readonly IDistributionModule _distribution;
public UdpGateway(IOptions<GatewayOptions> options, IConnectionHub hub, IContextualSerializationToolKit serializationToolkit) public UdpGateway(IOptions<GatewayOptions> options, IConnectionHub hub,
IContextualSerializationToolKit serializationToolkit, IDistributionModule distribution)
{ {
_hub = hub; _hub = hub;
_serializationToolkit = serializationToolkit; _serializationToolkit = serializationToolkit;
_distribution = distribution;
_client = new UdpClient(options.Value.Endpoint); _client = new UdpClient(options.Value.Endpoint);
} }
@@ -40,7 +43,7 @@ namespace mROA.Implementation.Backend
while (token.IsCancellationRequested == false) while (token.IsCancellationRequested == false)
{ {
var incoming = await _client.ReceiveAsync(); var incoming = await _client.ReceiveAsync();
var parsed = _serializationToolkit.Deserialize<NetworkMessageHeader>(incoming.Buffer, null); var parsed = _serializationToolkit.Deserialize<NetworkMessage>(incoming.Buffer, null);
try try
{ {
int channelId; int channelId;
@@ -48,13 +51,12 @@ namespace mROA.Implementation.Backend
{ {
case UntrustedConnect: case UntrustedConnect:
channelId = BitConverter.ToInt32(parsed.Data); channelId = BitConverter.ToInt32(parsed.Data);
_reservedPorts[incoming.RemoteEndPoint] = channelId; _distributionActions[incoming.RemoteEndPoint] = _distribution.GetDistributionAction(channelId);
_ = UntrustedSend(_hub.GetInteraction(channelId), incoming.RemoteEndPoint); _ = UntrustedSend(_hub.GetInteraction(channelId), incoming.RemoteEndPoint);
break; break;
default: default:
channelId = _reservedPorts[incoming.RemoteEndPoint]; _distributionActions[incoming.RemoteEndPoint].Invoke(parsed);
var interaction = _hub.GetInteraction(channelId);
await interaction.ReceiveChanel.Writer.WriteAsync(parsed, token);
break; break;
} }
} }
@@ -72,7 +74,7 @@ namespace mROA.Implementation.Backend
{ {
await foreach (var post in interaction.UntrustedPostChanel.ReadAllAsync()) await foreach (var post in interaction.UntrustedPostChanel.ReadAllAsync())
{ {
if (post.MessageType is not (CallRequest or EMessageType.CancelRequest if (post.MessageType is not (EMessageType.CallRequest or EMessageType.CancelRequest
or EventRequest)) or EventRequest))
continue; continue;
+4 -23
View File
@@ -1,21 +1,10 @@
using System; using System;
// ReSharper disable UnusedAutoPropertyAccessor.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
namespace mROA.Implementation namespace mROA.Implementation
{ {
public interface ICallRequest public struct CallRequest
{ {
Guid Id { get; } public RequestId Id { get; set; }
int CommandId { get; }
ComplexObjectIdentifier ObjectId { get; }
object?[]? Parameters { get; }
}
public struct DefaultCallRequest : ICallRequest
{
public Guid Id { get; set; }
public int CommandId { get; set; } public int CommandId { get; set; }
public ComplexObjectIdentifier ObjectId { get; set; } public ComplexObjectIdentifier ObjectId { get; set; }
@@ -27,16 +16,8 @@ namespace mROA.Implementation
} }
} }
public class CancelRequest : ICallRequest public struct CancelRequest
{ {
public Guid Id { get; set; } public RequestId Id { get; set; }
public int CommandId { get; set; } = -2;
public ComplexObjectIdentifier ObjectId { get; set; } = ComplexObjectIdentifier.Null;
public object?[]? Parameters { get; set; } = null;
public override string ToString()
{
return $"Cancel request {{ Id : {Id}, CommandId : {CommandId}, ObjectId : {ObjectId} }}";
}
} }
} }
@@ -8,19 +8,19 @@ namespace mROA.Implementation
{ {
public class CancellationRepository : ICancellationRepository public class CancellationRepository : ICancellationRepository
{ {
private readonly ConcurrentDictionary<Guid, CancellationTokenSource> _cancellations = new(); private readonly ConcurrentDictionary<RequestId, CancellationTokenSource> _cancellations = new();
public void RegisterCancellation(Guid id, CancellationTokenSource cts) public void RegisterCancellation(RequestId id, CancellationTokenSource cts)
{ {
_cancellations.TryAdd(id, cts); _cancellations.TryAdd(id, cts);
} }
public CancellationTokenSource? GetCancellation(Guid id) public CancellationTokenSource? GetCancellation(RequestId id)
{ {
return _cancellations.GetValueOrDefault(id); return _cancellations.GetValueOrDefault(id);
} }
public void FreeCancelation(Guid id) public void FreeCancellation(RequestId id)
{ {
_cancellations.Remove(id, out _); _cancellations.Remove(id, out _);
} }
+47 -62
View File
@@ -1,6 +1,6 @@
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Channels; using System.Threading.Channels;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -10,11 +10,11 @@ namespace mROA.Implementation
{ {
public class ChannelInteractionModule : IChannelInteractionModule public class ChannelInteractionModule : IChannelInteractionModule
{ {
private readonly ChannelReader<NetworkMessageHeader> _receiveReader; private readonly ChannelReader<NetworkMessage> _receiveReader;
private readonly ChannelWriter<NetworkMessageHeader> _trustedWriter; private readonly ChannelWriter<NetworkMessage> _trustedWriter;
private readonly ChannelWriter<NetworkMessageHeader> _untrustedWriter; private readonly ChannelWriter<NetworkMessage> _untrustedWriter;
private readonly Channel<NetworkMessageHeader> _outputTrustedChannel; private readonly Channel<NetworkMessage> _outputTrustedChannel;
private readonly Channel<NetworkMessageHeader> _outputUntrustedChannel; private readonly Channel<NetworkMessage> _outputUntrustedChannel;
private readonly IContextualSerializationToolKit _serialization; private readonly IContextualSerializationToolKit _serialization;
private bool _isConnected = true; private bool _isConnected = true;
private bool _isActive = true; private bool _isActive = true;
@@ -29,19 +29,19 @@ namespace mROA.Implementation
public ChannelInteractionModule(IContextualSerializationToolKit serialization) public ChannelInteractionModule(IContextualSerializationToolKit serialization)
{ {
_serialization = serialization; _serialization = serialization;
ReceiveChanel = Channel.CreateUnbounded<NetworkMessageHeader>(new UnboundedChannelOptions ReceiveChanel = Channel.CreateUnbounded<NetworkMessage>(new UnboundedChannelOptions
{ {
SingleReader = false, SingleReader = false,
SingleWriter = false, SingleWriter = false,
}); });
_receiveReader = ReceiveChanel.Reader; _receiveReader = ReceiveChanel.Reader;
_outputTrustedChannel = Channel.CreateUnbounded<NetworkMessageHeader>(new UnboundedChannelOptions _outputTrustedChannel = Channel.CreateUnbounded<NetworkMessage>(new UnboundedChannelOptions
{ {
SingleReader = true, SingleReader = true,
SingleWriter = true SingleWriter = true
}); });
_trustedWriter = _outputTrustedChannel.Writer; _trustedWriter = _outputTrustedChannel.Writer;
_outputUntrustedChannel = Channel.CreateUnbounded<NetworkMessageHeader>(new UnboundedChannelOptions _outputUntrustedChannel = Channel.CreateUnbounded<NetworkMessage>(new UnboundedChannelOptions
{ {
SingleReader = true, SingleReader = true,
SingleWriter = true, SingleWriter = true,
@@ -53,41 +53,33 @@ namespace mROA.Implementation
public int ConnectionId { get; set; } public int ConnectionId { get; set; }
public IEndPointContext Context { get; set; } public IEndPointContext Context { get; set; }
public Channel<NetworkMessageHeader> ReceiveChanel { get; } public Channel<NetworkMessage> ReceiveChanel { get; }
public ChannelReader<NetworkMessageHeader> TrustedPostChanel => _outputTrustedChannel.Reader; public ChannelReader<NetworkMessage> TrustedPostChanel => _outputTrustedChannel.Reader;
public ChannelReader<NetworkMessageHeader> UntrustedPostChanel => _outputUntrustedChannel.Reader; public ChannelReader<NetworkMessage> UntrustedPostChanel => _outputUntrustedChannel.Reader;
public Func<bool> IsConnected { get; set; } = () => false; public Func<bool> IsConnected { get; set; } = () => false;
public ValueTask<NetworkMessageHeader> GetNextMessageReceiving(bool infinite = true) public ValueTask<NetworkMessage> GetNextMessageReceiving()
{ {
return _receiveReader.ReadAsync(); return _receiveReader.ReadAsync();
// if (_currentReceiving != null) return _currentReceiving;
// _currentReceiving = Task.Run(async () => await GetNextMessage());
// return _currentReceiving;
} }
#pragma warning disable CS8602 // Dereference of a possibly null reference.
private async ValueTask<bool> PostMessageInternal(NetworkMessageHeader messageHeader) private async ValueTask<bool> PostMessageInternal(NetworkMessage message)
{ {
if (!IsConnected()) if (!IsConnected())
{ {
return false; return false;
} }
await _trustedWriter.WriteAsync(messageHeader); await _trustedWriter.WriteAsync(message);
return true; return true;
} }
#pragma warning restore CS8602 // Dereference of a possibly null reference.
public async Task PostMessageAsync(NetworkMessage message)
public async Task PostMessageAsync(NetworkMessageHeader messageHeader)
{ {
if (_serialization == null)
throw new NullReferenceException("Serialization toolkit is not initialized");
while (true) while (true)
{ {
if (await PostMessageInternal(messageHeader)) if (await PostMessageInternal(message))
break; break;
if (!_isActive) if (!_isActive)
@@ -100,9 +92,9 @@ namespace mROA.Implementation
} }
} }
public async Task PostMessageUntrustedAsync(NetworkMessageHeader messageHeader) public async Task PostMessageUntrustedAsync(NetworkMessage message)
{ {
await _untrustedWriter.WriteAsync(messageHeader); await _untrustedWriter.WriteAsync(message);
} }
public event Action<int>? OnDisconnected; public event Action<int>? OnDisconnected;
@@ -112,12 +104,12 @@ namespace mROA.Implementation
if (sendRecovery) if (sendRecovery)
{ {
await PostMessageAsync( await PostMessageAsync(
new NetworkMessageHeader(_serialization, new ClientRecovery(Math.Abs(ConnectionId)), Context)); new NetworkMessage(_serialization, new ClientRecovery(Math.Abs(ConnectionId)), Context));
await ReceiveChanel.Reader.ReadAsync(); await ReceiveChanel.Reader.ReadAsync();
} }
else else
{ {
await _trustedWriter.WriteAsync(new NetworkMessageHeader()); await _trustedWriter.WriteAsync(new NetworkMessage());
} }
PassReconnection(); PassReconnection();
@@ -150,42 +142,35 @@ namespace mROA.Implementation
public class StreamExtractor public class StreamExtractor
{ {
private const int BufferSize = ushort.MaxValue; private const int BufferSize = ushort.MaxValue + 19;
private readonly Stream _ioStream; private readonly Stream _ioStream;
private readonly IContextualSerializationToolKit _serializationToolkit;
private readonly Memory<byte> _buffer = new byte[BufferSize]; private readonly Memory<byte> _buffer = new byte[BufferSize];
private readonly IEndPointContext _context;
private readonly byte[] _lenBuffer;
public StreamExtractor(Stream ioStream, IContextualSerializationToolKit serializationToolkit, public StreamExtractor(Stream ioStream)
IEndPointContext context)
{ {
_ioStream = ioStream; _ioStream = ioStream;
_serializationToolkit = serializationToolkit;
_context = context;
_lenBuffer = new byte[2];
} }
public Action<NetworkMessageHeader> MessageReceived = _ => { }; public Action<NetworkMessage> MessageReceived = _ => { };
private async Task<ushort> ReadMessageLength()
{
await _ioStream.ReadAsync(_lenBuffer);
var len = BitConverter.ToUInt16(_lenBuffer);
return len;
}
public async Task SingleReceive(CancellationToken token = default) public async Task SingleReceive(CancellationToken token = default)
{ {
var len = await ReadMessageLength();
var localSpan = _buffer[..len];
await _ioStream.ReadExactlyAsync(localSpan, cancellationToken: token); _ = await _ioStream.ReadExactlyAsync(_buffer[..19], token);
var message = _serializationToolkit.Deserialize<NetworkMessageHeader>(localSpan, _context);
MessageReceived(message); 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) public async Task LoopedReceive(CancellationToken token = default)
@@ -196,17 +181,17 @@ namespace mROA.Implementation
} }
} }
private async Task Send(NetworkMessageHeader message, CancellationToken token = default) private async Task Send(NetworkMessage message, CancellationToken token = default)
{ {
var bodySpan = _buffer[2..]; var meta = message.ToMeta();
var len = _serializationToolkit.Serialize(message, bodySpan.Span, _context); MemoryMarshal.Write(_buffer.Span, ref meta);
var header = BitConverter.GetBytes((ushort)len); message.Data.CopyTo(_buffer.Span[19..]);
header.CopyTo(_buffer); var sendingSpan = _buffer[..(19 + meta.BodyLength)];
var sendingSpan = _buffer[..(len + 2)]; // Console.WriteLine("SEND " + message);
await _ioStream.WriteAsync(sendingSpan, token); await _ioStream.WriteAsync(sendingSpan, token);
} }
public async Task SendFromChannel(ChannelReader<NetworkMessageHeader> channel, public async Task SendFromChannel(ChannelReader<NetworkMessage> channel,
CancellationToken token = default) CancellationToken token = default)
{ {
while (token.IsCancellationRequested == false && IsConnected) while (token.IsCancellationRequested == false && IsConnected)
@@ -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 System.Collections.Generic;
using mROA.Abstract; using mROA.Abstract;
@@ -5,19 +6,16 @@ namespace mROA.Implementation
{ {
public class CollectableMethodRepository : IMethodRepository public class CollectableMethodRepository : IMethodRepository
{ {
private List<IMethodInvoker> _methods = new(); private readonly List<IMethodInvoker> _methods = new() { MethodInvoker.Dispose };
private IMethodInvoker[] _baked = Array.Empty<IMethodInvoker>();
public void AppendInvokers(IEnumerable<IMethodInvoker> methodInvokers) public void AppendInvokers(IEnumerable<IMethodInvoker> methodInvokers)
{ {
_methods.AddRange(methodInvokers); _methods.AddRange(methodInvokers);
_baked = _methods.ToArray();
} }
public IMethodInvoker GetMethod(int id) public IMethodInvoker GetMethod(int id)
{ {
if (id == -1) return _baked[++id];
return MethodInvoker.Dispose;
return _methods[id];
} }
} }
} }
@@ -5,7 +5,7 @@ namespace mROA.Implementation.CommandExecution
{ {
public class AsyncCommandExecution : ICommandExecution public class AsyncCommandExecution : ICommandExecution
{ {
public Guid Id { get; set; } public RequestId Id { get; set; }
public EMessageType MessageType => EMessageType.Unknown; public EMessageType MessageType => EMessageType.Unknown;
} }
} }
@@ -6,7 +6,7 @@ namespace mROA.Implementation.CommandExecution
{ {
public class ExceptionCommandExecution : ICommandExecution public class ExceptionCommandExecution : ICommandExecution
{ {
public Guid Id { get; set; } public RequestId Id { get; set; }
public EMessageType MessageType => EMessageType.ExceptionCommandExecution; public EMessageType MessageType => EMessageType.ExceptionCommandExecution;
public string Exception { get; set; } public string Exception { get; set; }
@@ -1,19 +1,17 @@
using System; using System;
using mROA.Abstract; using mROA.Abstract;
// ReSharper disable UnusedAutoPropertyAccessor.Global
namespace mROA.Implementation.CommandExecution namespace mROA.Implementation.CommandExecution
{ {
public struct FinalCommandExecution : ICommandExecution public struct FinalCommandExecution : ICommandExecution
{ {
public Guid Id { get; set; } public RequestId Id { get; set; }
public EMessageType MessageType => EMessageType.FinishedCommandExecution; public EMessageType MessageType => EMessageType.FinishedCommandExecution;
} }
public struct FinalCommandExecution<T> : ICommandExecution public struct FinalCommandExecution<T> : ICommandExecution
{ {
public Guid Id { get; set; } public RequestId Id { get; set; }
public EMessageType MessageType => EMessageType.FinishedCommandExecution; public EMessageType MessageType => EMessageType.FinishedCommandExecution;
public T? Result { get; set; } public T? Result { get; set; }
} }
@@ -2,7 +2,6 @@ using System;
namespace mROA.Implementation namespace mROA.Implementation
{ {
#pragma warning disable CS8618, CS9264
public struct ComplexObjectIdentifier : IEquatable<ComplexObjectIdentifier> public struct ComplexObjectIdentifier : IEquatable<ComplexObjectIdentifier>
{ {
public int ContextId; public int ContextId;
@@ -14,14 +13,9 @@ namespace mROA.Implementation
OwnerId = ownerId; OwnerId = ownerId;
} }
public static ComplexObjectIdentifier Singleton(int ownerId) => new() { ContextId = -1, OwnerId = ownerId }; public static ComplexObjectIdentifier Null = new() { ContextId = -2, OwnerId = 0 };
public static ComplexObjectIdentifier Null = new ComplexObjectIdentifier { ContextId = -2, OwnerId = 0 };
public static ComplexObjectIdentifier FromFlat(ulong flat) => new() { Flat = flat }; public static ComplexObjectIdentifier FromFlat(ulong flat) => new() { Flat = flat };
public int ClientId => Math.Abs(OwnerId);
public bool IsSererStored => OwnerId > 0;
public bool IsClientStored => OwnerId < 0;
public override string ToString() public override string ToString()
{ {
@@ -1,29 +1,23 @@
using System; using mROA.Abstract;
using mROA.Abstract;
namespace mROA.Implementation namespace mROA.Implementation
{ {
public class CreativeRepresentationModuleProducer : IRepresentationModuleProducer public class CreativeRepresentationModuleProducer : IRepresentationModuleProducer
{ {
private IServiceProvider _creationModules; private readonly IConnectionHub _hub;
private IConnectionHub _hub; private readonly IContextualSerializationToolKit _serialization;
private IContextualSerializationToolKit _serialization;
public CreativeRepresentationModuleProducer(IServiceProvider creationModules, IConnectionHub hub, IContextualSerializationToolKit serialization) public CreativeRepresentationModuleProducer(IConnectionHub hub, IContextualSerializationToolKit serialization)
{ {
_creationModules = creationModules;
_hub = hub; _hub = hub;
_serialization = serialization; _serialization = serialization;
} }
public IRepresentationModule Produce(int id) public IRepresentationModule Produce(int id)
{ {
if (_hub == null)
throw new NullReferenceException("Interaction module is null");
var interaction = _hub.GetInteraction(id); var interaction = _hub.GetInteraction(id);
var produced = new RepresentationModule(interaction, _serialization); var produced = new RepresentationModule(interaction, _serialization.Clone());
return produced; 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);
};
}
}
}
@@ -0,0 +1,13 @@
namespace mROA.Implementation
{
public class DistributionOptions
{
public EDistributionType DistributionType { get; set; }
}
public enum EDistributionType
{
Channeled,
ExtractorFirst
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
namespace mROA.Implementation namespace mROA.Implementation
{ {
public enum EMessageType public enum EMessageType : byte
{ {
Unknown, Unknown,
FinishedCommandExecution, FinishedCommandExecution,
@@ -12,6 +12,6 @@ namespace mROA.Implementation
ClientRecovery, ClientRecovery,
ClientConnect, ClientConnect,
ClientDisconnect, ClientDisconnect,
UntrustedConnect, UntrustedConnect
} }
} }
+1 -1
View File
@@ -6,8 +6,8 @@ namespace mROA.Implementation
{ {
public EndPointContext() public EndPointContext()
{ {
} }
public EndPointContext(IRealStoreInstanceRepository realRepository, IInstanceRepository remoteRepository) public EndPointContext(IRealStoreInstanceRepository realRepository, IInstanceRepository remoteRepository)
{ {
RealRepository = realRepository; RealRepository = realRepository;
+1 -1
View File
@@ -55,7 +55,7 @@ namespace mROA.Implementation
public void Free(int index) public void Free(int index)
{ {
_freePlaces.AddFirst(index); _freePlaces.AddFirst(index);
_array[index] = default; _array[index] = null;
} }
} }
} }
@@ -1,4 +1,4 @@
using System; using System.IO;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading; using System.Threading;
@@ -14,39 +14,36 @@ namespace mROA.Implementation.Frontend
{ {
private readonly IPEndPoint _serverEndPoint; private readonly IPEndPoint _serverEndPoint;
private TcpClient _tcpClient = new(); private TcpClient _tcpClient = new();
private IChannelInteractionModule _interactionModule; private readonly IChannelInteractionModule _interactionModule;
private IContextualSerializationToolKit _serialization; private readonly IContextualSerializationToolKit _serialization;
private ChannelInteractionModule.StreamExtractor? _currentExtractor; private ChannelInteractionModule.StreamExtractor _currentExtractor;
private CancellationTokenSource _rawExtractorCancellation; private CancellationTokenSource _rawExtractorCancellation;
private IEndPointContext _context; private readonly IEndPointContext _context;
public NetworkFrontendBridge(IOptions<GatewayOptions> options, IEndPointContext context, IContextualSerializationToolKit serialization, IChannelInteractionModule interactionModule) public NetworkFrontendBridge(IOptions<GatewayOptions> options, IEndPointContext context,
IContextualSerializationToolKit serialization, IChannelInteractionModule interactionModule)
{ {
_serverEndPoint = options.Value.Endpoint; _serverEndPoint = options.Value.Endpoint;
_context = context; _context = context;
_serialization = serialization; _serialization = serialization;
_interactionModule = interactionModule; _interactionModule = interactionModule;
_rawExtractorCancellation = new CancellationTokenSource(); _rawExtractorCancellation = new CancellationTokenSource();
_currentExtractor = new ChannelInteractionModule.StreamExtractor(Stream.Null);
} }
public async Task Connect() public async Task Connect()
{ {
if (_interactionModule is null)
throw new Exception("Interaction module was not injected");
if (_serialization == null)
throw new NullReferenceException("Serialization toolkit is not initialized");
_tcpClient.Connect(_serverEndPoint); _tcpClient.Connect(_serverEndPoint);
_tcpClient.NoDelay = true; _tcpClient.NoDelay = true;
PrepareExtractor(); PrepareExtractor();
_interactionModule.IsConnected = () => _currentExtractor.IsConnected; _interactionModule.IsConnected = () => _currentExtractor.IsConnected;
_interactionModule.OnDisconnected += _ => { Reconnect(); }; _interactionModule.OnDisconnected += _ => { Reconnect().ConfigureAwait(false); };
_interactionModule.PostMessageAsync(new NetworkMessageHeader(_serialization, new ClientConnect(), _context)) _interactionModule.PostMessageAsync(new NetworkMessage(_serialization, new ClientConnect(), _context))
.Wait(); .Wait();
_currentExtractor.SingleReceive(); _ = _currentExtractor.SingleReceive().ConfigureAwait(false);
var idMessage = await _interactionModule.GetNextMessageReceiving(false); var idMessage = await _interactionModule.GetNextMessageReceiving();
if (idMessage.MessageType != EMessageType.IdAssigning) if (idMessage.MessageType != EMessageType.IdAssigning)
{ {
@@ -54,8 +51,7 @@ namespace mROA.Implementation.Frontend
$"Incorrect message type. Must be IdAssigning, current : {idMessage.MessageType.ToString()}"); $"Incorrect message type. Must be IdAssigning, current : {idMessage.MessageType.ToString()}");
} }
_ = Task.Run(async () => await _currentExtractor.LoopedReceive(_rawExtractorCancellation.Token));
Task.Run(async () => await _currentExtractor.LoopedReceive(_rawExtractorCancellation.Token));
var assignment = _serialization.Deserialize<IdAssignment>(idMessage.Data, _context); var assignment = _serialization.Deserialize<IdAssignment>(idMessage.Data, _context);
_interactionModule.ConnectionId = -assignment.Id; _interactionModule.ConnectionId = -assignment.Id;
@@ -66,9 +62,9 @@ namespace mROA.Implementation.Frontend
private void PrepareExtractor() private void PrepareExtractor()
{ {
_currentExtractor = _currentExtractor =
new ChannelInteractionModule.StreamExtractor(_tcpClient.GetStream(), _serialization, _context); new ChannelInteractionModule.StreamExtractor(_tcpClient.GetStream());
_ = _currentExtractor.SendFromChannel(_interactionModule!.TrustedPostChanel, _ = _currentExtractor.SendFromChannel(_interactionModule.TrustedPostChanel,
_rawExtractorCancellation.Token); _rawExtractorCancellation.Token);
_currentExtractor.MessageReceived = message => _currentExtractor.MessageReceived = message =>
{ {
@@ -86,7 +82,7 @@ namespace mROA.Implementation.Frontend
PrepareExtractor(); PrepareExtractor();
Task.Run(async () => await _currentExtractor.LoopedReceive(_rawExtractorCancellation.Token)); _ = Task.Run(async () => await _currentExtractor.LoopedReceive(_rawExtractorCancellation.Token));
await _interactionModule.Restart(true); await _interactionModule.Restart(true);
} }
@@ -98,7 +94,7 @@ namespace mROA.Implementation.Frontend
public void Disconnect() public void Disconnect()
{ {
_ = _interactionModule!.PostMessageAsync(new NetworkMessageHeader(_serialization, new ClientDisconnect(), _ = _interactionModule.PostMessageAsync(new NetworkMessage(_serialization, new ClientDisconnect(),
_context)); _context));
_interactionModule.Dispose(); _interactionModule.Dispose();
_tcpClient.Dispose(); _tcpClient.Dispose();
@@ -4,7 +4,7 @@ namespace mROA.Implementation.Frontend
{ {
public class RemoteException : Exception public class RemoteException : Exception
{ {
public Guid CallRequestId; public RequestId CallRequestId;
private readonly string _error; private readonly string _error;
public RemoteException(string error) public RemoteException(string error)
@@ -3,98 +3,90 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using mROA.Abstract; using mROA.Abstract;
// ReSharper disable MethodHasAsyncOverload
namespace mROA.Implementation.Frontend namespace mROA.Implementation.Frontend
{ {
public class RequestExtractor : IRequestExtractor public class RequestExtractor : IRequestExtractor
{ {
private IExecuteModule _executeModule; private readonly IExecuteModule _executeModule;
private IMethodRepository _methodRepository; private readonly IRepresentationModule _representationModule;
private readonly IEndPointContext _context;
private IRepresentationModule _representationModule; public RequestExtractor(IExecuteModule executeModule, IRepresentationModule representationModule,
private IContextualSerializationToolKit _serializationToolkit; IEndPointContext context)
private IEndPointContext _context;
public RequestExtractor(IExecuteModule executeModule, IMethodRepository methodRepository, IRepresentationModule representationModule, IContextualSerializationToolKit serializationToolkit, IEndPointContext context)
{ {
_executeModule = executeModule; _executeModule = executeModule;
_methodRepository = methodRepository;
_representationModule = representationModule; _representationModule = representationModule;
_serializationToolkit = serializationToolkit;
_context = context; _context = context;
} }
public async Task StartExtraction() public async Task StartExtraction()
{ {
ThrowIfNotInjected();
var streamTokenSource = new CancellationTokenSource(); var streamTokenSource = new CancellationTokenSource();
var query = _representationModule.GetStream(m => var query = _representationModule.GetStream(Rule, _context,
m.MessageType is EMessageType.CallRequest or EMessageType.CancelRequest
or EMessageType.EventRequest or EMessageType.ClientDisconnect, _context,
streamTokenSource.Token, streamTokenSource.Token,
m => m.MessageType == EMessageType.CallRequest ? typeof(DefaultCallRequest) : null, Converters);
m => m.MessageType == EMessageType.CancelRequest ? typeof(CancelRequest) : null,
m => m.MessageType == EMessageType.EventRequest ? typeof(DefaultCallRequest) : null,
m => m.MessageType == EMessageType.ClientDisconnect ? typeof(ClientDisconnect) : null);
await foreach (var command in query) await foreach (var command in query)
{ {
switch (command.originalType) PushMessage(command.parced, command.originalType);
{
case EMessageType.CallRequest:
HandleCallRequest((DefaultCallRequest)command.parced);
break;
case EMessageType.ClientDisconnect:
return;
case EMessageType.EventRequest:
HandleEventRequest((DefaultCallRequest)command.parced);
break;
case EMessageType.CancelRequest:
HandleCancelRequest((command.parced as CancelRequest)!);
break;
default:
continue;
}
} }
} }
private void ThrowIfNotInjected() public void PushMessage(object parsed, EMessageType originalType)
{ {
if (_serializationToolkit == null) switch (originalType)
throw new NullReferenceException("Serializing toolkit is null."); {
if (_executeModule == null) case EMessageType.CallRequest:
throw new NullReferenceException("Execute module is null."); HandleCallRequest((CallRequest)parsed);
if (_representationModule == null) break;
throw new NullReferenceException("Representation module is null."); case EMessageType.ClientDisconnect:
if (_methodRepository == null) return;
throw new NullReferenceException("Method repository is null."); case EMessageType.EventRequest:
HandleEventRequest((CallRequest)parsed);
break;
case EMessageType.CancelRequest:
HandleCancelRequest((CancelRequest)parsed);
break;
default:
throw new ArgumentOutOfRangeException();
}
} }
public Predicate<NetworkMessage> Rule { get; } = m =>
m.MessageType is EMessageType.CallRequest or EMessageType.CancelRequest
or EMessageType.EventRequest or EMessageType.ClientDisconnect;
public Func<NetworkMessage, Type?>[] Converters { get; } =
{
m => m.MessageType == EMessageType.CallRequest ? typeof(CallRequest) : null,
m => m.MessageType == EMessageType.CancelRequest ? typeof(CancelRequest) : null,
m => m.MessageType == EMessageType.EventRequest ? typeof(CallRequest) : null,
m => m.MessageType == EMessageType.ClientDisconnect ? typeof(ClientDisconnect) : null
};
private void HandleCancelRequest(CancelRequest req) private void HandleCancelRequest(CancelRequest req)
{ {
_executeModule.Execute(req, _context.RealRepository, _representationModule, _context); _executeModule.Cancel(req);
} }
private void HandleCallRequest(DefaultCallRequest request) private void HandleCallRequest(CallRequest request)
{ {
var result = _executeModule.Execute(request, _context.RealRepository, _representationModule, _context); var result = _executeModule.Execute(request, _context.RealRepository, _representationModule, _context);
var resultType = result.MessageType; if (result is null)
if (resultType == EMessageType.Unknown)
{ {
return; return;
} }
_representationModule.PostCallMessage(request.Id, resultType, result, _context); var resultType = result.MessageType;
_representationModule.PostCallMessageAsync(request.Id, resultType, result, _context).ConfigureAwait(false);
} }
private void HandleEventRequest(DefaultCallRequest request) private void HandleEventRequest(CallRequest request)
{ {
_executeModule.Execute(request, _context.RemoteRepository, _representationModule, _context); _executeModule.Execute(request, _context.RemoteRepository, _representationModule, _context);
} }
@@ -1,24 +0,0 @@
using mROA.Abstract;
namespace mROA.Implementation.Frontend
{
public class StaticOwnershipRepository : IOwnershipRepository
{
private readonly int _id;
public StaticOwnershipRepository(int id)
{
_id = id;
}
public int GetOwnershipId()
{
return _id;
}
public int GetHostOwnershipId()
{
return _id;
}
}
}
@@ -14,7 +14,8 @@ namespace mROA.Implementation.Frontend
private readonly CancellationTokenSource _tokenSource = new(); private readonly CancellationTokenSource _tokenSource = new();
private readonly IEndPointContext _context; private readonly IEndPointContext _context;
public UdpUntrustedInteraction(IContextualSerializationToolKit serializationToolkit, IChannelInteractionModule channelInteractionModule, IEndPointContext context) public UdpUntrustedInteraction(IContextualSerializationToolKit serializationToolkit,
IChannelInteractionModule channelInteractionModule, IEndPointContext context)
{ {
_serializationToolkit = serializationToolkit; _serializationToolkit = serializationToolkit;
_channelInteractionModule = channelInteractionModule; _channelInteractionModule = channelInteractionModule;
@@ -43,7 +44,7 @@ namespace mROA.Implementation.Frontend
while (token.IsCancellationRequested == false) while (token.IsCancellationRequested == false)
{ {
var message = new Memory<byte>((await udpClient.ReceiveAsync()).Buffer); var message = new Memory<byte>((await udpClient.ReceiveAsync()).Buffer);
var parsed = _serializationToolkit.Deserialize<NetworkMessageHeader>(message, _context); var parsed = _serializationToolkit.Deserialize<NetworkMessage>(message, _context);
await writer.WriteAsync(parsed, token); await writer.WriteAsync(parsed, token);
} }
@@ -51,10 +52,10 @@ namespace mROA.Implementation.Frontend
private async Task Posting(UdpClient udpClient, CancellationToken token) private async Task Posting(UdpClient udpClient, CancellationToken token)
{ {
var initMessage = new NetworkMessageHeader var initMessage = new NetworkMessage
{ {
MessageType = EMessageType.UntrustedConnect, Id = Guid.NewGuid(), MessageType = EMessageType.UntrustedConnect, Id = RequestId.Generate(),
Data = BitConverter.GetBytes(_channelInteractionModule.ConnectionId) Data = BitConverter.GetBytes(-_channelInteractionModule.ConnectionId)
}; };
var initParsed = _serializationToolkit.Serialize(initMessage, _context); var initParsed = _serializationToolkit.Serialize(initMessage, _context);
+2
View File
@@ -9,6 +9,7 @@ namespace mROA.Implementation
public class ClientRecovery : INetworkMessage public class ClientRecovery : INetworkMessage
{ {
// ReSharper disable once UnusedMember.Global
public ClientRecovery() public ClientRecovery()
{ {
Id = 0; Id = 0;
@@ -26,5 +27,6 @@ namespace mROA.Implementation
public class ClientConnect : INetworkMessage public class ClientConnect : INetworkMessage
{ {
public EMessageType MessageType => EMessageType.ClientConnect; public EMessageType MessageType => EMessageType.ClientConnect;
public string[]? Config { get; set; }
} }
} }
+1 -1
View File
@@ -37,7 +37,7 @@ namespace mROA.Implementation
public Type[] ParameterTypes { get; set; } = Type.EmptyTypes; public Type[] ParameterTypes { get; set; } = Type.EmptyTypes;
public Type? ReturnType { get; set; } public Type? ReturnType { get; set; }
public Type SuitableType { get; set; } = typeof(object); public Type SuitableType { get; set; } = typeof(object);
public bool RequireCancellation { get; set; } = true;
public Action<object, object?[]?, object[], Action<object?>> Invoking { get; set; } = public Action<object, object?[]?, object[], Action<object?>> Invoking { get; set; } =
(_, _, _, post) => { post.Invoke(null); }; (_, _, _, post) => { post.Invoke(null); };
+72
View File
@@ -0,0 +1,72 @@
using System;
using mROA.Abstract;
namespace mROA.Implementation
{
public class NetworkMessage
{
private bool Equals(NetworkMessage other)
{
return Id.Equals(other.Id) && MessageType == other.MessageType;
}
public override bool Equals(object? obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.GetType() == GetType() && Equals((NetworkMessage)obj);
}
public NetworkMessage()
{
Data = Array.Empty<byte>();
}
public NetworkMessage(IContextualSerializationToolKit serializationToolkit,
INetworkMessage networkMessage, IEndPointContext? context)
{
MessageType = networkMessage.MessageType;
Data = serializationToolkit.Serialize(networkMessage, context);
Id = RequestId.Generate();
}
public RequestId Id { get; set; }
public EMessageType MessageType { get; set; }
public byte[] Data { get; set; }
public override string ToString()
{
return $" {Id}:{MessageType} [{Data.Length}]";
}
public NetworkMessageMeta ToMeta()
{
return new NetworkMessageMeta
{
BodyLength = (ushort)(Data == null ? 0 : Data.Length),
Type = (byte)MessageType,
Id = Id
};
}
public struct NetworkMessageMeta
{
public RequestId Id;
public ushort BodyLength;
public byte Type;
public NetworkMessage ToMessage(Span<byte> memory)
{
var data = memory[19..][..BodyLength];
return new NetworkMessage
{
Data = data.ToArray(),
Id = Id,
MessageType = (EMessageType)Type
};
}
}
}
}
@@ -1,49 +0,0 @@
using System;
using mROA.Abstract;
// ReSharper disable UnusedMember.Global
namespace mROA.Implementation
{
public class NetworkMessageHeader
{
private bool Equals(NetworkMessageHeader other)
{
return Id.Equals(other.Id) && MessageType == other.MessageType;
}
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((NetworkMessageHeader)obj);
}
public static readonly NetworkMessageHeader Null = new();
public NetworkMessageHeader()
{
Data = Array.Empty<byte>();
}
public NetworkMessageHeader(IContextualSerializationToolKit serializationToolkit,
INetworkMessage networkMessage, IEndPointContext? context)
{
MessageType = networkMessage.MessageType;
Data = serializationToolkit.Serialize(networkMessage, context);
Id = Guid.NewGuid();
}
public Guid Id { get; set; }
public EMessageType MessageType { get; set; }
public byte[] Data { get; set; }
public override string ToString()
{
return $" {Id}:{MessageType} [{Data.Length}]";
}
}
}
+16 -16
View File
@@ -7,12 +7,14 @@ namespace mROA.Implementation
{ {
public class RemoteInstanceRepository : IInstanceRepository public class RemoteInstanceRepository : IInstanceRepository
{ {
private List<RemoteObjectBase> _producedProxys = new(); private readonly List<RemoteObjectBase> _producedProxies = new();
private ICallIndexProvider _callIndexProvider; private readonly List<RemoteObjectBase> _producedSingletonProxies = new();
private readonly ICallIndexProvider _callIndexProvider;
private IRepresentationModuleProducer _representationProducer; private readonly IRepresentationModuleProducer _representationProducer;
public RemoteInstanceRepository(ICallIndexProvider callIndexProvider, IRepresentationModuleProducer representationProducer) public RemoteInstanceRepository(ICallIndexProvider callIndexProvider,
IRepresentationModuleProducer representationProducer)
{ {
_callIndexProvider = callIndexProvider; _callIndexProvider = callIndexProvider;
_representationProducer = representationProducer; _representationProducer = representationProducer;
@@ -31,11 +33,9 @@ namespace mROA.Implementation
public T GetObject<T>(ComplexObjectIdentifier id, IEndPointContext context) where T : class public T GetObject<T>(ComplexObjectIdentifier id, IEndPointContext context) where T : class
{ {
var index = _producedProxys.Find(i => i.Identifier.Equals(id)); var existing = _producedProxies.Find(i => i.Identifier.Equals(id));
if (index is not null) if (existing is not null)
return (T)(index as object); return (T)(existing as object);
if (_representationProducer == null)
throw new NullReferenceException("representation producer is not initialized");
if (!_callIndexProvider.Activators.TryGetValue(typeof(T), out var remoteType)) if (!_callIndexProvider.Activators.TryGetValue(typeof(T), out var remoteType))
throw new NotSupportedException(); throw new NotSupportedException();
@@ -44,7 +44,7 @@ namespace mROA.Implementation
var remote = remoteType(id.ContextId, var remote = remoteType(id.ContextId,
representationModule, context, _callIndexProvider.GetIndices(typeof(T))); representationModule, context, _callIndexProvider.GetIndices(typeof(T)));
_producedProxys.Add(remote!); _producedProxies.Add(remote!);
return (remote as T)!; return (remote as T)!;
} }
@@ -56,8 +56,9 @@ namespace mROA.Implementation
public object GetSingletonObject(Type type, IEndPointContext context) public object GetSingletonObject(Type type, IEndPointContext context)
{ {
if (_representationProducer == null) var existing = _producedSingletonProxies.Find(i => i.GetType() == type);
throw new NullReferenceException("representation producer is not initialized"); if (existing is not null)
return existing;
var representationModule = var representationModule =
_representationProducer.Produce(context.OwnerId); _representationProducer.Produce(context.OwnerId);
@@ -65,11 +66,10 @@ namespace mROA.Implementation
var instance = _callIndexProvider.Activators[type](-1, representationModule, context, var instance = _callIndexProvider.Activators[type](-1, representationModule, context,
_callIndexProvider.GetIndices(type))!; _callIndexProvider.GetIndices(type))!;
var remoteObjectBase = instance; _producedProxies.Add(instance);
_producedSingletonProxies.Add(instance);
_producedProxys.Add(remoteObjectBase); return _producedProxies.Last();
return _producedProxys.Last();
} }
public int GetObjectIndex<T>(object o, IEndPointContext context) public int GetObjectIndex<T>(object o, IEndPointContext context)
+9 -9
View File
@@ -4,7 +4,6 @@ using System.Threading.Tasks;
using mROA.Abstract; using mROA.Abstract;
using mROA.Implementation.CommandExecution; using mROA.Implementation.CommandExecution;
// ReSharper disable UnusedMember.Global
namespace mROA.Implementation namespace mROA.Implementation
{ {
@@ -21,8 +20,7 @@ namespace mROA.Implementation
{ {
if (obj is null) return false; if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true; if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false; return obj.GetType() == GetType() && Equals((RemoteObjectBase)obj);
return Equals((RemoteObjectBase)obj);
} }
public override int GetHashCode() public override int GetHashCode()
@@ -57,9 +55,9 @@ namespace mROA.Implementation
protected async Task<T> GetResultAsync<T>(int methodId, object?[]? parameters = null, protected async Task<T> GetResultAsync<T>(int methodId, object?[]? parameters = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var request = new DefaultCallRequest var request = new CallRequest
{ {
Id = Guid.NewGuid(), CommandId = methodId, ObjectId = _identifier, Parameters = parameters Id = RequestId.Generate(), CommandId = methodId, ObjectId = _identifier, Parameters = parameters
}; };
await _representationModule.PostCallMessageAsync(request.Id, EMessageType.CallRequest, request, _context); await _representationModule.PostCallMessageAsync(request.Id, EMessageType.CallRequest, request, _context);
@@ -102,9 +100,9 @@ namespace mROA.Implementation
protected async Task CallAsync(int methodId, object?[]? parameters = null, protected async Task CallAsync(int methodId, object?[]? parameters = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var request = new DefaultCallRequest var request = new CallRequest
{ {
Id = Guid.NewGuid(), CommandId = methodId, ObjectId = _identifier, Parameters = parameters Id = RequestId.Generate(), CommandId = methodId, ObjectId = _identifier, Parameters = parameters
}; };
await _representationModule.PostCallMessageAsync(request.Id, EMessageType.CallRequest, request, _context); await _representationModule.PostCallMessageAsync(request.Id, EMessageType.CallRequest, request, _context);
@@ -142,14 +140,16 @@ namespace mROA.Implementation
return; return;
case EMessageType.ExceptionCommandExecution: case EMessageType.ExceptionCommandExecution:
throw (responseRequest.Deserialized as ExceptionCommandExecution)!.GetException(); throw (responseRequest.Deserialized as ExceptionCommandExecution)!.GetException();
default:
throw new ArgumentOutOfRangeException();
} }
} }
protected async Task CallUntrustedAsync(int methodId, object?[]? parameters = null) protected async Task CallUntrustedAsync(int methodId, object?[]? parameters = null)
{ {
var request = new DefaultCallRequest var request = new CallRequest
{ {
Id = Guid.NewGuid(), CommandId = methodId, ObjectId = _identifier, Parameters = parameters Id = RequestId.Generate(), CommandId = methodId, ObjectId = _identifier, Parameters = parameters
}; };
await _representationModule.PostCallMessageUntrustedAsync(request.Id, EMessageType.CallRequest, request, await _representationModule.PostCallMessageUntrustedAsync(request.Id, EMessageType.CallRequest, request,
_context); _context);
+15 -21
View File
@@ -6,16 +6,16 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using mROA.Abstract; using mROA.Abstract;
#pragma warning disable CS8602 // Dereference of a possibly null reference.
namespace mROA.Implementation namespace mROA.Implementation
{ {
public class RepresentationModule : IRepresentationModule public class RepresentationModule : IRepresentationModule
{ {
private IChannelInteractionModule _interaction; private readonly IChannelInteractionModule _interaction;
private IContextualSerializationToolKit _serialization; private readonly IContextualSerializationToolKit _serialization;
public RepresentationModule(IChannelInteractionModule interaction, IContextualSerializationToolKit serialization) public RepresentationModule(IChannelInteractionModule interaction,
IContextualSerializationToolKit serialization)
{ {
_interaction = interaction; _interaction = interaction;
_serialization = serialization; _serialization = serialization;
@@ -28,13 +28,12 @@ namespace mROA.Implementation
public IEndPointContext Context => _interaction.Context; public IEndPointContext Context => _interaction.Context;
public async Task<(object? Deserialized, EMessageType MessageType)> GetSingle( public async Task<(object? Deserialized, EMessageType MessageType)> GetSingle(
Predicate<NetworkMessageHeader> rule, IEndPointContext? context, Predicate<NetworkMessage> rule, IEndPointContext? context,
CancellationToken token = default, params Func<NetworkMessageHeader, Type?>[] converter) CancellationToken token = default, params Func<NetworkMessage, Type?>[] converter)
{ {
var writer = _interaction.ReceiveChanel.Writer; var writer = _interaction.ReceiveChanel.Writer;
var reader = _interaction.ReceiveChanel.Reader; var reader = _interaction.ReceiveChanel.Reader;
await foreach (var message in reader.ReadAllAsync(token)) await foreach (var message in reader.ReadAllAsync(token))
{ {
if (!rule(message)) if (!rule(message))
@@ -52,11 +51,11 @@ namespace mROA.Implementation
} }
public async IAsyncEnumerable<(object parced, EMessageType originalType)> GetStream( public async IAsyncEnumerable<(object parced, EMessageType originalType)> GetStream(
Predicate<NetworkMessageHeader> rule, IEndPointContext? context, Predicate<NetworkMessage> rule, IEndPointContext? context,
[EnumeratorCancellation] CancellationToken token = default, [EnumeratorCancellation] CancellationToken token = default,
params Func<NetworkMessageHeader, Type?>[] converter) params Func<NetworkMessage, Type?>[] converter)
{ {
var writer = _interaction?.ReceiveChanel.Writer; var writer = _interaction.ReceiveChanel.Writer;
await foreach (var message in _interaction.ReceiveChanel.Reader.ReadAllAsync(token)) await foreach (var message in _interaction.ReceiveChanel.Reader.ReadAllAsync(token))
{ {
if (!rule(message)) if (!rule(message))
@@ -66,7 +65,7 @@ namespace mROA.Implementation
} }
for (int i = 0; i < converter.Length; i++) for (var i = 0; i < converter.Length; i++)
{ {
var func = converter[i]; var func = converter[i];
if (func(message) is { } t) if (func(message) is { } t)
@@ -79,30 +78,25 @@ namespace mROA.Implementation
} }
} }
public async Task PostCallMessageAsync<T>(Guid id, EMessageType eMessageType, T payload, public async Task PostCallMessageAsync<T>(RequestId id, EMessageType eMessageType, T payload,
IEndPointContext? context) where T : notnull IEndPointContext? context) where T : notnull
{ {
if (_interaction == null)
throw new NullReferenceException("Interaction toolkit is not initialized");
if (_serialization == null)
throw new NullReferenceException("Serialization toolkit is not initialized");
var serialized = _serialization.Serialize(payload, context); var serialized = _serialization.Serialize(payload, context);
await _interaction.PostMessageAsync(new NetworkMessageHeader await _interaction.PostMessageAsync(new NetworkMessage
{ Id = id, MessageType = eMessageType, Data = serialized }); { Id = id, MessageType = eMessageType, Data = serialized });
} }
public void PostCallMessage<T>(Guid id, EMessageType eMessageType, T payload, IEndPointContext? context) public void PostCallMessage<T>(RequestId id, EMessageType eMessageType, T payload, IEndPointContext? context)
where T : notnull where T : notnull
{ {
PostCallMessageAsync(id, eMessageType, payload, context).GetAwaiter().GetResult(); PostCallMessageAsync(id, eMessageType, payload, context).GetAwaiter().GetResult();
} }
public async Task PostCallMessageUntrustedAsync<T>(Guid id, EMessageType eMessageType, T payload, public async Task PostCallMessageUntrustedAsync<T>(RequestId id, EMessageType eMessageType, T payload,
IEndPointContext? context) where T : notnull IEndPointContext? context) where T : notnull
{ {
var serialized = _serialization.Serialize(payload, context); var serialized = _serialization.Serialize(payload, context);
await _interaction.PostMessageUntrustedAsync(new NetworkMessageHeader await _interaction.PostMessageUntrustedAsync(new NetworkMessage
{ Id = id, MessageType = eMessageType, Data = serialized }); { Id = id, MessageType = eMessageType, Data = serialized });
} }
} }

Some files were not shown because too many files have changed in this diff Show More