Бэкенд отмены задачи

This commit is contained in:
2025-02-22 09:28:59 +03:00
parent a887b614c7
commit c71585b100
12 changed files with 141 additions and 36 deletions
+2 -1
View File
@@ -2,7 +2,8 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
+7
View File
@@ -1,4 +1,6 @@
using System; using System;
using System.Threading;
using System.Threading.Tasks;
using Example.Shared; using Example.Shared;
using mROA.Implementation.Attributes; using mROA.Implementation.Attributes;
@@ -26,5 +28,10 @@ namespace Example.Backend
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public async Task AsyncTest(CancellationToken token)
{
await Task.Delay(TimeSpan.FromSeconds(5), token);
}
} }
} }
+2 -1
View File
@@ -34,7 +34,8 @@ class Program
builder.Modules.Add(new CreativeRepresentationModuleProducer( builder.Modules.Add(new CreativeRepresentationModuleProducer(
new IInjectableModule[] { builder.GetModule<JsonSerializationToolkit>()! }, new IInjectableModule[] { builder.GetModule<JsonSerializationToolkit>()! },
typeof(RepresentationModule))); typeof(RepresentationModule)));
builder.Modules.Add(new CancellationRepository());
builder.Build(); builder.Build();
new RemoteTypeBinder(); new RemoteTypeBinder();
+1
View File
@@ -47,6 +47,7 @@ class Program
var name = printer.Value.GetName(); var name = printer.Value.GetName();
Console.WriteLine("Printer name : {0}", name); Console.WriteLine("Printer name : {0}", name);
Thread.Sleep(100); Thread.Sleep(100);
factory.Register(new SharedObject<IPrinter>(new ClientBasedPrinter())); factory.Register(new SharedObject<IPrinter>(new ClientBasedPrinter()));
+5 -1
View File
@@ -1,4 +1,6 @@
using mROA.Implementation.Attributes; using System.Threading;
using System.Threading.Tasks;
using mROA.Implementation.Attributes;
namespace Example.Shared namespace Example.Shared
{ {
@@ -9,6 +11,8 @@ namespace Example.Shared
int Last(int next); int Last(int next);
void C(); void C();
void A(); void A();
Task AsyncTest(CancellationToken token = default);
} }
} }
+15 -7
View File
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
@@ -120,7 +121,8 @@ namespace {Namespace}
bool isAsync = method.ReturnType.Name == "Task"; bool isAsync = method.ReturnType.Name == "Task";
bool isVoid = method.ReturnType.Name == "Void" || method.ReturnType.ToString() == "Task"; bool isVoid = method.ReturnType.Name == "Void" ||
method.ReturnType.ToString() == "System.Threading.Tasks.Task";
bool isParametrized = method.Parameters.Length == 1 && !isAsync || bool isParametrized = method.Parameters.Length == 1 && !isAsync ||
method.Parameters.Length == 2 && isAsync; method.Parameters.Length == 2 && isAsync;
@@ -135,9 +137,16 @@ namespace {Namespace}
var prefix = isAsync ? "await " : ""; var prefix = isAsync ? "await " : "";
var postfix = !isAsync ? (isVoid ? ".Wait()" : ".GetAwaiter().GetResult()") : ""; var postfix = !isAsync ? (isVoid ? ".Wait()" : ".GetAwaiter().GetResult()") : "";
var parameterLink = isParametrized ? ", " + method.Parameters.First().Name : string.Empty; var parameterLink = isParametrized ? ", " + method.Parameters.First().Name : string.Empty;
var caller = isVoid ? $"CallAsync({index}{parameterLink})" : var tokenInsert = isAsync
isAsync ? $"GetResultAsync<{ExtractTaskType(method.ReturnType)}>({index}{parameterLink})" : ? isParametrized
$"GetResultAsync<{ToFullString(method.ReturnType)}>({index}{parameterLink})"; ? ", cancellationToken : " + method.Parameters[1].Name
: ", cancellationToken : " + method.Parameters[0].Name
: String.Empty;
var caller = isVoid
? $"CallAsync({index}{parameterLink}{tokenInsert})"
: isAsync
? $"GetResultAsync<{ExtractTaskType(method.ReturnType)}>({index}{parameterLink}{tokenInsert})"
: $"GetResultAsync<{ToFullString(method.ReturnType)}>({index}{parameterLink}{tokenInsert})";
if (!isVoid) if (!isVoid)
prefix = "return " + prefix; prefix = "return " + prefix;
@@ -195,7 +204,6 @@ namespace {namespaceName}
"; ";
// Add the source code to the compilation. // Add the source code to the compilation.
context.AddSource($"{className}.g.cs", SourceText.From(code, Encoding.UTF8)); context.AddSource($"{className}.g.cs", SourceText.From(code, Encoding.UTF8));
@@ -250,7 +258,7 @@ namespace mROA.Codegen
"; ";
context.AddSource($"CoCodegenMethodRepository.g.cs", SourceText.From(coCodegenRepoCode, Encoding.UTF8)); context.AddSource($"CoCodegenMethodRepository.g.cs", SourceText.From(coCodegenRepoCode, Encoding.UTF8));
} }
if (frontendContextRepo.Count != 0) if (frontendContextRepo.Count != 0)
{ {
var fronendRepoCode = @$"// <auto-generated/> var fronendRepoCode = @$"// <auto-generated/>
@@ -324,7 +332,7 @@ namespace mROA.Codegen
GenerateCode(context, context.Compilation, interfaces.ToImmutableArray()); GenerateCode(context, context.Compilation, interfaces.ToImmutableArray());
} }
private bool ContainsSOIAttribute(SyntaxList<AttributeListSyntax> attributes, GeneratorExecutionContext context, private bool ContainsSOIAttribute(SyntaxList<AttributeListSyntax> attributes, GeneratorExecutionContext context,
InterfaceDeclarationSyntax interfaceDeclarationSyntax) InterfaceDeclarationSyntax interfaceDeclarationSyntax)
{ {
+12
View File
@@ -0,0 +1,12 @@
using System;
using System.Threading;
namespace mROA.Abstract
{
public interface ICancellationRepository : IInjectableModule
{
void RegisterCancellation(Guid id, CancellationTokenSource cts);
CancellationTokenSource? GetCancellation(Guid id);
void FreeCancelation(Guid id);
}
}
+1 -1
View File
@@ -4,6 +4,6 @@ namespace mROA.Abstract
{ {
public interface IExecuteModule : IInjectableModule public interface IExecuteModule : IInjectableModule
{ {
ICommandExecution Execute(ICallRequest command, IContextRepository contextRepository); ICommandExecution Execute(ICallRequest command, IContextRepository contextRepository, IRepresentationModule representationModule);
} }
} }
@@ -10,20 +10,26 @@ namespace mROA.Implementation.Backend
public class BasicExecutionModule : IExecuteModule public class BasicExecutionModule : IExecuteModule
{ {
private IMethodRepository? _methodRepo; private IMethodRepository? _methodRepo;
private ICancellationRepository? _cancellationRepo;
public void Inject<T>(T dependency) public void Inject<T>(T dependency)
{ {
if (dependency is IMethodRepository methodRepo) _methodRepo = methodRepo; if (dependency is IMethodRepository methodRepo) _methodRepo = methodRepo;
if (dependency is ICancellationRepository cancellationRepo) _cancellationRepo = cancellationRepo;
} }
public ICommandExecution Execute(ICallRequest command, IContextRepository contextRepository) public ICommandExecution Execute(ICallRequest command, IContextRepository contextRepository,
IRepresentationModule representationModule)
{ {
if (_cancellationRepo is null)
throw new NullReferenceException("Method repository was not defined");
if (_methodRepo is null) if (_methodRepo is null)
throw new NullReferenceException("Method repository was not defined"); throw new NullReferenceException("Method repository was not defined");
if (contextRepository is null) if (contextRepository is null)
throw new NullReferenceException("Context repository was not defined"); throw new NullReferenceException("Context repository was not defined");
var currentCommand = _methodRepo.GetMethod(command.CommandId); var currentCommand = _methodRepo.GetMethod(command.CommandId);
if (currentCommand == null) if (currentCommand == null)
throw new Exception($"Command {command.CommandId} not found"); throw new Exception($"Command {command.CommandId} not found");
@@ -35,10 +41,10 @@ namespace mROA.Implementation.Backend
if (currentCommand.ReturnType.BaseType == typeof(Task) && if (currentCommand.ReturnType.BaseType == typeof(Task) &&
currentCommand.ReturnType.GenericTypeArguments.Length == 1) currentCommand.ReturnType.GenericTypeArguments.Length == 1)
return TypedExecuteAsync(currentCommand, context, parameter, command); return TypedExecuteAsync(currentCommand, context, parameter, command, _cancellationRepo, representationModule);
if (currentCommand.ReturnType == typeof(Task)) if (currentCommand.ReturnType == typeof(Task))
return ExecuteAsync(currentCommand, context, parameter, command); return ExecuteAsync(currentCommand, context, parameter, command, _cancellationRepo, representationModule);
return Execute(currentCommand, context, parameter, command); return Execute(currentCommand, context, parameter, command);
} }
@@ -48,8 +54,10 @@ namespace mROA.Implementation.Backend
{ {
try try
{ {
var finalResult = currentCommand.Invoke(context, parameter is null ? new object[0] : new[] var finalResult = currentCommand.Invoke(context, parameter is null
{ parameter }); ? new object[0]
: new[]
{ parameter });
return new TypedFinalCommandExecution return new TypedFinalCommandExecution
{ {
CommandId = command.CommandId, Result = finalResult, CommandId = command.CommandId, Result = finalResult,
@@ -68,20 +76,34 @@ namespace mROA.Implementation.Backend
} }
private static ICommandExecution ExecuteAsync(MethodInfo currentCommand, object context, object? parameter, private static ICommandExecution ExecuteAsync(MethodInfo currentCommand, object context, object? parameter,
ICallRequest command) ICallRequest command, ICancellationRepository cancellationRepository, IRepresentationModule representationModule)
{ {
var tokenSource = new CancellationTokenSource(); var tokenSource = new CancellationTokenSource();
cancellationRepository.RegisterCancellation(command.Id, tokenSource);
var token = tokenSource.Token; var token = tokenSource.Token;
try try
{ {
var result = (Task)currentCommand.Invoke(context, parameter is null ? new object[] { token } : new[] var result = (Task)currentCommand.Invoke(context, parameter is null
{ parameter, token })!; ? new object[] { token }
: new[]
{ parameter, token })!;
result.Wait(token); result.ContinueWith(_ =>
{
var payload = new FinalCommandExecution
{
Id = command.Id,
CommandId = command.CommandId
};
representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution, payload);
}, token);
return new AsyncCommandExecution
return new FinalCommandExecution { CommandId = command.CommandId, Id = command.Id }; {
Id = command.Id, CommandId = command.CommandId
};
} }
catch (Exception e) catch (Exception e)
{ {
@@ -94,25 +116,36 @@ namespace mROA.Implementation.Backend
} }
private static ICommandExecution TypedExecuteAsync(MethodInfo currentCommand, object context, object? parameter, private static ICommandExecution TypedExecuteAsync(MethodInfo currentCommand, object context, object? parameter,
ICallRequest command) ICallRequest command, ICancellationRepository cancellationRepository, IRepresentationModule representationModule)
{ {
var tokenSource = new CancellationTokenSource(); var tokenSource = new CancellationTokenSource();
cancellationRepository.RegisterCancellation(command.Id, tokenSource);
var token = tokenSource.Token; var token = tokenSource.Token;
try try
{ {
var result = var result =
(Task)currentCommand.Invoke(context, parameter is null ? new object[] { token } : new[] (Task)currentCommand.Invoke(context, parameter is null
{ parameter, token })!; ? new object[] { token }
: new[]
{ parameter, token })!;
result.Wait(token); result.ContinueWith(t =>
var finalResult = result.GetType().GetProperty("Result")?.GetValue(result);
return new TypedFinalCommandExecution
{ {
Id = command.Id, var finalResult = t.GetType().GetProperty("Result")?.GetValue(t);
Result = finalResult, var payload = new TypedFinalCommandExecution
CommandId = command.CommandId, {
Type = finalResult?.GetType() Id = command.Id,
Result = finalResult,
CommandId = command.CommandId,
Type = finalResult?.GetType()
};
representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution, payload);
}, token);
return new AsyncCommandExecution
{
Id = command.Id, CommandId = command.CommandId
}; };
} }
catch (Exception e) catch (Exception e)
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Threading;
using mROA.Abstract;
namespace mROA.Implementation
{
public class CancellationRepository : ICancellationRepository
{
private Dictionary<Guid, CancellationTokenSource> _cancellations = new();
public void RegisterCancellation(Guid id, CancellationTokenSource cts)
{
_cancellations.TryAdd(id, cts);
}
public CancellationTokenSource? GetCancellation(Guid id)
{
return _cancellations.GetValueOrDefault(id, null);
}
public void FreeCancelation(Guid id)
{
_cancellations.Remove(id);
}
public void Inject<T>(T dependency)
{
}
}
}
@@ -16,4 +16,11 @@ namespace mROA.Implementation.CommandExecution
return new RemoteException(Exception) { CallRequestId = Id }; return new RemoteException(Exception) { CallRequestId = Id };
} }
} }
public class AsyncCommandExecution : ICommandExecution
{
public Guid Id { get; set; }
public int ClientId { get; set; }
public int CommandId { get; set; }
}
} }
@@ -74,7 +74,7 @@ namespace mROA.Implementation.Frontend
request.Parameter = _serializationToolkit.Cast(request.Parameter, parameterType); request.Parameter = _serializationToolkit.Cast(request.Parameter, parameterType);
} }
var result = _executeModule.Execute(request, _contextRepository); var result = _executeModule.Execute(request, _contextRepository, _representationModule);
var resultType = result is FinalCommandExecution var resultType = result is FinalCommandExecution
? MessageType.FinishedCommandExecution ? MessageType.FinishedCommandExecution