예제 #1
0
        protected override async ValueTask <IComputed <T> > ComputeAsync(
            InterceptedInput input, IComputed <T>?existing,
            CancellationToken cancellationToken)
        {
            var tag      = VersionGenerator.Next();
            var computed = CreateComputed(input, tag);

            try {
                using var _ = Computed.ChangeCurrent(computed);
                var result = input.InvokeOriginalFunction(cancellationToken);
                if (input.Method.ReturnsValueTask)
                {
                    var output = await((ValueTask <T>)result).ConfigureAwait(false);
                    computed.TrySetOutput(output);
                }
                else
                {
                    var output = await((Task <T>)result).ConfigureAwait(false);
                    computed.TrySetOutput(output);
                }
            }
            catch (OperationCanceledException) {
                throw;
            }
            catch (Exception e) {
                computed.TrySetOutput(Result.Error <T>(e));
                // Weird case: if the output is already set, all we can
                // is to ignore the exception we've just caught;
                // throwing it further will probably make it just worse,
                // since the the caller have to take this scenario into acc.
            }
            return(computed);
        }
예제 #2
0
        protected override async ValueTask <IComputed <T> > ComputeAsync(
            InterceptedInput input, IComputed <T>?cached,
            CancellationToken cancellationToken)
        {
            var tag    = LTagGenerator.Next();
            var method = Method;
            var output = new Computed <InterceptedInput, T>(method.Options, input, tag);

            try {
                using var _ = Computed.ChangeCurrent(output);
                var resultTask = input.InvokeOriginalFunction(cancellationToken);
                if (method.ReturnsComputed)
                {
                    if (method.ReturnsValueTask)
                    {
                        var task = (ValueTask <IComputed <T> >)resultTask;
                        await task.ConfigureAwait(false);

                        // output == task.Result here, so no need to call output.TrySetOutput(...)
                    }
                    else
                    {
                        var task = (Task <IComputed <T> >)resultTask;
                        await task.ConfigureAwait(false);

                        // output == task.Result here, so no need to call output.TrySetOutput(...)
                    }
                }
                else
                {
                    if (method.ReturnsValueTask)
                    {
                        var task  = (ValueTask <T>)resultTask;
                        var value = await task.ConfigureAwait(false);

                        output.TrySetOutput(value !);
                    }
                    else
                    {
                        var task  = (Task <T>)resultTask;
                        var value = await task.ConfigureAwait(false);

                        output.TrySetOutput(value !);
                    }
                }
            }
            catch (OperationCanceledException) {
                throw;
            }
            catch (Exception e) {
                output.TrySetOutput(Result.Error <T>(e));
                // Weird case: if the output is already set, all we can
                // is to ignore the exception we've just caught;
                // throwing it further will probably make it just worse,
                // since the the caller have to take this scenario into acc.
            }
            return(output);
        }
        // Protected methods

        protected static void SetReturnValue(InterceptedInput input, Result <TOut> output)
        {
            if (input.Method.ReturnsValueTask)
            {
                input.Invocation.ReturnValue =
                    output.IsValue(out var v)
                        ? ValueTaskEx.FromResult(v)
                        : ValueTaskEx.FromException <TOut>(output.Error !);
            }
            else
            {
                input.Invocation.ReturnValue =
                    output.IsValue(out var v)
                        ? Task.FromResult(v)
                        : Task.FromException <TOut>(output.Error !);
            }
        }
        // Protected methods

        protected static void SetReturnValue(InterceptedInput input, Result <TOut> output)
        {
            if (input.Method.ReturnsValueTask)
            {
                input.Invocation.ReturnValue =
                    // ReSharper disable once HeapView.BoxingAllocation
                    output.IsValue(out var v)
                        ? ValueTaskEx.FromResult(v)
                        : ValueTaskEx.FromException <TOut>(output.Error !);
            }
            else
            {
                input.Invocation.ReturnValue =
                    output.IsValue(out var v)
                        ? Task.FromResult(v)
                        : Task.FromException <TOut>(output.Error !);
            }
        }
        public override async Task <T> InvokeAndStripAsync(
            InterceptedInput input, IComputed?usedBy, ComputeContext?context,
            CancellationToken cancellationToken = default)
        {
            context ??= ComputeContext.Current;
            ResultBox <T>?output;

            // Read-Lock-RetryRead-Compute-Store pattern

            var computed = TryGetExisting(input);

            if (computed != null)
            {
                output = await computed.TryUseExistingAsync(context, usedBy, cancellationToken)
                         .ConfigureAwait(false);

                if (output != null)
                {
                    return(output.Value);
                }
            }

            using var @lock = await Locks.LockAsync(input, cancellationToken).ConfigureAwait(false);

            computed = TryGetExisting(input);
            if (computed != null)
            {
                output = await computed.TryUseExistingAsync(context, usedBy, cancellationToken)
                         .ConfigureAwait(false);

                if (output != null)
                {
                    return(output.Value);
                }
            }

            computed = (IAsyncComputed <T>) await ComputeAsync(input, computed, cancellationToken)
                       .ConfigureAwait(false);

            output = computed.Output; // RenewTimeouts isn't called yet, so it's ok
            computed.UseNew(context, usedBy);
            return(output !.Value);
        }
 protected override IComputed <T> CreateComputed(InterceptedInput input, LTag tag)
 => new Computed <T>(Options, input, tag);
예제 #7
0
 protected abstract IComputed <T> CreateComputed(InterceptedInput input, LTag tag);
        new protected IAsyncComputed <T>?TryGetExisting(InterceptedInput input)
        {
            var computed = ComputedRegistry.Instance.TryGet(input);

            return(computed as IAsyncComputed <T>);
        }