예제 #1
0
        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());
        }
예제 #2
0
        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);
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
        }
예제 #5
0
    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);
    }
예제 #6
0
    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);
    }
예제 #7
0
 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);
     }
 }
예제 #8
0
        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);
        }
예제 #9
0
        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));
        }
예제 #10
0
        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);
        }
예제 #11
0
        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));
        }
예제 #12
0
        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);
        }
예제 #13
0
        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);
        }
예제 #14
0
        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);
        }
예제 #15
0
        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);
        }
예제 #16
0
 internal static void UseNew <T>(this IComputed <T> computed, ComputeContext context, IComputed?usedBy)
 {
     ((IComputedImpl?)usedBy)?.AddUsed((IComputedImpl)computed);
     ((IComputedImpl?)computed)?.RenewTimeouts();
 }
예제 #17
0
        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);
        }
예제 #18
0
        // Conversion to IComputed

        public IComputed?TryGetCachedComputed(IComputed?usedBy = null)
        => Function.TryGetCached(this, usedBy);
예제 #19
0
 protected abstract Task InvokeAndStripAsync(
     ComputedInput input, IComputed?usedBy, ComputeContext?context,
     CancellationToken cancellationToken);
예제 #20
0
        public IComputed?TryGetCachedComputed(LTag lTag, IComputed?usedBy = null)
        {
            var computed = TryGetCachedComputed(usedBy);

            return(computed == null ? computed : computed.LTag == lTag ? computed : null);
        }
예제 #21
0
        // IFunction

        Task <IComputed> IFunction.InvokeAsync(
            ComputedInput input, IComputed?usedBy, ComputeContext?context,
            CancellationToken cancellationToken) =>
        InvokeAsync(input, usedBy, context, cancellationToken);
예제 #22
0
 public Task <IComputed> GetComputedAsync(IComputed?usedBy       = null,
                                          ComputeContext?context = null,
                                          CancellationToken cancellationToken = default)
 => Function.InvokeAsync(this, usedBy, context, cancellationToken);
예제 #23
0
 IComputed?IFunction.TryGetCached(ComputedInput input, IComputed?usedBy)
 => TryGetCached((TIn)input, null);
예제 #24
0
 Task IFunction.InvokeAndStripAsync(ComputedInput input,
                                    IComputed?usedBy,
                                    ComputeContext?context,
                                    CancellationToken cancellationToken)
 => InvokeAndStripAsync((TIn)input, usedBy, context, cancellationToken);
예제 #25
0
 public Entry(IComputed?computed, GCHandle handle)
 {
     Computed = computed;
     Handle   = handle;
 }
예제 #26
0
 async Task <IComputed> IFunction.InvokeAsync(ComputedInput input,
                                              IComputed?usedBy,
                                              ComputeContext?context,
                                              CancellationToken cancellationToken)
 => await InvokeAsync((TIn)input, usedBy, context, cancellationToken).ConfigureAwait(false);
예제 #27
0
 IComputed?IFunction.TryGetCached(ComputedInput input, IComputed?usedBy)
 => TryGetCached(input, usedBy);
예제 #28
0
 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);
 }
예제 #29
0
 protected abstract IComputed?TryGetCached(ComputedInput input, IComputed?usedBy);