From ae4979c9f9c80fe844caed28ad2dd3b2e1a94a85 Mon Sep 17 00:00:00 2001 From: Mikhail Mitrofanov Date: Sun, 23 Feb 2025 10:18:38 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20IDisposable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Example.Backend/LoadTestImp.cs | 16 +++- Example.Backend/Printer.cs | 6 ++ Example.Frontend/ClientBasedPrinter.cs | 5 ++ Example.Frontend/Program.cs | 59 +++++++++----- Example.Shared/IPrinter.cs | 3 +- mROA.Codegen/mROASourceGenerator.cs | 3 + .../Backend/BasicExecutionModule.cs | 39 ++++++++- mROA/Implementation/CallRequest.cs | 12 ++- .../Frontend/RequestExtractor.cs | 81 ++++++++++++------- .../NextGenerationInteractionModule.cs | 5 +- mROA/Implementation/RemoteObjectBase.cs | 43 +++++++--- mROA/Implementation/RepresentationModule.cs | 7 +- 12 files changed, 207 insertions(+), 72 deletions(-) diff --git a/Example.Backend/LoadTestImp.cs b/Example.Backend/LoadTestImp.cs index e980aae..a1fec60 100644 --- a/Example.Backend/LoadTestImp.cs +++ b/Example.Backend/LoadTestImp.cs @@ -31,7 +31,21 @@ namespace Example.Backend public async Task AsyncTest(CancellationToken token) { - await Task.Delay(TimeSpan.FromSeconds(5), token); + Console.WriteLine("Async Test"); + + for (int i = 0; i < 10; i++) + { + if (token.IsCancellationRequested) + { + Console.WriteLine("Waiting canceled"); + return; + } + Console.WriteLine("Waiting..."); + await Task.Delay(1000); + } + + + Console.WriteLine("Waited until the end"); } } } \ No newline at end of file diff --git a/Example.Backend/Printer.cs b/Example.Backend/Printer.cs index 59ef144..ba8c624 100644 --- a/Example.Backend/Printer.cs +++ b/Example.Backend/Printer.cs @@ -1,3 +1,4 @@ +using System; using System.Threading; using System.Threading.Tasks; using Example.Shared; @@ -18,5 +19,10 @@ namespace Example.Backend // throw new Exception("The method or operation is not implemented."); return new Page {Text = text}; } + + public void Dispose() + { + Console.WriteLine("Dispose printer with name {0}", Name); + } } } \ No newline at end of file diff --git a/Example.Frontend/ClientBasedPrinter.cs b/Example.Frontend/ClientBasedPrinter.cs index 32c3de0..00b1984 100644 --- a/Example.Frontend/ClientBasedPrinter.cs +++ b/Example.Frontend/ClientBasedPrinter.cs @@ -20,6 +20,11 @@ namespace Example.Frontend await Task.Yield(); return new ClientBasedPage(); } + + public void Dispose() + { + + } } public class ClientBasedPage : IPage diff --git a/Example.Frontend/Program.cs b/Example.Frontend/Program.cs index f5323d2..44860ad 100644 --- a/Example.Frontend/Program.cs +++ b/Example.Frontend/Program.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Net; using System.Text; using System.Threading; +using System.Threading.Tasks; using Example.Frontend; using Example.Shared; using mROA.Codegen; @@ -44,36 +45,50 @@ class Program //правильный порядок команд 8-5-10-7 var printer = factory.Create("Test"); - Console.WriteLine("Printer created"); - Thread.Sleep(100); + using (var disposingPrinter = printer.Value) + { + Console.WriteLine("Printer created"); + Thread.Sleep(100); - var name = printer.Value.GetName(); - Console.WriteLine("Printer name : {0}", name); - - Thread.Sleep(100); + var name = disposingPrinter.GetName(); + Console.WriteLine("Printer name : {0}", name); - factory.Register(new SharedObject(new ClientBasedPrinter())); - Console.WriteLine("Registered printer"); - Thread.Sleep(100); + Thread.Sleep(100); + + factory.Register(new SharedObject(new ClientBasedPrinter())); + Console.WriteLine("Registered printer"); + Thread.Sleep(100); - var registred = factory.GetFirstPrinter(); - Console.WriteLine("First printer"); - Thread.Sleep(100); + var registred = factory.GetFirstPrinter(); + Console.WriteLine("First printer"); + Thread.Sleep(100); - Console.WriteLine(registred.Value); - Console.WriteLine("Collecting all printers"); - var names = factory.CollectAllNames(); - Thread.Sleep(100); + Console.WriteLine(registred.Value); + Console.WriteLine("Collecting all printers"); + var names = factory.CollectAllNames(); + Thread.Sleep(100); - Console.WriteLine(string.Join(", ", names)); + Console.WriteLine(string.Join(", ", names)); - var page = printer.Value.Print("Test Page", new CancellationToken()).GetAwaiter().GetResult(); - Console.WriteLine("Page printed"); - var data = page.Value.GetData(); - Console.WriteLine("Data : {0}", Encoding.UTF8.GetString(data)); + var page = disposingPrinter.Print("Test Page", new CancellationToken()).GetAwaiter().GetResult(); + Console.WriteLine("Page printed"); + var data = page.Value.GetData(); + Console.WriteLine("Data : {0}", Encoding.UTF8.GetString(data)); + } - // var loadSingleton = context.GetSingleObject(typeof(ILoadTest)) as ILoadTest; + + var loadSingleton = context.GetSingleObject(typeof(ILoadTest)) as ILoadTest; + + + var cts = new CancellationTokenSource(); + var token = cts.Token; + var t = Task.Run(async () => await loadSingleton!.AsyncTest(token)); + + Thread.Sleep(5000); + cts.Cancel(); + Console.WriteLine($"Token state {cts.Token.IsCancellationRequested}"); + Console.ReadKey(); // // const int iterations = 10000; // var timer = Stopwatch.StartNew(); diff --git a/Example.Shared/IPrinter.cs b/Example.Shared/IPrinter.cs index a2c789f..6c4fe9a 100644 --- a/Example.Shared/IPrinter.cs +++ b/Example.Shared/IPrinter.cs @@ -1,3 +1,4 @@ +using System; using System.Threading; using System.Threading.Tasks; using mROA.Implementation; @@ -6,7 +7,7 @@ using mROA.Implementation.Attributes; namespace Example.Shared { [SharedObjectInterface] - public interface IPrinter + public interface IPrinter : IDisposable { string GetName(); Task> Print(string text, CancellationToken cancellationToken); diff --git a/mROA.Codegen/mROASourceGenerator.cs b/mROA.Codegen/mROASourceGenerator.cs index 84fef04..70eefa9 100644 --- a/mROA.Codegen/mROASourceGenerator.cs +++ b/mROA.Codegen/mROASourceGenerator.cs @@ -233,6 +233,9 @@ namespace mROA.Codegen public MethodInfo GetMethod(int id) {{ + if (id == -1) + return typeof(IDisposable).GetMethod(""Dispose""); + if (_methods.Count <= id) return null; diff --git a/mROA/Implementation/Backend/BasicExecutionModule.cs b/mROA/Implementation/Backend/BasicExecutionModule.cs index 3207c34..01343ca 100644 --- a/mROA/Implementation/Backend/BasicExecutionModule.cs +++ b/mROA/Implementation/Backend/BasicExecutionModule.cs @@ -21,6 +21,8 @@ namespace mROA.Implementation.Backend public ICommandExecution Execute(ICallRequest command, IContextRepository contextRepository, IRepresentationModule representationModule) { + Console.WriteLine(command.GetType().Name); + if (_cancellationRepo is null) throw new NullReferenceException("Method repository was not defined"); @@ -30,6 +32,19 @@ namespace mROA.Implementation.Backend if (contextRepository is null) throw new NullReferenceException("Context repository was not defined"); + if (command is CancelRequest) + { + Console.WriteLine("Final cancelling request"); + var cts = _cancellationRepo.GetCancellation(command.Id); + cts.Cancel(); + _cancellationRepo.FreeCancelation(command.Id); + return new FinalCommandExecution + { + Id = command.Id, + CommandId = command.CommandId + }; + } + var currentCommand = _methodRepo.GetMethod(command.CommandId); if (currentCommand == null) throw new Exception($"Command {command.CommandId} not found"); @@ -48,7 +63,14 @@ namespace mROA.Implementation.Backend return ExecuteAsync(currentCommand, context, parameter, command, _cancellationRepo, representationModule); - return Execute(currentCommand, context, parameter, command); + var result = Execute(currentCommand, context, parameter, command); + + if (command.CommandId == -1) + { + contextRepository.ClearObject(command.ObjectId); + } + + return result; } private static ICommandExecution Execute(MethodInfo currentCommand, object context, object? parameter, @@ -57,9 +79,10 @@ namespace mROA.Implementation.Backend try { var finalResult = currentCommand.Invoke(context, parameter is null - ? new object[0] + ? Array.Empty() : new[] { parameter }); + return new TypedFinalCommandExecution { CommandId = command.CommandId, Result = finalResult, @@ -77,13 +100,14 @@ namespace mROA.Implementation.Backend } } - private static ICommandExecution ExecuteAsync(MethodInfo currentCommand, object context, object? parameter, + private ICommandExecution ExecuteAsync(MethodInfo currentCommand, object context, object? parameter, ICallRequest command, ICancellationRepository cancellationRepository, IRepresentationModule representationModule) { var tokenSource = new CancellationTokenSource(); cancellationRepository.RegisterCancellation(command.Id, tokenSource); var token = tokenSource.Token; + token.Register(() => Console.WriteLine($"Cancellation requested check {command.Id}")); try { var result = (Task)currentCommand.Invoke(context, parameter is null @@ -94,11 +118,16 @@ namespace mROA.Implementation.Backend result.ContinueWith(_ => { + if (token.IsCancellationRequested) + return; + var payload = new FinalCommandExecution { Id = command.Id, CommandId = command.CommandId }; + _cancellationRepo.FreeCancelation(command.Id); + var multiClientOwnershipRepository = TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id); @@ -121,7 +150,7 @@ namespace mROA.Implementation.Backend } } - private static ICommandExecution TypedExecuteAsync(MethodInfo currentCommand, object context, object? parameter, + private ICommandExecution TypedExecuteAsync(MethodInfo currentCommand, object context, object? parameter, ICallRequest command, ICancellationRepository cancellationRepository, IRepresentationModule representationModule) { @@ -147,6 +176,8 @@ namespace mROA.Implementation.Backend CommandId = command.CommandId, Type = finalResult?.GetType() }; + _cancellationRepo.FreeCancelation(command.Id); + var multiClientOwnershipRepository = TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id); diff --git a/mROA/Implementation/CallRequest.cs b/mROA/Implementation/CallRequest.cs index 8a24b42..783cce2 100644 --- a/mROA/Implementation/CallRequest.cs +++ b/mROA/Implementation/CallRequest.cs @@ -1,5 +1,6 @@ using System; using System.Text.Json.Serialization; + // ReSharper disable UnusedAutoPropertyAccessor.Global // ReSharper disable AutoPropertyCanBeMadeGetOnly.Global @@ -18,9 +19,18 @@ namespace mROA.Implementation public Guid Id { get; set; } = Guid.NewGuid(); public int CommandId { get; set; } public int ObjectId { get; set; } = -1; - + [JsonIgnore] public Type? ParameterType { get; set; } + public object? Parameter { get; set; } } + + public class CancelRequest : ICallRequest + { + public Guid Id { get; set; } + public int CommandId { get; set; } = -2; + public int ObjectId { get; set; } = -2; + public object? Parameter { get; set; } = null; + } } \ No newline at end of file diff --git a/mROA/Implementation/Frontend/RequestExtractor.cs b/mROA/Implementation/Frontend/RequestExtractor.cs index fe89331..83bc460 100644 --- a/mROA/Implementation/Frontend/RequestExtractor.cs +++ b/mROA/Implementation/Frontend/RequestExtractor.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading; using System.Threading.Tasks; using mROA.Abstract; using mROA.Implementation.Backend; @@ -53,45 +54,68 @@ namespace mROA.Implementation.Frontend throw new NullReferenceException("Method repository is null."); await Task.Yield(); - - var multiClientOwnershipRepository = TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; + + var multiClientOwnershipRepository = + TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; multiClientOwnershipRepository?.RegisterOwnership(_representationModule.Id); try { while (true) { - var request = - _representationModule!.GetMessage(messageType: MessageType.CallRequest); + Console.WriteLine("Waiting for request..."); + var tokenSource = new CancellationTokenSource(); + var token = tokenSource.Token; + var defaultRequest = + _representationModule!.GetMessageAsync( + messageType: MessageType.CallRequest, token: token); + var cancelRequest = + _representationModule!.GetMessageAsync( + messageType: MessageType.CancelRequest, token: token); - // Console.WriteLine("Executing {0}", request.Id); - - if (request.Parameter is not null) + Task.WaitAny(defaultRequest, cancelRequest); + + Console.WriteLine("Request received"); + + if (cancelRequest.IsCompleted) { - var parameterType = _methodRepository!.GetMethod(request.CommandId).GetParameters().First() - .ParameterType; - - request.Parameter = _serializationToolkit.Cast(request.Parameter, parameterType); + Console.WriteLine("Cancelling request"); + var req = cancelRequest.Result; + tokenSource.Cancel(); + _executeModule.Execute(req, _contextRepository, _representationModule); } - - var result = _executeModule.Execute(request, _contextRepository, _representationModule); - - var resultType = MessageType.Unknown; - - switch (result) + else { - case FinalCommandExecution: - resultType = MessageType.FinishedCommandExecution; - break; - case AsyncCommandExecution: - resultType = MessageType.AsyncCommandExecution; - break; - case ExceptionCommandExecution: - resultType = MessageType.ExceptionCommandExecution; - break; - } + tokenSource.Cancel(); + var request = defaultRequest.Result; - _representationModule.PostCallMessage(request.Id, resultType, result, result.GetType()); + if (request.Parameter is not null) + { + var parameterType = _methodRepository!.GetMethod(request.CommandId).GetParameters().First() + .ParameterType; + + request.Parameter = _serializationToolkit.Cast(request.Parameter, parameterType); + } + + var result = _executeModule.Execute(request, _contextRepository, _representationModule); + + var resultType = MessageType.Unknown; + + switch (result) + { + case FinalCommandExecution: + resultType = MessageType.FinishedCommandExecution; + break; + case AsyncCommandExecution: + resultType = MessageType.AsyncCommandExecution; + break; + case ExceptionCommandExecution: + resultType = MessageType.ExceptionCommandExecution; + break; + } + + _representationModule.PostCallMessage(request.Id, resultType, result, result.GetType()); + } } } catch @@ -99,6 +123,5 @@ namespace mROA.Implementation.Frontend multiClientOwnershipRepository?.FreeOwnership(); } } - } } \ No newline at end of file diff --git a/mROA/Implementation/NextGenerationInteractionModule.cs b/mROA/Implementation/NextGenerationInteractionModule.cs index 72402b9..cbe6731 100644 --- a/mROA/Implementation/NextGenerationInteractionModule.cs +++ b/mROA/Implementation/NextGenerationInteractionModule.cs @@ -86,10 +86,11 @@ namespace mROA.Implementation // Console.WriteLine("Receiving {0}", Encoding.Default.GetString(_buffer[..len])); var message = _serialization.Deserialize(localSpan.Span); - _messageBuffer.Add(message!); + Console.WriteLine($"Received Message {message.SchemaId} - {message.Id}"); + _messageBuffer.Add(message); _currentReceiving = Task.Run(async () => await GetNextMessage()); - return message!; + return message; } } } \ No newline at end of file diff --git a/mROA/Implementation/RemoteObjectBase.cs b/mROA/Implementation/RemoteObjectBase.cs index 62bcf76..5afc4c9 100644 --- a/mROA/Implementation/RemoteObjectBase.cs +++ b/mROA/Implementation/RemoteObjectBase.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System; +using System.Threading; using System.Threading.Tasks; using mROA.Abstract; using mROA.Implementation.CommandExecution; @@ -7,7 +8,7 @@ using mROA.Implementation.CommandExecution; namespace mROA.Implementation { - public abstract class RemoteObjectBase + public abstract class RemoteObjectBase : IDisposable { private readonly int _id; private readonly IRepresentationModule _representationModule; @@ -20,7 +21,7 @@ namespace mROA.Implementation public int Id => _id; public int OwnerId => _representationModule.Id; - + protected async Task GetResultAsync(int methodId, object? parameter = default, CancellationToken cancellationToken = default) { @@ -46,6 +47,7 @@ namespace mROA.Implementation if (cancellationToken.IsCancellationRequested) { await _representationModule.PostCallMessageAsync(request.Id, MessageType.CancelRequest, request.Id); + localTokenSource.Cancel(); cancellationToken.ThrowIfCancellationRequested(); } @@ -76,21 +78,42 @@ namespace mROA.Implementation _representationModule.GetMessageAsync(requestId: request.Id, MessageType.ExceptionCommandExecution, localTokenSource.Token); + cancellationToken.Register(async () => + { + Console.WriteLine("Cancelling task"); + await _representationModule.PostCallMessageAsync(request.Id, MessageType.CancelRequest, + new CancelRequest + { + Id = request.Id + }); + localTokenSource.Cancel(); + }); + Task.WaitAny(new Task[] { - successResponse, errorResponse + errorResponse, successResponse }, cancellationToken); - if (cancellationToken.IsCancellationRequested) - { - await _representationModule.PostCallMessageAsync(request.Id, MessageType.CancelRequest, request.Id); - cancellationToken.ThrowIfCancellationRequested(); - } + Console.WriteLine($"Handling message"); + + // if (cancellationToken.IsCancellationRequested) + // { + // localTokenSource.Cancel(); + // return; + // } if (successResponse.IsCompletedSuccessfully) return; - throw errorResponse.Result.GetException(); + if (errorResponse.IsCompletedSuccessfully) + throw errorResponse.Result.GetException(); + } + + public void Dispose() + { + if (_id == -1) + return; + CallAsync(-1).Wait(); } } } \ No newline at end of file diff --git a/mROA/Implementation/RepresentationModule.cs b/mROA/Implementation/RepresentationModule.cs index dcba18f..54c9c76 100644 --- a/mROA/Implementation/RepresentationModule.cs +++ b/mROA/Implementation/RepresentationModule.cs @@ -57,7 +57,8 @@ namespace mROA.Implementation { var message = await _interaction.GetNextMessageReceiving(); if ((requestId is not null && message.Id != requestId) || - (messageType is not null && message.SchemaId != messageType)) continue; + (messageType is not null && message.SchemaId != messageType)) + continue; _interaction.HandleMessage(message); return message.Data; @@ -79,7 +80,9 @@ namespace mROA.Implementation throw new NullReferenceException("Interaction toolkit is not initialized"); if (_serialization == null) throw new NullReferenceException("Serialization toolkit is not initialized"); - + + Console.WriteLine($"Posting message: {id} - {messageType}"); + await _interaction.PostMessage(new NetworkMessage { Id = id, SchemaId = messageType, Data = _serialization.Serialize(payload, payloadType) }); }