From dfad8b91417c0a61bed5212e54d33d457ecdfec1 Mon Sep 17 00:00:00 2001 From: Mikhail Mitrofanov Date: Sat, 16 Aug 2025 19:51:35 +0300 Subject: [PATCH] Prepare for zero-copy sending --- Example.Backend/Program.cs | 2 +- Example.Frontend/Program.cs | 1 + mROA.Cbor/CborSerializationToolkit.cs | 29 +++++++++----- mROA.Codegen/MethodRepo.cstmpl | 1 + mROA.Codegen/Templates/InvokerTemplate.cs | 6 +++ mROA.Codegen/mROASourceGenerator.cs | 5 +++ .../Backend/BasicExecutionModule.cs | 40 ++++++++++++++----- .../CollectableMethodRepository.cs | 9 +++-- mROA/Implementation/MethodInvoker.cs | 2 +- .../SerializationBufferOffset.cs | 7 ++++ 10 files changed, 76 insertions(+), 26 deletions(-) create mode 100644 mROA/Implementation/SerializationBufferOffset.cs diff --git a/Example.Backend/Program.cs b/Example.Backend/Program.cs index 3a5ba85..3115c1b 100644 --- a/Example.Backend/Program.cs +++ b/Example.Backend/Program.cs @@ -27,7 +27,7 @@ class Program var listening = new IPEndPoint(IPAddress.Any, 4567); builder.Services.Configure(options => options.Endpoint = listening); builder.Services.AddSingleton(); - + builder.Services.Configure(options => options.Offset = 0); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/Example.Frontend/Program.cs b/Example.Frontend/Program.cs index 607c6ea..4de39e4 100644 --- a/Example.Frontend/Program.cs +++ b/Example.Frontend/Program.cs @@ -46,6 +46,7 @@ class Program builder.Services.AddOptions(); builder.Services.Configure(options => options.Endpoint = serverEndPoint); builder.Services.Configure(o => o.DistributionType = EDistributionType.Channeled); + builder.Services.Configure(options => options.Offset = 0); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/mROA.Cbor/CborSerializationToolkit.cs b/mROA.Cbor/CborSerializationToolkit.cs index a3f3f72..ef0bfb6 100644 --- a/mROA.Cbor/CborSerializationToolkit.cs +++ b/mROA.Cbor/CborSerializationToolkit.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Formats.Cbor; using System.Linq; using System.Reflection; +using Microsoft.Extensions.Options; using mROA.Abstract; using mROA.Implementation; using mROA.Implementation.Attributes; @@ -15,7 +16,12 @@ namespace mROA.Cbor public class CborSerializationToolkit : IContextualSerializationToolKit { private readonly CborWriter _writer = new(initialCapacity: 2048); + private readonly int _offset; + public CborSerializationToolkit(IOptions offsetOptions) : this(offsetOptions.Value.Offset) + { + } + private readonly IOrdinaryStructureParser[] _parsers = { new CallRequestParser(), new FinalCommandExecutionParser(), @@ -23,6 +29,12 @@ namespace mROA.Cbor }; private readonly Dictionary> _propertiesCache = new(); + + public CborSerializationToolkit(int offset) + { + _offset = offset; + } + public static TimeSpan SerializationTime = TimeSpan.Zero; private bool FindParser(Type t, out IOrdinaryStructureParser parser) @@ -56,7 +68,9 @@ namespace mROA.Cbor { _writer.Reset(); WriteData(objectToSerialize, _writer, context); - result = _writer.Encode(); + result = new byte[_offset + _writer.BytesWritten]; + var span = result.AsSpan(); + _writer.Encode(span[_offset..]); } return result; @@ -94,18 +108,13 @@ namespace mROA.Cbor var reader = new CborReader(rawMemory); return ReadData(reader, type, context); } - catch (Exception e) + catch (Exception) { - Console.WriteLine($"Bad deserialization. Bytes: {rawMemory.ToArray().Select(b => $"{b:X}")}"); + Console.WriteLine($"Bad deserialization. Bytes: {BitConverter.ToString(rawMemory.ToArray())}"); throw; } } - public T Cast(object nonCasted, IEndPointContext? context) - { - return (T)Cast(nonCasted, typeof(T), context); - } - public object? Cast(object? nonCasted, Type type, IEndPointContext? context) { if (nonCasted == null) @@ -133,7 +142,7 @@ namespace mROA.Cbor public IContextualSerializationToolKit Clone() { - return new CborSerializationToolkit(); + return new CborSerializationToolkit(_offset) ; } public void WriteData(object? obj, CborWriter writer, IEndPointContext? context) @@ -465,7 +474,7 @@ namespace mROA.Cbor value = Convert.ChangeType(value, typeof(byte)); } } - + property.SetValue(obj, value); } diff --git a/mROA.Codegen/MethodRepo.cstmpl b/mROA.Codegen/MethodRepo.cstmpl index e231aef..ff6bbe6 100644 --- a/mROA.Codegen/MethodRepo.cstmpl +++ b/mROA.Codegen/MethodRepo.cstmpl @@ -23,6 +23,7 @@ namespace mROA.Codegen ReturnType = typeof(), ParameterTypes = new Type[] { }, SuitableType = typeof(), + RequireCancellation = , Invoking = (i, parameters, special, post) => , } diff --git a/mROA.Codegen/Templates/InvokerTemplate.cs b/mROA.Codegen/Templates/InvokerTemplate.cs index 4373de2..f7d0e47 100644 --- a/mROA.Codegen/Templates/InvokerTemplate.cs +++ b/mROA.Codegen/Templates/InvokerTemplate.cs @@ -9,6 +9,7 @@ namespace mROA.Codegen.Templates private const string ParametersTypeTag = "parametersType"; private const string SuitableTypeTag = "suitableType"; private const string FuncInvokingTag = "funcInvoking"; + private const string CancellationTag = "cancellation"; private const string IsTrustedTag = "isTrusted"; public InvokerTemplate(TemplateDocument template) : base(template) { } @@ -42,5 +43,10 @@ namespace mROA.Codegen.Templates { Define(IsTrustedTag, value); } + + public void DefineCancellation(string value) + { + Define(CancellationTag, value); + } } } \ No newline at end of file diff --git a/mROA.Codegen/mROASourceGenerator.cs b/mROA.Codegen/mROASourceGenerator.cs index ebdeba0..7274098 100644 --- a/mROA.Codegen/mROASourceGenerator.cs +++ b/mROA.Codegen/mROASourceGenerator.cs @@ -316,11 +316,14 @@ namespace mROA.Codegen var parametersInsertList = new List(); + var useCancellationToken = false; + foreach (var parameter in method.Parameters) switch (parameter.Type.Name) { case "CancellationToken": parametersInsertList.Add("(CancellationToken)special[1]"); + useCancellationToken = true; break; case "RequestContext": parametersInsertList.Add("(RequestContext)special[0]"); @@ -359,6 +362,7 @@ namespace mROA.Codegen invokerTemplate.DefineSuitableType(baseInterface.ToUnityString()); invokerTemplate.DefineFuncInvoking(funcInvoking); invokerTemplate.DefineIsTrusted((!isUntrusted).ToString().ToLower()); + invokerTemplate.DefineCancellation(useCancellationToken.ToString().ToLower()); backend = invokerTemplate.Compile(); } else @@ -370,6 +374,7 @@ namespace mROA.Codegen invokerTemplate.DefineSuitableType(baseInterface.ToUnityString()); invokerTemplate.DefineFuncInvoking(funcInvoking); invokerTemplate.DefineIsTrusted((!isUntrusted).ToString().ToLower()); + backend = invokerTemplate.Compile(); } diff --git a/mROA/Implementation/Backend/BasicExecutionModule.cs b/mROA/Implementation/Backend/BasicExecutionModule.cs index c794500..5c56d4e 100644 --- a/mROA/Implementation/Backend/BasicExecutionModule.cs +++ b/mROA/Implementation/Backend/BasicExecutionModule.cs @@ -145,20 +145,29 @@ namespace mROA.Implementation.Backend CallRequest command, ICancellationRepository cancellationRepository, IRepresentationModule representationModule, RequestContext executionContext, IEndPointContext context) { - var tokenSource = new CancellationTokenSource(); - cancellationRepository.RegisterCancellation(command.Id, tokenSource); - var token = tokenSource.Token; + CancellationToken? token = null; + if (invoker.RequireCancellation) + { + var tokenSource = new CancellationTokenSource(); + cancellationRepository.RegisterCancellation(command.Id, tokenSource); + + token = tokenSource.Token; + } invoker.Invoke(instance, parameters, new object[] { executionContext, token }, _ => { - if (token.IsCancellationRequested) - return; + if (invoker.RequireCancellation) + { + _cancellationRepo.FreeCancellation(command.Id); + + if (token.Value.IsCancellationRequested) + return; + } var payload = new FinalCommandExecution { Id = command.Id }; - _cancellationRepo.FreeCancellation(command.Id); if (invoker.IsTrusted) @@ -174,20 +183,31 @@ namespace mROA.Implementation.Backend CallRequest command, ICancellationRepository cancellationRepository, IRepresentationModule representationModule, RequestContext executionContext, IEndPointContext context) { - var tokenSource = new CancellationTokenSource(); - cancellationRepository.RegisterCancellation(command.Id, tokenSource); + CancellationToken? token = null; + if (invoker.RequireCancellation) + { + var tokenSource = new CancellationTokenSource(); + cancellationRepository.RegisterCancellation(command.Id, tokenSource); - var token = tokenSource.Token; + token = tokenSource.Token; + } invoker.Invoke(instance, parameters, new object[] { executionContext, token }, finalResult => { + if (invoker.RequireCancellation) + { + _cancellationRepo.FreeCancellation(command.Id); + + if (token.Value.IsCancellationRequested) + return; + } + var payload = new FinalCommandExecution { Id = command.Id, Result = finalResult }; - _cancellationRepo.FreeCancellation(command.Id); representationModule.PostCallMessageAsync(command.Id, EMessageType.FinishedCommandExecution, payload, context); diff --git a/mROA/Implementation/CollectableMethodRepository.cs b/mROA/Implementation/CollectableMethodRepository.cs index b52ef2e..f3a20d2 100644 --- a/mROA/Implementation/CollectableMethodRepository.cs +++ b/mROA/Implementation/CollectableMethodRepository.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using mROA.Abstract; @@ -5,16 +6,16 @@ namespace mROA.Implementation { public class CollectableMethodRepository : IMethodRepository { - private readonly List _methods = new(); - + private readonly List _methods = new() { MethodInvoker.Dispose }; + private IMethodInvoker[] _baked = Array.Empty(); public void AppendInvokers(IEnumerable methodInvokers) { _methods.AddRange(methodInvokers); + _baked = _methods.ToArray(); } - public IMethodInvoker GetMethod(int id) { - return id == -1 ? MethodInvoker.Dispose : _methods[id]; + return _baked[++id]; } } } \ No newline at end of file diff --git a/mROA/Implementation/MethodInvoker.cs b/mROA/Implementation/MethodInvoker.cs index 4f1e26c..7c6c02a 100644 --- a/mROA/Implementation/MethodInvoker.cs +++ b/mROA/Implementation/MethodInvoker.cs @@ -37,7 +37,7 @@ namespace mROA.Implementation public Type[] ParameterTypes { get; set; } = Type.EmptyTypes; public Type? ReturnType { get; set; } public Type SuitableType { get; set; } = typeof(object); - + public bool RequireCancellation { get; set; } = true; public Action> Invoking { get; set; } = (_, _, _, post) => { post.Invoke(null); }; diff --git a/mROA/Implementation/SerializationBufferOffset.cs b/mROA/Implementation/SerializationBufferOffset.cs new file mode 100644 index 0000000..97a08b4 --- /dev/null +++ b/mROA/Implementation/SerializationBufferOffset.cs @@ -0,0 +1,7 @@ +namespace mROA.Implementation +{ + public class SerializationBufferOffset + { + public int Offset { get; set; } = 19; + } +} \ No newline at end of file