// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT License. // See the LICENSE file in the project root for more information. using System.Threading; namespace System.Reactive.Linq.ObservableImpl { internal sealed class Amb : Producer.AmbCoordinator> { private readonly IObservable _left; private readonly IObservable _right; public Amb(IObservable left, IObservable right) { _left = left; _right = right; } protected override AmbCoordinator CreateSink(IObserver observer) => new(observer); protected override void Run(AmbCoordinator sink) => sink.Run(_left, _right); internal sealed class AmbCoordinator : IDisposable { private readonly AmbObserver _leftObserver; private readonly AmbObserver _rightObserver; private int _winner; public AmbCoordinator(IObserver observer) { _leftObserver = new AmbObserver(observer, this, true); _rightObserver = new AmbObserver(observer, this, false); } public void Run(IObservable left, IObservable right) { _leftObserver.Run(left); _rightObserver.Run(right); } public void Dispose() { _leftObserver.Dispose(); _rightObserver.Dispose(); } /// /// Try winning the race for the right of emission. /// /// If true, the contender is the left source. /// True if the contender has won the race. public bool TryWin(bool isLeft) { var index = isLeft ? 1 : 2; if (Volatile.Read(ref _winner) == index) { return true; } if (Interlocked.CompareExchange(ref _winner, index, 0) == 0) { (isLeft ? _rightObserver : _leftObserver).Dispose(); return true; } return false; } private sealed class AmbObserver : IdentitySink { private readonly AmbCoordinator _parent; private readonly bool _isLeft; /// /// If true, this observer won the race and now can emit /// on a fast path. /// private bool _iwon; public AmbObserver(IObserver downstream, AmbCoordinator parent, bool isLeft) : base(downstream) { _parent = parent; _isLeft = isLeft; } public override void OnCompleted() { if (_iwon) { ForwardOnCompleted(); } else if (_parent.TryWin(_isLeft)) { _iwon = true; ForwardOnCompleted(); } else { Dispose(); } } public override void OnError(Exception error) { if (_iwon) { ForwardOnError(error); } else if (_parent.TryWin(_isLeft)) { _iwon = true; ForwardOnError(error); } else { Dispose(); } } public override void OnNext(TSource value) { if (_iwon) { ForwardOnNext(value); } else if (_parent.TryWin(_isLeft)) { _iwon = true; ForwardOnNext(value); } } } } } }