From 1a55131cb317d09bbcceaef410f039449c45c019 Mon Sep 17 00:00:00 2001 From: Mikhail Mitrofanov Date: Sun, 12 Jan 2025 14:17:19 +0300 Subject: [PATCH] =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=BD=D0=B0?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=83=20=D0=BC=D0=BE=D0=B4?= =?UTF-8?q?=D1=83=D0=BB=D1=8F=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D1=8E?= =?UTF-8?q?=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mROA.Test/ITestController.cs | 37 ++++++++++++++ mROA/Abstract/IContextRepository.cs | 1 + ...InputModule.cs => ISerialisationModule.cs} | 3 +- mROA/Implementation/CallRequest.cs | 8 +-- mROA/Implementation/ContextRepository.cs | 28 +++++++--- ...utModule.cs => JsonSerialisationModule.cs} | 34 ++++++------- mROA/Implementation/MethodRepository.cs | 18 +++++-- .../PrepairedExecutionModule.cs | 51 ++++++++++++++----- .../SharedObjectInterafceAttribute.cs | 3 ++ .../SharedObjectSingletonAttribute.cs | 3 ++ .../Test/MockSerializationModule.cs | 19 +++++++ 11 files changed, 158 insertions(+), 47 deletions(-) create mode 100644 mROA.Test/ITestController.cs rename mROA/Abstract/{IInputModule.cs => ISerialisationModule.cs} (62%) rename mROA/Implementation/{InputModule.cs => JsonSerialisationModule.cs} (66%) create mode 100644 mROA/Implementation/SharedObjectInterafceAttribute.cs create mode 100644 mROA/Implementation/SharedObjectSingletonAttribute.cs create mode 100644 mROA/Implementation/Test/MockSerializationModule.cs diff --git a/mROA.Test/ITestController.cs b/mROA.Test/ITestController.cs new file mode 100644 index 0000000..48a1e1d --- /dev/null +++ b/mROA.Test/ITestController.cs @@ -0,0 +1,37 @@ +using mROA.Implementation; + +namespace mROA.Test; + +[SharedObjectInterafce] +public interface ITestController +{ + void A(); + Task AAsync(CancellationToken cancellationToken); + int B(); + Task BAsync(CancellationToken cancellationToken); +} + +[SharedObjectSingleton] +public class TestController : ITestController +{ + private int _bOut = 5; + public void A() + { + Console.WriteLine("A called"); + } + + public Task AAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + public int B() + { + return _bOut++; + } + + public Task BAsync(CancellationToken cancellationToken) + { + return Task.FromResult(5); + } +} \ No newline at end of file diff --git a/mROA/Abstract/IContextRepository.cs b/mROA/Abstract/IContextRepository.cs index beb7fa2..4b27db2 100644 --- a/mROA/Abstract/IContextRepository.cs +++ b/mROA/Abstract/IContextRepository.cs @@ -5,4 +5,5 @@ public interface IContextRepository int ResisterObject(object o); void ClearObject(int id); object GetObject(int id); + object GetSingleObject(Type type); } \ No newline at end of file diff --git a/mROA/Abstract/IInputModule.cs b/mROA/Abstract/ISerialisationModule.cs similarity index 62% rename from mROA/Abstract/IInputModule.cs rename to mROA/Abstract/ISerialisationModule.cs index c794cec..f3e5358 100644 --- a/mROA/Abstract/IInputModule.cs +++ b/mROA/Abstract/ISerialisationModule.cs @@ -2,8 +2,9 @@ using mROA.Implementation; namespace mROA; -public interface IInputModule +public interface ISerialisationModule { void HandleIncomingRequest(int clientId, string command); void PostResponse(ICommandExecution call); + void SetExecuteModule(IExecuteModule executeModule); } \ No newline at end of file diff --git a/mROA/Implementation/CallRequest.cs b/mROA/Implementation/CallRequest.cs index f887642..7640f20 100644 --- a/mROA/Implementation/CallRequest.cs +++ b/mROA/Implementation/CallRequest.cs @@ -7,14 +7,14 @@ public interface ICallRequest int ClientId { get; set; } } -public class StaticCallRequest : ICallRequest +public class SingletonCallRequest : ICallRequest { - public virtual int RequestTypeId => (int)RequestType.Static; + public virtual int RequestTypeId => (int)RequestType.Singleton; public int CommandId { get; set; } public int ClientId { get; set; } } -public class CallRequest : StaticCallRequest +public class CallRequest : SingletonCallRequest { public override int RequestTypeId => (int)RequestType.NonParametrized; public int ObjectId { get; set; } @@ -28,7 +28,7 @@ public class ParametrizedCallRequest : CallRequest enum RequestType { - Static, + Singleton, NonParametrized, Parametrized } \ No newline at end of file diff --git a/mROA/Implementation/ContextRepository.cs b/mROA/Implementation/ContextRepository.cs index b4475ed..1002840 100644 --- a/mROA/Implementation/ContextRepository.cs +++ b/mROA/Implementation/ContextRepository.cs @@ -1,7 +1,11 @@ -namespace mROA.Implementation; +using System.Collections.Frozen; +using System.Reflection; + +namespace mROA.Implementation; public class ContextRepository : IContextRepository { + private FrozenDictionary _singletons; private object[] _storage; private int _lastIndex; @@ -9,13 +13,21 @@ public class ContextRepository : IContextRepository const int StartupSize = 1024; const int GrowSize = 128; - + public ContextRepository() { _storage = new object[StartupSize]; } + public void FillSingletons(Assembly assembly) + { + var types = assembly.GetTypes().Where(type => + type is { IsClass: true, IsAbstract: false, IsGenericType: false } && + type.GetCustomAttributes(typeof(SharedObjectSingletonAttribute), true).Length > 0); + _singletons = types.ToFrozenDictionary(t => t.GetInterfaces().FirstOrDefault(i => i.GetCustomAttributes(typeof(SharedObjectInterafceAttribute), true).Length > 0)!.GetHashCode(), Activator.CreateInstance); + } + public int ResisterObject(object o) { if (_lastIndexFinder is not null) @@ -26,8 +38,8 @@ public class ContextRepository : IContextRepository _storage[oldIndex] = o; - _lastIndexFinder = FindLastIndex(); - + _lastIndexFinder = FindLastIndex(); + return _lastIndex; } @@ -42,6 +54,11 @@ public class ContextRepository : IContextRepository return _storage.Length == -1 || _storage.Length <= id ? null : _storage[id]; } + public object GetSingleObject(Type type) + { + return _singletons.TryGetValue(type.GetHashCode(), out var value) ? value : null; + } + private async Task FindLastIndex() { for (int i = 0; i < _storage.Length; i++) @@ -49,10 +66,9 @@ public class ContextRepository : IContextRepository if (_storage[i] is null) return i; } - + var nextStorage = new object[_storage.Length + GrowSize]; Array.Copy(_storage, nextStorage, _storage.Length); return _storage.Length; } - } \ No newline at end of file diff --git a/mROA/Implementation/InputModule.cs b/mROA/Implementation/JsonSerialisationModule.cs similarity index 66% rename from mROA/Implementation/InputModule.cs rename to mROA/Implementation/JsonSerialisationModule.cs index 2cd4a89..60a2cb3 100644 --- a/mROA/Implementation/InputModule.cs +++ b/mROA/Implementation/JsonSerialisationModule.cs @@ -4,18 +4,18 @@ using System.Text.Json.Serialization; namespace mROA.Implementation; -public class InputModule : IInputModule +public class JsonSerialisationModule : ISerialisationModule { private readonly IInteractionModule _dataSource; - private readonly IExecuteModule _executeModule; - - public InputModule(IInteractionModule dataSource, IExecuteModule executeModule) + private IExecuteModule _executeModule; + public JsonSerialisationModule(IInteractionModule dataSource) { _dataSource = dataSource; - _executeModule = executeModule; _dataSource.SetMessageHandler(HandleIncomingRequest); } + public void SetExecuteModule(IExecuteModule executeModule) => _executeModule = executeModule; + public void HandleIncomingRequest(int clientId, string command) { var type = JsonDocument.Parse(command).RootElement.GetProperty("RequestTypeId").GetInt32(); @@ -25,7 +25,7 @@ public class InputModule : IInputModule switch (type) { case 0: - request = JsonSerializer.Deserialize(command); + request = JsonSerializer.Deserialize(command); break; case 1: request = JsonSerializer.Deserialize(command); @@ -34,27 +34,25 @@ public class InputModule : IInputModule request = JsonSerializer.Deserialize(command); break; default: - request = JsonSerializer.Deserialize(command); + request = JsonSerializer.Deserialize(command); break; } request.ClientId = clientId; var response = _executeModule.Execute(request); - var texted = string.Empty; - if (response is FinalCommandExecution finalCommand) - { - texted = JsonSerializer.Serialize(finalCommand); - }else if (response is AsyncCommandExecution asyncCommand) - { - texted = JsonSerializer.Serialize(asyncCommand); - } - var binary = Encoding.UTF8.GetBytes(texted); - _dataSource.SendTo(clientId, binary); + PostResponse(response); } public void PostResponse(ICommandExecution call) { - var texted = JsonSerializer.Serialize(call); + var texted = string.Empty; + if (call is FinalCommandExecution finalCommand) + { + texted = JsonSerializer.Serialize(finalCommand); + }else if (call is AsyncCommandExecution asyncCommand) + { + texted = JsonSerializer.Serialize(asyncCommand); + } var binary = Encoding.UTF8.GetBytes(texted); _dataSource.SendTo(call.ClientId, binary); } diff --git a/mROA/Implementation/MethodRepository.cs b/mROA/Implementation/MethodRepository.cs index ac163ee..a15c986 100644 --- a/mROA/Implementation/MethodRepository.cs +++ b/mROA/Implementation/MethodRepository.cs @@ -4,13 +4,13 @@ namespace mROA.Implementation; public class MethodRepository : IMethodRepository { - private List _methods; - + private List _methods = []; + public MethodInfo GetMethod(int id) { - if (_methods.Count >= id) + if (_methods.Count <= id) return null; - + return _methods[id]; } @@ -24,4 +24,14 @@ public class MethodRepository : IMethodRepository { return _methods; } + + public void CollectForAssembly(Assembly assembly) + { + var types = assembly.GetTypes().Where(i => i.IsInterface && i.GetCustomAttributes(typeof(SharedObjectInterafceAttribute), true).Length > 0); + foreach (var type in types) + { + foreach (var method in type.GetMethods()) + RegisterMethod(method); + } + } } \ No newline at end of file diff --git a/mROA/Implementation/PrepairedExecutionModule.cs b/mROA/Implementation/PrepairedExecutionModule.cs index 6d8fd0e..7b22e08 100644 --- a/mROA/Implementation/PrepairedExecutionModule.cs +++ b/mROA/Implementation/PrepairedExecutionModule.cs @@ -2,44 +2,67 @@ namespace mROA.Implementation; -public class PrepairedExecutionModule( - IMethodRepository methodRepo, - IInputModule inputModule, - IContextRepository contextRepo) - : IExecuteModule +public class PrepairedExecutionModule : IExecuteModule { + private readonly IMethodRepository _methodRepo; + private readonly ISerialisationModule _serialisationModule; + private readonly IContextRepository _contextRepo; + private readonly MethodInfo _resultExtractionMethod; + + public PrepairedExecutionModule(IMethodRepository methodRepo, + ISerialisationModule serialisationModule, + IContextRepository contextRepo) + { + _methodRepo = methodRepo; + _serialisationModule = serialisationModule; + _contextRepo = contextRepo; + _serialisationModule.SetExecuteModule(this); + + } + public ICommandExecution Execute(ICallRequest command) { - var currentCommand = methodRepo.GetMethod(command.CommandId); + var currentCommand = _methodRepo.GetMethod(command.CommandId); if (currentCommand == null) throw new Exception($"Command {command.CommandId} not found"); - var context = command is CallRequest request ? contextRepo.GetObject(request.ObjectId) : null; + var context = command is CallRequest request ? _contextRepo.GetObject(request.ObjectId) : _contextRepo.GetSingleObject(currentCommand.DeclaringType); var parameter = command is ParametrizedCallRequest callRequest ? callRequest.Parameter : null; - if (currentCommand.ReturnType == typeof(Task<>)) + if (currentCommand.ReturnType.BaseType == typeof(Task) && currentCommand.ReturnType.GenericTypeArguments.Length == 1) { var tokenSource = new CancellationTokenSource(); var token = tokenSource.Token; var result = - currentCommand.Invoke(context, parameter is null ? [token] : [parameter, token]) as Task; + currentCommand.Invoke(context, parameter is null ? [token] : [parameter, token]) as Task; var exec = new AsyncCommandExecution(tokenSource) { CommandId = command.CommandId, ClientId = command.ClientId }; - + result.ContinueWith(task => { - var result = task.Result; - inputModule.PostResponse(new FinalCommandExecution + var result = task.GetType().GetProperty("Result").GetValue(task); + _serialisationModule.PostResponse(new FinalCommandExecution { ExecutionId = exec.ExecutionId, Result = result, CommandId = command.CommandId, ClientId = command.ClientId }); }, token); + + // result.ContinueWith(task => + // { + // var result = task.Result; + // _serialisationModule.PostResponse(new FinalCommandExecution + // { + // ExecutionId = exec.ExecutionId, Result = result, CommandId = command.CommandId, + // ClientId = command.ClientId + // }); + // }, token); return exec; } - else + + if (currentCommand.ReturnType == typeof(Task)) { var tokenSource = new CancellationTokenSource(); var token = tokenSource.Token; @@ -50,7 +73,7 @@ public class PrepairedExecutionModule( result.ContinueWith(_ => { - inputModule.PostResponse(new FinalCommandExecution + _serialisationModule.PostResponse(new FinalCommandExecution { ExecutionId = exec.ExecutionId, Result = null, CommandId = command.CommandId, ClientId = command.ClientId diff --git a/mROA/Implementation/SharedObjectInterafceAttribute.cs b/mROA/Implementation/SharedObjectInterafceAttribute.cs new file mode 100644 index 0000000..798e581 --- /dev/null +++ b/mROA/Implementation/SharedObjectInterafceAttribute.cs @@ -0,0 +1,3 @@ +namespace mROA.Implementation; + +public class SharedObjectInterafceAttribute : Attribute; \ No newline at end of file diff --git a/mROA/Implementation/SharedObjectSingletonAttribute.cs b/mROA/Implementation/SharedObjectSingletonAttribute.cs new file mode 100644 index 0000000..aac1c9e --- /dev/null +++ b/mROA/Implementation/SharedObjectSingletonAttribute.cs @@ -0,0 +1,3 @@ +namespace mROA.Implementation; + +public class SharedObjectSingletonAttribute : Attribute; \ No newline at end of file diff --git a/mROA/Implementation/Test/MockSerializationModule.cs b/mROA/Implementation/Test/MockSerializationModule.cs new file mode 100644 index 0000000..f5544fe --- /dev/null +++ b/mROA/Implementation/Test/MockSerializationModule.cs @@ -0,0 +1,19 @@ +namespace mROA.Implementation; + +public class MockSerializationModule : ISerialisationModule +{ + public void HandleIncomingRequest(int clientId, string command) + { + + } + + public void PostResponse(ICommandExecution call) + { + + } + + public void SetExecuteModule(IExecuteModule executeModule) + { + + } +} \ No newline at end of file