/// <summary> /// Terminate the stream with failure. After that you are not allowed to /// call <see cref="OnNext"/>, <see cref="OnError"/> and <see cref="OnComplete"/>. /// </summary> /// <param name="cause">TBD</param> /// <exception cref="IllegalStateException"> /// This exception is thrown for a number of reasons. These include: /// <dl> /// <dt>when in the <see cref="LifecycleState.ErrorEmitted"/> state</dt> /// <dd>This exception is thrown when this <see cref="ActorPublisher{T}"/> has already terminated due to an error.</dd> /// <dt>when in the <see cref="LifecycleState.Completed"/> or <see cref="LifecycleState.CompleteThenStop"/> state</dt> /// <dd>This exception is thrown when this <see cref="ActorPublisher{T}"/> has already completed.</dd> /// </dl> /// </exception> public void OnError(Exception cause) { switch (_lifecycleState) { case LifecycleState.Active: case LifecycleState.PreSubscriber: _lifecycleState = LifecycleState.ErrorEmitted; _onError = new OnErrorBlock(cause, false); if (_subscriber != null) { // otherwise onError will be called when the subscription arrives try { ReactiveStreamsCompliance.TryOnError(_subscriber, cause); } finally { _subscriber = null; } } break; case LifecycleState.ErrorEmitted: throw new IllegalStateException("OnError must only be called once"); case LifecycleState.Completed: case LifecycleState.CompleteThenStop: throw new IllegalStateException("OnError must not be called after OnComplete"); case LifecycleState.Canceled: break; } }
/// <summary> /// <para> /// Terminate the stream with failure. After that you are not allowed to /// call <see cref="OnNext"/>, <see cref="OnError"/> and <see cref="OnComplete"/>. /// </para> /// <para> /// After signalling the Error the Actor will then stop itself as it has completed the protocol. /// When <see cref="OnError"/> is called before any <see cref="ISubscriber{T}"/> has had the chance to subscribe /// to this <see cref="ActorPublisher{T}"/> the error signal (and therefore stopping of the Actor as well) /// will be delayed until such <see cref="ISubscriber{T}"/> arrives. /// </para> /// </summary> /// <param name="cause">TBD</param> public void OnErrorThenStop(Exception cause) { switch (_lifecycleState) { case LifecycleState.Active: case LifecycleState.PreSubscriber: _lifecycleState = LifecycleState.ErrorEmitted; _onError = new OnErrorBlock(cause, stop: true); if (_subscriber != null) { // otherwise onError will be called when the subscription arrives try { ReactiveStreamsCompliance.TryOnError(_subscriber, cause); } finally { Context.Stop(Self); } } break; default: OnError(cause); break; } }
public void Fail(Exception reason) { // No need to fail if had already been cancelled, or we closed earlier if (!(_downstreamCompleted || _upstreamCompleted)) { _upstreamCompleted = true; _upstreamFailed = reason; if (!ReferenceEquals(_exposedPublisher, null)) { _exposedPublisher.Shutdown(reason); } if (!ReferenceEquals(_subscriber, null) && !(reason is ISpecViolation)) { ReactiveStreamsCompliance.TryOnError(_subscriber, reason); } } }
/// <summary> /// TBD /// </summary> /// <param name="receive">TBD</param> /// <param name="message">TBD</param> /// <returns>TBD</returns> protected internal override bool AroundReceive(Receive receive, object message) { if (message is Request) { var req = (Request)message; if (req.IsProcessed) { // it's an unstashed Request, demand is already handled base.AroundReceive(receive, req); } else { if (req.Count < 1) { if (_lifecycleState == LifecycleState.Active) { OnError(new ArgumentException("Number of requested elements must be positive. Rule 3.9")); } } else { _demand += req.Count; if (_demand < 0) { _demand = long.MaxValue; // long overflow: effectively unbounded } req.MarkProcessed(); base.AroundReceive(receive, message); } } } else if (message is Subscribe <T> ) { var sub = (Subscribe <T>)message; var subscriber = sub.Subscriber; switch (_lifecycleState) { case LifecycleState.PreSubscriber: _scheduledSubscriptionTimeout.Cancel(); _subscriber = subscriber; _lifecycleState = LifecycleState.Active; ReactiveStreamsCompliance.TryOnSubscribe(subscriber, new ActorPublisherSubscription(Self)); break; case LifecycleState.ErrorEmitted: if (_onError.Stop) { Context.Stop(Self); } ReactiveStreamsCompliance.TryOnSubscribe(subscriber, CancelledSubscription.Instance); ReactiveStreamsCompliance.TryOnError(subscriber, _onError.Cause); break; case LifecycleState.Completed: ReactiveStreamsCompliance.TryOnSubscribe(subscriber, CancelledSubscription.Instance); ReactiveStreamsCompliance.TryOnComplete(subscriber); break; case LifecycleState.CompleteThenStop: Context.Stop(Self); ReactiveStreamsCompliance.TryOnSubscribe(subscriber, CancelledSubscription.Instance); ReactiveStreamsCompliance.TryOnComplete(subscriber); break; case LifecycleState.Active: case LifecycleState.Canceled: if (_subscriber == subscriber) { ReactiveStreamsCompliance.RejectDuplicateSubscriber(subscriber); } else { ReactiveStreamsCompliance.RejectAdditionalSubscriber(subscriber, "ActorPublisher"); } break; } } else if (message is Cancel) { if (_lifecycleState != LifecycleState.Canceled) { // possible to receive again in case of stash CancelSelf(); base.AroundReceive(receive, message); } } else if (message is SubscriptionTimeoutExceeded) { if (!_scheduledSubscriptionTimeout.IsCancellationRequested) { CancelSelf(); base.AroundReceive(receive, message); } } else { return(base.AroundReceive(receive, message)); } return(true); }
protected override bool AroundReceive(Receive receive, object message) { if (message is Request) { var req = (Request)message; if (req.Count < 1) { if (_lifecycleState == LifecycleState.Active) { OnError(new ArgumentException("Number of requested elements must be positive. Rule 3.9")); } else { base.AroundReceive(receive, message); } } else { _demand += req.Count; if (_demand < 0) { _demand = long.MaxValue; // long overflow: effectively unbounded } base.AroundReceive(receive, message); } } else if (message is Subscribe <T> ) { var sub = (Subscribe <T>)message; var subscriber = sub.Subscriber; switch (_lifecycleState) { case LifecycleState.PreSubscriber: _scheduledSubscriptionTimeout.Cancel(); _subscriber = subscriber; _lifecycleState = LifecycleState.Active; ReactiveStreamsCompliance.TryOnSubscribe(subscriber, new ActorPublisherSubscription(Self)); break; case LifecycleState.ErrorEmitted: if (_onError.Stop) { Context.Stop(Self); } ReactiveStreamsCompliance.TryOnSubscribe(subscriber, CancelledSubscription.Instance); ReactiveStreamsCompliance.TryOnError(subscriber, _onError.Cause); break; case LifecycleState.Completed: ReactiveStreamsCompliance.TryOnSubscribe(subscriber, CancelledSubscription.Instance); ReactiveStreamsCompliance.TryOnComplete(subscriber); break; case LifecycleState.CompleteThenStop: Context.Stop(Self); ReactiveStreamsCompliance.TryOnSubscribe(subscriber, CancelledSubscription.Instance); ReactiveStreamsCompliance.TryOnComplete(subscriber); break; case LifecycleState.Active: case LifecycleState.Canceled: ReactiveStreamsCompliance.TryOnSubscribe(subscriber, CancelledSubscription.Instance); ReactiveStreamsCompliance.TryOnError(subscriber, _subscriber == subscriber ? new IllegalStateException("Cannot subscribe the same subscriber multiple times") : new IllegalStateException("Only supports one subscriber")); break; } } else if (message is Cancel) { CancelSelf(); base.AroundReceive(receive, message); } else if (message is SubscriptionTimeoutExceeded) { if (!_scheduledSubscriptionTimeout.IsCancellationRequested) { CancelSelf(); base.AroundReceive(receive, message); } } else { return(base.AroundReceive(receive, message)); } return(true); }