public void OnCancelled(ForwardingConsumer forwardingConsumer) { lock (_gate) { // Check for late callbacks if (_forwardingConsumer != forwardingConsumer) { return; } _forwardingConsumer = null; _multiplexProducerContext = null; CloseSafely(_lastIntermediateResult); _lastIntermediateResult = default(T); } StartInputProducerIfHasAttachedConsumers(); }
/// <summary> /// Starts next producer if it is not started yet and there is /// at least one Consumer waiting for the data. If all consumers /// are cancelled, then this multiplexer is removed from _request /// map to clean up. /// </summary> internal void StartInputProducerIfHasAttachedConsumers() { BaseProducerContext multiplexProducerContext; ForwardingConsumer forwardingConsumer; lock (_gate) { Preconditions.CheckArgument(_multiplexProducerContext == null); Preconditions.CheckArgument(_forwardingConsumer == null); // Cleanup if all consumers have been cancelled before // this method was called if (_consumerContextPairs.IsEmpty) { _parent.RemoveMultiplexer(_key, this); return; } var iterator = _consumerContextPairs.GetEnumerator(); iterator.MoveNext(); IProducerContext producerContext = iterator.Current.Key.Item2; _multiplexProducerContext = new BaseProducerContext( producerContext.ImageRequest, producerContext.Id, producerContext.Listener, producerContext.CallerContext, producerContext.LowestPermittedRequestLevel, ComputeIsPrefetch(), ComputeIsIntermediateResultExpected(), ComputePriority()); _forwardingConsumer = new ForwardingConsumer(this); multiplexProducerContext = _multiplexProducerContext; forwardingConsumer = _forwardingConsumer; } _inputProducer.ProduceResults( forwardingConsumer, multiplexProducerContext); }
/// <summary> /// Register callbacks to be called when cancellation of consumer /// is requested, or if the prefetch status of the consumer changes. /// </summary> private void AddCallbacks( Tuple <IConsumer <T>, IProducerContext> consumerContextPair, IProducerContext producerContext) { producerContext.AddCallbacks( new BaseProducerContextCallbacks( () => { BaseProducerContext contextToCancel = null; IList <IProducerContextCallbacks> isPrefetchCallbacks = null; IList <IProducerContextCallbacks> priorityCallbacks = null; IList <IProducerContextCallbacks> isIntermediateResultExpectedCallbacks = null; bool pairWasRemoved = false; lock (_gate) { object val = default(object); pairWasRemoved = _consumerContextPairs.TryRemove(consumerContextPair, out val); if (pairWasRemoved) { if (_consumerContextPairs.IsEmpty) { contextToCancel = _multiplexProducerContext; } else { isPrefetchCallbacks = UpdateIsPrefetch(); priorityCallbacks = UpdatePriority(); isIntermediateResultExpectedCallbacks = UpdateIsIntermediateResultExpected(); } } } BaseProducerContext.CallOnIsPrefetchChanged(isPrefetchCallbacks); BaseProducerContext.CallOnPriorityChanged(priorityCallbacks); BaseProducerContext.CallOnIsIntermediateResultExpectedChanged( isIntermediateResultExpectedCallbacks); if (contextToCancel != null) { contextToCancel.Cancel(); } if (pairWasRemoved) { consumerContextPair.Item1.OnCancellation(); } }, () => { BaseProducerContext.CallOnIsPrefetchChanged(UpdateIsPrefetch()); }, () => { BaseProducerContext.CallOnIsIntermediateResultExpectedChanged( UpdateIsIntermediateResultExpected()); }, () => { BaseProducerContext.CallOnPriorityChanged(UpdatePriority()); })); }
/// <summary> /// Tries to add consumer to set of consumers participating in /// multiplexing. If successful and appropriate intermediate /// result is already known, then it will be passed to the /// consumer. /// /// <para />This function will fail and return false if the /// multiplexer is not present in _multiplexers map. /// </summary> /// <returns> /// true if consumer was added successfully. /// </returns> internal bool AddNewConsumer( IConsumer <T> consumer, IProducerContext producerContext) { var consumerContextPair = new Tuple <IConsumer <T>, IProducerContext>(consumer, producerContext); T lastIntermediateResult; IList <IProducerContextCallbacks> prefetchCallbacks; IList <IProducerContextCallbacks> priorityCallbacks; IList <IProducerContextCallbacks> intermediateResultsCallbacks; float lastProgress; // Check if Multiplexer is still in _multiplexers map, and if so // add new consumer. Also store current intermediate result - we // will notify consumer after acquiring appropriate lock. lock (_gate) { if (_parent.GetExistingMultiplexer(_key) != this) { return(false); } _consumerContextPairs.TryAdd(consumerContextPair, new object()); prefetchCallbacks = UpdateIsPrefetch(); priorityCallbacks = UpdatePriority(); intermediateResultsCallbacks = UpdateIsIntermediateResultExpected(); lastIntermediateResult = _lastIntermediateResult; lastProgress = _lastProgress; } BaseProducerContext.CallOnIsPrefetchChanged(prefetchCallbacks); BaseProducerContext.CallOnPriorityChanged(priorityCallbacks); BaseProducerContext.CallOnIsIntermediateResultExpectedChanged(intermediateResultsCallbacks); lock (consumerContextPair) { // Check if last result changed in the mean time. // In such case we should not propagate it lock (_gate) { if (!Equals(lastIntermediateResult, _lastIntermediateResult)) { lastIntermediateResult = default(T); } else if (lastIntermediateResult != null) { lastIntermediateResult = _parent.CloneOrNull(lastIntermediateResult); } } if (lastIntermediateResult != null) { if (lastProgress > 0) { consumer.OnProgressUpdate(lastProgress); } consumer.OnNewResult(lastIntermediateResult, false); CloseSafely(lastIntermediateResult); } } AddCallbacks(consumerContextPair, producerContext); return(true); }