private void SetPollingMethod(TrackedInvocation trackedInvocation) { var methodDefinition = _methodResolver.Resolve( _serviceResolver.Resolve(trackedInvocation.ServiceId).Definition, trackedInvocation.MethodId).Definition; // TODO: add a preference for polling if has access to the storage of the external service if (methodDefinition.Service.Type == ServiceType.External) { var communicator = _communicatorProvider.GetCommunicator(trackedInvocation.ServiceId, trackedInvocation.MethodId); if (communicator.Traits.HasFlag(CommunicationTraits.SyncReplies) && communicator is ISynchronousCommunicator syncCommunicator) { trackedInvocation.Communicator = syncCommunicator; } } if (trackedInvocation.Communicator == null) { // TODO: somehow make sure that an external service shares the same result storage. trackedInvocation.StateStorage = _methodStateStorageProvider.GetStorage(trackedInvocation.ServiceId, trackedInvocation.MethodId); } // TODO: helper method Type taskResultType = methodDefinition.MethodInfo.ReturnType == typeof(void) ? TaskAccessor.VoidTaskResultType : TaskAccessor.GetTaskResultType(methodDefinition.MethodInfo.ReturnType); trackedInvocation.ResultValueType = taskResultType == TaskAccessor.VoidTaskResultType ? typeof(object) : taskResultType; }
private async Task <InvokeRoutineResult> RunRoutineAsync( ITransitionCarrier transitionCarrier, TransitionDescriptor transitionDescriptor, CancellationToken ct) { var invocationResult = new InvokeRoutineResult(); using (_transitionScope.Enter(transitionDescriptor)) { var transitionMonitor = _transitionScope.CurrentMonitor; var serviceId = await transitionCarrier.GetServiceIdAsync(ct); var methodId = await transitionCarrier.GetRoutineDescriptorAsync(ct); var serviceReference = _serviceResolver.Resolve(serviceId); var methodReference = _methodResolver.Resolve(serviceReference.Definition, methodId); object serviceInstance = serviceReference.GetInstance(); //var serviceStateContainer = _serviceStateValueContainerProvider.CreateContainer(serviceInstance); //var isStatefullService = serviceStateContainer.GetCount() > 0; //if (isStatefullService) // await transitionCarrier.ReadServiceStateAsync(serviceStateContainer, ct); Type taskResultType = methodReference.Definition.MethodInfo.ReturnType == typeof(void) ? TaskAccessor.VoidTaskResultType : TaskAccessor.GetTaskResultType(methodReference.Definition.MethodInfo.ReturnType); Task completionTask; IValueContainer asmValueContainer = null; if (TryCreateAsyncStateMachine(methodReference.Definition.MethodInfo, methodId.IntentId, out var asmInstance, out var asmMetadata)) { var isContinuation = transitionDescriptor.Type == TransitionType.ContinueRoutine; asmValueContainer = await LoadRoutineStateAsync(transitionCarrier, asmInstance, asmMetadata, isContinuation, ct); asmMetadata.Owner.FieldInfo?.SetValue(asmInstance, serviceInstance); transitionMonitor.OnRoutineStart( serviceReference, methodReference, methodId, serviceInstance, asmInstance, (transitionCarrier as TransitionCarrier)?.Caller); try { asmInstance.MoveNext(); completionTask = GetCompletionTask(asmInstance, asmMetadata); } catch (Exception ex) { // The MoveNext() must not throw, but instead complete the task with an error. // try-catch is added just in case for a non-compiler-generated state machine. completionTask = TaskAccessor.FromException(taskResultType, ex); } }
public object Compose(IValueContainer container, Type valueType) { var values = (TaskContainer)container; var resultType = TaskAccessor.GetTaskResultType(valueType); Task task = TaskAccessor.CreateTask(values.State, resultType); if (values.Status == TaskStatus.Canceled) { task.TrySetCanceled(); } else if (values.Status == TaskStatus.Faulted) { task.TrySetException(values.Exception); } else if (values.Status == TaskStatus.RanToCompletion) { var result = values.Result; //if (result != null && resultType != result.GetType()) // result = Convert.ChangeType(result, resultType); task.TrySetResult(result); } task.SetStatus(values.Status); // TODO: task.SetCreationOptions(values.Options); return(task); }
public Task Execute <TParameters>(IProxy proxy, MethodInfo methodInfo, ref TParameters parameters) where TParameters : IValueContainer { var serviceProxyContext = (ServiceProxyContext)proxy.Context; var intent = new ExecuteRoutineIntent { Id = _numericIdGenerator.NewId(), ServiceId = serviceProxyContext.Descriptor.Id, MethodId = _routineMethodIdProvider.GetId(methodInfo), Parameters = parameters }; var taskResultType = // Dispose() does not return a task, and is the only exception. #warning check if it's really IDisposable.Dispose methodInfo.ReturnType == typeof(void) ? TaskAccessor.VoidTaskResultType : TaskAccessor.GetTaskResultType(methodInfo.ReturnType); var taskState = new RoutineReference { IntentId = intent.Id #warning must have id of actual routine for dynamic subscription (subscribe after a routine already scheduled). }; var proxyTask = TaskAccessor.CreateTask(taskState, taskResultType); bool executeInline = !_transitionScope.IsActive || !IsCalledByRoutine( _transitionScope.CurrentMonitor.Context, // Skip 2 stack frames: current method and dynamically-generated proxy. // WARNING! DO NOT PUT 'new StackFrame()' into a helper method! new StackFrame(skipFrames: 2, fNeedFileInfo: false)); if (executeInline) { ExecuteAndAwaitInBackground(intent, proxyTask); } else { _transitionScope.CurrentMonitor.RegisterIntent(intent, proxyTask); } return(proxyTask); }
public Task Execute <TParameters>(IProxy proxy, MethodInfo methodInfo, ref TParameters parameters) where TParameters : IValueContainer { var serviceProxyContext = (ServiceProxyContext)proxy.Context; var intent = new ExecuteRoutineIntent { Id = _numericIdGenerator.NewId(), ServiceId = serviceProxyContext.Service.Id, MethodId = _routineMethodIdProvider.GetId(methodInfo), Parameters = parameters }; var taskResultType = // Dispose() does not return a task, and is the only exception. #warning check if it's really IDisposable.Dispose methodInfo.ReturnType == typeof(void) ? TaskAccessor.VoidTaskResultType : TaskAccessor.GetTaskResultType(methodInfo.ReturnType); var taskState = new RoutineReference { IntentId = intent.Id #warning must have id of actual routine for dynamic subscription (subscribe after a routine already scheduled). }; var proxyTask = TaskAccessor.CreateTask(taskState, taskResultType); if (_transitionScope.IsActive) { _transitionScope.CurrentMonitor.RegisterIntent(intent, proxyTask); } else { ExecuteAndAwaitInBackground(intent, proxyTask); } return(proxyTask); }
public async Task <InvokeRoutineResult> RunAsync(MethodInvocationData data, ICommunicatorMessage message) { var serviceReference = _serviceResolver.Resolve(data.Service); var methodReference = _methodResolver.Resolve(serviceReference.Definition, data.Method); var behaviorSettings = _communicationSettingsProvider.GetMethodSettings(methodReference.Definition); //------------------------------------------------------------------------------- // MESSGE DE-DUPLICATION //------------------------------------------------------------------------------- bool needsDeduplication = behaviorSettings.Deduplicate; if (methodReference.Definition.IsQuery) { // NOTE: you can run queries using message passing (non-volatile), // or the volatile method uses a callback instead of re-trying. if (message.CommunicatorTraits.HasFlag(CommunicationTraits.Volatile) && data.Continuation == null) { needsDeduplication = false; } } if (needsDeduplication && !message.CommunicatorTraits.HasFlag(CommunicationTraits.MessageDeduplication) && (message.IsRetry != false || string.IsNullOrEmpty(message.RequestId))) { // TODO: if has message de-dup'er, check if a dedup // return new InvokeRoutineResult { Outcome = InvocationOutcome.Deduplicated }; } //------------------------------------------------------------------------------- // UNIT OF WORK //------------------------------------------------------------------------------- // TODO: Unit of Work - check if there is cached/stored data if (message.IsRetry != false) { // TODO: If has entities in transitions and they have been already committed, // skip method transition and re-try sending commands and events. } //------------------------------------------------------------------------------- // DELEGATION TO RESILIENT COMMUNICATOR //------------------------------------------------------------------------------- IMessageHandle messageHandle = null; if (behaviorSettings.Persistent && message.CommunicatorTraits.HasFlag(CommunicationTraits.Volatile)) { // TODO: check if can poll for result! (think about continuation and sync invocation) var preferredCommunicator = _communicatorProvider.GetCommunicator(data.Service, data.Method); if (message.CommunicatorType != preferredCommunicator.Type && !preferredCommunicator.Traits.HasFlag(CommunicationTraits.Volatile)) { var resultValueType = methodReference.Definition.MethodInfo.ReturnType; if (resultValueType != typeof(void)) { resultValueType = TaskAccessor.GetTaskResultType(resultValueType); if (resultValueType == TaskAccessor.VoidTaskResultType) { resultValueType = typeof(void); } } var preferences = new InvocationPreferences { LockMessage = behaviorSettings.RunInPlace && preferredCommunicator.Traits.HasFlag(CommunicationTraits.MessageLockOnPublish), ResultValueType = resultValueType }; var invocationResult = await preferredCommunicator.InvokeAsync(data, preferences); if (invocationResult.Outcome == InvocationOutcome.Complete && !string.IsNullOrEmpty(data.IntentId)) { _routineCompletionSink.OnRoutineCompleted(data.Service, data.Method, data.IntentId, invocationResult.Result); } if (invocationResult.Outcome == InvocationOutcome.Scheduled && invocationResult.MessageHandle != null) { // NOTE: will run synchronously below messageHandle = invocationResult.MessageHandle; } else { return(invocationResult); } } } //------------------------------------------------------------------------------- // RUN METHOD TRANSITION //------------------------------------------------------------------------------- try { var adapter = new TransitionCarrier(data, _valueContainerCopier, message); var transitionDescriptor = new TransitionDescriptor { Type = TransitionType.InvokeRoutine }; var result = await RunRoutineAsync(adapter, transitionDescriptor, default); if (result.Outcome == InvocationOutcome.Complete && !string.IsNullOrEmpty(data.IntentId)) { _routineCompletionSink.OnRoutineCompleted(data.Service, data.Method, data.IntentId, result.Result); } if (messageHandle != null) { await messageHandle.Complete(); } return(result); } catch { messageHandle?.ReleaseLock(); throw; } }
private async Task RunRoutineAsync( ITransitionCarrier transitionCarrier, TransitionDescriptor transitionDescriptor, CancellationToken ct) { using (_transitionScope.Enter(transitionDescriptor)) { var transitionMonitor = _transitionScope.CurrentMonitor; var serviceId = await transitionCarrier.GetServiceIdAsync(ct); var routineDescriptor = await transitionCarrier.GetRoutineDescriptorAsync(ct); object serviceInstance; IServiceDefinition serviceDefinition; #warning IntrinsicRoutines must be registered in the service registry, but it needs the engine IoC to resolve. if (serviceId.ProxyName == nameof(IntrinsicRoutines)) { serviceInstance = _intrinsicRoutines; serviceDefinition = IntrinsicCommunicationModel.IntrinsicRoutinesServiceDefinition; } else { serviceInstance = _serviceProxyBuilder.Build(serviceId); serviceDefinition = ((ServiceProxyContext)((IProxy)serviceInstance).Context).Definition; } var routineMethod = _routineMethodResolver.Resolve(serviceDefinition, routineDescriptor.MethodId); //var serviceStateContainer = _serviceStateValueContainerProvider.CreateContainer(serviceInstance); //var isStatefullService = serviceStateContainer.GetCount() > 0; //if (isStatefullService) // await transitionCarrier.ReadServiceStateAsync(serviceStateContainer, ct); Task completionTask; IValueContainer asmValueContainer = null; if (TryCreateAsyncStateMachine(routineMethod, out var asmInstance, out var asmMetadata)) { var isContinuation = transitionDescriptor.Type == TransitionType.ContinueRoutine; asmValueContainer = await LoadRoutineStateAsync(transitionCarrier, asmInstance, asmMetadata, isContinuation, ct); asmMetadata.Owner.FieldInfo?.SetValue(asmInstance, serviceInstance); transitionMonitor.OnRoutineStart( serviceId, routineDescriptor, serviceInstance, routineMethod, asmInstance); try { #warning possibly need to create a proxy? on a sealed ASM class? How to capture Task.Delay if it's not immediate after first MoveNext? asmInstance.MoveNext(); completionTask = GetCompletionTask(asmInstance, asmMetadata); } catch (Exception ex) { // The MoveNext() must not throw, but instead complete the task with an error. // try-catch is added just in case for a non-compiler-generated state machine. var taskResultType = TaskAccessor.GetTaskResultType(routineMethod.ReturnType); completionTask = TaskAccessor.FromException(taskResultType, ex); } }
private async Task RunRoutineAsync( ITransitionCarrier transitionCarrier, ITransitionData transitionData, TransitionDescriptor transitionDescriptor, CancellationToken ct) { using (_transitionScope.Enter(transitionDescriptor)) { var transitionMonitor = _transitionScope.CurrentMonitor; var serviceId = await transitionData.GetServiceIdAsync(ct); var routineDescriptor = await transitionData.GetRoutineDescriptorAsync(ct); var serviceInstance = #warning IntrinsicRoutines must be registered in the service registry, but it needs the engine IoC to resolve. serviceId.ProxyName == nameof(IntrinsicRoutines) ? _intrinsicRoutines : _serviceProxyBuilder.Build(serviceId); #warning check if the serviceInstance proxy is an actual non-abstract class with implementation // Need exact underlying type of the service implementation type to call // the routine method directly without using the virtual method table. var serviceType = (serviceInstance as IProxy)?.ObjectType ?? serviceInstance.GetType(); var routineMethod = _routineMethodResolver.Resolve(serviceType, routineDescriptor.MethodId); var serviceStateContainer = _serviceStateValueContainerProvider.CreateContainer(serviceInstance); var isStatefullService = serviceStateContainer.GetCount() > 0; if (isStatefullService) { await transitionData.ReadServiceStateAsync(serviceStateContainer, ct); } Task completionTask; IValueContainer asmValueContainer = null; if (TryCreateAsyncStateMachine(routineMethod, out var asmInstance, out var asmMetadata, out completionTask)) { var isContinuation = transitionDescriptor.Type == TransitionType.ContinueRoutine; asmValueContainer = await LoadRoutineStateAsync(transitionData, asmInstance, asmMetadata, isContinuation, ct); asmMetadata.Owner.FieldInfo?.SetValue(asmInstance, serviceInstance); transitionMonitor.OnRoutineStart( serviceId, routineDescriptor, serviceInstance, routineMethod, asmInstance); try { #warning possibly need to create a proxy? on a sealed ASM class? How to capture Task.Delay if it's not immediate after first MoveNext? asmInstance.MoveNext(); } catch (Exception ex) { // The MoveNext() must not throw, but instead complete the task with an error. // try-catch is added just in case for a non-compiler-generated state machine. var taskResultType = TaskAccessor.GetTaskResultType(routineMethod.ReturnType); completionTask = TaskAccessor.FromException(taskResultType, ex); } }
public Task Execute <TParameters>(IProxy proxy, MethodInfo methodInfo, ref TParameters parameters) where TParameters : IValueContainer { var serviceProxyContext = (ServiceProxyContext)proxy.Context; var methodDefinition = serviceProxyContext.Definition.FindMethod(methodInfo); if (methodDefinition == null || methodDefinition.IsIgnored) { var invoker = _methodInvokerFactory.Create(methodInfo); return(invoker.Invoke(proxy, parameters)); } var intent = new ExecuteRoutineIntent { Id = _idGenerator.NewId(), Service = serviceProxyContext.Descriptor.Id, Method = _routineMethodIdProvider.GetId(methodInfo), Parameters = parameters }; Type taskResultType = methodInfo.ReturnType == typeof(void) ? TaskAccessor.VoidTaskResultType : TaskAccessor.GetTaskResultType(methodInfo.ReturnType); var taskState = new RoutineReference { ServiceId = intent.Service, MethodId = intent.Method, IntentId = intent.Id #warning must have id of actual routine for dynamic subscription (subscribe after a routine already scheduled). }; var proxyTask = TaskAccessor.CreateTask(taskState, taskResultType); bool invokedByRunningMethod = _transitionScope.IsActive && IsCalledByRoutine( _transitionScope.CurrentMonitor.Context, // Skip 2 stack frames: current method and dynamically-generated proxy. // WARNING! DO NOT PUT 'new StackFrame()' into a helper method! new StackFrame(skipFrames: 2, fNeedFileInfo: false)); bool ignoreTransaction = !invokedByRunningMethod; if (!ignoreTransaction && _transitionScope.IsActive) { var runningMethodSettings = _communicationSettingsProvider.GetMethodSettings( _transitionScope.CurrentMonitor.Context.MethodRef.Definition); if (!runningMethodSettings.Transactional) { ignoreTransaction = true; } } if (!ignoreTransaction) { var methodSettings = _communicationSettingsProvider.GetMethodSettings(methodDefinition); if (methodSettings.IgnoreTransaction) { ignoreTransaction = true; } } if (ignoreTransaction) { ExecuteAndAwaitInBackground(intent, proxyTask); } else { _transitionScope.CurrentMonitor.RegisterIntent(intent, proxyTask); } return(proxyTask); }
public async Task <InvokeRoutineResult> InvokeAsync(ExecuteRoutineIntent intent) { var serviceRef = _serviceResolver.Resolve(intent.Service); var methodRef = _methodResolver.Resolve(serviceRef.Definition, intent.Method); var behaviorSettings = _communicationSettingsProvider.GetMethodSettings(methodRef.Definition); ICommunicator communicator = _communicatorProvider.GetCommunicator(serviceRef.Id, methodRef.Id); bool preferToRunInPlace = behaviorSettings.RunInPlace && serviceRef.Definition.Type != ServiceType.External; bool runInPlace = preferToRunInPlace && (!behaviorSettings.Persistent || communicator.Traits.HasFlag(CommunicationTraits.MessageLockOnPublish)); var invocationData = InvocationDataUtils.CreateMethodInvocationData(intent, _transitionScope.IsActive ? _transitionScope.CurrentMonitor.Context : null); var resultValueType = methodRef.Definition.MethodInfo.ReturnType; if (resultValueType != typeof(void)) { resultValueType = TaskAccessor.GetTaskResultType(resultValueType); if (resultValueType == TaskAccessor.VoidTaskResultType) { resultValueType = typeof(void); } } if (runInPlace) { IMessageHandle messageHandle = null; if (behaviorSettings.Persistent) { var preferences = new InvocationPreferences { LockMessage = true, ResultValueType = resultValueType }; var invocationResult = await communicator.InvokeAsync(invocationData, preferences); if (invocationResult.Outcome == InvocationOutcome.Complete) { return(invocationResult); } messageHandle = invocationResult.MessageHandle; } try { var result = await _localMethodRunner.RunAsync( invocationData, RuntimeCommunicatorMessage.Instance); if (messageHandle != null) { await messageHandle.Complete(); } return(result); } catch (Exception ex) // TODO: infra exceptions? should not be there, right? { if (messageHandle != null) { messageHandle.ReleaseLock(); } return(new InvokeRoutineResult { Outcome = InvocationOutcome.Scheduled, MessageHandle = messageHandle }); } } else { var preferences = new InvocationPreferences { // TODO: check this option //LockMessage = behaviorSettings.Resilient && communicator.Traits.HasFlag(CommunicationTraits.MessageLockOnPublish), Synchronous = communicator.Traits.HasFlag(CommunicationTraits.SyncReplies), ResultValueType = resultValueType }; return(await communicator.InvokeAsync(invocationData, preferences)); } }