// Protected methods protected static void SetReturnValue(ComputeMethodInput 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> InvokeAndStrip( ComputeMethodInput 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.TryUseExisting(context, usedBy, cancellationToken) .ConfigureAwait(false); if (output != null) { return(output.Value); } } using var @lock = await Locks.Lock(input, cancellationToken).ConfigureAwait(false); computed = TryGetExisting(input); if (computed != null) { output = await computed.TryUseExisting(context, usedBy, cancellationToken) .ConfigureAwait(false); if (output != null) { return(output.Value); } } computed = (IAsyncComputed <T>) await Compute(input, computed, cancellationToken) .ConfigureAwait(false); var rOutput = computed.Output; // RenewTimeouts isn't called yet, so it's ok computed.UseNew(context, usedBy); return(rOutput !.Value); }
protected override async ValueTask <IComputed <T> > Compute( ComputeMethodInput 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 e) { computed.TrySetOutput(Result.Error <T>(e)); computed.Invalidate(); throw; } catch (Exception e) { if (e is AggregateException ae) { e = ae.GetFirstInnerException(); } 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); }
protected abstract IComputed <T> CreateComputed(ComputeMethodInput input, LTag tag);
protected new IAsyncComputed <T>?TryGetExisting(ComputeMethodInput input) { var computed = ComputedRegistry.Instance.TryGet(input); return(computed as IAsyncComputed <T>); }
protected override IComputed <T> CreateComputed(ComputeMethodInput input, LTag tag) => new SwappingComputed <T>(Options, input, tag);