Kit.Core/LibExternal/System.Reactive/Linq/Observable/Amb.cs

137 lines
4.4 KiB
C#

// 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<TSource> : Producer<TSource, Amb<TSource>.AmbCoordinator>
{
private readonly IObservable<TSource> _left;
private readonly IObservable<TSource> _right;
public Amb(IObservable<TSource> left, IObservable<TSource> right)
{
_left = left;
_right = right;
}
protected override AmbCoordinator CreateSink(IObserver<TSource> 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<TSource> observer)
{
_leftObserver = new AmbObserver(observer, this, true);
_rightObserver = new AmbObserver(observer, this, false);
}
public void Run(IObservable<TSource> left, IObservable<TSource> right)
{
_leftObserver.Run(left);
_rightObserver.Run(right);
}
public void Dispose()
{
_leftObserver.Dispose();
_rightObserver.Dispose();
}
/// <summary>
/// Try winning the race for the right of emission.
/// </summary>
/// <param name="isLeft">If true, the contender is the left source.</param>
/// <returns>True if the contender has won the race.</returns>
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<TSource>
{
private readonly AmbCoordinator _parent;
private readonly bool _isLeft;
/// <summary>
/// If true, this observer won the race and now can emit
/// on a fast path.
/// </summary>
private bool _iwon;
public AmbObserver(IObserver<TSource> 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);
}
}
}
}
}
}