public virtual async Task <TOut> InvokeAndStripAsync(TIn input, IComputed?usedBy, ComputeContext?context, CancellationToken cancellationToken = default) { context ??= ComputeContext.Current; // Read-Lock-RetryRead-Compute-Store pattern var result = TryGetCached(input); if (result.TryUseCached(context, usedBy)) { return(result.Strip()); } using var @lock = await Locks.LockAsync(input, cancellationToken).ConfigureAwait(false); result = TryGetCached(input); if (result.TryUseCached(context, usedBy)) { return(result.Strip()); } result = await ComputeAsync(input, result, cancellationToken).ConfigureAwait(false); result.UseNew(context, usedBy); return(result.Strip()); }
public virtual async Task <IComputed <TOut> > InvokeAsync(TIn input, IComputed?usedBy, ComputeContext?context, CancellationToken cancellationToken = default) { context ??= ComputeContext.Current; // Read-Lock-RetryRead-Compute-Store pattern var result = TryGetCached(input); if (result.TryUseCached(context, usedBy)) { return(result !); } using var @lock = await Locks.LockAsync(input, cancellationToken).ConfigureAwait(false); result = TryGetCached(input); if (result.TryUseCached(context, usedBy)) { return(result !); } result = await ComputeAsync(input, result, cancellationToken).ConfigureAwait(false); if (InvalidatedHandler != null) { result.Invalidated += InvalidatedHandler; } Register((IComputed <TIn, TOut>)result); result.UseNew(context, usedBy); return(result); }
public virtual async Task <TOut> InvokeAndStripAsync(TIn input, IComputed?usedBy, ComputeContext?context, CancellationToken cancellationToken = default) { context ??= ComputeContext.Current; Result <TOut> output; // Read-Lock-RetryRead-Compute-Store pattern var result = TryGetExisting(input); if (result.TryUseExisting(context, usedBy)) { return(result.Strip()); } using var @lock = await Locks.LockAsync(input, cancellationToken).ConfigureAwait(false); result = TryGetExisting(input); if (result.TryUseExisting(context, usedBy)) { return(result.Strip()); } result = await ComputeAsync(input, result, cancellationToken).ConfigureAwait(false); output = result.Output; // It can't be gone here b/c KeepAlive isn't called yet result.UseNew(context, usedBy); return(output.Value); }
internal static bool TryUseCached(this IComputed?cached, ComputeContext context, IComputed?usedBy) { var callOptions = context.CallOptions; var useCached = (callOptions & CallOptions.TryGetCached) != 0; if (cached == null) { // AY: Below code is commented out since context.CapturedComputed // should be null anyway in this case. // if (useCached && (callOptions & CallOptions.Capture) != 0) // Interlocked.Exchange(ref context.CapturedComputed, null); return(useCached); } useCached |= cached.IsConsistent; if (!useCached) { return(false); } if ((callOptions & CallOptions.Invalidate) == CallOptions.Invalidate) { cached.Invalidate(); } ((IComputedImpl?)usedBy)?.AddUsed((IComputedImpl)cached !); if ((callOptions & CallOptions.Capture) != 0) { Interlocked.Exchange(ref context.CapturedComputed, cached); } return(true); }
public virtual async Task <IComputed <TOut> > Invoke(TIn input, IComputed?usedBy, ComputeContext?context, CancellationToken cancellationToken = default) { context ??= ComputeContext.Current; // Read-Lock-RetryRead-Compute-Store pattern var result = GetExisting(input); if (result.TryUseExisting(context, usedBy)) { return(result !); } using var @lock = await Locks.Lock(input, cancellationToken).ConfigureAwait(false); result = GetExisting(input); if (result.TryUseExisting(context, usedBy)) { return(result !); } result = await Compute(input, result, cancellationToken).ConfigureAwait(false); result.UseNew(context, usedBy); return(result); }
internal static async ValueTask <ResultBox <T>?> TryUseExistingFromUse <T>( this IAsyncComputed <T>?existing, ComputeContext context, IComputed?usedBy, CancellationToken cancellationToken) { if (existing == null || !existing.IsConsistent()) { return(null); } var result = existing.MaybeOutput; if (result == null) { result = await existing.GetOutput(cancellationToken).ConfigureAwait(false); if (result == null) { return(null); } } context.TryCapture(existing); ((IComputedImpl?)usedBy)?.AddUsed((IComputedImpl)existing !); ((IComputedImpl?)existing)?.RenewTimeouts(); return(result); }
internal static void UseNew(this IComputed computed, ComputeContext context, IComputed?usedBy) { ((IComputedImpl?)usedBy)?.AddUsed((IComputedImpl)computed); if ((context.CallOptions & CallOptions.Capture) != 0) { Interlocked.Exchange(ref context.CapturedComputed, computed); } }
public virtual IComputed <TOut>?TryGetCached(TIn input, IComputed?usedBy) { var result = ComputedRegistry.TryGet(input) as IComputed <TIn, TOut>; if (result != null) { ((IComputedImpl?)usedBy)?.AddUsed((IComputedImpl)result); } return(result); }
public static Disposable <IComputed?> ChangeCurrent(IComputed?newCurrent) { var oldCurrent = GetCurrent(); if (oldCurrent == newCurrent) { return(Disposable.New(oldCurrent, _ => { })); } CurrentLocal.Value = newCurrent; return(Disposable.New(oldCurrent, oldCurrent1 => CurrentLocal.Value = oldCurrent1)); }
internal static async ValueTask <ResultBox <T>?> TryUseExistingAsync <T>( this IAsyncComputed <T>?existing, ComputeContext context, IComputed?usedBy, CancellationToken cancellationToken) { var callOptions = context.CallOptions; var useExisting = (callOptions & CallOptions.TryGetExisting) != 0; if (existing == null) { return(useExisting ? ResultBox <T> .Default : null); } if (!(useExisting || existing.IsConsistent())) { return(null); } var invalidate = (callOptions & CallOptions.Invalidate) == CallOptions.Invalidate; if (invalidate) { existing.Invalidate(); if ((callOptions & CallOptions.Capture) != 0) { Interlocked.Exchange(ref context.CapturedComputed, existing); } return(existing.MaybeOutput ?? ResultBox <T> .Default); } var result = existing.MaybeOutput; if (result == null) { result = await existing.GetOutputAsync(cancellationToken).ConfigureAwait(false); if (result == null) { return(null); } } if (!useExisting) { ((IComputedImpl?)usedBy)?.AddUsed((IComputedImpl)existing !); } if ((callOptions & CallOptions.Capture) != 0) { Interlocked.Exchange(ref context.CapturedComputed, existing); } ((IComputedImpl?)existing)?.RenewTimeouts(); return(result); }
public static ClosedDisposable <IComputed?> ChangeCurrent(IComputed?newCurrent) { var oldCurrent = GetCurrent(); if (newCurrent != null) { ComputeContext.Current.TryCapture(newCurrent); } if (oldCurrent == newCurrent) { return(Disposable.NewClosed(oldCurrent, _ => { })); } CurrentLocal.Value = newCurrent; return(Disposable.NewClosed(oldCurrent, oldCurrent1 => CurrentLocal.Value = oldCurrent1)); }
public virtual async Task <IComputed <TOut> > InvokeAsync(TIn input, IComputed?usedBy, ComputeContext?context, CancellationToken cancellationToken = default) { using var contextUseScope = context.Use(); context = contextUseScope.Context; // Read-Lock-RetryRead-Compute-Store pattern var result = TryGetCached(input, usedBy); var resultIsConsistent = result?.IsConsistent ?? false; if (resultIsConsistent || (context.CallOptions & CallOptions.TryGetCached) != 0) { if ((context.CallOptions & CallOptions.Invalidate) == CallOptions.Invalidate) { result?.Invalidate(); } context.TryCaptureValue(result); return(result !); } using var @lock = await Locks.LockAsync(input, cancellationToken).ConfigureAwait(false); result = TryGetCached(input, usedBy); resultIsConsistent = result?.IsConsistent ?? false; if (resultIsConsistent || (context.CallOptions & CallOptions.TryGetCached) != 0) { if ((context.CallOptions & CallOptions.Invalidate) == CallOptions.Invalidate) { result?.Invalidate(); } context.TryCaptureValue(result); return(result !); } result = await ComputeAsync(input, result, cancellationToken).ConfigureAwait(false); if (InvalidatedHandler != null) { result.Invalidated += InvalidatedHandler; } ((IComputedImpl?)usedBy)?.AddUsed((IComputedImpl)result); Register((IComputed <TIn, TOut>)result); context.TryCaptureValue(result); return(result); }
public override IComputed <T>?TryGetCached(InterceptedInput input, IComputed?usedBy) { if (!(ComputedRegistry.TryGet(input) is IReplicaServiceComputed <T> computed)) { return(null); } var replica = computed.Replica; if (replica == null || replica.UpdateError != null || replica.DisposalState != DisposalState.Active) { ComputedRegistry.Remove(computed); return(null); } ((IComputedImpl?)usedBy)?.AddUsed((IComputedImpl)computed); return(computed); }
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); }
internal static bool TryUseExisting <T>(this IComputed <T>?existing, ComputeContext context, IComputed?usedBy) { var callOptions = context.CallOptions; var useExisting = (callOptions & CallOptions.TryGetExisting) != 0; if (existing == null) { return(useExisting); } useExisting |= existing.IsConsistent(); if (!useExisting) { return(false); } if ((callOptions & CallOptions.Capture) != 0) { Interlocked.Exchange(ref context.CapturedComputed, existing); } if ((callOptions & CallOptions.Invalidate) == CallOptions.Invalidate) { existing.Invalidate(); return(true); } ((IComputedImpl?)usedBy)?.AddUsed((IComputedImpl)existing !); ((IComputedImpl?)existing)?.RenewTimeouts(); return(true); }
internal static void UseNew <T>(this IComputed <T> computed, ComputeContext context, IComputed?usedBy) { ((IComputedImpl?)usedBy)?.AddUsed((IComputedImpl)computed); ((IComputedImpl?)computed)?.RenewTimeouts(); }
internal static bool TryUseExisting <T>(this IComputed <T>?existing, ComputeContext context, IComputed?usedBy) { var callOptions = context.CallOptions; var useExisting = (callOptions & CallOptions.TryGetExisting) != 0; if (existing == null) { return(useExisting); } if (!(useExisting || existing.IsConsistent())) { return(false); } context.TryCapture(existing); var invalidate = (callOptions & CallOptions.Invalidate) == CallOptions.Invalidate; if (invalidate) { existing.Invalidate(); return(true); } if (!useExisting) { ((IComputedImpl?)usedBy)?.AddUsed((IComputedImpl)existing !); } ((IComputedImpl?)existing)?.RenewTimeouts(); return(true); }
// Conversion to IComputed public IComputed?TryGetCachedComputed(IComputed?usedBy = null) => Function.TryGetCached(this, usedBy);
protected abstract Task InvokeAndStripAsync( ComputedInput input, IComputed?usedBy, ComputeContext?context, CancellationToken cancellationToken);
public IComputed?TryGetCachedComputed(LTag lTag, IComputed?usedBy = null) { var computed = TryGetCachedComputed(usedBy); return(computed == null ? computed : computed.LTag == lTag ? computed : null); }
// IFunction Task <IComputed> IFunction.InvokeAsync( ComputedInput input, IComputed?usedBy, ComputeContext?context, CancellationToken cancellationToken) => InvokeAsync(input, usedBy, context, cancellationToken);
public Task <IComputed> GetComputedAsync(IComputed?usedBy = null, ComputeContext?context = null, CancellationToken cancellationToken = default) => Function.InvokeAsync(this, usedBy, context, cancellationToken);
IComputed?IFunction.TryGetCached(ComputedInput input, IComputed?usedBy) => TryGetCached((TIn)input, null);
Task IFunction.InvokeAndStripAsync(ComputedInput input, IComputed?usedBy, ComputeContext?context, CancellationToken cancellationToken) => InvokeAndStripAsync((TIn)input, usedBy, context, cancellationToken);
public Entry(IComputed?computed, GCHandle handle) { Computed = computed; Handle = handle; }
async Task <IComputed> IFunction.InvokeAsync(ComputedInput input, IComputed?usedBy, ComputeContext?context, CancellationToken cancellationToken) => await InvokeAsync((TIn)input, usedBy, context, cancellationToken).ConfigureAwait(false);
IComputed?IFunction.TryGetCached(ComputedInput input, IComputed?usedBy) => TryGetCached(input, usedBy);
internal static bool TryUseExistingFromUse <T>(this IComputed <T>?existing, ComputeContext context, IComputed?usedBy) { if (existing == null || !existing.IsConsistent()) { return(false); } context.TryCapture(existing); ((IComputedImpl?)usedBy)?.AddUsed((IComputedImpl)existing !); ((IComputedImpl?)existing)?.RenewTimeouts(); return(true); }
protected abstract IComputed?TryGetCached(ComputedInput input, IComputed?usedBy);