diff options
Diffstat (limited to 'Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable')
98 files changed, 21568 insertions, 0 deletions
diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/AddRef.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/AddRef.cs new file mode 100644 index 0000000..3fc6962 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/AddRef.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class AddRef<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly RefCountDisposable _refCount; + + public AddRef(IObservable<TSource> source, RefCountDisposable refCount) + { + _source = source; + _refCount = refCount; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var d = new CompositeDisposable(_refCount.GetDisposable(), cancel); + + var sink = new _(observer, d); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + public _(IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + public void OnNext(TSource value) + { + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Aggregate.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Aggregate.cs new file mode 100644 index 0000000..d1b6ef8 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Aggregate.cs @@ -0,0 +1,158 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class Aggregate<TSource, TAccumulate, TResult> : Producer<TResult> + { + private readonly IObservable<TSource> _source; + private readonly TAccumulate _seed; + private readonly Func<TAccumulate, TSource, TAccumulate> _accumulator; + private readonly Func<TAccumulate, TResult> _resultSelector; + + public Aggregate(IObservable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator, Func<TAccumulate, TResult> resultSelector) + { + _source = source; + _seed = seed; + _accumulator = accumulator; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TResult>, IObserver<TSource> + { + private readonly Aggregate<TSource, TAccumulate, TResult> _parent; + private TAccumulate _accumulation; + + public _(Aggregate<TSource, TAccumulate, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _accumulation = _parent._seed; + } + + public void OnNext(TSource value) + { + try + { + _accumulation = _parent._accumulator(_accumulation, value); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + var result = default(TResult); + try + { + result = _parent._resultSelector(_accumulation); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + base._observer.OnNext(result); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class Aggregate<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, TSource, TSource> _accumulator; + + public Aggregate(IObservable<TSource> source, Func<TSource, TSource, TSource> accumulator) + { + _source = source; + _accumulator = accumulator; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Aggregate<TSource> _parent; + private TSource _accumulation; + private bool _hasAccumulation; + + public _(Aggregate<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _accumulation = default(TSource); + _hasAccumulation = false; + } + + public void OnNext(TSource value) + { + if (!_hasAccumulation) + { + _accumulation = value; + _hasAccumulation = true; + } + else + { + try + { + _accumulation = _parent._accumulator(_accumulation, value); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + } + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_hasAccumulation) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + base.Dispose(); + } + else + { + base._observer.OnNext(_accumulation); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/All.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/All.cs new file mode 100644 index 0000000..09c825d --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/All.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class All<TSource> : Producer<bool> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, bool> _predicate; + + public All(IObservable<TSource> source, Func<TSource, bool> predicate) + { + _source = source; + _predicate = predicate; + } + + protected override IDisposable Run(IObserver<bool> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<bool>, IObserver<TSource> + { + private readonly All<TSource> _parent; + + public _(All<TSource> parent, IObserver<bool> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + var res = false; + try + { + res = _parent._predicate(value); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + if (!res) + { + base._observer.OnNext(false); + base._observer.OnCompleted(); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(true); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Amb.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Amb.cs new file mode 100644 index 0000000..7272cee --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Amb.cs @@ -0,0 +1,173 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Amb<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _left; + private readonly IObservable<TSource> _right; + + public Amb(IObservable<TSource> left, IObservable<TSource> right) + { + _left = left; + _right = right; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource> + { + private readonly Amb<TSource> _parent; + + public _(Amb<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private AmbState _choice; + + public IDisposable Run() + { + var ls = new SingleAssignmentDisposable(); + var rs = new SingleAssignmentDisposable(); + var d = new CompositeDisposable(ls, rs); + + var gate = new object(); + + var lo = new AmbObserver(); + lo._disposable = d; + lo._target = new DecisionObserver(this, gate, AmbState.Left, ls, rs, lo); + + var ro = new AmbObserver(); + ro._disposable = d; + ro._target = new DecisionObserver(this, gate, AmbState.Right, rs, ls, ro); + + _choice = AmbState.Neither; + + ls.Disposable = _parent._left.SubscribeSafe(lo); + rs.Disposable = _parent._right.SubscribeSafe(ro); + + return d; + } + + class DecisionObserver : IObserver<TSource> + { + private readonly _ _parent; + private readonly AmbState _me; + private readonly IDisposable _subscription; + private readonly IDisposable _otherSubscription; + private readonly object _gate; + private readonly AmbObserver _observer; + + public DecisionObserver(_ parent, object gate, AmbState me, IDisposable subscription, IDisposable otherSubscription, AmbObserver observer) + { + _parent = parent; + _gate = gate; + _me = me; + _subscription = subscription; + _otherSubscription = otherSubscription; + _observer = observer; + } + + public void OnNext(TSource value) + { + lock (_gate) + { + if (_parent._choice == AmbState.Neither) + { + _parent._choice = _me; + _otherSubscription.Dispose(); + _observer._disposable = _subscription; + _observer._target = _parent._observer; + } + + if (_parent._choice == _me) + _parent._observer.OnNext(value); + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + if (_parent._choice == AmbState.Neither) + { + _parent._choice = _me; + _otherSubscription.Dispose(); + _observer._disposable = _subscription; + _observer._target = _parent._observer; + } + + if (_parent._choice == _me) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + } + + public void OnCompleted() + { + lock (_gate) + { + if (_parent._choice == AmbState.Neither) + { + _parent._choice = _me; + _otherSubscription.Dispose(); + _observer._disposable = _subscription; + _observer._target = _parent._observer; + } + + if (_parent._choice == _me) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + } + } + + class AmbObserver : IObserver<TSource> + { + public IObserver<TSource> _target; + + public IDisposable _disposable; + + public void OnNext(TSource value) + { + _target.OnNext(value); + } + + public void OnError(Exception error) + { + _target.OnError(error); + _disposable.Dispose(); + } + + public void OnCompleted() + { + _target.OnCompleted(); + _disposable.Dispose(); + } + } + + enum AmbState + { + Left, + Right, + Neither + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Any.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Any.cs new file mode 100644 index 0000000..1414bdd --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Any.cs @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class Any<TSource> : Producer<bool> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, bool> _predicate; + + public Any(IObservable<TSource> source) + { + _source = source; + } + + public Any(IObservable<TSource> source, Func<TSource, bool> predicate) + { + _source = source; + _predicate = predicate; + } + + protected override IDisposable Run(IObserver<bool> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_predicate != null) + { + var sink = new π(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + } + + class _ : Sink<bool>, IObserver<TSource> + { + public _(IObserver<bool> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + public void OnNext(TSource value) + { + base._observer.OnNext(true); + base._observer.OnCompleted(); + base.Dispose(); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(false); + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class π : Sink<bool>, IObserver<TSource> + { + private readonly Any<TSource> _parent; + + public π(Any<TSource> parent, IObserver<bool> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + var res = false; + try + { + res = _parent._predicate(value); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + if (res) + { + base._observer.OnNext(true); + base._observer.OnCompleted(); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(false); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/AsObservable.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/AsObservable.cs new file mode 100644 index 0000000..09d5f14 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/AsObservable.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class AsObservable<TSource> : Producer<TSource>, IEvaluatableObservable<TSource> + { + private readonly IObservable<TSource> _source; + + public AsObservable(IObservable<TSource> source) + { + _source = source; + } + + public IObservable<TSource> Ω() + { + return this; + } + + public IObservable<TSource> Eval() + { + return _source; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + public _(IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + public void OnNext(TSource value) + { + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Average.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Average.cs new file mode 100644 index 0000000..fb2d6b7 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Average.cs @@ -0,0 +1,703 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class AverageDouble : Producer<double> + { + private readonly IObservable<double> _source; + + public AverageDouble(IObservable<double> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<double> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<double>, IObserver<double> + { + private double _sum; + private long _count; + + public _(IObserver<double> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0.0; + _count = 0L; + } + + public void OnNext(double value) + { + try + { + checked + { + _sum += value; + _count++; + } + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (_count > 0) + { + base._observer.OnNext(_sum / _count); + base._observer.OnCompleted(); + } + else + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + + base.Dispose(); + } + } + } + + class AverageSingle : Producer<float> + { + private readonly IObservable<float> _source; + + public AverageSingle(IObservable<float> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<float> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<float>, IObserver<float> + { + private double _sum; // NOTE: Uses a different accumulator type (double), conform LINQ to Objects. + private long _count; + + public _(IObserver<float> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0.0; + _count = 0L; + } + + public void OnNext(float value) + { + try + { + checked + { + _sum += value; + _count++; + } + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (_count > 0) + { + base._observer.OnNext((float)(_sum / _count)); + base._observer.OnCompleted(); + } + else + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + + base.Dispose(); + } + } + } + + class AverageDecimal : Producer<decimal> + { + private readonly IObservable<decimal> _source; + + public AverageDecimal(IObservable<decimal> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<decimal> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<decimal>, IObserver<decimal> + { + private decimal _sum; + private long _count; + + public _(IObserver<decimal> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0M; + _count = 0L; + } + + public void OnNext(decimal value) + { + try + { + checked + { + _sum += value; + _count++; + } + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (_count > 0) + { + base._observer.OnNext(_sum / _count); + base._observer.OnCompleted(); + } + else + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + + base.Dispose(); + } + } + } + + class AverageInt32 : Producer<double> + { + private readonly IObservable<int> _source; + + public AverageInt32(IObservable<int> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<double> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<double>, IObserver<int> + { + private long _sum; + private long _count; + + public _(IObserver<double> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0L; + _count = 0L; + } + + public void OnNext(int value) + { + try + { + checked + { + _sum += value; + _count++; + } + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (_count > 0) + { + base._observer.OnNext((double)_sum / _count); + base._observer.OnCompleted(); + } + else + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + + base.Dispose(); + } + } + } + + class AverageInt64 : Producer<double> + { + private readonly IObservable<long> _source; + + public AverageInt64(IObservable<long> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<double> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<double>, IObserver<long> + { + private long _sum; + private long _count; + + public _(IObserver<double> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0L; + _count = 0L; + } + + public void OnNext(long value) + { + try + { + checked + { + _sum += value; + _count++; + } + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (_count > 0) + { + base._observer.OnNext((double)_sum / _count); + base._observer.OnCompleted(); + } + else + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + + base.Dispose(); + } + } + } + + class AverageDoubleNullable : Producer<double?> + { + private readonly IObservable<double?> _source; + + public AverageDoubleNullable(IObservable<double?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<double?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<double?>, IObserver<double?> + { + private double _sum; + private long _count; + + public _(IObserver<double?> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0.0; + _count = 0L; + } + + public void OnNext(double? value) + { + try + { + checked + { + if (value != null) + { + _sum += value.Value; + _count++; + } + } + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (_count > 0) + { + base._observer.OnNext(_sum / _count); + } + else + { + base._observer.OnNext(null); + } + + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class AverageSingleNullable : Producer<float?> + { + private readonly IObservable<float?> _source; + + public AverageSingleNullable(IObservable<float?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<float?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<float?>, IObserver<float?> + { + private double _sum; // NOTE: Uses a different accumulator type (double), conform LINQ to Objects. + private long _count; + + public _(IObserver<float?> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0.0; + _count = 0L; + } + + public void OnNext(float? value) + { + try + { + checked + { + if (value != null) + { + _sum += value.Value; + _count++; + } + } + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (_count > 0) + { + base._observer.OnNext((float)(_sum / _count)); + } + else + { + base._observer.OnNext(null); + } + + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class AverageDecimalNullable : Producer<decimal?> + { + private readonly IObservable<decimal?> _source; + + public AverageDecimalNullable(IObservable<decimal?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<decimal?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<decimal?>, IObserver<decimal?> + { + private decimal _sum; + private long _count; + + public _(IObserver<decimal?> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0M; + _count = 0L; + } + + public void OnNext(decimal? value) + { + try + { + checked + { + if (value != null) + { + _sum += value.Value; + _count++; + } + } + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (_count > 0) + { + base._observer.OnNext(_sum / _count); + } + else + { + base._observer.OnNext(null); + } + + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class AverageInt32Nullable : Producer<double?> + { + private readonly IObservable<int?> _source; + + public AverageInt32Nullable(IObservable<int?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<double?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<double?>, IObserver<int?> + { + private long _sum; + private long _count; + + public _(IObserver<double?> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0L; + _count = 0L; + } + + public void OnNext(int? value) + { + try + { + checked + { + if (value != null) + { + _sum += value.Value; + _count++; + } + } + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (_count > 0) + { + base._observer.OnNext((double)_sum / _count); + } + else + { + base._observer.OnNext(null); + } + + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class AverageInt64Nullable : Producer<double?> + { + private readonly IObservable<long?> _source; + + public AverageInt64Nullable(IObservable<long?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<double?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<double?>, IObserver<long?> + { + private long _sum; + private long _count; + + public _(IObserver<double?> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0L; + _count = 0L; + } + + public void OnNext(long? value) + { + try + { + checked + { + if (value != null) + { + _sum += value.Value; + _count++; + } + } + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (_count > 0) + { + base._observer.OnNext((double)_sum / _count); + } + else + { + base._observer.OnNext(null); + } + + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Buffer.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Buffer.cs new file mode 100644 index 0000000..a82b550 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Buffer.cs @@ -0,0 +1,709 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Threading; + +namespace System.Reactive.Linq.Observαble +{ + class Buffer<TSource> : Producer<IList<TSource>> + { + private readonly IObservable<TSource> _source; + private readonly int _count; + private readonly int _skip; + + private readonly TimeSpan _timeSpan; + private readonly TimeSpan _timeShift; + private readonly IScheduler _scheduler; + + public Buffer(IObservable<TSource> source, int count, int skip) + { + _source = source; + _count = count; + _skip = skip; + } + + public Buffer(IObservable<TSource> source, TimeSpan timeSpan, TimeSpan timeShift, IScheduler scheduler) + { + _source = source; + _timeSpan = timeSpan; + _timeShift = timeShift; + _scheduler = scheduler; + } + + public Buffer(IObservable<TSource> source, TimeSpan timeSpan, int count, IScheduler scheduler) + { + _source = source; + _timeSpan = timeSpan; + _count = count; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<IList<TSource>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_scheduler == null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else if (_count > 0) + { + var sink = new μ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else + { + if (_timeSpan == _timeShift) + { + var sink = new π(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else + { + var sink = new τ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + } + + class _ : Sink<IList<TSource>>, IObserver<TSource> + { + private readonly Buffer<TSource> _parent; + + public _(Buffer<TSource> parent, IObserver<IList<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private Queue<IList<TSource>> _queue; + private int _n; + + public IDisposable Run() + { + _queue = new Queue<IList<TSource>>(); + _n = 0; + + CreateWindow(); + return _parent._source.SubscribeSafe(this); + } + + private void CreateWindow() + { + var s = new List<TSource>(); + _queue.Enqueue(s); + } + + public void OnNext(TSource value) + { + foreach (var s in _queue) + s.Add(value); + + var c = _n - _parent._count + 1; + if (c >= 0 && c % _parent._skip == 0) + { + var s = _queue.Dequeue(); + if (s.Count > 0) + base._observer.OnNext(s); + } + + _n++; + if (_n % _parent._skip == 0) + CreateWindow(); + } + + public void OnError(Exception error) + { + while (_queue.Count > 0) + _queue.Dequeue().Clear(); + + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + while (_queue.Count > 0) + { + var s = _queue.Dequeue(); + if (s.Count > 0) + base._observer.OnNext(s); + } + + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class τ : Sink<IList<TSource>>, IObserver<TSource> + { + private readonly Buffer<TSource> _parent; + + public τ(Buffer<TSource> parent, IObserver<IList<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private TimeSpan _totalTime; + private TimeSpan _nextShift; + private TimeSpan _nextSpan; + + private object _gate; + private Queue<List<TSource>> _q; + + private SerialDisposable _timerD; + + public IDisposable Run() + { + _totalTime = TimeSpan.Zero; + _nextShift = _parent._timeShift; + _nextSpan = _parent._timeSpan; + + _gate = new object(); + _q = new Queue<List<TSource>>(); + + _timerD = new SerialDisposable(); + + CreateWindow(); + CreateTimer(); + + var subscription = _parent._source.SubscribeSafe(this); + + return new CompositeDisposable { _timerD, subscription }; + } + + private void CreateWindow() + { + var s = new List<TSource>(); + _q.Enqueue(s); + } + + private void CreateTimer() + { + var m = new SingleAssignmentDisposable(); + _timerD.Disposable = m; + + var isSpan = false; + var isShift = false; + if (_nextSpan == _nextShift) + { + isSpan = true; + isShift = true; + } + else if (_nextSpan < _nextShift) + isSpan = true; + else + isShift = true; + + var newTotalTime = isSpan ? _nextSpan : _nextShift; + var ts = newTotalTime - _totalTime; + _totalTime = newTotalTime; + + if (isSpan) + _nextSpan += _parent._timeShift; + if (isShift) + _nextShift += _parent._timeShift; + + m.Disposable = _parent._scheduler.Schedule(new State { isSpan = isSpan, isShift = isShift }, ts, Tick); + } + + struct State + { + public bool isSpan; + public bool isShift; + } + + private IDisposable Tick(IScheduler self, State state) + { + lock (_gate) + { + // + // Before v2, the two operations below were reversed. This doesn't have an observable + // difference for Buffer, but is done to keep code consistent with Window, where we + // took a breaking change in v2 to ensure consistency across overloads. For more info, + // see the comment in Tick for Window. + // + if (state.isSpan) + { + var s = _q.Dequeue(); + base._observer.OnNext(s); + } + + if (state.isShift) + { + CreateWindow(); + } + } + + CreateTimer(); + + return Disposable.Empty; + } + + public void OnNext(TSource value) + { + lock (_gate) + { + foreach (var s in _q) + s.Add(value); + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + while (_q.Count > 0) + _q.Dequeue().Clear(); + + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + while (_q.Count > 0) + base._observer.OnNext(_q.Dequeue()); + + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class π : Sink<IList<TSource>>, IObserver<TSource> + { + private readonly Buffer<TSource> _parent; + + public π(Buffer<TSource> parent, IObserver<IList<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private List<TSource> _list; + + public IDisposable Run() + { + _gate = new object(); + _list = new List<TSource>(); + + var d = _parent._scheduler.SchedulePeriodic(_parent._timeSpan, Tick); + var s = _parent._source.SubscribeSafe(this); + + return new CompositeDisposable(d, s); + } + + private void Tick() + { + lock (_gate) + { + base._observer.OnNext(_list); + _list = new List<TSource>(); + } + } + + public void OnNext(TSource value) + { + lock (_gate) + { + _list.Add(value); + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + _list.Clear(); + + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + base._observer.OnNext(_list); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class μ : Sink<IList<TSource>>, IObserver<TSource> + { + private readonly Buffer<TSource> _parent; + + public μ(Buffer<TSource> parent, IObserver<IList<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private IList<TSource> _s; + private int _n; + private int _windowId; + + private SerialDisposable _timerD; + + public IDisposable Run() + { + _gate = new object(); + _s = default(IList<TSource>); + _n = 0; + _windowId = 0; + + _timerD = new SerialDisposable(); + + _s = new List<TSource>(); + CreateTimer(0); + + var subscription = _parent._source.SubscribeSafe(this); + + return new CompositeDisposable { _timerD, subscription }; + } + + private void CreateTimer(int id) + { + var m = new SingleAssignmentDisposable(); + _timerD.Disposable = m; + + m.Disposable = _parent._scheduler.Schedule(id, _parent._timeSpan, Tick); + } + + private IDisposable Tick(IScheduler self, int id) + { + var d = Disposable.Empty; + + var newId = 0; + lock (_gate) + { + if (id != _windowId) + return d; + + _n = 0; + newId = ++_windowId; + + var res = _s; + _s = new List<TSource>(); + base._observer.OnNext(res); + } + + CreateTimer(newId); + + return d; + } + + public void OnNext(TSource value) + { + var newWindow = false; + var newId = 0; + + lock (_gate) + { + _s.Add(value); + + _n++; + if (_n == _parent._count) + { + newWindow = true; + _n = 0; + newId = ++_windowId; + + var res = _s; + _s = new List<TSource>(); + base._observer.OnNext(res); + } + } + + if (newWindow) + CreateTimer(newId); + } + + public void OnError(Exception error) + { + lock (_gate) + { + _s.Clear(); + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + base._observer.OnNext(_s); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + } + + class Buffer<TSource, TBufferClosing> : Producer<IList<TSource>> + { + private readonly IObservable<TSource> _source; + private readonly Func<IObservable<TBufferClosing>> _bufferClosingSelector; + private readonly IObservable<TBufferClosing> _bufferBoundaries; + + public Buffer(IObservable<TSource> source, Func<IObservable<TBufferClosing>> bufferClosingSelector) + { + _source = source; + _bufferClosingSelector = bufferClosingSelector; + } + + public Buffer(IObservable<TSource> source, IObservable<TBufferClosing> bufferBoundaries) + { + _source = source; + _bufferBoundaries = bufferBoundaries; + } + + protected override IDisposable Run(IObserver<IList<TSource>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_bufferClosingSelector != null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else + { + var sink = new β(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + + class _ : Sink<IList<TSource>>, IObserver<TSource> + { + private readonly Buffer<TSource, TBufferClosing> _parent; + + public _(Buffer<TSource, TBufferClosing> parent, IObserver<IList<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private IList<TSource> _buffer; + private object _gate; + private AsyncLock _bufferGate; + + private SerialDisposable _m; + + public IDisposable Run() + { + _buffer = new List<TSource>(); + _gate = new object(); + _bufferGate = new AsyncLock(); + + _m = new SerialDisposable(); + var groupDisposable = new CompositeDisposable(2) { _m }; + + groupDisposable.Add(_parent._source.SubscribeSafe(this)); + + _bufferGate.Wait(CreateBufferClose); + + return groupDisposable; + } + + private void CreateBufferClose() + { + var bufferClose = default(IObservable<TBufferClosing>); + try + { + bufferClose = _parent._bufferClosingSelector(); + } + catch (Exception exception) + { + lock (_gate) + { + base._observer.OnError(exception); + base.Dispose(); + } + return; + } + + var closingSubscription = new SingleAssignmentDisposable(); + _m.Disposable = closingSubscription; + closingSubscription.Disposable = bufferClose.SubscribeSafe(new ω(this, closingSubscription)); + } + + private void CloseBuffer(IDisposable closingSubscription) + { + closingSubscription.Dispose(); + + lock (_gate) + { + var res = _buffer; + _buffer = new List<TSource>(); + base._observer.OnNext(res); + } + + _bufferGate.Wait(CreateBufferClose); + } + + class ω : IObserver<TBufferClosing> + { + private readonly _ _parent; + private readonly IDisposable _self; + + public ω(_ parent, IDisposable self) + { + _parent = parent; + _self = self; + } + + public void OnNext(TBufferClosing value) + { + _parent.CloseBuffer(_self); + } + + public void OnError(Exception error) + { + _parent.OnError(error); + } + + public void OnCompleted() + { + _parent.CloseBuffer(_self); + } + } + + public void OnNext(TSource value) + { + lock (_gate) + { + _buffer.Add(value); + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + _buffer.Clear(); + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + base._observer.OnNext(_buffer); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class β : Sink<IList<TSource>>, IObserver<TSource> + { + private readonly Buffer<TSource, TBufferClosing> _parent; + + public β(Buffer<TSource, TBufferClosing> parent, IObserver<IList<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private IList<TSource> _buffer; + private object _gate; + + private RefCountDisposable _refCountDisposable; + + public IDisposable Run() + { + _buffer = new List<TSource>(); + _gate = new object(); + + var d = new CompositeDisposable(2); + _refCountDisposable = new RefCountDisposable(d); + + d.Add(_parent._source.SubscribeSafe(this)); + d.Add(_parent._bufferBoundaries.SubscribeSafe(new ω(this))); + + return _refCountDisposable; + } + + class ω : IObserver<TBufferClosing> + { + private readonly β _parent; + + public ω(β parent) + { + _parent = parent; + } + + public void OnNext(TBufferClosing value) + { + lock (_parent._gate) + { + var res = _parent._buffer; + _parent._buffer = new List<TSource>(); + _parent._observer.OnNext(res); + } + } + + public void OnError(Exception error) + { + _parent.OnError(error); + } + + public void OnCompleted() + { + _parent.OnCompleted(); + } + } + + public void OnNext(TSource value) + { + lock (_gate) + { + _buffer.Add(value); + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + _buffer.Clear(); + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + base._observer.OnNext(_buffer); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Case.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Case.cs new file mode 100644 index 0000000..b104050 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Case.cs @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Case<TValue, TResult> : Producer<TResult>, IEvaluatableObservable<TResult> + { + private readonly Func<TValue> _selector; + private readonly IDictionary<TValue, IObservable<TResult>> _sources; + private readonly IObservable<TResult> _defaultSource; + + public Case(Func<TValue> selector, IDictionary<TValue, IObservable<TResult>> sources, IObservable<TResult> defaultSource) + { + _selector = selector; + _sources = sources; + _defaultSource = defaultSource; + } + + public IObservable<TResult> Eval() + { + var res = default(IObservable<TResult>); + if (_sources.TryGetValue(_selector(), out res)) + return res; + + return _defaultSource; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TResult>, IObserver<TResult> + { + private readonly Case<TValue, TResult> _parent; + + public _(Case<TValue, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + var result = default(IObservable<TResult>); + try + { + result = _parent.Eval(); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return Disposable.Empty; + } + + return result.SubscribeSafe(this); + } + + public void OnNext(TResult value) + { + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Cast.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Cast.cs new file mode 100644 index 0000000..48820b3 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Cast.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class Cast<TSource, TResult> : Producer<TResult> /* Could optimize further by deriving from Select<TResult> and providing Ω<TResult2>. We're not doing this (yet) for debuggability. */ + { + private readonly IObservable<TSource> _source; + + public Cast(IObservable<TSource> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TResult>, IObserver<TSource> + { + public _(IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + public void OnNext(TSource value) + { + var result = default(TResult); + try + { + result = (TResult)(object)value; + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + base._observer.OnNext(result); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Catch.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Catch.cs new file mode 100644 index 0000000..71e0037 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Catch.cs @@ -0,0 +1,183 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Catch<TSource> : Producer<TSource> + { + private readonly IEnumerable<IObservable<TSource>> _sources; + + public Catch(IEnumerable<IObservable<TSource>> sources) + { + _sources = sources; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return sink.Run(_sources); + } + + class _ : TailRecursiveSink<TSource> + { + public _(IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + protected override IEnumerable<IObservable<TSource>> Extract(IObservable<TSource> source) + { + var @catch = source as Catch<TSource>; + if (@catch != null) + return @catch._sources; + + return null; + } + + public override void OnNext(TSource value) + { + base._observer.OnNext(value); + } + + private Exception _lastException; + + public override void OnError(Exception error) + { + _lastException = error; + _recurse(); + } + + public override void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + + protected override void Done() + { + if (_lastException != null) + base._observer.OnError(_lastException); + else + base._observer.OnCompleted(); + + base.Dispose(); + } + } + } + + class Catch<TSource, TException> : Producer<TSource> where TException : Exception + { + private readonly IObservable<TSource> _source; + private readonly Func<TException, IObservable<TSource>> _handler; + + public Catch(IObservable<TSource> source, Func<TException, IObservable<TSource>> handler) + { + _source = source; + _handler = handler; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Catch<TSource, TException> _parent; + + public _(Catch<TSource, TException> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private SerialDisposable _subscription; + + public IDisposable Run() + { + _subscription = new SerialDisposable(); + + var d1 = new SingleAssignmentDisposable(); + _subscription.Disposable = d1; + d1.Disposable = _parent._source.SubscribeSafe(this); + + return _subscription; + } + + public void OnNext(TSource value) + { + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + var e = error as TException; + if (e != null) + { + var result = default(IObservable<TSource>); + try + { + result = _parent._handler(e); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + var d = new SingleAssignmentDisposable(); + _subscription.Disposable = d; + d.Disposable = result.SubscribeSafe(new ε(this)); + } + else + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + + class ε : IObserver<TSource> + { + private readonly _ _parent; + + public ε(_ parent) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + _parent._observer.OnNext(value); + } + + public void OnError(Exception error) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + + public void OnCompleted() + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Collect.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Collect.cs new file mode 100644 index 0000000..2cfd240 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Collect.cs @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive; +using System.Reactive.Threading; +using System.Threading; + +namespace System.Reactive.Linq.Observαble +{ + class Collect<TSource, TResult> : PushToPullAdapter<TSource, TResult> + { + private readonly Func<TResult> _getInitialCollector; + private readonly Func<TResult, TSource, TResult> _merge; + private readonly Func<TResult, TResult> _getNewCollector; + + public Collect(IObservable<TSource> source, Func<TResult> getInitialCollector, Func<TResult, TSource, TResult> merge, Func<TResult, TResult> getNewCollector) + : base(source) + { + _getInitialCollector = getInitialCollector; + _merge = merge; + _getNewCollector = getNewCollector; + } + + protected override PushToPullSink<TSource, TResult> Run(IDisposable subscription) + { + var sink = new _(this, subscription); + sink.Initialize(); + return sink; + } + + class _ : PushToPullSink<TSource, TResult> + { + private readonly Collect<TSource, TResult> _parent; + + public _(Collect<TSource, TResult> parent, IDisposable subscription) + : base(subscription) + { + _parent = parent; + } + + private object _gate; + private TResult _collector; + private bool _hasFailed; + private Exception _error; + private bool _hasCompleted; + private bool _done; + + public void Initialize() + { + _gate = new object(); + _collector = _parent._getInitialCollector(); + } + + public override void OnNext(TSource value) + { + lock (_gate) + { + try + { + _collector = _parent._merge(_collector, value); + } + catch (Exception ex) + { + _error = ex; + _hasFailed = true; + + base.Dispose(); + } + } + } + + public override void OnError(Exception error) + { + base.Dispose(); + + lock (_gate) + { + _error = error; + _hasFailed = true; + } + } + + public override void OnCompleted() + { + base.Dispose(); + + lock (_gate) + { + _hasCompleted = true; + } + } + + public override bool TryMoveNext(out TResult current) + { + lock (_gate) + { + if (_hasFailed) + { + current = default(TResult); + _error.Throw(); + } + else + { + if (_hasCompleted) + { + if (_done) + { + current = default(TResult); + return false; + } + + current = _collector; + _done = true; + } + else + { + current = _collector; + + try + { + _collector = _parent._getNewCollector(current); + } + catch + { + base.Dispose(); + throw; + } + } + } + + return true; + } + } + } + } +} +#endif diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/CombineLatest.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/CombineLatest.cs new file mode 100644 index 0000000..2188307 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/CombineLatest.cs @@ -0,0 +1,1863 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + #region Binary + + class CombineLatest<TFirst, TSecond, TResult> : Producer<TResult> + { + private readonly IObservable<TFirst> _first; + private readonly IObservable<TSecond> _second; + private readonly Func<TFirst, TSecond, TResult> _resultSelector; + + public CombineLatest(IObservable<TFirst> first, IObservable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector) + { + _first = first; + _second = second; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TResult> + { + private readonly CombineLatest<TFirst, TSecond, TResult> _parent; + + public _(CombineLatest<TFirst, TSecond, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + + public IDisposable Run() + { + _gate = new object(); + + var fstSubscription = new SingleAssignmentDisposable(); + var sndSubscription = new SingleAssignmentDisposable(); + + var fstO = new F(this, fstSubscription); + var sndO = new S(this, sndSubscription); + + fstO.Other = sndO; + sndO.Other = fstO; + + fstSubscription.Disposable = _parent._first.SubscribeSafe(fstO); + sndSubscription.Disposable = _parent._second.SubscribeSafe(sndO); + + return new CompositeDisposable(fstSubscription, sndSubscription); + } + + class F : IObserver<TFirst> + { + private readonly _ _parent; + private readonly IDisposable _self; + private S _other; + + public F(_ parent, IDisposable self) + { + _parent = parent; + _self = self; + } + + public S Other { set { _other = value; } } + + public bool HasValue { get; private set; } + public TFirst Value { get; private set; } + public bool Done { get; private set; } + + public void OnNext(TFirst value) + { + lock (_parent._gate) + { + HasValue = true; + Value = value; + + if (_other.HasValue) + { + var res = default(TResult); + try + { + res = _parent._parent._resultSelector(value, _other.Value); + } + catch (Exception ex) + { + _parent._observer.OnError(ex); + _parent.Dispose(); + return; + } + + _parent._observer.OnNext(res); + } + else if (_other.Done) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + return; + } + } + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + lock (_parent._gate) + { + Done = true; + + if (_other.Done) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + return; + } + else + { + _self.Dispose(); + } + } + } + } + + class S : IObserver<TSecond> + { + private readonly _ _parent; + private readonly IDisposable _self; + private F _other; + + public S(_ parent, IDisposable self) + { + _parent = parent; + _self = self; + } + + public F Other { set { _other = value; } } + + public bool HasValue { get; private set; } + public TSecond Value { get; private set; } + public bool Done { get; private set; } + + public void OnNext(TSecond value) + { + lock (_parent._gate) + { + HasValue = true; + Value = value; + + if (_other.HasValue) + { + var res = default(TResult); + try + { + res = _parent._parent._resultSelector(_other.Value, value); + } + catch (Exception ex) + { + _parent._observer.OnError(ex); + _parent.Dispose(); + return; + } + + _parent._observer.OnNext(res); + } + else if (_other.Done) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + return; + } + } + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + lock (_parent._gate) + { + Done = true; + + if (_other.Done) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + return; + } + else + { + _self.Dispose(); + } + } + } + } + } + } + + #endregion + + #region [3,16]-ary + + /* The following code is generated by a tool checked in to $/.../Source/Tools/CodeGenerators. */ + + #region CombineLatest auto-generated code (6/10/2012 7:22:14 PM) + + class CombineLatest<T1, T2, T3, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly Func<T1, T2, T3, TResult> _resultSelector; + + public CombineLatest(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, Func<T1, T2, T3, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : CombineLatestSink<TResult> + { + private readonly CombineLatest<T1, T2, T3, TResult> _parent; + + public _(CombineLatest<T1, T2, T3, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(3, observer, cancel) + { + _parent = parent; + } + + private CombineLatestObserver<T1> _observer1; + private CombineLatestObserver<T2> _observer2; + private CombineLatestObserver<T3> _observer3; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[3]; + for (int i = 0; i < 3; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new CombineLatestObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new CombineLatestObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new CombineLatestObserver<T3>(_gate, this, 2, subscriptions[2]); + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + + return new CompositeDisposable(subscriptions); + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Value, _observer2.Value, _observer3.Value); + } + } + } + + class CombineLatest<T1, T2, T3, T4, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly Func<T1, T2, T3, T4, TResult> _resultSelector; + + public CombineLatest(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, Func<T1, T2, T3, T4, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : CombineLatestSink<TResult> + { + private readonly CombineLatest<T1, T2, T3, T4, TResult> _parent; + + public _(CombineLatest<T1, T2, T3, T4, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(4, observer, cancel) + { + _parent = parent; + } + + private CombineLatestObserver<T1> _observer1; + private CombineLatestObserver<T2> _observer2; + private CombineLatestObserver<T3> _observer3; + private CombineLatestObserver<T4> _observer4; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[4]; + for (int i = 0; i < 4; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new CombineLatestObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new CombineLatestObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new CombineLatestObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new CombineLatestObserver<T4>(_gate, this, 3, subscriptions[3]); + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + + return new CompositeDisposable(subscriptions); + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Value, _observer2.Value, _observer3.Value, _observer4.Value); + } + } + } + +#if !NO_LARGEARITY + + class CombineLatest<T1, T2, T3, T4, T5, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly Func<T1, T2, T3, T4, T5, TResult> _resultSelector; + + public CombineLatest(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, Func<T1, T2, T3, T4, T5, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : CombineLatestSink<TResult> + { + private readonly CombineLatest<T1, T2, T3, T4, T5, TResult> _parent; + + public _(CombineLatest<T1, T2, T3, T4, T5, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(5, observer, cancel) + { + _parent = parent; + } + + private CombineLatestObserver<T1> _observer1; + private CombineLatestObserver<T2> _observer2; + private CombineLatestObserver<T3> _observer3; + private CombineLatestObserver<T4> _observer4; + private CombineLatestObserver<T5> _observer5; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[5]; + for (int i = 0; i < 5; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new CombineLatestObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new CombineLatestObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new CombineLatestObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new CombineLatestObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new CombineLatestObserver<T5>(_gate, this, 4, subscriptions[4]); + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + + return new CompositeDisposable(subscriptions); + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Value, _observer2.Value, _observer3.Value, _observer4.Value, _observer5.Value); + } + } + } + + class CombineLatest<T1, T2, T3, T4, T5, T6, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly Func<T1, T2, T3, T4, T5, T6, TResult> _resultSelector; + + public CombineLatest(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, Func<T1, T2, T3, T4, T5, T6, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : CombineLatestSink<TResult> + { + private readonly CombineLatest<T1, T2, T3, T4, T5, T6, TResult> _parent; + + public _(CombineLatest<T1, T2, T3, T4, T5, T6, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(6, observer, cancel) + { + _parent = parent; + } + + private CombineLatestObserver<T1> _observer1; + private CombineLatestObserver<T2> _observer2; + private CombineLatestObserver<T3> _observer3; + private CombineLatestObserver<T4> _observer4; + private CombineLatestObserver<T5> _observer5; + private CombineLatestObserver<T6> _observer6; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[6]; + for (int i = 0; i < 6; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new CombineLatestObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new CombineLatestObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new CombineLatestObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new CombineLatestObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new CombineLatestObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new CombineLatestObserver<T6>(_gate, this, 5, subscriptions[5]); + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + + return new CompositeDisposable(subscriptions); + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Value, _observer2.Value, _observer3.Value, _observer4.Value, _observer5.Value, _observer6.Value); + } + } + } + + class CombineLatest<T1, T2, T3, T4, T5, T6, T7, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, TResult> _resultSelector; + + public CombineLatest(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, Func<T1, T2, T3, T4, T5, T6, T7, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : CombineLatestSink<TResult> + { + private readonly CombineLatest<T1, T2, T3, T4, T5, T6, T7, TResult> _parent; + + public _(CombineLatest<T1, T2, T3, T4, T5, T6, T7, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(7, observer, cancel) + { + _parent = parent; + } + + private CombineLatestObserver<T1> _observer1; + private CombineLatestObserver<T2> _observer2; + private CombineLatestObserver<T3> _observer3; + private CombineLatestObserver<T4> _observer4; + private CombineLatestObserver<T5> _observer5; + private CombineLatestObserver<T6> _observer6; + private CombineLatestObserver<T7> _observer7; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[7]; + for (int i = 0; i < 7; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new CombineLatestObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new CombineLatestObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new CombineLatestObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new CombineLatestObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new CombineLatestObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new CombineLatestObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new CombineLatestObserver<T7>(_gate, this, 6, subscriptions[6]); + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + + return new CompositeDisposable(subscriptions); + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Value, _observer2.Value, _observer3.Value, _observer4.Value, _observer5.Value, _observer6.Value, _observer7.Value); + } + } + } + + class CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> _resultSelector; + + public CombineLatest(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : CombineLatestSink<TResult> + { + private readonly CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, TResult> _parent; + + public _(CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(8, observer, cancel) + { + _parent = parent; + } + + private CombineLatestObserver<T1> _observer1; + private CombineLatestObserver<T2> _observer2; + private CombineLatestObserver<T3> _observer3; + private CombineLatestObserver<T4> _observer4; + private CombineLatestObserver<T5> _observer5; + private CombineLatestObserver<T6> _observer6; + private CombineLatestObserver<T7> _observer7; + private CombineLatestObserver<T8> _observer8; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[8]; + for (int i = 0; i < 8; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new CombineLatestObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new CombineLatestObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new CombineLatestObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new CombineLatestObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new CombineLatestObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new CombineLatestObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new CombineLatestObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new CombineLatestObserver<T8>(_gate, this, 7, subscriptions[7]); + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + + return new CompositeDisposable(subscriptions); + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Value, _observer2.Value, _observer3.Value, _observer4.Value, _observer5.Value, _observer6.Value, _observer7.Value, _observer8.Value); + } + } + } + + class CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> _resultSelector; + + public CombineLatest(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : CombineLatestSink<TResult> + { + private readonly CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> _parent; + + public _(CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(9, observer, cancel) + { + _parent = parent; + } + + private CombineLatestObserver<T1> _observer1; + private CombineLatestObserver<T2> _observer2; + private CombineLatestObserver<T3> _observer3; + private CombineLatestObserver<T4> _observer4; + private CombineLatestObserver<T5> _observer5; + private CombineLatestObserver<T6> _observer6; + private CombineLatestObserver<T7> _observer7; + private CombineLatestObserver<T8> _observer8; + private CombineLatestObserver<T9> _observer9; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[9]; + for (int i = 0; i < 9; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new CombineLatestObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new CombineLatestObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new CombineLatestObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new CombineLatestObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new CombineLatestObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new CombineLatestObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new CombineLatestObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new CombineLatestObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new CombineLatestObserver<T9>(_gate, this, 8, subscriptions[8]); + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + + return new CompositeDisposable(subscriptions); + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Value, _observer2.Value, _observer3.Value, _observer4.Value, _observer5.Value, _observer6.Value, _observer7.Value, _observer8.Value, _observer9.Value); + } + } + } + + class CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly IObservable<T10> _source10; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> _resultSelector; + + public CombineLatest(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, IObservable<T10> source10, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _source10 = source10; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : CombineLatestSink<TResult> + { + private readonly CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> _parent; + + public _(CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(10, observer, cancel) + { + _parent = parent; + } + + private CombineLatestObserver<T1> _observer1; + private CombineLatestObserver<T2> _observer2; + private CombineLatestObserver<T3> _observer3; + private CombineLatestObserver<T4> _observer4; + private CombineLatestObserver<T5> _observer5; + private CombineLatestObserver<T6> _observer6; + private CombineLatestObserver<T7> _observer7; + private CombineLatestObserver<T8> _observer8; + private CombineLatestObserver<T9> _observer9; + private CombineLatestObserver<T10> _observer10; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[10]; + for (int i = 0; i < 10; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new CombineLatestObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new CombineLatestObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new CombineLatestObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new CombineLatestObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new CombineLatestObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new CombineLatestObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new CombineLatestObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new CombineLatestObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new CombineLatestObserver<T9>(_gate, this, 8, subscriptions[8]); + _observer10 = new CombineLatestObserver<T10>(_gate, this, 9, subscriptions[9]); + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + subscriptions[9].Disposable = _parent._source10.SubscribeSafe(_observer10); + + return new CompositeDisposable(subscriptions); + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Value, _observer2.Value, _observer3.Value, _observer4.Value, _observer5.Value, _observer6.Value, _observer7.Value, _observer8.Value, _observer9.Value, _observer10.Value); + } + } + } + + class CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly IObservable<T10> _source10; + private readonly IObservable<T11> _source11; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult> _resultSelector; + + public CombineLatest(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, IObservable<T10> source10, IObservable<T11> source11, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _source10 = source10; + _source11 = source11; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : CombineLatestSink<TResult> + { + private readonly CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult> _parent; + + public _(CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(11, observer, cancel) + { + _parent = parent; + } + + private CombineLatestObserver<T1> _observer1; + private CombineLatestObserver<T2> _observer2; + private CombineLatestObserver<T3> _observer3; + private CombineLatestObserver<T4> _observer4; + private CombineLatestObserver<T5> _observer5; + private CombineLatestObserver<T6> _observer6; + private CombineLatestObserver<T7> _observer7; + private CombineLatestObserver<T8> _observer8; + private CombineLatestObserver<T9> _observer9; + private CombineLatestObserver<T10> _observer10; + private CombineLatestObserver<T11> _observer11; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[11]; + for (int i = 0; i < 11; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new CombineLatestObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new CombineLatestObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new CombineLatestObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new CombineLatestObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new CombineLatestObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new CombineLatestObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new CombineLatestObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new CombineLatestObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new CombineLatestObserver<T9>(_gate, this, 8, subscriptions[8]); + _observer10 = new CombineLatestObserver<T10>(_gate, this, 9, subscriptions[9]); + _observer11 = new CombineLatestObserver<T11>(_gate, this, 10, subscriptions[10]); + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + subscriptions[9].Disposable = _parent._source10.SubscribeSafe(_observer10); + subscriptions[10].Disposable = _parent._source11.SubscribeSafe(_observer11); + + return new CompositeDisposable(subscriptions); + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Value, _observer2.Value, _observer3.Value, _observer4.Value, _observer5.Value, _observer6.Value, _observer7.Value, _observer8.Value, _observer9.Value, _observer10.Value, _observer11.Value); + } + } + } + + class CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly IObservable<T10> _source10; + private readonly IObservable<T11> _source11; + private readonly IObservable<T12> _source12; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult> _resultSelector; + + public CombineLatest(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, IObservable<T10> source10, IObservable<T11> source11, IObservable<T12> source12, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _source10 = source10; + _source11 = source11; + _source12 = source12; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : CombineLatestSink<TResult> + { + private readonly CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult> _parent; + + public _(CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(12, observer, cancel) + { + _parent = parent; + } + + private CombineLatestObserver<T1> _observer1; + private CombineLatestObserver<T2> _observer2; + private CombineLatestObserver<T3> _observer3; + private CombineLatestObserver<T4> _observer4; + private CombineLatestObserver<T5> _observer5; + private CombineLatestObserver<T6> _observer6; + private CombineLatestObserver<T7> _observer7; + private CombineLatestObserver<T8> _observer8; + private CombineLatestObserver<T9> _observer9; + private CombineLatestObserver<T10> _observer10; + private CombineLatestObserver<T11> _observer11; + private CombineLatestObserver<T12> _observer12; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[12]; + for (int i = 0; i < 12; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new CombineLatestObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new CombineLatestObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new CombineLatestObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new CombineLatestObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new CombineLatestObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new CombineLatestObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new CombineLatestObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new CombineLatestObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new CombineLatestObserver<T9>(_gate, this, 8, subscriptions[8]); + _observer10 = new CombineLatestObserver<T10>(_gate, this, 9, subscriptions[9]); + _observer11 = new CombineLatestObserver<T11>(_gate, this, 10, subscriptions[10]); + _observer12 = new CombineLatestObserver<T12>(_gate, this, 11, subscriptions[11]); + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + subscriptions[9].Disposable = _parent._source10.SubscribeSafe(_observer10); + subscriptions[10].Disposable = _parent._source11.SubscribeSafe(_observer11); + subscriptions[11].Disposable = _parent._source12.SubscribeSafe(_observer12); + + return new CompositeDisposable(subscriptions); + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Value, _observer2.Value, _observer3.Value, _observer4.Value, _observer5.Value, _observer6.Value, _observer7.Value, _observer8.Value, _observer9.Value, _observer10.Value, _observer11.Value, _observer12.Value); + } + } + } + + class CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly IObservable<T10> _source10; + private readonly IObservable<T11> _source11; + private readonly IObservable<T12> _source12; + private readonly IObservable<T13> _source13; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult> _resultSelector; + + public CombineLatest(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, IObservable<T10> source10, IObservable<T11> source11, IObservable<T12> source12, IObservable<T13> source13, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _source10 = source10; + _source11 = source11; + _source12 = source12; + _source13 = source13; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : CombineLatestSink<TResult> + { + private readonly CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult> _parent; + + public _(CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(13, observer, cancel) + { + _parent = parent; + } + + private CombineLatestObserver<T1> _observer1; + private CombineLatestObserver<T2> _observer2; + private CombineLatestObserver<T3> _observer3; + private CombineLatestObserver<T4> _observer4; + private CombineLatestObserver<T5> _observer5; + private CombineLatestObserver<T6> _observer6; + private CombineLatestObserver<T7> _observer7; + private CombineLatestObserver<T8> _observer8; + private CombineLatestObserver<T9> _observer9; + private CombineLatestObserver<T10> _observer10; + private CombineLatestObserver<T11> _observer11; + private CombineLatestObserver<T12> _observer12; + private CombineLatestObserver<T13> _observer13; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[13]; + for (int i = 0; i < 13; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new CombineLatestObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new CombineLatestObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new CombineLatestObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new CombineLatestObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new CombineLatestObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new CombineLatestObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new CombineLatestObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new CombineLatestObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new CombineLatestObserver<T9>(_gate, this, 8, subscriptions[8]); + _observer10 = new CombineLatestObserver<T10>(_gate, this, 9, subscriptions[9]); + _observer11 = new CombineLatestObserver<T11>(_gate, this, 10, subscriptions[10]); + _observer12 = new CombineLatestObserver<T12>(_gate, this, 11, subscriptions[11]); + _observer13 = new CombineLatestObserver<T13>(_gate, this, 12, subscriptions[12]); + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + subscriptions[9].Disposable = _parent._source10.SubscribeSafe(_observer10); + subscriptions[10].Disposable = _parent._source11.SubscribeSafe(_observer11); + subscriptions[11].Disposable = _parent._source12.SubscribeSafe(_observer12); + subscriptions[12].Disposable = _parent._source13.SubscribeSafe(_observer13); + + return new CompositeDisposable(subscriptions); + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Value, _observer2.Value, _observer3.Value, _observer4.Value, _observer5.Value, _observer6.Value, _observer7.Value, _observer8.Value, _observer9.Value, _observer10.Value, _observer11.Value, _observer12.Value, _observer13.Value); + } + } + } + + class CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly IObservable<T10> _source10; + private readonly IObservable<T11> _source11; + private readonly IObservable<T12> _source12; + private readonly IObservable<T13> _source13; + private readonly IObservable<T14> _source14; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult> _resultSelector; + + public CombineLatest(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, IObservable<T10> source10, IObservable<T11> source11, IObservable<T12> source12, IObservable<T13> source13, IObservable<T14> source14, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _source10 = source10; + _source11 = source11; + _source12 = source12; + _source13 = source13; + _source14 = source14; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : CombineLatestSink<TResult> + { + private readonly CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult> _parent; + + public _(CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(14, observer, cancel) + { + _parent = parent; + } + + private CombineLatestObserver<T1> _observer1; + private CombineLatestObserver<T2> _observer2; + private CombineLatestObserver<T3> _observer3; + private CombineLatestObserver<T4> _observer4; + private CombineLatestObserver<T5> _observer5; + private CombineLatestObserver<T6> _observer6; + private CombineLatestObserver<T7> _observer7; + private CombineLatestObserver<T8> _observer8; + private CombineLatestObserver<T9> _observer9; + private CombineLatestObserver<T10> _observer10; + private CombineLatestObserver<T11> _observer11; + private CombineLatestObserver<T12> _observer12; + private CombineLatestObserver<T13> _observer13; + private CombineLatestObserver<T14> _observer14; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[14]; + for (int i = 0; i < 14; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new CombineLatestObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new CombineLatestObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new CombineLatestObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new CombineLatestObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new CombineLatestObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new CombineLatestObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new CombineLatestObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new CombineLatestObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new CombineLatestObserver<T9>(_gate, this, 8, subscriptions[8]); + _observer10 = new CombineLatestObserver<T10>(_gate, this, 9, subscriptions[9]); + _observer11 = new CombineLatestObserver<T11>(_gate, this, 10, subscriptions[10]); + _observer12 = new CombineLatestObserver<T12>(_gate, this, 11, subscriptions[11]); + _observer13 = new CombineLatestObserver<T13>(_gate, this, 12, subscriptions[12]); + _observer14 = new CombineLatestObserver<T14>(_gate, this, 13, subscriptions[13]); + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + subscriptions[9].Disposable = _parent._source10.SubscribeSafe(_observer10); + subscriptions[10].Disposable = _parent._source11.SubscribeSafe(_observer11); + subscriptions[11].Disposable = _parent._source12.SubscribeSafe(_observer12); + subscriptions[12].Disposable = _parent._source13.SubscribeSafe(_observer13); + subscriptions[13].Disposable = _parent._source14.SubscribeSafe(_observer14); + + return new CompositeDisposable(subscriptions); + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Value, _observer2.Value, _observer3.Value, _observer4.Value, _observer5.Value, _observer6.Value, _observer7.Value, _observer8.Value, _observer9.Value, _observer10.Value, _observer11.Value, _observer12.Value, _observer13.Value, _observer14.Value); + } + } + } + + class CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly IObservable<T10> _source10; + private readonly IObservable<T11> _source11; + private readonly IObservable<T12> _source12; + private readonly IObservable<T13> _source13; + private readonly IObservable<T14> _source14; + private readonly IObservable<T15> _source15; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult> _resultSelector; + + public CombineLatest(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, IObservable<T10> source10, IObservable<T11> source11, IObservable<T12> source12, IObservable<T13> source13, IObservable<T14> source14, IObservable<T15> source15, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _source10 = source10; + _source11 = source11; + _source12 = source12; + _source13 = source13; + _source14 = source14; + _source15 = source15; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : CombineLatestSink<TResult> + { + private readonly CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult> _parent; + + public _(CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(15, observer, cancel) + { + _parent = parent; + } + + private CombineLatestObserver<T1> _observer1; + private CombineLatestObserver<T2> _observer2; + private CombineLatestObserver<T3> _observer3; + private CombineLatestObserver<T4> _observer4; + private CombineLatestObserver<T5> _observer5; + private CombineLatestObserver<T6> _observer6; + private CombineLatestObserver<T7> _observer7; + private CombineLatestObserver<T8> _observer8; + private CombineLatestObserver<T9> _observer9; + private CombineLatestObserver<T10> _observer10; + private CombineLatestObserver<T11> _observer11; + private CombineLatestObserver<T12> _observer12; + private CombineLatestObserver<T13> _observer13; + private CombineLatestObserver<T14> _observer14; + private CombineLatestObserver<T15> _observer15; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[15]; + for (int i = 0; i < 15; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new CombineLatestObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new CombineLatestObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new CombineLatestObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new CombineLatestObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new CombineLatestObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new CombineLatestObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new CombineLatestObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new CombineLatestObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new CombineLatestObserver<T9>(_gate, this, 8, subscriptions[8]); + _observer10 = new CombineLatestObserver<T10>(_gate, this, 9, subscriptions[9]); + _observer11 = new CombineLatestObserver<T11>(_gate, this, 10, subscriptions[10]); + _observer12 = new CombineLatestObserver<T12>(_gate, this, 11, subscriptions[11]); + _observer13 = new CombineLatestObserver<T13>(_gate, this, 12, subscriptions[12]); + _observer14 = new CombineLatestObserver<T14>(_gate, this, 13, subscriptions[13]); + _observer15 = new CombineLatestObserver<T15>(_gate, this, 14, subscriptions[14]); + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + subscriptions[9].Disposable = _parent._source10.SubscribeSafe(_observer10); + subscriptions[10].Disposable = _parent._source11.SubscribeSafe(_observer11); + subscriptions[11].Disposable = _parent._source12.SubscribeSafe(_observer12); + subscriptions[12].Disposable = _parent._source13.SubscribeSafe(_observer13); + subscriptions[13].Disposable = _parent._source14.SubscribeSafe(_observer14); + subscriptions[14].Disposable = _parent._source15.SubscribeSafe(_observer15); + + return new CompositeDisposable(subscriptions); + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Value, _observer2.Value, _observer3.Value, _observer4.Value, _observer5.Value, _observer6.Value, _observer7.Value, _observer8.Value, _observer9.Value, _observer10.Value, _observer11.Value, _observer12.Value, _observer13.Value, _observer14.Value, _observer15.Value); + } + } + } + + class CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly IObservable<T10> _source10; + private readonly IObservable<T11> _source11; + private readonly IObservable<T12> _source12; + private readonly IObservable<T13> _source13; + private readonly IObservable<T14> _source14; + private readonly IObservable<T15> _source15; + private readonly IObservable<T16> _source16; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> _resultSelector; + + public CombineLatest(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, IObservable<T10> source10, IObservable<T11> source11, IObservable<T12> source12, IObservable<T13> source13, IObservable<T14> source14, IObservable<T15> source15, IObservable<T16> source16, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _source10 = source10; + _source11 = source11; + _source12 = source12; + _source13 = source13; + _source14 = source14; + _source15 = source15; + _source16 = source16; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : CombineLatestSink<TResult> + { + private readonly CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> _parent; + + public _(CombineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(16, observer, cancel) + { + _parent = parent; + } + + private CombineLatestObserver<T1> _observer1; + private CombineLatestObserver<T2> _observer2; + private CombineLatestObserver<T3> _observer3; + private CombineLatestObserver<T4> _observer4; + private CombineLatestObserver<T5> _observer5; + private CombineLatestObserver<T6> _observer6; + private CombineLatestObserver<T7> _observer7; + private CombineLatestObserver<T8> _observer8; + private CombineLatestObserver<T9> _observer9; + private CombineLatestObserver<T10> _observer10; + private CombineLatestObserver<T11> _observer11; + private CombineLatestObserver<T12> _observer12; + private CombineLatestObserver<T13> _observer13; + private CombineLatestObserver<T14> _observer14; + private CombineLatestObserver<T15> _observer15; + private CombineLatestObserver<T16> _observer16; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[16]; + for (int i = 0; i < 16; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new CombineLatestObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new CombineLatestObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new CombineLatestObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new CombineLatestObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new CombineLatestObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new CombineLatestObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new CombineLatestObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new CombineLatestObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new CombineLatestObserver<T9>(_gate, this, 8, subscriptions[8]); + _observer10 = new CombineLatestObserver<T10>(_gate, this, 9, subscriptions[9]); + _observer11 = new CombineLatestObserver<T11>(_gate, this, 10, subscriptions[10]); + _observer12 = new CombineLatestObserver<T12>(_gate, this, 11, subscriptions[11]); + _observer13 = new CombineLatestObserver<T13>(_gate, this, 12, subscriptions[12]); + _observer14 = new CombineLatestObserver<T14>(_gate, this, 13, subscriptions[13]); + _observer15 = new CombineLatestObserver<T15>(_gate, this, 14, subscriptions[14]); + _observer16 = new CombineLatestObserver<T16>(_gate, this, 15, subscriptions[15]); + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + subscriptions[9].Disposable = _parent._source10.SubscribeSafe(_observer10); + subscriptions[10].Disposable = _parent._source11.SubscribeSafe(_observer11); + subscriptions[11].Disposable = _parent._source12.SubscribeSafe(_observer12); + subscriptions[12].Disposable = _parent._source13.SubscribeSafe(_observer13); + subscriptions[13].Disposable = _parent._source14.SubscribeSafe(_observer14); + subscriptions[14].Disposable = _parent._source15.SubscribeSafe(_observer15); + subscriptions[15].Disposable = _parent._source16.SubscribeSafe(_observer16); + + return new CompositeDisposable(subscriptions); + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Value, _observer2.Value, _observer3.Value, _observer4.Value, _observer5.Value, _observer6.Value, _observer7.Value, _observer8.Value, _observer9.Value, _observer10.Value, _observer11.Value, _observer12.Value, _observer13.Value, _observer14.Value, _observer15.Value, _observer16.Value); + } + } + } + +#endif + + #endregion + + #region Helpers for n-ary overloads + + interface ICombineLatest + { + void Next(int index); + void Fail(Exception error); + void Done(int index); + } + + abstract class CombineLatestSink<TResult> : Sink<TResult>, ICombineLatest + { + protected readonly object _gate; + + private bool _hasValueAll; + private readonly bool[] _hasValue; + private readonly bool[] _isDone; + + public CombineLatestSink(int arity, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _gate = new object(); + + _hasValue = new bool[arity]; + _isDone = new bool[arity]; + } + + public void Next(int index) + { + if (!_hasValueAll) + { + _hasValue[index] = true; + + var hasValueAll = true; + foreach (var hasValue in _hasValue) + { + if (!hasValue) + { + hasValueAll = false; + break; + } + } + + _hasValueAll = hasValueAll; + } + + if (_hasValueAll) + { + var res = default(TResult); + try + { + res = GetResult(); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + base._observer.OnNext(res); + } + else + { + var allOthersDone = true; + for (int i = 0; i < _isDone.Length; i++) + { + if (i != index && !_isDone[i]) + { + allOthersDone = false; + break; + } + } + + if (allOthersDone) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + protected abstract TResult GetResult(); + + public void Fail(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void Done(int index) + { + _isDone[index] = true; + + var allDone = true; + foreach (var isDone in _isDone) + { + if (!isDone) + { + allDone = false; + break; + } + } + + if (allDone) + { + base._observer.OnCompleted(); + base.Dispose(); + return; + } + } + } + + class CombineLatestObserver<T> : IObserver<T> + { + private readonly object _gate; + private readonly ICombineLatest _parent; + private readonly int _index; + private readonly IDisposable _self; + private T _value; + + public CombineLatestObserver(object gate, ICombineLatest parent, int index, IDisposable self) + { + _gate = gate; + _parent = parent; + _index = index; + _self = self; + } + + public T Value + { + get { return _value; } + } + + public void OnNext(T value) + { + lock (_gate) + { + _value = value; + _parent.Next(_index); + } + } + + public void OnError(Exception error) + { + _self.Dispose(); + + lock (_gate) + { + _parent.Fail(error); + } + } + + public void OnCompleted() + { + _self.Dispose(); + + lock (_gate) + { + _parent.Done(_index); + } + } + } + + #endregion + + #endregion + + #region N-ary + + class CombineLatest<TSource, TResult> : Producer<TResult> + { + private readonly IEnumerable<IObservable<TSource>> _sources; + private readonly Func<IList<TSource>, TResult> _resultSelector; + + public CombineLatest(IEnumerable<IObservable<TSource>> sources, Func<IList<TSource>, TResult> resultSelector) + { + _sources = sources; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TResult> + { + private readonly CombineLatest<TSource, TResult> _parent; + + public _(CombineLatest<TSource, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private bool[] _hasValue; + private bool _hasValueAll; + private List<TSource> _values; + private bool[] _isDone; + private IDisposable[] _subscriptions; + + public IDisposable Run() + { + var srcs = _parent._sources.ToArray(); + + var N = srcs.Length; + + _hasValue = new bool[N]; + _hasValueAll = false; + + _values = new List<TSource>(N); + for (int i = 0; i < N; i++) + _values.Add(default(TSource)); + + _isDone = new bool[N]; + + _subscriptions = new IDisposable[N]; + + _gate = new object(); + + for (int i = 0; i < N; i++) + { + var j = i; + + var d = new SingleAssignmentDisposable(); + _subscriptions[j] = d; + + var o = new O(this, j); + d.Disposable = srcs[j].SubscribeSafe(o); + } + + return new CompositeDisposable(_subscriptions); + } + + private void OnNext(int index, TSource value) + { + lock (_gate) + { + _values[index] = value; + + _hasValue[index] = true; + + if (_hasValueAll || (_hasValueAll = _hasValue.All(Stubs<bool>.I))) + { + var res = default(TResult); + try + { + res = _parent._resultSelector(new ReadOnlyCollection<TSource>(_values)); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + _observer.OnNext(res); + } + else if (_isDone.Where((x, i) => i != index).All(Stubs<bool>.I)) + { + base._observer.OnCompleted(); + base.Dispose(); + return; + } + } + } + + private void OnError(Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + private void OnCompleted(int index) + { + lock (_gate) + { + _isDone[index] = true; + + if (_isDone.All(Stubs<bool>.I)) + { + base._observer.OnCompleted(); + base.Dispose(); + return; + } + else + { + _subscriptions[index].Dispose(); + } + } + } + + class O : IObserver<TSource> + { + private readonly _ _parent; + private readonly int _index; + + public O(_ parent, int index) + { + _parent = parent; + _index = index; + } + + public void OnNext(TSource value) + { + _parent.OnNext(_index, value); + } + + public void OnError(Exception error) + { + _parent.OnError(error); + } + + public void OnCompleted() + { + _parent.OnCompleted(_index); + } + } + } + } + + #endregion +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Concat.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Concat.cs new file mode 100644 index 0000000..5a9b2e4 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Concat.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Concat<TSource> : Producer<TSource>, IConcatenatable<TSource> + { + private readonly IEnumerable<IObservable<TSource>> _sources; + + public Concat(IEnumerable<IObservable<TSource>> sources) + { + _sources = sources; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return sink.Run(_sources); + } + + public IEnumerable<IObservable<TSource>> GetSources() + { + return _sources; + } + + class _ : ConcatSink<TSource> + { + public _(IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + public override void OnNext(TSource value) + { + base._observer.OnNext(value); + } + + public override void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Contains.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Contains.cs new file mode 100644 index 0000000..b26e2d4 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Contains.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; + +namespace System.Reactive.Linq.Observαble +{ + class Contains<TSource> : Producer<bool> + { + private readonly IObservable<TSource> _source; + private readonly TSource _value; + private readonly IEqualityComparer<TSource> _comparer; + + public Contains(IObservable<TSource> source, TSource value, IEqualityComparer<TSource> comparer) + { + _source = source; + _value = value; + _comparer = comparer; + } + + protected override IDisposable Run(IObserver<bool> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<bool>, IObserver<TSource> + { + private readonly Contains<TSource> _parent; + + public _(Contains<TSource> parent, IObserver<bool> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + var res = false; + try + { + res = _parent._comparer.Equals(value, _parent._value); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + if (res) + { + base._observer.OnNext(true); + base._observer.OnCompleted(); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(false); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Count.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Count.cs new file mode 100644 index 0000000..7432816 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Count.cs @@ -0,0 +1,124 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class Count<TSource> : Producer<int> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, bool> _predicate; + + public Count(IObservable<TSource> source) + { + _source = source; + } + + public Count(IObservable<TSource> source, Func<TSource, bool> predicate) + { + _source = source; + _predicate = predicate; + } + + protected override IDisposable Run(IObserver<int> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_predicate == null) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new π(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + } + + class _ : Sink<int>, IObserver<TSource> + { + private int _count; + + public _(IObserver<int> observer, IDisposable cancel) + : base(observer, cancel) + { + _count = 0; + } + + public void OnNext(TSource value) + { + try + { + checked + { + _count++; + } + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_count); + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class π : Sink<int>, IObserver<TSource> + { + private readonly Count<TSource> _parent; + private int _count; + + public π(Count<TSource> parent, IObserver<int> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _count = 0; + } + + public void OnNext(TSource value) + { + try + { + checked + { + if (_parent._predicate(value)) + _count++; + } + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_count); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/DefaultIfEmpty.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/DefaultIfEmpty.cs new file mode 100644 index 0000000..ed4bb89 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/DefaultIfEmpty.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class DefaultIfEmpty<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly TSource _defaultValue; + + public DefaultIfEmpty(IObservable<TSource> source, TSource defaultValue) + { + _source = source; + _defaultValue = defaultValue; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly DefaultIfEmpty<TSource> _parent; + private bool _found; + + public _(DefaultIfEmpty<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _found = false; + } + + public void OnNext(TSource value) + { + _found = true; + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_found) + base._observer.OnNext(_parent._defaultValue); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Defer.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Defer.cs new file mode 100644 index 0000000..ba3dcc4 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Defer.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Defer<TValue> : Producer<TValue>, IEvaluatableObservable<TValue> + { + private readonly Func<IObservable<TValue>> _observableFactory; + + public Defer(Func<IObservable<TValue>> observableFactory) + { + _observableFactory = observableFactory; + } + + protected override IDisposable Run(IObserver<TValue> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + public IObservable<TValue> Eval() + { + return _observableFactory(); + } + + class _ : Sink<TValue>, IObserver<TValue> + { + private readonly Defer<TValue> _parent; + + public _(Defer<TValue> parent, IObserver<TValue> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + var result = default(IObservable<TValue>); + try + { + result = _parent.Eval(); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return Disposable.Empty; + } + + return result.SubscribeSafe(this); + } + + public void OnNext(TValue value) + { + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Delay.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Delay.cs new file mode 100644 index 0000000..3e2cbba --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Delay.cs @@ -0,0 +1,768 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System.Collections.Generic; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Threading; + +#if NO_SEMAPHORE +using System.Reactive.Threading; +#endif + +namespace System.Reactive.Linq.Observαble +{ + class Delay<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly TimeSpan? _dueTimeR; + private readonly DateTimeOffset? _dueTimeA; + private readonly IScheduler _scheduler; + + public Delay(IObservable<TSource> source, TimeSpan dueTime, IScheduler scheduler) + { + _source = source; + _dueTimeR = dueTime; + _scheduler = scheduler; + } + + public Delay(IObservable<TSource> source, DateTimeOffset dueTime, IScheduler scheduler) + { + _source = source; + _dueTimeA = dueTime; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_scheduler.AsLongRunning() != null) + { + var sink = new λ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Delay<TSource> _parent; + + public _(Delay<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private IScheduler _scheduler; + private IDisposable _sourceSubscription; + private SerialDisposable _cancelable; + private TimeSpan _delay; + private IStopwatch _watch; + + private object _gate; + private bool _ready; + private bool _active; + private bool _running; + private Queue<System.Reactive.TimeInterval<TSource>> _queue; + private bool _hasCompleted; + private TimeSpan _completeAt; + private bool _hasFailed; + private Exception _exception; + + public IDisposable Run() + { + _scheduler = _parent._scheduler; + + _cancelable = new SerialDisposable(); + + _gate = new object(); + _active = false; + _running = false; + _queue = new Queue<System.Reactive.TimeInterval<TSource>>(); + _hasCompleted = false; + _completeAt = default(TimeSpan); + _hasFailed = false; + _exception = default(Exception); + + _watch = _scheduler.StartStopwatch(); + + if (_parent._dueTimeA.HasValue) + { + _ready = false; + + var dueTimeA = _parent._dueTimeA.Value; + _cancelable.Disposable = _scheduler.Schedule(dueTimeA, Start); + } + else + { + _ready = true; + + var dueTimeR = _parent._dueTimeR.Value; + _delay = Scheduler.Normalize(dueTimeR); + } + + var sourceSubscription = new SingleAssignmentDisposable(); + _sourceSubscription = sourceSubscription; + sourceSubscription.Disposable = _parent._source.SubscribeSafe(this); + + return new CompositeDisposable(_sourceSubscription, _cancelable); + } + + private void Start() + { + var next = default(TimeSpan); + var shouldRun = false; + + lock (_gate) + { + _delay = _watch.Elapsed; + + var oldQueue = _queue; + _queue = new Queue<Reactive.TimeInterval<TSource>>(); + + if (oldQueue.Count > 0) + { + next = oldQueue.Peek().Interval; + + while (oldQueue.Count > 0) + { + var item = oldQueue.Dequeue(); + _queue.Enqueue(new Reactive.TimeInterval<TSource>(item.Value, item.Interval.Add(_delay))); + } + + shouldRun = true; + _active = true; + } + + _ready = true; + } + + if (shouldRun) + { + _cancelable.Disposable = _scheduler.Schedule(next, DrainQueue); + } + } + + public void OnNext(TSource value) + { + var next = _watch.Elapsed.Add(_delay); + var shouldRun = false; + + lock (_gate) + { + _queue.Enqueue(new System.Reactive.TimeInterval<TSource>(value, next)); + + shouldRun = _ready && !_active; + _active = true; + } + + if (shouldRun) + { + _cancelable.Disposable = _scheduler.Schedule(_delay, DrainQueue); + } + } + + public void OnError(Exception error) + { + _sourceSubscription.Dispose(); + + var shouldRun = false; + + lock (_gate) + { + _queue.Clear(); + + _exception = error; + _hasFailed = true; + + shouldRun = !_running; + } + + if (shouldRun) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + _sourceSubscription.Dispose(); + + var next = _watch.Elapsed.Add(_delay); + var shouldRun = false; + + lock (_gate) + { + _completeAt = next; + _hasCompleted = true; + + shouldRun = _ready && !_active; + _active = true; + } + + if (shouldRun) + { + _cancelable.Disposable = _scheduler.Schedule(_delay, DrainQueue); + } + } + + private void DrainQueue(Action<TimeSpan> recurse) + { + lock (_gate) + { + if (_hasFailed) + return; + _running = true; + } + + // + // The shouldYield flag was added to address TFS 487881: "Delay can be unfair". In the old + // implementation, the loop below kept running while there was work for immediate dispatch, + // potentially causing a long running work item on the target scheduler. With the addition + // of long-running scheduling in Rx v2.0, we can check whether the scheduler supports this + // interface and perform different processing (see λ). To reduce the code churn in the old + // loop code here, we set the shouldYield flag to true after the first dispatch iteration, + // in order to break from the loop and enter the recursive scheduling path. + // + var shouldYield = false; + + while (true) + { + var hasFailed = false; + var error = default(Exception); + + var hasValue = false; + var value = default(TSource); + var hasCompleted = false; + + var shouldRecurse = false; + var recurseDueTime = default(TimeSpan); + + lock (_gate) + { + if (_hasFailed) + { + error = _exception; + hasFailed = true; + _running = false; + } + else + { + var now = _watch.Elapsed; + + if (_queue.Count > 0) + { + var nextDue = _queue.Peek().Interval; + + if (nextDue.CompareTo(now) <= 0 && !shouldYield) + { + value = _queue.Dequeue().Value; + hasValue = true; + } + else + { + shouldRecurse = true; + recurseDueTime = Scheduler.Normalize(nextDue.Subtract(now)); + _running = false; + } + } + else if (_hasCompleted) + { + if (_completeAt.CompareTo(now) <= 0 && !shouldYield) + { + hasCompleted = true; + } + else + { + shouldRecurse = true; + recurseDueTime = Scheduler.Normalize(_completeAt.Subtract(now)); + _running = false; + } + } + else + { + _running = false; + _active = false; + } + } + } /* lock (_gate) */ + + if (hasValue) + { + base._observer.OnNext(value); + shouldYield = true; + } + else + { + if (hasCompleted) + { + base._observer.OnCompleted(); + base.Dispose(); + } + else if (hasFailed) + { + base._observer.OnError(error); + base.Dispose(); + } + else if (shouldRecurse) + { + recurse(recurseDueTime); + } + + return; + } + } /* while (true) */ + } + } + + class λ : Sink<TSource>, IObserver<TSource> + { + private readonly Delay<TSource> _parent; + + public λ(Delay<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private IDisposable _sourceSubscription; + private SerialDisposable _cancelable; + private TimeSpan _delay; + private IStopwatch _watch; + + private object _gate; +#if !NO_CDS + private SemaphoreSlim _evt; + private CancellationTokenSource _stop; +#else + private Semaphore _evt; + private bool _stopped; + private ManualResetEvent _stop; +#endif + private Queue<System.Reactive.TimeInterval<TSource>> _queue; + private bool _hasCompleted; + private TimeSpan _completeAt; + private bool _hasFailed; + private Exception _exception; + + public IDisposable Run() + { + _cancelable = new SerialDisposable(); + + _gate = new object(); +#if !NO_CDS + _evt = new SemaphoreSlim(0); +#else + _evt = new Semaphore(0, int.MaxValue); +#endif + _queue = new Queue<System.Reactive.TimeInterval<TSource>>(); + _hasCompleted = false; + _completeAt = default(TimeSpan); + _hasFailed = false; + _exception = default(Exception); + + _watch = _parent._scheduler.StartStopwatch(); + + if (_parent._dueTimeA.HasValue) + { + var dueTimeA = _parent._dueTimeA.Value; + _cancelable.Disposable = _parent._scheduler.Schedule(dueTimeA, Start); + } + else + { + var dueTimeR = _parent._dueTimeR.Value; + _delay = Scheduler.Normalize(dueTimeR); + ScheduleDrain(); + } + + var sourceSubscription = new SingleAssignmentDisposable(); + _sourceSubscription = sourceSubscription; + sourceSubscription.Disposable = _parent._source.SubscribeSafe(this); + + return new CompositeDisposable(_sourceSubscription, _cancelable); + } + + private void Start() + { + lock (_gate) + { + _delay = _watch.Elapsed; + + var oldQueue = _queue; + _queue = new Queue<Reactive.TimeInterval<TSource>>(); + + while (oldQueue.Count > 0) + { + var item = oldQueue.Dequeue(); + _queue.Enqueue(new Reactive.TimeInterval<TSource>(item.Value, item.Interval.Add(_delay))); + } + } + + ScheduleDrain(); + } + + private void ScheduleDrain() + { +#if !NO_CDS + _stop = new CancellationTokenSource(); + _cancelable.Disposable = Disposable.Create(() => _stop.Cancel()); +#else + _stop = new ManualResetEvent(false); + _cancelable.Disposable = Disposable.Create(() => + { + _stopped = true; + _stop.Set(); + _evt.Release(); + }); +#endif + + _parent._scheduler.AsLongRunning().ScheduleLongRunning(DrainQueue); + } + + public void OnNext(TSource value) + { + var next = _watch.Elapsed.Add(_delay); + + lock (_gate) + { + _queue.Enqueue(new System.Reactive.TimeInterval<TSource>(value, next)); + + _evt.Release(); + } + } + + public void OnError(Exception error) + { + _sourceSubscription.Dispose(); + + lock (_gate) + { + _queue.Clear(); + + _exception = error; + _hasFailed = true; + + _evt.Release(); + } + } + + public void OnCompleted() + { + _sourceSubscription.Dispose(); + + var next = _watch.Elapsed.Add(_delay); + + lock (_gate) + { + _completeAt = next; + _hasCompleted = true; + + _evt.Release(); + } + } + + private void DrainQueue(ICancelable cancel) + { + while (true) + { +#if !NO_CDS + try + { + _evt.Wait(_stop.Token); + } + catch (OperationCanceledException) + { + return; + } +#else + _evt.WaitOne(); + if (_stopped) + return; +#endif + + var hasFailed = false; + var error = default(Exception); + + var hasValue = false; + var value = default(TSource); + var hasCompleted = false; + + var shouldWait = false; + var waitTime = default(TimeSpan); + + lock (_gate) + { + if (_hasFailed) + { + error = _exception; + hasFailed = true; + } + else + { + var now = _watch.Elapsed; + + if (_queue.Count > 0) + { + var next = _queue.Dequeue(); + + hasValue = true; + value = next.Value; + + var nextDue = next.Interval; + if (nextDue.CompareTo(now) > 0) + { + shouldWait = true; + waitTime = Scheduler.Normalize(nextDue.Subtract(now)); + } + } + else if (_hasCompleted) + { + hasCompleted = true; + + if (_completeAt.CompareTo(now) > 0) + { + shouldWait = true; + waitTime = Scheduler.Normalize(_completeAt.Subtract(now)); + } + } + } + } /* lock (_gate) */ + + if (shouldWait) + { +#if !NO_CDS + var timer = new ManualResetEventSlim(); + _parent._scheduler.Schedule(waitTime, () => { timer.Set(); }); + + try + { + timer.Wait(_stop.Token); + } + catch (OperationCanceledException) + { + return; + } +#else + var timer = new ManualResetEvent(false); + _parent._scheduler.Schedule(waitTime, () => { timer.Set(); }); + if (WaitHandle.WaitAny(new[] { timer, _stop }) == 1) + return; +#endif + } + + if (hasValue) + { + base._observer.OnNext(value); + } + else + { + if (hasCompleted) + { + base._observer.OnCompleted(); + base.Dispose(); + } + else if (hasFailed) + { + base._observer.OnError(error); + base.Dispose(); + } + + return; + } + } + } + } + } + + class Delay<TSource, TDelay> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly IObservable<TDelay> _subscriptionDelay; + private readonly Func<TSource, IObservable<TDelay>> _delaySelector; + + public Delay(IObservable<TSource> source, IObservable<TDelay> subscriptionDelay, Func<TSource, IObservable<TDelay>> delaySelector) + { + _source = source; + _subscriptionDelay = subscriptionDelay; + _delaySelector = delaySelector; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Delay<TSource, TDelay> _parent; + + public _(Delay<TSource, TDelay> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private CompositeDisposable _delays; + private object _gate; + private bool _atEnd; + private SerialDisposable _subscription; + + public IDisposable Run() + { + _delays = new CompositeDisposable(); + _gate = new object(); + _atEnd = false; + _subscription = new SerialDisposable(); + + if (_parent._subscriptionDelay == null) + { + Start(); + } + else + { + _subscription.Disposable = _parent._subscriptionDelay.SubscribeSafe(new σ(this)); + } + + return new CompositeDisposable(_subscription, _delays); + } + + private void Start() + { + _subscription.Disposable = _parent._source.SubscribeSafe(this); + } + + public void OnNext(TSource value) + { + var delay = default(IObservable<TDelay>); + try + { + delay = _parent._delaySelector(value); + } + catch (Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + + return; + } + + var d = new SingleAssignmentDisposable(); + _delays.Add(d); + d.Disposable = delay.SubscribeSafe(new δ(this, value, d)); + } + + public void OnError(Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + _atEnd = true; + _subscription.Dispose(); + + CheckDone(); + } + } + + private void CheckDone() + { + if (_atEnd && _delays.Count == 0) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class σ : IObserver<TDelay> + { + private readonly _ _parent; + + public σ(_ parent) + { + _parent = parent; + } + + public void OnNext(TDelay value) + { + _parent.Start(); + } + + public void OnError(Exception error) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + + public void OnCompleted() + { + _parent.Start(); + } + } + + class δ : IObserver<TDelay> + { + private readonly _ _parent; + private readonly TSource _value; + private readonly IDisposable _self; + + public δ(_ parent, TSource value, IDisposable self) + { + _parent = parent; + _value = value; + _self = self; + } + + public void OnNext(TDelay value) + { + lock (_parent._gate) + { + _parent._observer.OnNext(_value); + + _parent._delays.Remove(_self); + _parent.CheckDone(); + } + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + lock (_parent._gate) + { + _parent._observer.OnNext(_value); + + _parent._delays.Remove(_self); + _parent.CheckDone(); + } + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/DelaySubscription.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/DelaySubscription.cs new file mode 100644 index 0000000..c6a05cc --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/DelaySubscription.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; + +namespace System.Reactive.Linq.Observαble +{ + class DelaySubscription<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly DateTimeOffset? _dueTimeA; + private readonly TimeSpan? _dueTimeR; + private readonly IScheduler _scheduler; + + public DelaySubscription(IObservable<TSource> source, DateTimeOffset dueTime, IScheduler scheduler) + { + _source = source; + _dueTimeA = dueTime; + _scheduler = scheduler; + } + + public DelaySubscription(IObservable<TSource> source, TimeSpan dueTime, IScheduler scheduler) + { + _source = source; + _dueTimeR = dueTime; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + + if (_dueTimeA.HasValue) + { + return _scheduler.Schedule(sink, _dueTimeA.Value, Subscribe); + } + else + { + return _scheduler.Schedule(sink, _dueTimeR.Value, Subscribe); + } + } + + private IDisposable Subscribe(IScheduler _, _ sink) + { + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + public _(IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + public void OnNext(TSource value) + { + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Dematerialize.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Dematerialize.cs new file mode 100644 index 0000000..112b156 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Dematerialize.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class Dematerialize<TSource> : Producer<TSource> + { + private readonly IObservable<Notification<TSource>> _source; + + public Dematerialize(IObservable<Notification<TSource>> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TSource>, IObserver<Notification<TSource>> + { + public _(IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + public void OnNext(Notification<TSource> value) + { + switch (value.Kind) + { + case NotificationKind.OnNext: + base._observer.OnNext(value.Value); + break; + case NotificationKind.OnError: + base._observer.OnError(value.Exception); + base.Dispose(); + break; + case NotificationKind.OnCompleted: + base._observer.OnCompleted(); + base.Dispose(); + break; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Distinct.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Distinct.cs new file mode 100644 index 0000000..af20277 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Distinct.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; + +namespace System.Reactive.Linq.Observαble +{ + class Distinct<TSource, TKey> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, TKey> _keySelector; + private readonly IEqualityComparer<TKey> _comparer; + + public Distinct(IObservable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer) + { + _source = source; + _keySelector = keySelector; + _comparer = comparer; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Distinct<TSource, TKey> _parent; + private HashSet<TKey> _hashSet; + + public _(Distinct<TSource, TKey> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _hashSet = new HashSet<TKey>(_parent._comparer); + } + + public void OnNext(TSource value) + { + var key = default(TKey); + var hasAdded = false; + try + { + key = _parent._keySelector(value); + hasAdded = _hashSet.Add(key); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + if (hasAdded) + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/DistinctUntilChanged.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/DistinctUntilChanged.cs new file mode 100644 index 0000000..10c7923 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/DistinctUntilChanged.cs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; + +namespace System.Reactive.Linq.Observαble +{ + class DistinctUntilChanged<TSource, TKey> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, TKey> _keySelector; + private readonly IEqualityComparer<TKey> _comparer; + + public DistinctUntilChanged(IObservable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer) + { + _source = source; + _keySelector = keySelector; + _comparer = comparer; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly DistinctUntilChanged<TSource, TKey> _parent; + private TKey _currentKey; + private bool _hasCurrentKey; + + public _(DistinctUntilChanged<TSource, TKey> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _currentKey = default(TKey); + _hasCurrentKey = false; + } + + public void OnNext(TSource value) + { + var key = default(TKey); + try + { + key = _parent._keySelector(value); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + var comparerEquals = false; + if (_hasCurrentKey) + { + try + { + comparerEquals = _parent._comparer.Equals(_currentKey, key); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + } + + if (!_hasCurrentKey || !comparerEquals) + { + _hasCurrentKey = true; + _currentKey = key; + base._observer.OnNext(value); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Do.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Do.cs new file mode 100644 index 0000000..2f73cfa --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Do.cs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class Do<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly Action<TSource> _onNext; + private readonly Action<Exception> _onError; + private readonly Action _onCompleted; + + public Do(IObservable<TSource> source, Action<TSource> onNext, Action<Exception> onError, Action onCompleted) + { + _source = source; + _onNext = onNext; + _onError = onError; + _onCompleted = onCompleted; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Do<TSource> _parent; + + public _(Do<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + try + { + _parent._onNext(value); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + try + { + _parent._onError(error); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + try + { + _parent._onCompleted(); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/DoWhile.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/DoWhile.cs new file mode 100644 index 0000000..3ef40eb --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/DoWhile.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; + +namespace System.Reactive.Linq.Observαble +{ + class DoWhile<TSource> : Producer<TSource>, IConcatenatable<TSource> + { + private readonly IObservable<TSource> _source; + private readonly Func<bool> _condition; + + public DoWhile(IObservable<TSource> source, Func<bool> condition) + { + _condition = condition; + _source = source; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return sink.Run(GetSources()); + } + + public IEnumerable<IObservable<TSource>> GetSources() + { + yield return _source; + while (_condition()) + yield return _source; + } + + class _ : ConcatSink<TSource> + { + public _(IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + public override void OnNext(TSource value) + { + base._observer.OnNext(value); + } + + public override void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + } + } +} +#endif diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ElementAt.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ElementAt.cs new file mode 100644 index 0000000..b2b1212 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ElementAt.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class ElementAt<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly int _index; + private readonly bool _throwOnEmpty; + + public ElementAt(IObservable<TSource> source, int index, bool throwOnEmpty) + { + _source = source; + _index = index; + _throwOnEmpty = throwOnEmpty; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly ElementAt<TSource> _parent; + private int _i; + + public _(ElementAt<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _i = _parent._index; + } + + public void OnNext(TSource value) + { + if (_i == 0) + { + base._observer.OnNext(value); + base._observer.OnCompleted(); + base.Dispose(); + } + + _i--; + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (_parent._throwOnEmpty) + { + base._observer.OnError(new ArgumentOutOfRangeException("index")); + } + else + { + base._observer.OnNext(default(TSource)); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Empty.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Empty.cs new file mode 100644 index 0000000..42c7241 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Empty.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; + +namespace System.Reactive.Linq.Observαble +{ + class Empty<TResult> : Producer<TResult> + { + private readonly IScheduler _scheduler; + + public Empty(IScheduler scheduler) + { + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TResult> + { + private readonly Empty<TResult> _parent; + + public _(Empty<TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + return _parent._scheduler.Schedule(Invoke); + } + + private void Invoke() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Finally.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Finally.cs new file mode 100644 index 0000000..ca9a5d8 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Finally.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Finally<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly Action _finallyAction; + + public Finally(IObservable<TSource> source, Action finallyAction) + { + _source = source; + _finallyAction = finallyAction; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Finally<TSource> _parent; + + public _(Finally<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + var subscription = _parent._source.SubscribeSafe(this); + + return Disposable.Create(() => + { + try + { + subscription.Dispose(); + } + finally + { + _parent._finallyAction(); + } + }); + } + + public void OnNext(TSource value) + { + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/FirstAsync.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/FirstAsync.cs new file mode 100644 index 0000000..8d8bba7 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/FirstAsync.cs @@ -0,0 +1,132 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class FirstAsync<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, bool> _predicate; + private readonly bool _throwOnEmpty; + + public FirstAsync(IObservable<TSource> source, Func<TSource, bool> predicate, bool throwOnEmpty) + { + _source = source; + _predicate = predicate; + _throwOnEmpty = throwOnEmpty; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_predicate != null) + { + var sink = new π(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly FirstAsync<TSource> _parent; + + public _(FirstAsync<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + base._observer.OnNext(value); + base._observer.OnCompleted(); + base.Dispose(); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (_parent._throwOnEmpty) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(default(TSource)); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + + class π : Sink<TSource>, IObserver<TSource> + { + private readonly FirstAsync<TSource> _parent; + + public π(FirstAsync<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + var b = false; + + try + { + b = _parent._predicate(value); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + if (b) + { + base._observer.OnNext(value); + base._observer.OnCompleted(); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (_parent._throwOnEmpty) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_MATCHING_ELEMENTS)); + } + else + { + base._observer.OnNext(default(TSource)); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/For.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/For.cs new file mode 100644 index 0000000..85b9e03 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/For.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; + +namespace System.Reactive.Linq.Observαble +{ + class For<TSource, TResult> : Producer<TResult>, IConcatenatable<TResult> + { + private readonly IEnumerable<TSource> _source; + private readonly Func<TSource, IObservable<TResult>> _resultSelector; + + public For(IEnumerable<TSource> source, Func<TSource, IObservable<TResult>> resultSelector) + { + _source = source; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return sink.Run(GetSources()); + } + + public IEnumerable<IObservable<TResult>> GetSources() + { + foreach (var item in _source) + yield return _resultSelector(item); + } + + class _ : ConcatSink<TResult> + { + public _(IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + public override void OnNext(TResult value) + { + base._observer.OnNext(value); + } + + public override void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + } + } +} +#endif diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ForEach.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ForEach.cs new file mode 100644 index 0000000..38e112d --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ForEach.cs @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Threading; + +namespace System.Reactive.Linq.Observαble +{ + class ForEach<TSource> + { + public class _ : IObserver<TSource> + { + private readonly Action<TSource> _onNext; + private readonly Action _done; + + private Exception _exception; + private int _stopped; + + public _(Action<TSource> onNext, Action done) + { + _onNext = onNext; + _done = done; + + _stopped = 0; + } + + public Exception Error + { + get { return _exception; } + } + + public void OnNext(TSource value) + { + if (_stopped == 0) + { + try + { + _onNext(value); + } + catch (Exception ex) + { + OnError(ex); + } + } + } + + public void OnError(Exception error) + { + if (Interlocked.Exchange(ref _stopped, 1) == 0) + { + _exception = error; + _done(); + } + } + + public void OnCompleted() + { + if (Interlocked.Exchange(ref _stopped, 1) == 0) + { + _done(); + } + } + } + + public class τ : IObserver<TSource> + { + private readonly Action<TSource, int> _onNext; + private readonly Action _done; + + private int _index; + private Exception _exception; + private int _stopped; + + public τ(Action<TSource, int> onNext, Action done) + { + _onNext = onNext; + _done = done; + + _index = 0; + _stopped = 0; + } + + public Exception Error + { + get { return _exception; } + } + + public void OnNext(TSource value) + { + if (_stopped == 0) + { + try + { + _onNext(value, checked(_index++)); + } + catch (Exception ex) + { + OnError(ex); + } + } + } + + public void OnError(Exception error) + { + if (Interlocked.Exchange(ref _stopped, 1) == 0) + { + _exception = error; + _done(); + } + } + + public void OnCompleted() + { + if (Interlocked.Exchange(ref _stopped, 1) == 0) + { + _done(); + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/FromEvent.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/FromEvent.cs new file mode 100644 index 0000000..82c4348 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/FromEvent.cs @@ -0,0 +1,368 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Diagnostics; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Reactive.Subjects; + +// +// BREAKING CHANGE v2 > v1.x - FromEvent[Pattern] now has an implicit SubscribeOn and Publish operation. +// +// The free-threaded nature of Rx is key to the performance characteristics of the event processing +// pipeline. However, in places where we bridge with the external world, this sometimes has negative +// effects due to thread-affine operations involved. The FromEvent[Pattern] bridges are one such +// place where we reach out to add and remove operations on events. +// +// Consider the following piece of code, assuming Rx v1.x usage: +// +// var txt = Observable.FromEventPattern(txtInput, "TextChanged"); +// var res = from term in txt +// from word in svc.Lookup(term).TakeUntil(txt) +// select word; +// +// This code is flawed for various reasons. Seasoned Rx developers will immediately suggest usage of +// the Publish operator to share the side-effects of subscribing to the txt sequence, resulting in +// only one subscription to the event: +// +// var txt = Observable.FromEventPattern(txtInput, "TextChanged"); +// var res = txt.Publish(txt_ => from term in txt_ +// from word in svc.Lookup(term).TakeUntil(txt_) +// select word); +// +// Customers are typically confused as to why FromEvent[Pattern] causes multiple handlers to be added +// to the underlying event. This is in contrast with other From* bridges which involve the use of a +// subject (e.g. FromAsyncPattern, FromAsync, and ToObservable on Task<T>). +// +// But there are more issues with the code fragment above. Upon completion of the svc.Lookup(term) +// sequence, TakeUntil will unsubscribe from both sequences, causing the unsubscription to happen in +// the context of the source's OnCompleted, which may be the thread pool. Some thread-affine events +// don't quite like this. In UI frameworks like WPF and Silverlight, this turns out to be not much of +// a problem typically, but it's merely an accident things work out. From an e-mail conversion with +// the WPF/SL/Jupiter experts: +// +// "Unfortunately, as I expected, it’s confusing, and implementation details are showing through. +// The bottom line is that event add/remove should always be done on the right thread. +// +// Where events are implemented with compiler-generated code, i.e. MultiCastDelegate, the add/remove +// will be thread safe/agile. Where events are implemented in custom code, across Wpf/SL/WP/Jupiter, +// the add/remove are expected to happen on the Dispatcher thread. +// +// Jupiter actually has the consistent story here, where all the event add/remove implementations do +// the thread check. It should still be a “wrong thread” error, though, not an AV. +// +// In SL there’s a mix of core events (which do the thread check) and framework events (which use +// compiler-generated event implementations). So you get an exception if you unhook Button.Loaded +// from off thread, but you don’t get an exception if you unhook Button.Click. +// +// In WPF there’s a similar mix (some events are compiler-generated and some use the EventHandlerStore). +// But I don’t see any thread safety or thread check in the EventHandlerStore. So while it works, IIUC, +// it should have race conditions and corruptions." +// +// Starting with "Jupiter" (Windows XAML aka "Metro"), checks are added to ensure the add and remove +// operations for UI events are called from the UI thread. As a result, the dictionary suggest sample +// code shown above starts to fail. A possible fix is to use SubscribeOnDispatcher: +// +// var txt = Observable.FromEventPattern(txtInput, "TextChanged").SubscribeOnDispatcher(); +// var res = from term in txt +// from word in svc.Lookup(term).TakeUntil(txt) +// select word; +// +// This fix has two problems: +// +// 1. Customers often don't quite understand the difference between ObserveOn and SubscribeOn. In fact, +// we've given guidance that use of the latter is typically indicative of a misunderstanding, and +// is used rarely. Also, the fragment above would likely be extended with some UI binding code where +// one needs to use ObserveOnDispatcher, so the combination of both becomes even more confusing. +// +// 2. There's a subtle race condition now. Upon receiving a new term from the txt sequence, SelectMany's +// invocation of the result selector involves TakeUntil subscribing to txt again. However, the use +// of SubscribeOnDispatcher means the subscription is now happening asynchronously, leaving a time +// gap between returning from Subscribe and doing the += on the underlying event: +// +// (Subscription of TakeUntil to txt) +// | +// v +// txt -------------------------------------------------------------- +// | +// +-----...----+ (SubscribeOnDispatcher's post of Subscribe) +// | +// TextChanged ------"re"---------"rea"-------------"reac"-----"react"----... +// ^ +// | +// (where += on the event happens) +// +// While this problem is rare and sometimes gets mitigated by accident because code is posting back +// to e.g. the UI message loop, it's extremely hard to debug when things go wrong. +// +// In order to fix this behavior such that code has the expected behavior, we do two things in Rx v2.0: +// +// - To solve the cross-thread add/remove handler operations and make them single-thread affine, we +// now do an implicit SubscribeOn with the SynchronizationContext.Current retrieved eagerly upon +// calling FromEvent[Pattern]. This goes hand-in-hand with a recommendation: +// +// "Always call FromEvent[Pattern] in a place where you'd normally write += and -= operations +// yourself. Don't inline the creation of a FromEvent[Pattern] object inside a query." +// +// This recommendation helps to keep code clean (bridging operations are moved outside queries) and +// ensures the captured SynchronizationContext is the least surprising one. E.g in the sample code +// above, the whole query likely lives in a button_Click handler or so. +// +// - To solve the time gap issue, we now add implicit Publish behavior with ref-counted behavior. In +// other words, the new FromEvent[Pattern] is pretty much the same as: +// +// Observable_v2.FromEvent[Pattern](<args>) +// == +// Observable_v1.FromEvent[Pattern](<args>).SubscribeOn(SynchronizationContext.Current) +// .Publish() +// .RefCount() +// +// Overloads to FromEvent[Pattern] allow to specify the scheduler used for the SubscribeOn operation +// that's taking place internally. When omitted, a SynchronizationContextScheduler will be supplied +// if a current SynchronizationContext is found. If no current SynchronizationContext is found, the +// default scheduler is the immediate scheduler, falling back to the free-threaded behavior we had +// before in v1.x. (See GetSchedulerForCurrentContext in QueryLanguage.Events.cs). +// +// Notice a time gap can still occur at the point of the first subscription to the event sequence, +// or when the ref count fell back to zero. In cases of nested uses of the sequence (such as in the +// running example here), this is fine because the top-level subscription is kept alive for the whole +// duration. In other cases, there's already a race condition between the underlying event and the +// observable wrapper (assuming events are hot). For cold events that have side-effects upon add and +// remove handler operations, use of Observable.Create is recommended. This should be rather rare, +// as most events follow the typical MulticastDelegate implementation pattern: +// +// public event EventHandler<BarEventArgs> Bar; +// +// protected void OnBar(int value) +// { +// var bar = Bar; +// if (bar != null) +// bar(this, new BarEventArgs(value)); +// } +// +// In here, there's already a race between the user hooking up an event handler through the += add +// operation and the event producer (possibly on a different thread) calling OnBar. It's also worth +// pointing out that this race condition is migitated by a check in SynchronizationContextScheduler +// causing synchronous execution in case the caller is already on the target SynchronizationContext. +// This situation is common when using FromEvent[Pattern] immediately after declaring it, e.g. in +// the context of a UI event handler. +// +// Finally, notice we can't simply connect the event to a Subject<T> upon a FromEvent[Pattern] call, +// because this would make it impossible to get rid of this one event handler (unless we expose some +// other means of resource maintenance, e.g. by making the returned object implement IDisposable). +// Also, this would cause the event producer to see the event's delegate in a non-null state all the +// time, causing event argument objects to be newed up, possibly sending those into a zero-observer +// subject (which is opaque to the event producer). Not to mention that the subject would always be +// rooted by the target event (even when the FromEvent[Pattern] observable wrapper is unreachable). +// +namespace System.Reactive.Linq.Observαble +{ + class FromEvent<TDelegate, TEventArgs> : ClassicEventProducer<TDelegate, TEventArgs> + { + private readonly Func<Action<TEventArgs>, TDelegate> _conversion; + + public FromEvent(Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler) + : base(addHandler, removeHandler, scheduler) + { + } + + public FromEvent(Func<Action<TEventArgs>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler) + : base(addHandler, removeHandler, scheduler) + { + _conversion = conversion; + } + + protected override TDelegate GetHandler(Action<TEventArgs> onNext) + { + var handler = default(TDelegate); + + if (_conversion == null) + { + handler = ReflectionUtils.CreateDelegate<TDelegate>(onNext, typeof(Action<TEventArgs>).GetMethod("Invoke")); + } + else + { + handler = _conversion(onNext); + } + + return handler; + } + } + + abstract class EventProducer<TDelegate, TArgs> : Producer<TArgs> + { + private readonly IScheduler _scheduler; + private readonly object _gate; + + public EventProducer(IScheduler scheduler) + { + _scheduler = scheduler; + _gate = new object(); + } + + protected abstract TDelegate GetHandler(Action<TArgs> onNext); + protected abstract IDisposable AddHandler(TDelegate handler); + + private Session _session; + + protected override IDisposable Run(IObserver<TArgs> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var connection = default(IDisposable); + + lock (_gate) + { + // + // A session object holds on to a single handler to the underlying event, feeding + // into a subject. It also ref counts the number of connections to the subject. + // + // When the ref count goes back to zero, the event handler is unregistered, and + // the session will reach out to reset the _session field to null under the _gate + // lock. Future subscriptions will cause a new session to be created. + // + if (_session == null) + _session = new Session(this); + + connection = _session.Connect(observer); + } + + return connection; + } + + class Session + { + private readonly EventProducer<TDelegate, TArgs> _parent; + private readonly Subject<TArgs> _subject; + + private SingleAssignmentDisposable _removeHandler; + private int _count; + + public Session(EventProducer<TDelegate, TArgs> parent) + { + _parent = parent; + _subject = new Subject<TArgs>(); + } + + public IDisposable Connect(IObserver<TArgs> observer) + { + /* + * CALLERS - Ensure this is called under the lock! + * + lock (_parent._gate) */ + { + // + // We connect the given observer to the subject first, before performing any kind + // of initialization which will register an event handler. This is done to ensure + // we don't have a time gap between adding the handler and connecting the user's + // subject, e.g. when the ImmediateScheduler is used. + // + // [OK] Use of unsafe Subscribe: called on a known subject implementation. + // + var connection = _subject.Subscribe/*Unsafe*/(observer); + + if (++_count == 1) + { + try + { + Initialize(); + } + catch (Exception exception) + { + --_count; + connection.Dispose(); + + observer.OnError(exception); + return Disposable.Empty; + } + } + + return Disposable.Create(() => + { + connection.Dispose(); + + lock (_parent._gate) + { + if (--_count == 0) + { + _parent._scheduler.Schedule(_removeHandler.Dispose); + _parent._session = null; + } + } + }); + } + } + + private void Initialize() + { + /* + * CALLERS - Ensure this is called under the lock! + * + lock (_parent._gate) */ + { + // + // When the ref count goes to zero, no-one should be able to perform operations on + // the session object anymore, because it gets nulled out. + // + Debug.Assert(_removeHandler == null); + _removeHandler = new SingleAssignmentDisposable(); + + // + // Conversion code is supposed to be a pure function and shouldn't be run on the + // scheduler, but the add handler call should. Notice the scheduler can be the + // ImmediateScheduler, causing synchronous invocation. This is the default when + // no SynchronizationContext is found (see QueryLanguage.Events.cs and search for + // the GetSchedulerForCurrentContext method). + // + var onNext = _parent.GetHandler(_subject.OnNext); + _parent._scheduler.Schedule(onNext, AddHandler); + } + } + + private IDisposable AddHandler(IScheduler self, TDelegate onNext) + { + var removeHandler = default(IDisposable); + try + { + removeHandler = _parent.AddHandler(onNext); + } + catch (Exception exception) + { + _subject.OnError(exception); + return Disposable.Empty; + } + + // + // We don't propagate the exception to the OnError channel upon Dispose. This is + // not possible at this stage, because we've already auto-detached in the base + // class Producer implementation. Even if we would switch the OnError and auto- + // detach calls, it wouldn't work because the remove handler logic is scheduled + // on the given scheduler, causing asynchrony. We can't block waiting for the + // remove handler to run on the scheduler. + // + _removeHandler.Disposable = removeHandler; + + return Disposable.Empty; + } + } + } + + abstract class ClassicEventProducer<TDelegate, TArgs> : EventProducer<TDelegate, TArgs> + { + private readonly Action<TDelegate> _addHandler; + private readonly Action<TDelegate> _removeHandler; + + public ClassicEventProducer(Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler) + : base(scheduler) + { + _addHandler = addHandler; + _removeHandler = removeHandler; + } + + protected override IDisposable AddHandler(TDelegate handler) + { + _addHandler(handler); + return Disposable.Create(() => _removeHandler(handler)); + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/FromEventPattern.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/FromEventPattern.cs new file mode 100644 index 0000000..76da050 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/FromEventPattern.cs @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Reflection; +using System.Threading; + +// +// BREAKING CHANGE v2 > v1.x - FromEvent[Pattern] now has an implicit SubscribeOn and Publish operation. +// +// See FromEvent.cs for more information. +// +namespace System.Reactive.Linq.Observαble +{ + class FromEventPattern + { + public class τ<TDelegate, TEventArgs> : ClassicEventProducer<TDelegate, EventPattern<TEventArgs>> +#if !NO_EVENTARGS_CONSTRAINT + where TEventArgs : EventArgs +#endif + { + private readonly Func<EventHandler<TEventArgs>, TDelegate> _conversion; + + public τ(Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler) + : base(addHandler, removeHandler, scheduler) + { + } + + public τ(Func<EventHandler<TEventArgs>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler) + : base(addHandler, removeHandler, scheduler) + { + _conversion = conversion; + } + + protected override TDelegate GetHandler(Action<EventPattern<TEventArgs>> onNext) + { + var handler = default(TDelegate); + + if (_conversion == null) + { + Action<object, TEventArgs> h = (sender, eventArgs) => onNext(new EventPattern<TEventArgs>(sender, eventArgs)); + handler = ReflectionUtils.CreateDelegate<TDelegate>(h, typeof(Action<object, TEventArgs>).GetMethod("Invoke")); + } + else + { + handler = _conversion((sender, eventArgs) => onNext(new EventPattern<TEventArgs>(sender, eventArgs))); + } + + return handler; + } + } + + public class τ<TDelegate, TSender, TEventArgs> : ClassicEventProducer<TDelegate, EventPattern<TSender, TEventArgs>> +#if !NO_EVENTARGS_CONSTRAINT + where TEventArgs : EventArgs +#endif + { + public τ(Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler) + : base(addHandler, removeHandler, scheduler) + { + } + + protected override TDelegate GetHandler(Action<EventPattern<TSender, TEventArgs>> onNext) + { + Action<TSender, TEventArgs> h = (sender, eventArgs) => onNext(new EventPattern<TSender, TEventArgs>(sender, eventArgs)); + return ReflectionUtils.CreateDelegate<TDelegate>(h, typeof(Action<TSender, TEventArgs>).GetMethod("Invoke")); + } + } + + public class ρ<TSender, TEventArgs, TResult> : EventProducer<Delegate, TResult> + { + private readonly object _target; + private readonly Type _delegateType; + private readonly MethodInfo _addMethod; + private readonly MethodInfo _removeMethod; + private readonly Func<TSender, TEventArgs, TResult> _getResult; +#if HAS_WINRT + private readonly bool _isWinRT; +#endif + + public ρ(object target, Type delegateType, MethodInfo addMethod, MethodInfo removeMethod, Func<TSender, TEventArgs, TResult> getResult, bool isWinRT, IScheduler scheduler) + : base(scheduler) + { +#if HAS_WINRT + _isWinRT = isWinRT; +#else + System.Diagnostics.Debug.Assert(!isWinRT); +#endif + _target = target; + _delegateType = delegateType; + _addMethod = addMethod; + _removeMethod = removeMethod; + _getResult = getResult; + } + + protected override Delegate GetHandler(Action<TResult> onNext) + { + Action<TSender, TEventArgs> h = (sender, eventArgs) => onNext(_getResult(sender, eventArgs)); + return ReflectionUtils.CreateDelegate(_delegateType, h, typeof(Action<TSender, TEventArgs>).GetMethod("Invoke")); + } + + protected override IDisposable AddHandler(Delegate handler) + { + var removeHandler = default(Action); + + try + { +#if HAS_WINRT + if (_isWinRT) + { + var token = _addMethod.Invoke(_target, new object[] { handler }); + removeHandler = () => _removeMethod.Invoke(_target, new object[] { token }); + } + else +#endif + { + _addMethod.Invoke(_target, new object[] { handler }); + removeHandler = () => _removeMethod.Invoke(_target, new object[] { handler }); + } + } + catch (TargetInvocationException tie) + { + throw tie.InnerException; + } + + return Disposable.Create(() => + { + try + { + removeHandler(); + } + catch (TargetInvocationException tie) + { + throw tie.InnerException; + } + }); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Generate.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Generate.cs new file mode 100644 index 0000000..89ef862 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Generate.cs @@ -0,0 +1,292 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Diagnostics; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Threading; + +namespace System.Reactive.Linq.Observαble +{ + class Generate<TState, TResult> : Producer<TResult> + { + private readonly TState _initialState; + private readonly Func<TState, bool> _condition; + private readonly Func<TState, TState> _iterate; + private readonly Func<TState, TResult> _resultSelector; + private readonly Func<TState, DateTimeOffset> _timeSelectorA; + private readonly Func<TState, TimeSpan> _timeSelectorR; + private readonly IScheduler _scheduler; + + public Generate(TState initialState, Func<TState, bool> condition, Func<TState, TState> iterate, Func<TState, TResult> resultSelector, IScheduler scheduler) + { + _initialState = initialState; + _condition = condition; + _iterate = iterate; + _resultSelector = resultSelector; + _scheduler = scheduler; + } + + public Generate(TState initialState, Func<TState, bool> condition, Func<TState, TState> iterate, Func<TState, TResult> resultSelector, Func<TState, DateTimeOffset> timeSelector, IScheduler scheduler) + { + _initialState = initialState; + _condition = condition; + _iterate = iterate; + _resultSelector = resultSelector; + _timeSelectorA = timeSelector; + _scheduler = scheduler; + } + + public Generate(TState initialState, Func<TState, bool> condition, Func<TState, TState> iterate, Func<TState, TResult> resultSelector, Func<TState, TimeSpan> timeSelector, IScheduler scheduler) + { + _initialState = initialState; + _condition = condition; + _iterate = iterate; + _resultSelector = resultSelector; + _timeSelectorR = timeSelector; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_timeSelectorA != null) + { + var sink = new α(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else if (_timeSelectorR != null) + { + var sink = new δ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + + class α : Sink<TResult> + { + private readonly Generate<TState, TResult> _parent; + + public α(Generate<TState, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private bool _first; + private bool _hasResult; + private TResult _result; + + public IDisposable Run() + { + _first = true; + _hasResult = false; + _result = default(TResult); + + return _parent._scheduler.Schedule(_parent._initialState, InvokeRec); + } + + private IDisposable InvokeRec(IScheduler self, TState state) + { + var time = default(DateTimeOffset); + + if (_hasResult) + base._observer.OnNext(_result); + try + { + if (_first) + _first = false; + else + state = _parent._iterate(state); + _hasResult = _parent._condition(state); + if (_hasResult) + { + _result = _parent._resultSelector(state); + time = _parent._timeSelectorA(state); + } + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return Disposable.Empty; + } + + if (!_hasResult) + { + base._observer.OnCompleted(); + base.Dispose(); + return Disposable.Empty; + } + + return self.Schedule(state, time, InvokeRec); + } + } + + class δ : Sink<TResult> + { + private readonly Generate<TState, TResult> _parent; + + public δ(Generate<TState, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private bool _first; + private bool _hasResult; + private TResult _result; + + public IDisposable Run() + { + _first = true; + _hasResult = false; + _result = default(TResult); + + return _parent._scheduler.Schedule(_parent._initialState, InvokeRec); + } + + private IDisposable InvokeRec(IScheduler self, TState state) + { + var time = default(TimeSpan); + + if (_hasResult) + base._observer.OnNext(_result); + try + { + if (_first) + _first = false; + else + state = _parent._iterate(state); + _hasResult = _parent._condition(state); + if (_hasResult) + { + _result = _parent._resultSelector(state); + time = _parent._timeSelectorR(state); + } + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return Disposable.Empty; + } + + if (!_hasResult) + { + base._observer.OnCompleted(); + base.Dispose(); + return Disposable.Empty; + } + + return self.Schedule(state, time, InvokeRec); + } + } + + class _ : Sink<TResult> + { + private readonly Generate<TState, TResult> _parent; + + public _(Generate<TState, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private TState _state; + private bool _first; + + public IDisposable Run() + { + _state = _parent._initialState; + _first = true; + + var longRunning = _parent._scheduler.AsLongRunning(); + if (longRunning != null) + { + return longRunning.ScheduleLongRunning(Loop); + } + else + { + return _parent._scheduler.Schedule(LoopRec); + } + } + + private void Loop(ICancelable cancel) + { + while (!cancel.IsDisposed) + { + var hasResult = false; + var result = default(TResult); + try + { + if (_first) + _first = false; + else + _state = _parent._iterate(_state); + hasResult = _parent._condition(_state); + if (hasResult) + result = _parent._resultSelector(_state); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + if (hasResult) + base._observer.OnNext(result); + else + break; + } + + if (!cancel.IsDisposed) + base._observer.OnCompleted(); + + base.Dispose(); + } + + private void LoopRec(Action recurse) + { + var hasResult = false; + var result = default(TResult); + try + { + if (_first) + _first = false; + else + _state = _parent._iterate(_state); + hasResult = _parent._condition(_state); + if (hasResult) + result = _parent._resultSelector(_state); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + if (hasResult) + { + base._observer.OnNext(result); + recurse(); + } + else + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/GetEnumerator.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/GetEnumerator.cs new file mode 100644 index 0000000..05bf20b --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/GetEnumerator.cs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF && !NO_CDS +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reactive.Disposables; +using System.Threading; + +namespace System.Reactive.Linq.Observαble +{ + class GetEnumerator<TSource> : IEnumerator<TSource>, IObserver<TSource> + { + private readonly ConcurrentQueue<TSource> _queue; + private TSource _current; + private Exception _error; + private bool _done; + private bool _disposed; + + private readonly SemaphoreSlim _gate; + private readonly SingleAssignmentDisposable _subscription; + + public GetEnumerator() + { + _queue = new ConcurrentQueue<TSource>(); + _gate = new SemaphoreSlim(0); + _subscription = new SingleAssignmentDisposable(); + } + + public IEnumerator<TSource> Run(IObservable<TSource> source) + { + // + // [OK] Use of unsafe Subscribe: non-pretentious exact mirror with the dual GetEnumerator method. + // + _subscription.Disposable = source.Subscribe/*Unsafe*/(this); + return this; + } + + public void OnNext(TSource value) + { + _queue.Enqueue(value); + _gate.Release(); + } + + public void OnError(Exception error) + { + _error = error; + _subscription.Dispose(); + _gate.Release(); + } + + public void OnCompleted() + { + _done = true; + _subscription.Dispose(); + _gate.Release(); + } + + public bool MoveNext() + { + _gate.Wait(); + + if (_disposed) + throw new ObjectDisposedException(""); + + if (_queue.TryDequeue(out _current)) + return true; + + _error.ThrowIfNotNull(); + + Debug.Assert(_done); + + _gate.Release(); // In the (rare) case the user calls MoveNext again we shouldn't block! + return false; + } + + public TSource Current + { + get { return _current; } + } + + object Collections.IEnumerator.Current + { + get { return _current; } + } + + public void Dispose() + { + _subscription.Dispose(); + + _disposed = true; + _gate.Release(); + } + + public void Reset() + { + throw new NotSupportedException(); + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/GroupBy.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/GroupBy.cs new file mode 100644 index 0000000..1e64100 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/GroupBy.cs @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Disposables; +using System.Reactive.Subjects; + +namespace System.Reactive.Linq.Observαble +{ + class GroupBy<TSource, TKey, TElement> : Producer<IGroupedObservable<TKey, TElement>> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, TKey> _keySelector; + private readonly Func<TSource, TElement> _elementSelector; + private readonly IEqualityComparer<TKey> _comparer; + + public GroupBy(IObservable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer) + { + _source = source; + _keySelector = keySelector; + _elementSelector = elementSelector; + _comparer = comparer; + } + + private CompositeDisposable _groupDisposable; + private RefCountDisposable _refCountDisposable; + + protected override IDisposable Run(IObserver<IGroupedObservable<TKey, TElement>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + _groupDisposable = new CompositeDisposable(); + _refCountDisposable = new RefCountDisposable(_groupDisposable); + + var sink = new _(this, observer, cancel); + setSink(sink); + _groupDisposable.Add(_source.SubscribeSafe(sink)); + + return _refCountDisposable; + } + + class _ : Sink<IGroupedObservable<TKey, TElement>>, IObserver<TSource> + { + private readonly GroupBy<TSource, TKey, TElement> _parent; + private readonly Dictionary<TKey, ISubject<TElement>> _map; + private ISubject<TElement> _null; + + public _(GroupBy<TSource, TKey, TElement> parent, IObserver<IGroupedObservable<TKey, TElement>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _map = new Dictionary<TKey, ISubject<TElement>>(_parent._comparer); + } + + public void OnNext(TSource value) + { + var key = default(TKey); + try + { + key = _parent._keySelector(value); + } + catch (Exception exception) + { + Error(exception); + return; + } + + var fireNewMapEntry = false; + var writer = default(ISubject<TElement>); + try + { + // + // Note: The box instruction in the IL will be erased by the JIT in case T is + // a value type. In fact, the whole if block will go away and we'll end + // up with nothing but the TryGetValue check below. + // + // // var fireNewMapEntry = false; + // C:\Projects\Rx\Rx\Experimental\Main\Source\Rx\System.Reactive.Linq\Reactive\Linq\Observable\GroupBy.cs @ 67: + // 000007fb`6d544b80 48c7452800000000 mov qword ptr [rbp+28h],0 + // + // // var writer = default(ISubject<TElement>); + // C:\Projects\Rx\Rx\Experimental\Main\Source\Rx\System.Reactive.Linq\Reactive\Linq\Observable\GroupBy.cs @ 66: + // 000007fb`6d544b88 c6453400 mov byte ptr [rbp+34h],0 + // + // // if (!_map.TryGetValue(key, out writer)) + // C:\Projects\Rx\Rx\Experimental\Main\Source\Rx\System.Reactive.Linq\Reactive\Linq\Observable\GroupBy.cs @ 86: + // 000007fb`6d544b8c 488b4560 mov rax,qword ptr [rbp+60h] + // ... + // + if (key == null) + { + if (_null == null) + { + _null = new Subject<TElement>(); + fireNewMapEntry = true; + } + + writer = _null; + } + else + { + if (!_map.TryGetValue(key, out writer)) + { + writer = new Subject<TElement>(); + _map.Add(key, writer); + fireNewMapEntry = true; + } + } + } + catch (Exception exception) + { + Error(exception); + return; + } + + if (fireNewMapEntry) + { + var group = new GroupedObservable<TKey, TElement>(key, writer, _parent._refCountDisposable); + _observer.OnNext(group); + } + + var element = default(TElement); + try + { + element = _parent._elementSelector(value); + } + catch (Exception exception) + { + Error(exception); + return; + } + + writer.OnNext(element); + } + + public void OnError(Exception error) + { + Error(error); + } + + public void OnCompleted() + { + if (_null != null) + _null.OnCompleted(); + + foreach (var w in _map.Values) + w.OnCompleted(); + + base._observer.OnCompleted(); + base.Dispose(); + } + + private void Error(Exception exception) + { + if (_null != null) + _null.OnError(exception); + + foreach (var w in _map.Values) + w.OnError(exception); + + base._observer.OnError(exception); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/GroupByUntil.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/GroupByUntil.cs new file mode 100644 index 0000000..7d91f73 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/GroupByUntil.cs @@ -0,0 +1,374 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + + #if !NO_PERF +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Disposables; +using System.Reactive.Subjects; + +namespace System.Reactive.Linq.Observαble +{ + class GroupByUntil<TSource, TKey, TElement, TDuration> : Producer<IGroupedObservable<TKey, TElement>> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, TKey> _keySelector; + private readonly Func<TSource, TElement> _elementSelector; + private readonly Func<IGroupedObservable<TKey, TElement>, IObservable<TDuration>> _durationSelector; + private readonly IEqualityComparer<TKey> _comparer; + + public GroupByUntil(IObservable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<IGroupedObservable<TKey, TElement>, IObservable<TDuration>> durationSelector, IEqualityComparer<TKey> comparer) + { + _source = source; + _keySelector = keySelector; + _elementSelector = elementSelector; + _durationSelector = durationSelector; + _comparer = comparer; + } + + private CompositeDisposable _groupDisposable; + private RefCountDisposable _refCountDisposable; + + protected override IDisposable Run(IObserver<IGroupedObservable<TKey, TElement>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + _groupDisposable = new CompositeDisposable(); + _refCountDisposable = new RefCountDisposable(_groupDisposable); + + var sink = new _(this, observer, cancel); + setSink(sink); + _groupDisposable.Add(_source.SubscribeSafe(sink)); + + return _refCountDisposable; + } + + class _ : Sink<IGroupedObservable<TKey, TElement>>, IObserver<TSource> + { + private readonly GroupByUntil<TSource, TKey, TElement, TDuration> _parent; + private readonly Map<TKey, ISubject<TElement>> _map; + private ISubject<TElement> _null; + private object _nullGate; + + public _(GroupByUntil<TSource, TKey, TElement, TDuration> parent, IObserver<IGroupedObservable<TKey, TElement>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _map = new Map<TKey, ISubject<TElement>>(_parent._comparer); + _nullGate = new object(); + } + + public void OnNext(TSource value) + { + var key = default(TKey); + try + { + key = _parent._keySelector(value); + } + catch (Exception exception) + { + Error(exception); + return; + } + + var fireNewMapEntry = false; + var writer = default(ISubject<TElement>); + try + { + // + // Note: The box instruction in the IL will be erased by the JIT in case T is + // a value type. In fact, the whole if block will go away and we'll end + // up with nothing but the GetOrAdd call below. + // + // See GroupBy for more information and confirmation of this fact using + // the SOS debugger extension. + // + if (key == null) + { + lock (_nullGate) + { + if (_null == null) + { + _null = new Subject<TElement>(); + fireNewMapEntry = true; + } + + writer = _null; + } + } + else + { + writer = _map.GetOrAdd(key, () => new Subject<TElement>(), out fireNewMapEntry); + } + } + catch (Exception exception) + { + Error(exception); + return; + } + + if (fireNewMapEntry) + { + var group = new GroupedObservable<TKey, TElement>(key, writer, _parent._refCountDisposable); + + var duration = default(IObservable<TDuration>); + + var durationGroup = new GroupedObservable<TKey, TElement>(key, writer); + try + { + duration = _parent._durationSelector(durationGroup); + } + catch (Exception exception) + { + Error(exception); + return; + } + + lock (base._observer) + base._observer.OnNext(group); + + var md = new SingleAssignmentDisposable(); + _parent._groupDisposable.Add(md); + md.Disposable = duration.SubscribeSafe(new δ(this, key, writer, md)); + } + + var element = default(TElement); + try + { + element = _parent._elementSelector(value); + } + catch (Exception exception) + { + Error(exception); + return; + } + + // + // ISSUE: Rx v1.x shipped without proper handling of the case where the duration + // sequence fires concurrently with the OnNext code path here. In such a + // case, the subject can be completed before we get a chance to send out + // a new element. However, a resurrected group for the same key won't get + // to see the element either. To guard against this case, we'd have to + // check whether the OnNext call below lost the race, and resurrect a new + // group if needed. Unfortunately, this complicates matters when the + // duration selector triggers synchronously (e.g. Return or Empty), which + // causes the group to terminate immediately. We should not get stuck in + // this case, repeatedly trying to resurrect a group that always ends + // before we can send the element into it. Also, users may expect this + // base case to mean no elements will ever be produced, so sending the + // element into the group before starting the duration sequence may not + // be a good idea either. For the time being, we'll leave this as-is and + // revisit the behavior for vNext. Nonetheless, we'll add synchronization + // to ensure no concurrent calls to the subject are made. + // + lock (writer) + writer.OnNext(element); + } + + class δ : IObserver<TDuration> + { + private readonly _ _parent; + private readonly TKey _key; + private readonly ISubject<TElement> _writer; + private readonly IDisposable _self; + + public δ(_ parent, TKey key, ISubject<TElement> writer, IDisposable self) + { + _parent = parent; + _key = key; + _writer = writer; + _self = self; + } + + public void OnNext(TDuration value) + { + OnCompleted(); + } + + public void OnError(Exception error) + { + _parent.Error(error); + _self.Dispose(); + } + + public void OnCompleted() + { + if (_key == null) + { + var @null = default(ISubject<TElement>); + lock (_parent._nullGate) + { + @null = _parent._null; + _parent._null = null; + } + + lock (@null) + @null.OnCompleted(); + } + else + { + if (_parent._map.Remove(_key)) + { + lock (_writer) + _writer.OnCompleted(); + } + } + + _parent._parent._groupDisposable.Remove(_self); + } + } + + public void OnError(Exception error) + { + Error(error); + } + + public void OnCompleted() + { + // + // NOTE: A race with OnCompleted triggered by a duration selector is fine when + // using Subject<T>. It will transition into a terminal state, making one + // of the two calls a no-op by swapping in a DoneObserver<T>. + // + var @null = default(ISubject<TElement>); + lock (_nullGate) + @null = _null; + + if (@null != null) + @null.OnCompleted(); + + foreach (var w in _map.Values) + w.OnCompleted(); + + lock (base._observer) + base._observer.OnCompleted(); + + base.Dispose(); + } + + private void Error(Exception exception) + { + // + // NOTE: A race with OnCompleted triggered by a duration selector is fine when + // using Subject<T>. It will transition into a terminal state, making one + // of the two calls a no-op by swapping in a DoneObserver<T>. + // + var @null = default(ISubject<TElement>); + lock (_nullGate) + @null = _null; + + if (@null != null) + @null.OnError(exception); + + foreach (var w in _map.Values) + w.OnError(exception); + + lock (base._observer) + base._observer.OnError(exception); + + base.Dispose(); + } + } + } + +#if !NO_CDS + class Map<TKey, TValue> + { + private readonly System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue> _map; + + public Map(IEqualityComparer<TKey> comparer) + { + _map = new System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>(comparer); + } + + public TValue GetOrAdd(TKey key, Func<TValue> valueFactory, out bool added) + { + added = false; + + var value = default(TValue); + var newValue = default(TValue); + var hasNewValue = false; + while (true) + { + if (_map.TryGetValue(key, out value)) + break; + + if (!hasNewValue) + { + newValue = valueFactory(); + hasNewValue = true; + } + + if (_map.TryAdd(key, newValue)) + { + added = true; + value = newValue; + break; + } + } + + return value; + } + + public IEnumerable<TValue> Values + { + get + { + return _map.Values.ToArray(); + } + } + + public bool Remove(TKey key) + { + var value = default(TValue); + return _map.TryRemove(key, out value); + } + } +#else + class Map<TKey, TValue> + { + private readonly Dictionary<TKey, TValue> _map; + + public Map(IEqualityComparer<TKey> comparer) + { + _map = new Dictionary<TKey, TValue>(comparer); + } + + public TValue GetOrAdd(TKey key, Func<TValue> valueFactory, out bool added) + { + lock (_map) + { + added = false; + + var value = default(TValue); + if (!_map.TryGetValue(key, out value)) + { + value = valueFactory(); + _map.Add(key, value); + added = true; + } + + return value; + } + } + + public IEnumerable<TValue> Values + { + get + { + lock (_map) + { + return _map.Values.ToArray(); + } + } + } + + public bool Remove(TKey key) + { + lock (_map) + { + return _map.Remove(key); + } + } + } +#endif +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/GroupJoin.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/GroupJoin.cs new file mode 100644 index 0000000..66e49be --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/GroupJoin.cs @@ -0,0 +1,302 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive.Disposables; +using System.Reactive.Subjects; + +namespace System.Reactive.Linq.Observαble +{ + class GroupJoin<TLeft, TRight, TLeftDuration, TRightDuration, TResult> : Producer<TResult> + { + private readonly IObservable<TLeft> _left; + private readonly IObservable<TRight> _right; + private readonly Func<TLeft, IObservable<TLeftDuration>> _leftDurationSelector; + private readonly Func<TRight, IObservable<TRightDuration>> _rightDurationSelector; + private readonly Func<TLeft, IObservable<TRight>, TResult> _resultSelector; + + public GroupJoin(IObservable<TLeft> left, IObservable<TRight> right, Func<TLeft, IObservable<TLeftDuration>> leftDurationSelector, Func<TRight, IObservable<TRightDuration>> rightDurationSelector, Func<TLeft, IObservable<TRight>, TResult> resultSelector) + { + _left = left; + _right = right; + _leftDurationSelector = leftDurationSelector; + _rightDurationSelector = rightDurationSelector; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TResult> + { + private readonly GroupJoin<TLeft, TRight, TLeftDuration, TRightDuration, TResult> _parent; + + public _(GroupJoin<TLeft, TRight, TLeftDuration, TRightDuration, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private CompositeDisposable _group; + private RefCountDisposable _refCount; + + private int _leftID; + private Dictionary<int, IObserver<TRight>> _leftMap; + + private int _rightID; + private Dictionary<int, TRight> _rightMap; + + public IDisposable Run() + { + _gate = new object(); + _group = new CompositeDisposable(); + _refCount = new RefCountDisposable(_group); + + var leftSubscription = new SingleAssignmentDisposable(); + _group.Add(leftSubscription); + _leftID = 0; + _leftMap = new Dictionary<int, IObserver<TRight>>(); + + var rightSubscription = new SingleAssignmentDisposable(); + _group.Add(rightSubscription); + _rightID = 0; + _rightMap = new Dictionary<int, TRight>(); + + leftSubscription.Disposable = _parent._left.SubscribeSafe(new λ(this, leftSubscription)); + rightSubscription.Disposable = _parent._right.SubscribeSafe(new ρ(this, rightSubscription)); + + return _refCount; + } + + class λ : IObserver<TLeft> + { + private readonly _ _parent; + private readonly IDisposable _self; + + public λ(_ parent, IDisposable self) + { + _parent = parent; + _self = self; + } + + private void Expire(int id, IObserver<TRight> group, IDisposable resource) + { + lock (_parent._gate) + if (_parent._leftMap.Remove(id)) + group.OnCompleted(); + + _parent._group.Remove(resource); + } + + public void OnNext(TLeft value) + { + var s = new Subject<TRight>(); + var id = 0; + lock (_parent._gate) + { + id = _parent._leftID++; + _parent._leftMap.Add(id, s); + } + + var window = new WindowObservable<TRight>(s, _parent._refCount); + + // BREAKING CHANGE v2 > v1.x - Order of evaluation or the _leftDurationSelector and _resultSelector now consistent with Join. + var md = new SingleAssignmentDisposable(); + _parent._group.Add(md); + + var duration = default(IObservable<TLeftDuration>); + try + { + duration = _parent._parent._leftDurationSelector(value); + } + catch (Exception exception) + { + OnError(exception); + return; + } + + // BREAKING CHANGE v2 > v1.x - The duration sequence is subscribed to before the result sequence is evaluated. + md.Disposable = duration.SubscribeSafe(new δ(this, id, s, md)); + + var result = default(TResult); + try + { + result = _parent._parent._resultSelector(value, window); + } + catch (Exception exception) + { + OnError(exception); + return; + } + + lock (_parent._gate) + { + _parent._observer.OnNext(result); + + foreach (var rightValue in _parent._rightMap.Values) + { + s.OnNext(rightValue); + } + } + } + + class δ : IObserver<TLeftDuration> + { + private readonly λ _parent; + private readonly int _id; + private readonly IObserver<TRight> _group; + private readonly IDisposable _self; + + public δ(λ parent, int id, IObserver<TRight> group, IDisposable self) + { + _parent = parent; + _id = id; + _group = group; + _self = self; + } + + public void OnNext(TLeftDuration value) + { + _parent.Expire(_id, _group, _self); + } + + public void OnError(Exception error) + { + _parent.OnError(error); + } + + public void OnCompleted() + { + _parent.Expire(_id, _group, _self); + } + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + foreach (var o in _parent._leftMap.Values) + o.OnError(error); + + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + lock (_parent._gate) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + + _self.Dispose(); + } + } + + class ρ : IObserver<TRight> + { + private readonly _ _parent; + private readonly IDisposable _self; + + public ρ(_ parent, IDisposable self) + { + _parent = parent; + _self = self; + } + + private void Expire(int id, IDisposable resource) + { + lock (_parent._gate) + _parent._rightMap.Remove(id); + + _parent._group.Remove(resource); + } + + public void OnNext(TRight value) + { + var id = 0; + lock (_parent._gate) + { + id = _parent._rightID++; + _parent._rightMap.Add(id, value); + } + + var md = new SingleAssignmentDisposable(); + _parent._group.Add(md); + + var duration = default(IObservable<TRightDuration>); + try + { + duration = _parent._parent._rightDurationSelector(value); + } + catch (Exception exception) + { + OnError(exception); + return; + } + md.Disposable = duration.SubscribeSafe(new δ(this, id, md)); + + lock (_parent._gate) + { + foreach (var o in _parent._leftMap.Values) + o.OnNext(value); + } + } + + class δ : IObserver<TRightDuration> + { + private readonly ρ _parent; + private readonly int _id; + private readonly IDisposable _self; + + public δ(ρ parent, int id, IDisposable self) + { + _parent = parent; + _id = id; + _self = self; + } + + public void OnNext(TRightDuration value) + { + _parent.Expire(_id, _self); + } + + public void OnError(Exception error) + { + _parent.OnError(error); + } + + public void OnCompleted() + { + _parent.Expire(_id, _self); + } + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + foreach (var o in _parent._leftMap.Values) + o.OnError(error); + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + _self.Dispose(); + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/If.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/If.cs new file mode 100644 index 0000000..8b1804a --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/If.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class If<TResult> : Producer<TResult>, IEvaluatableObservable<TResult> + { + private readonly Func<bool> _condition; + private readonly IObservable<TResult> _thenSource; + private readonly IObservable<TResult> _elseSource; + + public If(Func<bool> condition, IObservable<TResult> thenSource, IObservable<TResult> elseSource) + { + _condition = condition; + _thenSource = thenSource; + _elseSource = elseSource; + } + + public IObservable<TResult> Eval() + { + return _condition() ? _thenSource : _elseSource; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TResult>, IObserver<TResult> + { + private readonly If<TResult> _parent; + + public _(If<TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + var result = default(IObservable<TResult>); + try + { + result = _parent.Eval(); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return Disposable.Empty; + } + + return result.SubscribeSafe(this); + } + + public void OnNext(TResult value) + { + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/IgnoreElements.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/IgnoreElements.cs new file mode 100644 index 0000000..65441bd --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/IgnoreElements.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class IgnoreElements<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + + public IgnoreElements(IObservable<TSource> source) + { + _source = source; + } + + public IObservable<TSource> Ω() + { + return this; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + public _(IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + public void OnNext(TSource value) + { + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/IsEmpty.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/IsEmpty.cs new file mode 100644 index 0000000..674c6f8 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/IsEmpty.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class IsEmpty<TSource> : Producer<bool> + { + private readonly IObservable<TSource> _source; + + public IsEmpty(IObservable<TSource> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<bool> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<bool>, IObserver<TSource> + { + public _(IObserver<bool> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + public void OnNext(TSource value) + { + base._observer.OnNext(false); + base._observer.OnCompleted(); + base.Dispose(); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(true); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Join.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Join.cs new file mode 100644 index 0000000..13e6ad2 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Join.cs @@ -0,0 +1,336 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult> : Producer<TResult> + { + private readonly IObservable<TLeft> _left; + private readonly IObservable<TRight> _right; + private readonly Func<TLeft, IObservable<TLeftDuration>> _leftDurationSelector; + private readonly Func<TRight, IObservable<TRightDuration>> _rightDurationSelector; + private readonly Func<TLeft, TRight, TResult> _resultSelector; + + public Join(IObservable<TLeft> left, IObservable<TRight> right, Func<TLeft, IObservable<TLeftDuration>> leftDurationSelector, Func<TRight, IObservable<TRightDuration>> rightDurationSelector, Func<TLeft, TRight, TResult> resultSelector) + { + _left = left; + _right = right; + _leftDurationSelector = leftDurationSelector; + _rightDurationSelector = rightDurationSelector; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TResult> + { + private readonly Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult> _parent; + + public _(Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private CompositeDisposable _group; + + private bool _leftDone; + private int _leftID; + private Dictionary<int, TLeft> _leftMap; + + private bool _rightDone; + private int _rightID; + private Dictionary<int, TRight> _rightMap; + + public IDisposable Run() + { + _gate = new object(); + _group = new CompositeDisposable(); + + var leftSubscription = new SingleAssignmentDisposable(); + _group.Add(leftSubscription); + _leftDone = false; + _leftID = 0; + _leftMap = new Dictionary<int, TLeft>(); + + var rightSubscription = new SingleAssignmentDisposable(); + _group.Add(rightSubscription); + _rightDone = false; + _rightID = 0; + _rightMap = new Dictionary<int, TRight>(); + + leftSubscription.Disposable = _parent._left.SubscribeSafe(new λ(this, leftSubscription)); + rightSubscription.Disposable = _parent._right.SubscribeSafe(new ρ(this, rightSubscription)); + + return _group; + } + + class λ : IObserver<TLeft> + { + private readonly _ _parent; + private readonly IDisposable _self; + + public λ(_ parent, IDisposable self) + { + _parent = parent; + _self = self; + } + + private void Expire(int id, IDisposable resource) + { + lock (_parent._gate) + { + if (_parent._leftMap.Remove(id) && _parent._leftMap.Count == 0 && _parent._leftDone) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + + _parent._group.Remove(resource); + } + + public void OnNext(TLeft value) + { + var id = 0; + lock (_parent._gate) + { + id = _parent._leftID++; + _parent._leftMap.Add(id, value); + } + + var md = new SingleAssignmentDisposable(); + _parent._group.Add(md); + + var duration = default(IObservable<TLeftDuration>); + try + { + duration = _parent._parent._leftDurationSelector(value); + } + catch (Exception exception) + { + _parent._observer.OnError(exception); + _parent.Dispose(); + return; + } + + md.Disposable = duration.SubscribeSafe(new δ(this, id, md)); + + lock (_parent._gate) + { + foreach (var rightValue in _parent._rightMap.Values) + { + var result = default(TResult); + try + { + result = _parent._parent._resultSelector(value, rightValue); + } + catch (Exception exception) + { + _parent._observer.OnError(exception); + _parent.Dispose(); + return; + } + + _parent._observer.OnNext(result); + } + } + } + + class δ : IObserver<TLeftDuration> + { + private readonly λ _parent; + private readonly int _id; + private readonly IDisposable _self; + + public δ(λ parent, int id, IDisposable self) + { + _parent = parent; + _id = id; + _self = self; + } + + public void OnNext(TLeftDuration value) + { + _parent.Expire(_id, _self); + } + + public void OnError(Exception error) + { + _parent.OnError(error); + } + + public void OnCompleted() + { + _parent.Expire(_id, _self); + } + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + lock (_parent._gate) + { + _parent._leftDone = true; + if (_parent._rightDone || _parent._leftMap.Count == 0) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + else + { + _self.Dispose(); + } + } + } + } + + class ρ : IObserver<TRight> + { + private readonly _ _parent; + private readonly IDisposable _self; + + public ρ(_ parent, IDisposable self) + { + _parent = parent; + _self = self; + } + + private void Expire(int id, IDisposable resource) + { + lock (_parent._gate) + { + if (_parent._rightMap.Remove(id) && _parent._rightMap.Count == 0 && _parent._rightDone) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + + _parent._group.Remove(resource); + } + + public void OnNext(TRight value) + { + var id = 0; + lock (_parent._gate) + { + id = _parent._rightID++; + _parent._rightMap.Add(id, value); + } + + var md = new SingleAssignmentDisposable(); + _parent._group.Add(md); + + var duration = default(IObservable<TRightDuration>); + try + { + duration = _parent._parent._rightDurationSelector(value); + } + catch (Exception exception) + { + _parent._observer.OnError(exception); + _parent.Dispose(); + return; + } + + md.Disposable = duration.SubscribeSafe(new δ(this, id, md)); + + lock (_parent._gate) + { + foreach (var leftValue in _parent._leftMap.Values) + { + var result = default(TResult); + try + { + result = _parent._parent._resultSelector(leftValue, value); + } + catch (Exception exception) + { + _parent._observer.OnError(exception); + _parent.Dispose(); + return; + } + + _parent._observer.OnNext(result); + } + } + } + + class δ : IObserver<TRightDuration> + { + private readonly ρ _parent; + private readonly int _id; + private readonly IDisposable _self; + + public δ(ρ parent, int id, IDisposable self) + { + _parent = parent; + _id = id; + _self = self; + } + + public void OnNext(TRightDuration value) + { + _parent.Expire(_id, _self); + } + + public void OnError(Exception error) + { + _parent.OnError(error); + } + + public void OnCompleted() + { + _parent.Expire(_id, _self); + } + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + lock (_parent._gate) + { + _parent._rightDone = true; + if (_parent._leftDone || _parent._rightMap.Count == 0) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + else + { + _self.Dispose(); + } + } + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/LastAsync.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/LastAsync.cs new file mode 100644 index 0000000..1ecd930 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/LastAsync.cs @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class LastAsync<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, bool> _predicate; + private readonly bool _throwOnEmpty; + + public LastAsync(IObservable<TSource> source, Func<TSource, bool> predicate, bool throwOnEmpty) + { + _source = source; + _predicate = predicate; + _throwOnEmpty = throwOnEmpty; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_predicate != null) + { + var sink = new π(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly LastAsync<TSource> _parent; + private TSource _value; + private bool _seenValue; + + public _(LastAsync<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + + _value = default(TSource); + _seenValue = false; + } + + public void OnNext(TSource value) + { + _value = value; + _seenValue = true; + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_seenValue && _parent._throwOnEmpty) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(_value); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + + class π : Sink<TSource>, IObserver<TSource> + { + private readonly LastAsync<TSource> _parent; + private TSource _value; + private bool _seenValue; + + public π(LastAsync<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + + _value = default(TSource); + _seenValue = false; + } + + public void OnNext(TSource value) + { + var b = false; + + try + { + b = _parent._predicate(value); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + if (b) + { + _value = value; + _seenValue = true; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_seenValue && _parent._throwOnEmpty) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_MATCHING_ELEMENTS)); + } + else + { + base._observer.OnNext(_value); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Latest.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Latest.cs new file mode 100644 index 0000000..2699797 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Latest.cs @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive.Threading; +using System.Threading; + +namespace System.Reactive.Linq.Observαble +{ + class Latest<TSource> : PushToPullAdapter<TSource, TSource> + { + public Latest(IObservable<TSource> source) + : base(source) + { + } + + protected override PushToPullSink<TSource, TSource> Run(IDisposable subscription) + { + return new _(subscription); + } + + class _ : PushToPullSink<TSource, TSource> + { + private readonly object _gate; + +#if !NO_CDS + private readonly SemaphoreSlim _semaphore; +#else + private readonly Semaphore _semaphore; +#endif + + public _(IDisposable subscription) + : base(subscription) + { + _gate = new object(); + +#if !NO_CDS + _semaphore = new SemaphoreSlim(0, 1); +#else + _semaphore = new Semaphore(0, 1); +#endif + } + + private bool _notificationAvailable; + private NotificationKind _kind; + private TSource _value; + private Exception _error; + + public override void OnNext(TSource value) + { + var lackedValue = false; + lock (_gate) + { + lackedValue = !_notificationAvailable; + _notificationAvailable = true; + _kind = NotificationKind.OnNext; + _value = value; + } + + if (lackedValue) + _semaphore.Release(); + } + + public override void OnError(Exception error) + { + base.Dispose(); + + var lackedValue = false; + lock (_gate) + { + lackedValue = !_notificationAvailable; + _notificationAvailable = true; + _kind = NotificationKind.OnError; + _error = error; + } + + if (lackedValue) + _semaphore.Release(); + } + + public override void OnCompleted() + { + base.Dispose(); + + var lackedValue = false; + lock (_gate) + { + lackedValue = !_notificationAvailable; + _notificationAvailable = true; + _kind = NotificationKind.OnCompleted; + } + + if (lackedValue) + _semaphore.Release(); + } + + public override bool TryMoveNext(out TSource current) + { + var kind = default(NotificationKind); + var value = default(TSource); + var error = default(Exception); + +#if !NO_CDS + _semaphore.Wait(); +#else + _semaphore.WaitOne(); +#endif + + lock (_gate) + { + kind = _kind; + + switch (kind) + { + case NotificationKind.OnNext: + value = _value; + break; + case NotificationKind.OnError: + error = _error; + break; + } + + _notificationAvailable = false; + } + + switch (kind) + { + case NotificationKind.OnNext: + current = _value; + return true; + case NotificationKind.OnError: + error.Throw(); + break; + case NotificationKind.OnCompleted: + break; + } + + current = default(TSource); + return false; + } + } + } +} +#endif diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/LongCount.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/LongCount.cs new file mode 100644 index 0000000..0108e18 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/LongCount.cs @@ -0,0 +1,124 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class LongCount<TSource> : Producer<long> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, bool> _predicate; + + public LongCount(IObservable<TSource> source) + { + _source = source; + } + + public LongCount(IObservable<TSource> source, Func<TSource, bool> predicate) + { + _source = source; + _predicate = predicate; + } + + protected override IDisposable Run(IObserver<long> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_predicate == null) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new π(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + } + + class _ : Sink<long>, IObserver<TSource> + { + private long _count; + + public _(IObserver<long> observer, IDisposable cancel) + : base(observer, cancel) + { + _count = 0L; + } + + public void OnNext(TSource value) + { + try + { + checked + { + _count++; + } + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_count); + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class π : Sink<long>, IObserver<TSource> + { + private readonly LongCount<TSource> _parent; + private long _count; + + public π(LongCount<TSource> parent, IObserver<long> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _count = 0L; + } + + public void OnNext(TSource value) + { + try + { + checked + { + if (_parent._predicate(value)) + _count++; + } + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_count); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Materialize.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Materialize.cs new file mode 100644 index 0000000..102267d --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Materialize.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class Materialize<TSource> : Producer<Notification<TSource>> + { + private readonly IObservable<TSource> _source; + + public Materialize(IObservable<TSource> source) + { + _source = source; + } + + public IObservable<TSource> Dematerialize() + { + return _source.AsObservable(); + } + + protected override IDisposable Run(IObserver<Notification<TSource>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<Notification<TSource>>, IObserver<TSource> + { + public _(IObserver<Notification<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + public void OnNext(TSource value) + { + base._observer.OnNext(Notification.CreateOnNext<TSource>(value)); + } + + public void OnError(Exception error) + { + base._observer.OnNext(Notification.CreateOnError<TSource>(error)); + base._observer.OnCompleted(); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(Notification.CreateOnCompleted<TSource>()); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Max.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Max.cs new file mode 100644 index 0000000..da339da --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Max.cs @@ -0,0 +1,792 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; + +namespace System.Reactive.Linq.Observαble +{ + class Max<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly IComparer<TSource> _comparer; + + public Max(IObservable<TSource> source, IComparer<TSource> comparer) + { + _source = source; + _comparer = comparer; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + // LINQ to Objects makes this distinction in order to make [Max|Max] of an empty collection of reference type objects equal to null. + if (default(TSource) == null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new δ(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + } + + class δ : Sink<TSource>, IObserver<TSource> + { + private readonly Max<TSource> _parent; + private bool _hasValue; + private TSource _lastValue; + + public δ(Max<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + + _hasValue = false; + _lastValue = default(TSource); + } + + public void OnNext(TSource value) + { + if (_hasValue) + { + var comparison = 0; + + try + { + comparison = _parent._comparer.Compare(value, _lastValue); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + if (comparison > 0) + { + _lastValue = value; + } + } + else + { + _hasValue = true; + _lastValue = value; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_hasValue) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Max<TSource> _parent; + private TSource _lastValue; + + public _(Max<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + + _lastValue = default(TSource); + } + + public void OnNext(TSource value) + { + if (value != null) + { + if (_lastValue == null) + { + _lastValue = value; + } + else + { + var comparison = 0; + + try + { + comparison = _parent._comparer.Compare(value, _lastValue); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + if (comparison > 0) + { + _lastValue = value; + } + } + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class MaxDouble : Producer<double> + { + private readonly IObservable<double> _source; + + public MaxDouble(IObservable<double> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<double> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<double>, IObserver<double> + { + private bool _hasValue; + private double _lastValue; + + public _(IObserver<double> observer, IDisposable cancel) + : base(observer, cancel) + { + _hasValue = false; + _lastValue = default(double); + } + + public void OnNext(double value) + { + if (_hasValue) + { + if (value > _lastValue || double.IsNaN(value)) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + _hasValue = true; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_hasValue) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + } + + class MaxSingle : Producer<float> + { + private readonly IObservable<float> _source; + + public MaxSingle(IObservable<float> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<float> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<float>, IObserver<float> + { + private bool _hasValue; + private float _lastValue; + + public _(IObserver<float> observer, IDisposable cancel) + : base(observer, cancel) + { + _hasValue = false; + _lastValue = default(float); + } + + public void OnNext(float value) + { + if (_hasValue) + { + if (value > _lastValue || float.IsNaN(value)) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + _hasValue = true; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_hasValue) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + } + + class MaxDecimal : Producer<decimal> + { + private readonly IObservable<decimal> _source; + + public MaxDecimal(IObservable<decimal> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<decimal> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<decimal>, IObserver<decimal> + { + private bool _hasValue; + private decimal _lastValue; + + public _(IObserver<decimal> observer, IDisposable cancel) + : base(observer, cancel) + { + _hasValue = false; + _lastValue = default(decimal); + } + + public void OnNext(decimal value) + { + if (_hasValue) + { + if (value > _lastValue) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + _hasValue = true; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_hasValue) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + } + + class MaxInt32 : Producer<int> + { + private readonly IObservable<int> _source; + + public MaxInt32(IObservable<int> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<int> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<int>, IObserver<int> + { + private bool _hasValue; + private int _lastValue; + + public _(IObserver<int> observer, IDisposable cancel) + : base(observer, cancel) + { + _hasValue = false; + _lastValue = default(int); + } + + public void OnNext(int value) + { + if (_hasValue) + { + if (value > _lastValue) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + _hasValue = true; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_hasValue) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + } + + class MaxInt64 : Producer<long> + { + private readonly IObservable<long> _source; + + public MaxInt64(IObservable<long> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<long> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<long>, IObserver<long> + { + private bool _hasValue; + private long _lastValue; + + public _(IObserver<long> observer, IDisposable cancel) + : base(observer, cancel) + { + _hasValue = false; + _lastValue = default(long); + } + + public void OnNext(long value) + { + if (_hasValue) + { + if (value > _lastValue) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + _hasValue = true; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_hasValue) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + } + + class MaxDoubleNullable : Producer<double?> + { + private readonly IObservable<double?> _source; + + public MaxDoubleNullable(IObservable<double?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<double?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<double?>, IObserver<double?> + { + private double? _lastValue; + + public _(IObserver<double?> observer, IDisposable cancel) + : base(observer, cancel) + { + _lastValue = default(double?); + } + + public void OnNext(double? value) + { + if (!value.HasValue) + return; + + if (_lastValue.HasValue) + { + if (value > _lastValue || double.IsNaN((double)value)) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class MaxSingleNullable : Producer<float?> + { + private readonly IObservable<float?> _source; + + public MaxSingleNullable(IObservable<float?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<float?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<float?>, IObserver<float?> + { + private float? _lastValue; + + public _(IObserver<float?> observer, IDisposable cancel) + : base(observer, cancel) + { + _lastValue = default(float?); + } + + public void OnNext(float? value) + { + if (!value.HasValue) + return; + + if (_lastValue.HasValue) + { + if (value > _lastValue || float.IsNaN((float)value)) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class MaxDecimalNullable : Producer<decimal?> + { + private readonly IObservable<decimal?> _source; + + public MaxDecimalNullable(IObservable<decimal?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<decimal?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<decimal?>, IObserver<decimal?> + { + private decimal? _lastValue; + + public _(IObserver<decimal?> observer, IDisposable cancel) + : base(observer, cancel) + { + _lastValue = default(decimal?); + } + + public void OnNext(decimal? value) + { + if (!value.HasValue) + return; + + if (_lastValue.HasValue) + { + if (value > _lastValue) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class MaxInt32Nullable : Producer<int?> + { + private readonly IObservable<int?> _source; + + public MaxInt32Nullable(IObservable<int?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<int?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<int?>, IObserver<int?> + { + private int? _lastValue; + + public _(IObserver<int?> observer, IDisposable cancel) + : base(observer, cancel) + { + _lastValue = default(int?); + } + + public void OnNext(int? value) + { + if (!value.HasValue) + return; + + if (_lastValue.HasValue) + { + if (value > _lastValue) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class MaxInt64Nullable : Producer<long?> + { + private readonly IObservable<long?> _source; + + public MaxInt64Nullable(IObservable<long?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<long?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<long?>, IObserver<long?> + { + private long? _lastValue; + + public _(IObserver<long?> observer, IDisposable cancel) + : base(observer, cancel) + { + _lastValue = default(long?); + } + + public void OnNext(long? value) + { + if (!value.HasValue) + return; + + if (_lastValue.HasValue) + { + if (value > _lastValue) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/MaxBy.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/MaxBy.cs new file mode 100644 index 0000000..52a9a42 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/MaxBy.cs @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; + +namespace System.Reactive.Linq.Observαble +{ + class MaxBy<TSource, TKey> : Producer<IList<TSource>> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, TKey> _keySelector; + private readonly IComparer<TKey> _comparer; + + public MaxBy(IObservable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer) + { + _source = source; + _keySelector = keySelector; + _comparer = comparer; + } + + protected override IDisposable Run(IObserver<IList<TSource>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<IList<TSource>>, IObserver<TSource> + { + private readonly MaxBy<TSource, TKey> _parent; + private bool _hasValue; + private TKey _lastKey; + private List<TSource> _list; + + public _(MaxBy<TSource, TKey> parent, IObserver<IList<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + + _hasValue = false; + _lastKey = default(TKey); + _list = new List<TSource>(); + } + + public void OnNext(TSource value) + { + var key = default(TKey); + try + { + key = _parent._keySelector(value); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + var comparison = 0; + + if (!_hasValue) + { + _hasValue = true; + _lastKey = key; + } + else + { + try + { + comparison = _parent._comparer.Compare(key, _lastKey); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + } + + if (comparison > 0) + { + _lastKey = key; + _list.Clear(); + } + + if (comparison >= 0) + { + _list.Add(value); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_list); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Merge.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Merge.cs new file mode 100644 index 0000000..a27c3c3 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Merge.cs @@ -0,0 +1,403 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive.Disposables; + +#if !NO_TPL +using System.Threading; +using System.Threading.Tasks; +#endif + +namespace System.Reactive.Linq.Observαble +{ + class Merge<TSource> : Producer<TSource> + { + private readonly IObservable<IObservable<TSource>> _sources; + private readonly int _maxConcurrent; + + public Merge(IObservable<IObservable<TSource>> sources) + { + _sources = sources; + } + + public Merge(IObservable<IObservable<TSource>> sources, int maxConcurrent) + { + _sources = sources; + _maxConcurrent = maxConcurrent; + } + +#if !NO_TPL + private readonly IObservable<Task<TSource>> _sourcesT; + + public Merge(IObservable<Task<TSource>> sources) + { + _sourcesT = sources; + } +#endif + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_maxConcurrent > 0) + { + var sink = new μ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } +#if !NO_TPL + else if (_sourcesT != null) + { + var sink = new τ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } +#endif + else + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + + class _ : Sink<TSource>, IObserver<IObservable<TSource>> + { + private readonly Merge<TSource> _parent; + + public _(Merge<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private bool _isStopped; + private CompositeDisposable _group; + private SingleAssignmentDisposable _sourceSubscription; + + public IDisposable Run() + { + _gate = new object(); + _isStopped = false; + _group = new CompositeDisposable(); + + _sourceSubscription = new SingleAssignmentDisposable(); + _group.Add(_sourceSubscription); + _sourceSubscription.Disposable = _parent._sources.SubscribeSafe(this); + + return _group; + } + + public void OnNext(IObservable<TSource> value) + { + var innerSubscription = new SingleAssignmentDisposable(); + _group.Add(innerSubscription); + innerSubscription.Disposable = value.SubscribeSafe(new ι(this, innerSubscription)); + } + + public void OnError(Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + _isStopped = true; + if (_group.Count == 1) + { + // + // Notice there can be a race between OnCompleted of the source and any + // of the inner sequences, where both see _group.Count == 1, and one is + // waiting for the lock. There won't be a double OnCompleted observation + // though, because the call to Dispose silences the observer by swapping + // in a NopObserver<T>. + // + lock (_gate) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + else + { + _sourceSubscription.Dispose(); + } + } + + class ι : IObserver<TSource> + { + private readonly _ _parent; + private readonly IDisposable _self; + + public ι(_ parent, IDisposable self) + { + _parent = parent; + _self = self; + } + + public void OnNext(TSource value) + { + lock (_parent._gate) + _parent._observer.OnNext(value); + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + _parent._group.Remove(_self); + if (_parent._isStopped && _parent._group.Count == 1) + { + // + // Notice there can be a race between OnCompleted of the source and any + // of the inner sequences, where both see _group.Count == 1, and one is + // waiting for the lock. There won't be a double OnCompleted observation + // though, because the call to Dispose silences the observer by swapping + // in a NopObserver<T>. + // + lock (_parent._gate) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + } + } + } + + class μ : Sink<TSource>, IObserver<IObservable<TSource>> + { + private readonly Merge<TSource> _parent; + + public μ(Merge<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private Queue<IObservable<TSource>> _q; + private bool _isStopped; + private SingleAssignmentDisposable _sourceSubscription; + private CompositeDisposable _group; + private int _activeCount = 0; + + public IDisposable Run() + { + _gate = new object(); + _q = new Queue<IObservable<TSource>>(); + _isStopped = false; + _activeCount = 0; + + _group = new CompositeDisposable(); + _sourceSubscription = new SingleAssignmentDisposable(); + _sourceSubscription.Disposable = _parent._sources.SubscribeSafe(this); + _group.Add(_sourceSubscription); + + return _group; + } + + public void OnNext(IObservable<TSource> value) + { + lock (_gate) + { + if (_activeCount < _parent._maxConcurrent) + { + _activeCount++; + Subscribe(value); + } + else + _q.Enqueue(value); + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + _isStopped = true; + if (_activeCount == 0) + { + base._observer.OnCompleted(); + base.Dispose(); + } + else + { + _sourceSubscription.Dispose(); + } + } + } + + private void Subscribe(IObservable<TSource> innerSource) + { + var subscription = new SingleAssignmentDisposable(); + _group.Add(subscription); + subscription.Disposable = innerSource.SubscribeSafe(new ι(this, subscription)); + } + + class ι : IObserver<TSource> + { + private readonly μ _parent; + private readonly IDisposable _self; + + public ι(μ parent, IDisposable self) + { + _parent = parent; + _self = self; + } + + public void OnNext(TSource value) + { + lock (_parent._gate) + _parent._observer.OnNext(value); + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + _parent._group.Remove(_self); + lock (_parent._gate) + { + if (_parent._q.Count > 0) + { + var s = _parent._q.Dequeue(); + _parent.Subscribe(s); + } + else + { + _parent._activeCount--; + if (_parent._isStopped && _parent._activeCount == 0) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + } + } + } + } + +#if !NO_TPL +#pragma warning disable 0420 + class τ : Sink<TSource>, IObserver<Task<TSource>> + { + private readonly Merge<TSource> _parent; + + public τ(Merge<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private volatile int _count; + + public IDisposable Run() + { + _gate = new object(); + _count = 1; + + return _parent._sourcesT.SubscribeSafe(this); + } + + public void OnNext(Task<TSource> value) + { + Interlocked.Increment(ref _count); + if (value.IsCompleted) + { + OnCompletedTask(value); + } + else + { + value.ContinueWith(OnCompletedTask); + } + } + + private void OnCompletedTask(Task<TSource> task) + { + switch (task.Status) + { + case TaskStatus.RanToCompletion: + { + lock (_gate) + base._observer.OnNext(task.Result); + + OnCompleted(); + } + break; + case TaskStatus.Faulted: + { + lock (_gate) + { + base._observer.OnError(task.Exception.InnerException); + base.Dispose(); + } + } + break; + case TaskStatus.Canceled: + { + lock (_gate) + { + base._observer.OnError(new TaskCanceledException(task)); + base.Dispose(); + } + } + break; + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + if (Interlocked.Decrement(ref _count) == 0) + { + lock (_gate) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + } +#pragma warning restore 0420 +#endif + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Min.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Min.cs new file mode 100644 index 0000000..f7b6197 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Min.cs @@ -0,0 +1,792 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; + +namespace System.Reactive.Linq.Observαble +{ + class Min<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly IComparer<TSource> _comparer; + + public Min(IObservable<TSource> source, IComparer<TSource> comparer) + { + _source = source; + _comparer = comparer; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + // LINQ to Objects makes this distinction in order to make [Min|Max] of an empty collection of reference type objects equal to null. + if (default(TSource) == null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new δ(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + } + + class δ : Sink<TSource>, IObserver<TSource> + { + private readonly Min<TSource> _parent; + private bool _hasValue; + private TSource _lastValue; + + public δ(Min<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + + _hasValue = false; + _lastValue = default(TSource); + } + + public void OnNext(TSource value) + { + if (_hasValue) + { + var comparison = 0; + + try + { + comparison = _parent._comparer.Compare(value, _lastValue); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + if (comparison < 0) + { + _lastValue = value; + } + } + else + { + _hasValue = true; + _lastValue = value; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_hasValue) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Min<TSource> _parent; + private TSource _lastValue; + + public _(Min<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + + _lastValue = default(TSource); + } + + public void OnNext(TSource value) + { + if (value != null) + { + if (_lastValue == null) + { + _lastValue = value; + } + else + { + var comparison = 0; + + try + { + comparison = _parent._comparer.Compare(value, _lastValue); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + if (comparison < 0) + { + _lastValue = value; + } + } + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class MinDouble : Producer<double> + { + private readonly IObservable<double> _source; + + public MinDouble(IObservable<double> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<double> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<double>, IObserver<double> + { + private bool _hasValue; + private double _lastValue; + + public _(IObserver<double> observer, IDisposable cancel) + : base(observer, cancel) + { + _hasValue = false; + _lastValue = default(double); + } + + public void OnNext(double value) + { + if (_hasValue) + { + if (value < _lastValue || double.IsNaN(value)) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + _hasValue = true; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_hasValue) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + } + + class MinSingle : Producer<float> + { + private readonly IObservable<float> _source; + + public MinSingle(IObservable<float> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<float> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<float>, IObserver<float> + { + private bool _hasValue; + private float _lastValue; + + public _(IObserver<float> observer, IDisposable cancel) + : base(observer, cancel) + { + _hasValue = false; + _lastValue = default(float); + } + + public void OnNext(float value) + { + if (_hasValue) + { + if (value < _lastValue || float.IsNaN(value)) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + _hasValue = true; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_hasValue) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + } + + class MinDecimal : Producer<decimal> + { + private readonly IObservable<decimal> _source; + + public MinDecimal(IObservable<decimal> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<decimal> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<decimal>, IObserver<decimal> + { + private bool _hasValue; + private decimal _lastValue; + + public _(IObserver<decimal> observer, IDisposable cancel) + : base(observer, cancel) + { + _hasValue = false; + _lastValue = default(decimal); + } + + public void OnNext(decimal value) + { + if (_hasValue) + { + if (value < _lastValue) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + _hasValue = true; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_hasValue) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + } + + class MinInt32 : Producer<int> + { + private readonly IObservable<int> _source; + + public MinInt32(IObservable<int> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<int> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<int>, IObserver<int> + { + private bool _hasValue; + private int _lastValue; + + public _(IObserver<int> observer, IDisposable cancel) + : base(observer, cancel) + { + _hasValue = false; + _lastValue = default(int); + } + + public void OnNext(int value) + { + if (_hasValue) + { + if (value < _lastValue) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + _hasValue = true; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_hasValue) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + } + + class MinInt64 : Producer<long> + { + private readonly IObservable<long> _source; + + public MinInt64(IObservable<long> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<long> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<long>, IObserver<long> + { + private bool _hasValue; + private long _lastValue; + + public _(IObserver<long> observer, IDisposable cancel) + : base(observer, cancel) + { + _hasValue = false; + _lastValue = default(long); + } + + public void OnNext(long value) + { + if (_hasValue) + { + if (value < _lastValue) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + _hasValue = true; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_hasValue) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + } + + class MinDoubleNullable : Producer<double?> + { + private readonly IObservable<double?> _source; + + public MinDoubleNullable(IObservable<double?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<double?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<double?>, IObserver<double?> + { + private double? _lastValue; + + public _(IObserver<double?> observer, IDisposable cancel) + : base(observer, cancel) + { + _lastValue = default(double?); + } + + public void OnNext(double? value) + { + if (!value.HasValue) + return; + + if (_lastValue.HasValue) + { + if (value < _lastValue || double.IsNaN((double)value)) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class MinSingleNullable : Producer<float?> + { + private readonly IObservable<float?> _source; + + public MinSingleNullable(IObservable<float?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<float?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<float?>, IObserver<float?> + { + private float? _lastValue; + + public _(IObserver<float?> observer, IDisposable cancel) + : base(observer, cancel) + { + _lastValue = default(float?); + } + + public void OnNext(float? value) + { + if (!value.HasValue) + return; + + if (_lastValue.HasValue) + { + if (value < _lastValue || float.IsNaN((float)value)) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class MinDecimalNullable : Producer<decimal?> + { + private readonly IObservable<decimal?> _source; + + public MinDecimalNullable(IObservable<decimal?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<decimal?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<decimal?>, IObserver<decimal?> + { + private decimal? _lastValue; + + public _(IObserver<decimal?> observer, IDisposable cancel) + : base(observer, cancel) + { + _lastValue = default(decimal?); + } + + public void OnNext(decimal? value) + { + if (!value.HasValue) + return; + + if (_lastValue.HasValue) + { + if (value < _lastValue) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class MinInt32Nullable : Producer<int?> + { + private readonly IObservable<int?> _source; + + public MinInt32Nullable(IObservable<int?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<int?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<int?>, IObserver<int?> + { + private int? _lastValue; + + public _(IObserver<int?> observer, IDisposable cancel) + : base(observer, cancel) + { + _lastValue = default(int?); + } + + public void OnNext(int? value) + { + if (!value.HasValue) + return; + + if (_lastValue.HasValue) + { + if (value < _lastValue) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class MinInt64Nullable : Producer<long?> + { + private readonly IObservable<long?> _source; + + public MinInt64Nullable(IObservable<long?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<long?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<long?>, IObserver<long?> + { + private long? _lastValue; + + public _(IObserver<long?> observer, IDisposable cancel) + : base(observer, cancel) + { + _lastValue = default(long?); + } + + public void OnNext(long? value) + { + if (!value.HasValue) + return; + + if (_lastValue.HasValue) + { + if (value < _lastValue) + { + _lastValue = value; + } + } + else + { + _lastValue = value; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_lastValue); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/MinBy.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/MinBy.cs new file mode 100644 index 0000000..a32bf92 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/MinBy.cs @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; + +namespace System.Reactive.Linq.Observαble +{ + class MinBy<TSource, TKey> : Producer<IList<TSource>> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, TKey> _keySelector; + private readonly IComparer<TKey> _comparer; + + public MinBy(IObservable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer) + { + _source = source; + _keySelector = keySelector; + _comparer = comparer; + } + + protected override IDisposable Run(IObserver<IList<TSource>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<IList<TSource>>, IObserver<TSource> + { + private readonly MinBy<TSource, TKey> _parent; + private bool _hasValue; + private TKey _lastKey; + private List<TSource> _list; + + public _(MinBy<TSource, TKey> parent, IObserver<IList<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + + _hasValue = false; + _lastKey = default(TKey); + _list = new List<TSource>(); + } + + public void OnNext(TSource value) + { + var key = default(TKey); + try + { + key = _parent._keySelector(value); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + var comparison = 0; + + if (!_hasValue) + { + _hasValue = true; + _lastKey = key; + } + else + { + try + { + comparison = _parent._comparer.Compare(key, _lastKey); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + } + + if (comparison < 0) + { + _lastKey = key; + _list.Clear(); + } + + if (comparison <= 0) + { + _list.Add(value); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_list); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/MostRecent.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/MostRecent.cs new file mode 100644 index 0000000..937d811 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/MostRecent.cs @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Threading; + +namespace System.Reactive.Linq.Observαble +{ + class MostRecent<TSource> : PushToPullAdapter<TSource, TSource> + { + private readonly TSource _initialValue; + + public MostRecent(IObservable<TSource> source, TSource initialValue) + : base(source) + { + _initialValue = initialValue; + } + + protected override PushToPullSink<TSource, TSource> Run(IDisposable subscription) + { + return new _(_initialValue, subscription); + } + + class _ : PushToPullSink<TSource, TSource> + { + public _(TSource initialValue, IDisposable subscription) + : base(subscription) + { + _kind = NotificationKind.OnNext; + _value = initialValue; + } + + private volatile NotificationKind _kind; + private TSource _value; + private Exception _error; + + public override void OnNext(TSource value) + { + _value = value; + _kind = NotificationKind.OnNext; // Write last! + } + + public override void OnError(Exception error) + { + base.Dispose(); + + _error = error; + _kind = NotificationKind.OnError; // Write last! + } + + public override void OnCompleted() + { + base.Dispose(); + + _kind = NotificationKind.OnCompleted; // Write last! + } + + public override bool TryMoveNext(out TSource current) + { + // + // Notice the _kind field is marked volatile and read before the other fields. + // + // In case of a concurrent change, we may read a stale OnNext value, which is + // fine because this push-to-pull adapter is about sampling. + // + switch (_kind) + { + case NotificationKind.OnNext: + current = _value; + return true; + case NotificationKind.OnError: + _error.Throw(); + break; + case NotificationKind.OnCompleted: + break; + } + + current = default(TSource); + return false; + } + } + } +} +#endif diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Multicast.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Multicast.cs new file mode 100644 index 0000000..4a77300 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Multicast.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Disposables; +using System.Reactive.Subjects; + +namespace System.Reactive.Linq.Observαble +{ + class Multicast<TSource, TIntermediate, TResult> : Producer<TResult> + { + private readonly IObservable<TSource> _source; + private readonly Func<ISubject<TSource, TIntermediate>> _subjectSelector; + private readonly Func<IObservable<TIntermediate>, IObservable<TResult>> _selector; + + public Multicast(IObservable<TSource> source, Func<ISubject<TSource, TIntermediate>> subjectSelector, Func<IObservable<TIntermediate>, IObservable<TResult>> selector) + { + _source = source; + _subjectSelector = subjectSelector; + _selector = selector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TResult>, IObserver<TResult> + { + private readonly Multicast<TSource, TIntermediate, TResult> _parent; + + public _(Multicast<TSource, TIntermediate, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + var observable = default(IObservable<TResult>); + var connectable = default(IConnectableObservable<TIntermediate>); + try + { + var subject = _parent._subjectSelector(); + connectable = new ConnectableObservable<TSource, TIntermediate>(_parent._source, subject); + observable = _parent._selector(connectable); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return Disposable.Empty; + } + + var subscription = observable.SubscribeSafe(this); + var connection = connectable.Connect(); + + return new CompositeDisposable(subscription, connection); + } + + public void OnNext(TResult value) + { + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Never.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Never.cs new file mode 100644 index 0000000..a9adc52 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Never.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Never<TResult> : IObservable<TResult> + { + public IDisposable Subscribe(IObserver<TResult> observer) + { + if (observer == null) + throw new ArgumentNullException("observer"); + + return Disposable.Empty; + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Next.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Next.cs new file mode 100644 index 0000000..2d4ec45 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Next.cs @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive.Threading; +using System.Threading; + +namespace System.Reactive.Linq.Observαble +{ + class Next<TSource> : PushToPullAdapter<TSource, TSource> + { + public Next(IObservable<TSource> source) + : base(source) + { + } + + protected override PushToPullSink<TSource, TSource> Run(IDisposable subscription) + { + return new _(subscription); + } + + class _ : PushToPullSink<TSource, TSource> + { + private readonly object _gate; + +#if !NO_CDS + private readonly SemaphoreSlim _semaphore; +#else + private readonly Semaphore _semaphore; +#endif + + public _(IDisposable subscription) + : base(subscription) + { + _gate = new object(); + +#if !NO_CDS + _semaphore = new SemaphoreSlim(0, 1); +#else + _semaphore = new Semaphore(0, 1); +#endif + } + + private bool _waiting; + private NotificationKind _kind; + private TSource _value; + private Exception _error; + + public override void OnNext(TSource value) + { + lock (_gate) + { + if (_waiting) + { + _value = value; + _kind = NotificationKind.OnNext; + _semaphore.Release(); + } + + _waiting = false; + } + } + + public override void OnError(Exception error) + { + base.Dispose(); + + lock (_gate) + { + // + // BREAKING CHANGE v2 > v1.x - Next doesn't block indefinitely when it reaches the end. + // + _error = error; + _kind = NotificationKind.OnError; + + if (_waiting) + _semaphore.Release(); + + _waiting = false; + } + } + + public override void OnCompleted() + { + base.Dispose(); + + lock (_gate) + { + // + // BREAKING CHANGE v2 > v1.x - Next doesn't block indefinitely when it reaches the end. + // + _kind = NotificationKind.OnCompleted; + + if (_waiting) + _semaphore.Release(); + + _waiting = false; + } + } + + public override bool TryMoveNext(out TSource current) + { + var done = false; + + lock (_gate) + { + _waiting = true; + + // + // BREAKING CHANGE v2 > v1.x - Next doesn't block indefinitely when it reaches the end. + // + done = _kind != NotificationKind.OnNext; + } + + if (!done) + { +#if !NO_CDS + _semaphore.Wait(); +#else + _semaphore.WaitOne(); +#endif + } + + // + // When we reach this point, we released the lock and got the next notification + // from the observer. We assume no concurrent calls to the TryMoveNext method + // are made (per general guidance on usage of IEnumerable<T>). If the observer + // enters the lock again, it should have quit it first, causing _waiting to be + // set to false, hence future accesses of the lock won't set the _kind, _value, + // and _error fields, until TryMoveNext is entered again and _waiting is reset + // to true. In conclusion, the fields are stable for read below. + // + // Notice we rely on memory barrier acquire/release behavior due to the use of + // the semaphore, not the lock (we're still under the lock when we release the + // semaphore in the On* methods!). + // + switch (_kind) + { + case NotificationKind.OnNext: + current = _value; + return true; + case NotificationKind.OnError: + _error.Throw(); + break; + case NotificationKind.OnCompleted: + break; + } + + current = default(TSource); + return false; + } + } + } +} +#endif diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ObserveOn.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ObserveOn.cs new file mode 100644 index 0000000..d5d7428 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ObserveOn.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; +using System.Threading; + +namespace System.Reactive.Linq.Observαble +{ + class ObserveOn<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly IScheduler _scheduler; + + public ObserveOn(IObservable<TSource> source, IScheduler scheduler) + { + _source = source; + _scheduler = scheduler; + } + +#if !NO_SYNCCTX + private readonly SynchronizationContext _context; + + public ObserveOn(IObservable<TSource> source, SynchronizationContext context) + { + _source = source; + _context = context; + } +#endif + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { +#if !NO_SYNCCTX + if (_context != null) + { + var sink = new ς(this, observer, cancel); + setSink(sink); + return _source.Subscribe(sink); + } + else +#endif + { + var sink = new ObserveOnObserver<TSource>(_scheduler, observer, cancel); + setSink(sink); + return _source.Subscribe(sink); + } + } + +#if !NO_SYNCCTX + class ς : Sink<TSource>, IObserver<TSource> + { + private readonly ObserveOn<TSource> _parent; + + public ς(ObserveOn<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + _parent._context.PostWithStartComplete(() => + { + base._observer.OnNext(value); + }); + } + + public void OnError(Exception error) + { + _parent._context.PostWithStartComplete(() => + { + base._observer.OnError(error); + base.Dispose(); + }); + } + + public void OnCompleted() + { + _parent._context.PostWithStartComplete(() => + { + base._observer.OnCompleted(); + base.Dispose(); + }); + } + } +#endif + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/OfType.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/OfType.cs new file mode 100644 index 0000000..07e4515 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/OfType.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class OfType<TSource, TResult> : Producer<TResult> + { + private readonly IObservable<TSource> _source; + + public OfType(IObservable<TSource> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TResult>, IObserver<TSource> + { + public _(IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + public void OnNext(TSource value) + { + if (value is TResult) + { + base._observer.OnNext((TResult)(object)value); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/OnErrorResumeNext.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/OnErrorResumeNext.cs new file mode 100644 index 0000000..26dfd34 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/OnErrorResumeNext.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class OnErrorResumeNext<TSource> : Producer<TSource> + { + private readonly IEnumerable<IObservable<TSource>> _sources; + + public OnErrorResumeNext(IEnumerable<IObservable<TSource>> sources) + { + _sources = sources; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return sink.Run(_sources); + } + + class _ : TailRecursiveSink<TSource> + { + public _(IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + protected override IEnumerable<IObservable<TSource>> Extract(IObservable<TSource> source) + { + var oern = source as OnErrorResumeNext<TSource>; + if (oern != null) + return oern._sources; + + return null; + } + + public override void OnNext(TSource value) + { + base._observer.OnNext(value); + } + + public override void OnError(Exception error) + { + _recurse(); + } + + public override void OnCompleted() + { + _recurse(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/PushToPullAdapter.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/PushToPullAdapter.cs new file mode 100644 index 0000000..916d12b --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/PushToPullAdapter.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + abstract class PushToPullAdapter<TSource, TResult> : IEnumerable<TResult> + { + private readonly IObservable<TSource> _source; + + public PushToPullAdapter(IObservable<TSource> source) + { + _source = source; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator<TResult> GetEnumerator() + { + var d = new SingleAssignmentDisposable(); + var res = Run(d); + d.Disposable = _source.SubscribeSafe(res); + return res; + } + + protected abstract PushToPullSink<TSource, TResult> Run(IDisposable subscription); + } + + abstract class PushToPullSink<TSource, TResult> : IObserver<TSource>, IEnumerator<TResult>, IDisposable + { + private readonly IDisposable _subscription; + + public PushToPullSink(IDisposable subscription) + { + _subscription = subscription; + } + + public abstract void OnNext(TSource value); + public abstract void OnError(Exception error); + public abstract void OnCompleted(); + + public abstract bool TryMoveNext(out TResult current); + + private bool _done; + + public bool MoveNext() + { + if (!_done) + { + var current = default(TResult); + if (TryMoveNext(out current)) + { + Current = current; + return true; + } + else + { + _done = true; + _subscription.Dispose(); + } + } + + return false; + } + + public TResult Current + { + get; + private set; + } + + object IEnumerator.Current + { + get { return Current; } + } + + public void Reset() + { + throw new NotSupportedException(); + } + + public void Dispose() + { + _subscription.Dispose(); + } + } +} diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Range.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Range.cs new file mode 100644 index 0000000..c58d94c --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Range.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Range : Producer<int> + { + private readonly int _start; + private readonly int _count; + private readonly IScheduler _scheduler; + + public Range(int start, int count, IScheduler scheduler) + { + _start = start; + _count = count; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<int> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<int> + { + private readonly Range _parent; + + public _(Range parent, IObserver<int> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + var longRunning = _parent._scheduler.AsLongRunning(); + if (longRunning != null) + { + return longRunning.ScheduleLongRunning(0, Loop); + } + else + { + return _parent._scheduler.Schedule(0, LoopRec); + } + } + + private void Loop(int i, ICancelable cancel) + { + while (!cancel.IsDisposed && i < _parent._count) + { + base._observer.OnNext(_parent._start + i); + i++; + } + + if (!cancel.IsDisposed) + base._observer.OnCompleted(); + + base.Dispose(); + } + + private void LoopRec(int i, Action<int> recurse) + { + if (i < _parent._count) + { + base._observer.OnNext(_parent._start + i); + recurse(i + 1); + } + else + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/RefCount.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/RefCount.cs new file mode 100644 index 0000000..1762807 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/RefCount.cs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Disposables; +using System.Reactive.Subjects; + +namespace System.Reactive.Linq.Observαble +{ + class RefCount<TSource> : Producer<TSource> + { + private readonly IConnectableObservable<TSource> _source; + + private readonly object _gate; + private int _count; + private IDisposable _connectableSubscription; + + public RefCount(IConnectableObservable<TSource> source) + { + _source = source; + _gate = new object(); + _count = 0; + _connectableSubscription = default(IDisposable); + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly RefCount<TSource> _parent; + + public _(RefCount<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + var subscription = _parent._source.SubscribeSafe(this); + + lock (_parent._gate) + { + if (++_parent._count == 1) + { + _parent._connectableSubscription = _parent._source.Connect(); + } + } + + return Disposable.Create(() => + { + subscription.Dispose(); + + lock (_parent._gate) + { + if (--_parent._count == 0) + { + _parent._connectableSubscription.Dispose(); + } + } + }); + } + + public void OnNext(TSource value) + { + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Repeat.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Repeat.cs new file mode 100644 index 0000000..fab0607 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Repeat.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Repeat<TResult> : Producer<TResult> + { + private readonly TResult _value; + private readonly int? _repeatCount; + private readonly IScheduler _scheduler; + + public Repeat(TResult value, int? repeatCount, IScheduler scheduler) + { + _value = value; + _repeatCount = repeatCount; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TResult> + { + private readonly Repeat<TResult> _parent; + + public _(Repeat<TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + var longRunning = _parent._scheduler.AsLongRunning(); + if (longRunning != null) + { + return Run(longRunning); + } + else + { + return Run(_parent._scheduler); + } + } + + private IDisposable Run(IScheduler scheduler) + { + if (_parent._repeatCount == null) + { + return scheduler.Schedule(LoopRecInf); + } + else + { + return scheduler.Schedule(_parent._repeatCount.Value, LoopRec); + } + } + + private void LoopRecInf(Action recurse) + { + base._observer.OnNext(_parent._value); + recurse(); + } + + private void LoopRec(int n, Action<int> recurse) + { + if (n > 0) + { + base._observer.OnNext(_parent._value); + n--; + } + + if (n == 0) + { + base._observer.OnCompleted(); + base.Dispose(); + return; + } + + recurse(n); + } + + private IDisposable Run(ISchedulerLongRunning scheduler) + { + if (_parent._repeatCount == null) + { + return scheduler.ScheduleLongRunning(LoopInf); + } + else + { + return scheduler.ScheduleLongRunning(_parent._repeatCount.Value, Loop); + } + } + + private void LoopInf(ICancelable cancel) + { + var value = _parent._value; + while (!cancel.IsDisposed) + base._observer.OnNext(value); + + base.Dispose(); + } + + private void Loop(int n, ICancelable cancel) + { + var value = _parent._value; + while (n > 0 && !cancel.IsDisposed) + { + base._observer.OnNext(value); + n--; + } + + if (!cancel.IsDisposed) + base._observer.OnCompleted(); + + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Return.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Return.cs new file mode 100644 index 0000000..cd9ad02 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Return.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; + +namespace System.Reactive.Linq.Observαble +{ + class Return<TResult> : Producer<TResult> + { + private readonly TResult _value; + private readonly IScheduler _scheduler; + + public Return(TResult value, IScheduler scheduler) + { + _value = value; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TResult> + { + private readonly Return<TResult> _parent; + + public _(Return<TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + return _parent._scheduler.Schedule(Invoke); + } + + private void Invoke() + { + base._observer.OnNext(_parent._value); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Sample.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Sample.cs new file mode 100644 index 0000000..0d46c62 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Sample.cs @@ -0,0 +1,243 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Sample<TSource, TSample> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly IObservable<TSample> _sampler; + + public Sample(IObservable<TSource> source, IObservable<TSample> sampler) + { + _source = source; + _sampler = sampler; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Sample<TSource, TSample> _parent; + + public _(Sample<TSource, TSample> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + + private IDisposable _sourceSubscription; + + private bool _hasValue; + private TSource _value; + private bool _atEnd; + + public IDisposable Run() + { + _gate = new object(); + + var sourceSubscription = new SingleAssignmentDisposable(); + _sourceSubscription = sourceSubscription; + sourceSubscription.Disposable = _parent._source.SubscribeSafe(this); + + var samplerSubscription = _parent._sampler.SubscribeSafe(new σ(this)); + + return new CompositeDisposable(_sourceSubscription, samplerSubscription); + } + + public void OnNext(TSource value) + { + lock (_gate) + { + _hasValue = true; + _value = value; + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + _atEnd = true; + _sourceSubscription.Dispose(); + } + } + + class σ : IObserver<TSample> + { + private readonly _ _parent; + + public σ(_ parent) + { + _parent = parent; + } + + public void OnNext(TSample value) + { + lock (_parent._gate) + { + if (_parent._hasValue) + { + _parent._hasValue = false; + _parent._observer.OnNext(_parent._value); + } + + if (_parent._atEnd) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + } + + public void OnError(Exception error) + { + // BREAKING CHANGE v2 > v1.x - This error used to be swallowed + lock (_parent._gate) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + lock (_parent._gate) + { + if (_parent._hasValue) + { + _parent._hasValue = false; + _parent._observer.OnNext(_parent._value); + } + + if (_parent._atEnd) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + } + } + } + } + + class Sample<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly TimeSpan _interval; + private readonly IScheduler _scheduler; + + public Sample(IObservable<TSource> source, TimeSpan interval, IScheduler scheduler) + { + _source = source; + _interval = interval; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Sample<TSource> _parent; + + public _(Sample<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + + private IDisposable _sourceSubscription; + + private bool _hasValue; + private TSource _value; + private bool _atEnd; + + public IDisposable Run() + { + _gate = new object(); + + var sourceSubscription = new SingleAssignmentDisposable(); + _sourceSubscription = sourceSubscription; + sourceSubscription.Disposable = _parent._source.SubscribeSafe(this); + + return new CompositeDisposable( + sourceSubscription, + _parent._scheduler.SchedulePeriodic(_parent._interval, Tick) + ); + } + + private void Tick() + { + lock (_gate) + { + if (_hasValue) + { + _hasValue = false; + base._observer.OnNext(_value); + } + + if (_atEnd) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + public void OnNext(TSource value) + { + lock (_gate) + { + _hasValue = true; + _value = value; + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + _atEnd = true; + _sourceSubscription.Dispose(); + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Scan.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Scan.cs new file mode 100644 index 0000000..6c8f2fe --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Scan.cs @@ -0,0 +1,146 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class Scan<TSource, TAccumulate> : Producer<TAccumulate> + { + private readonly IObservable<TSource> _source; + private readonly TAccumulate _seed; + private readonly Func<TAccumulate, TSource, TAccumulate> _accumulator; + + public Scan(IObservable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator) + { + _source = source; + _seed = seed; + _accumulator = accumulator; + } + + protected override IDisposable Run(IObserver<TAccumulate> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TAccumulate>, IObserver<TSource> + { + private readonly Scan<TSource, TAccumulate> _parent; + private TAccumulate _accumulation; + private bool _hasAccumulation; + + public _(Scan<TSource, TAccumulate> parent, IObserver<TAccumulate> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _accumulation = default(TAccumulate); + _hasAccumulation = false; + } + + public void OnNext(TSource value) + { + try + { + if (_hasAccumulation) + _accumulation = _parent._accumulator(_accumulation, value); + else + { + _accumulation = _parent._accumulator(_parent._seed, value); + _hasAccumulation = true; + } + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + base._observer.OnNext(_accumulation); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class Scan<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, TSource, TSource> _accumulator; + + public Scan(IObservable<TSource> source, Func<TSource, TSource, TSource> accumulator) + { + _source = source; + _accumulator = accumulator; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Scan<TSource> _parent; + private TSource _accumulation; + private bool _hasAccumulation; + + public _(Scan<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _accumulation = default(TSource); + _hasAccumulation = false; + } + + public void OnNext(TSource value) + { + try + { + if (_hasAccumulation) + _accumulation = _parent._accumulator(_accumulation, value); + else + { + _accumulation = value; + _hasAccumulation = true; + } + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + base._observer.OnNext(_accumulation); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Select.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Select.cs new file mode 100644 index 0000000..e853210 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Select.cs @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + abstract class Select<TResult> : Producer<TResult> + { + public abstract IObservable<TResult2> Ω<TResult2>(Func<TResult, TResult2> selector); + } + + class Select<TSource, TResult> : Select<TResult> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, TResult> _selector; + private readonly Func<TSource, int, TResult> _selectorI; + + public Select(IObservable<TSource> source, Func<TSource, TResult> selector) + { + _source = source; + _selector = selector; + } + + public Select(IObservable<TSource> source, Func<TSource, int, TResult> selector) + { + _source = source; + _selectorI = selector; + } + + public override IObservable<TResult2> Ω<TResult2>(Func<TResult, TResult2> selector) + { + if (_selector != null) + return new Select<TSource, TResult2>(_source, x => selector(_selector(x))); + else + return new Select<TResult, TResult2>(this, selector); + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_selector != null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new τ(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + } + + class _ : Sink<TResult>, IObserver<TSource> + { + private readonly Select<TSource, TResult> _parent; + + public _(Select<TSource, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + var result = default(TResult); + try + { + result = _parent._selector(value); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + base._observer.OnNext(result); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class τ : Sink<TResult>, IObserver<TSource> + { + private readonly Select<TSource, TResult> _parent; + private int _index; + + public τ(Select<TSource, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _index = 0; + } + + public void OnNext(TSource value) + { + var result = default(TResult); + try + { + result = _parent._selectorI(value, checked(_index++)); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + base._observer.OnNext(result); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SelectMany.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SelectMany.cs new file mode 100644 index 0000000..687a894 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SelectMany.cs @@ -0,0 +1,888 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive; +using System.Reactive.Disposables; + +#if !NO_TPL +using System.Threading; +using System.Threading.Tasks; +#endif + +namespace System.Reactive.Linq.Observαble +{ + class SelectMany<TSource, TCollection, TResult> : Producer<TResult> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, IObservable<TCollection>> _collectionSelector; + private readonly Func<TSource, IEnumerable<TCollection>> _collectionSelectorE; + private readonly Func<TSource, TCollection, TResult> _resultSelector; + + public SelectMany(IObservable<TSource> source, Func<TSource, IObservable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector) + { + _source = source; + _collectionSelector = collectionSelector; + _resultSelector = resultSelector; + } + + public SelectMany(IObservable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector) + { + _source = source; + _collectionSelectorE = collectionSelector; + _resultSelector = resultSelector; + } + +#if !NO_TPL + private readonly Func<TSource, CancellationToken, Task<TCollection>> _collectionSelectorT; + + public SelectMany(IObservable<TSource> source, Func<TSource, CancellationToken, Task<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector) + { + _source = source; + _collectionSelectorT = collectionSelector; + _resultSelector = resultSelector; + } +#endif + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_collectionSelector != null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } +#if !NO_TPL + else if (_collectionSelectorT != null) + { + var sink = new τ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } +#endif + else + { + var sink = new ε(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + } + + class _ : Sink<TResult>, IObserver<TSource> + { + private readonly SelectMany<TSource, TCollection, TResult> _parent; + + public _(SelectMany<TSource, TCollection, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private bool _isStopped; + private CompositeDisposable _group; + private SingleAssignmentDisposable _sourceSubscription; + + public IDisposable Run() + { + _gate = new object(); + _isStopped = false; + _group = new CompositeDisposable(); + + _sourceSubscription = new SingleAssignmentDisposable(); + _group.Add(_sourceSubscription); + _sourceSubscription.Disposable = _parent._source.SubscribeSafe(this); + + return _group; + } + + public void OnNext(TSource value) + { + var collection = default(IObservable<TCollection>); + + try + { + collection = _parent._collectionSelector(value); + } + catch (Exception ex) + { + lock (_gate) + { + base._observer.OnError(ex); + base.Dispose(); + } + return; + } + + var innerSubscription = new SingleAssignmentDisposable(); + _group.Add(innerSubscription); + innerSubscription.Disposable = collection.SubscribeSafe(new ι(this, value, innerSubscription)); + } + + public void OnError(Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + _isStopped = true; + if (_group.Count == 1) + { + // + // Notice there can be a race between OnCompleted of the source and any + // of the inner sequences, where both see _group.Count == 1, and one is + // waiting for the lock. There won't be a double OnCompleted observation + // though, because the call to Dispose silences the observer by swapping + // in a NopObserver<T>. + // + lock (_gate) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + else + { + _sourceSubscription.Dispose(); + } + } + + class ι : IObserver<TCollection> + { + private readonly _ _parent; + private readonly TSource _value; + private readonly IDisposable _self; + + public ι(_ parent, TSource value, IDisposable self) + { + _parent = parent; + _value = value; + _self = self; + } + + public void OnNext(TCollection value) + { + var res = default(TResult); + + try + { + res = _parent._parent._resultSelector(_value, value); + } + catch (Exception ex) + { + lock (_parent._gate) + { + _parent._observer.OnError(ex); + _parent.Dispose(); + } + return; + } + + lock (_parent._gate) + _parent._observer.OnNext(res); + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + _parent._group.Remove(_self); + if (_parent._isStopped && _parent._group.Count == 1) + { + // + // Notice there can be a race between OnCompleted of the source and any + // of the inner sequences, where both see _group.Count == 1, and one is + // waiting for the lock. There won't be a double OnCompleted observation + // though, because the call to Dispose silences the observer by swapping + // in a NopObserver<T>. + // + lock (_parent._gate) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + } + } + } + + class ε : Sink<TResult>, IObserver<TSource> + { + private readonly SelectMany<TSource, TCollection, TResult> _parent; + + public ε(SelectMany<TSource, TCollection, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + var xs = default(IEnumerable<TCollection>); + try + { + xs = _parent._collectionSelectorE(value); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + var e = default(IEnumerator<TCollection>); + try + { + e = xs.GetEnumerator(); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + try + { + var hasNext = true; + while (hasNext) + { + hasNext = false; + var current = default(TResult); + + try + { + hasNext = e.MoveNext(); + if (hasNext) + current = _parent._resultSelector(value, e.Current); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + if (hasNext) + base._observer.OnNext(current); + } + } + finally + { + if (e != null) + e.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + +#if !NO_TPL +#pragma warning disable 0420 + class τ : Sink<TResult>, IObserver<TSource> + { + private readonly SelectMany<TSource, TCollection, TResult> _parent; + + public τ(SelectMany<TSource, TCollection, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private CancellationDisposable _cancel; + private volatile int _count; + + public IDisposable Run() + { + _gate = new object(); + _cancel = new CancellationDisposable(); + _count = 1; + + return new CompositeDisposable(_parent._source.SubscribeSafe(this), _cancel); + } + + public void OnNext(TSource value) + { + var task = default(Task<TCollection>); + try + { + Interlocked.Increment(ref _count); + task = _parent._collectionSelectorT(value, _cancel.Token); + } + catch (Exception ex) + { + lock (_gate) + { + base._observer.OnError(ex); + base.Dispose(); + } + + return; + } + + if (task.IsCompleted) + { + OnCompletedTask(value, task); + } + else + { + AttachContinuation(value, task); + } + } + + private void AttachContinuation(TSource value, Task<TCollection> task) + { + // + // Separate method to avoid closure in synchronous completion case. + // + task.ContinueWith(t => OnCompletedTask(value, t)); + } + + private void OnCompletedTask(TSource value, Task<TCollection> task) + { + switch (task.Status) + { + case TaskStatus.RanToCompletion: + { + var res = default(TResult); + try + { + res = _parent._resultSelector(value, task.Result); + } + catch (Exception ex) + { + lock (_gate) + { + base._observer.OnError(ex); + base.Dispose(); + } + + return; + } + + lock (_gate) + base._observer.OnNext(res); + + OnCompleted(); + } + break; + case TaskStatus.Faulted: + { + lock (_gate) + { + base._observer.OnError(task.Exception.InnerException); + base.Dispose(); + } + } + break; + case TaskStatus.Canceled: + { + if (!_cancel.IsDisposed) + { + lock (_gate) + { + base._observer.OnError(new TaskCanceledException(task)); + base.Dispose(); + } + } + } + break; + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + if (Interlocked.Decrement(ref _count) == 0) + { + lock (_gate) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + } +#pragma warning restore 0420 +#endif + } + + class SelectMany<TSource, TResult> : Producer<TResult> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, IObservable<TResult>> _selector; + private readonly Func<Exception, IObservable<TResult>> _selectorOnError; + private readonly Func<IObservable<TResult>> _selectorOnCompleted; + private readonly Func<TSource, IEnumerable<TResult>> _selectorE; + + public SelectMany(IObservable<TSource> source, Func<TSource, IObservable<TResult>> selector) + { + _source = source; + _selector = selector; + } + + public SelectMany(IObservable<TSource> source, Func<TSource, IObservable<TResult>> selector, Func<Exception, IObservable<TResult>> selectorOnError, Func<IObservable<TResult>> selectorOnCompleted) + { + _source = source; + _selector = selector; + _selectorOnError = selectorOnError; + _selectorOnCompleted = selectorOnCompleted; + } + + public SelectMany(IObservable<TSource> source, Func<TSource, IEnumerable<TResult>> selector) + { + _source = source; + _selectorE = selector; + } + +#if !NO_TPL + private readonly Func<TSource, CancellationToken, Task<TResult>> _selectorT; + + public SelectMany(IObservable<TSource> source, Func<TSource, CancellationToken, Task<TResult>> selector) + { + _source = source; + _selectorT = selector; + } +#endif + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_selector != null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } +#if !NO_TPL + else if (_selectorT != null) + { + var sink = new τ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } +#endif + else + { + var sink = new ε(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + } + + class _ : Sink<TResult>, IObserver<TSource> + { + private readonly SelectMany<TSource, TResult> _parent; + + public _(SelectMany<TSource, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private bool _isStopped; + private CompositeDisposable _group; + private SingleAssignmentDisposable _sourceSubscription; + + public IDisposable Run() + { + _gate = new object(); + _isStopped = false; + _group = new CompositeDisposable(); + + _sourceSubscription = new SingleAssignmentDisposable(); + _group.Add(_sourceSubscription); + _sourceSubscription.Disposable = _parent._source.SubscribeSafe(this); + + return _group; + } + + public void OnNext(TSource value) + { + var inner = default(IObservable<TResult>); + + try + { + inner = _parent._selector(value); + } + catch (Exception ex) + { + lock (_gate) + { + base._observer.OnError(ex); + base.Dispose(); + } + return; + } + + SubscribeInner(inner); + } + + public void OnError(Exception error) + { + if (_parent._selectorOnError != null) + { + var inner = default(IObservable<TResult>); + + try + { + inner = _parent._selectorOnError(error); + } + catch (Exception ex) + { + lock (_gate) + { + base._observer.OnError(ex); + base.Dispose(); + } + return; + } + + SubscribeInner(inner); + + Final(); + } + else + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + } + + public void OnCompleted() + { + if (_parent._selectorOnCompleted != null) + { + var inner = default(IObservable<TResult>); + + try + { + inner = _parent._selectorOnCompleted(); + } + catch (Exception ex) + { + lock (_gate) + { + base._observer.OnError(ex); + base.Dispose(); + } + return; + } + + SubscribeInner(inner); + } + + Final(); + } + + private void Final() + { + _isStopped = true; + if (_group.Count == 1) + { + // + // Notice there can be a race between OnCompleted of the source and any + // of the inner sequences, where both see _group.Count == 1, and one is + // waiting for the lock. There won't be a double OnCompleted observation + // though, because the call to Dispose silences the observer by swapping + // in a NopObserver<T>. + // + lock (_gate) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + else + { + _sourceSubscription.Dispose(); + } + } + + private void SubscribeInner(IObservable<TResult> inner) + { + var innerSubscription = new SingleAssignmentDisposable(); + _group.Add(innerSubscription); + innerSubscription.Disposable = inner.SubscribeSafe(new ι(this, innerSubscription)); + } + + class ι : IObserver<TResult> + { + private readonly _ _parent; + private readonly IDisposable _self; + + public ι(_ parent, IDisposable self) + { + _parent = parent; + _self = self; + } + + public void OnNext(TResult value) + { + lock (_parent._gate) + _parent._observer.OnNext(value); + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + _parent._group.Remove(_self); + if (_parent._isStopped && _parent._group.Count == 1) + { + // + // Notice there can be a race between OnCompleted of the source and any + // of the inner sequences, where both see _group.Count == 1, and one is + // waiting for the lock. There won't be a double OnCompleted observation + // though, because the call to Dispose silences the observer by swapping + // in a NopObserver<T>. + // + lock (_parent._gate) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + } + } + } + + class ε : Sink<TResult>, IObserver<TSource> + { + private readonly SelectMany<TSource, TResult> _parent; + + public ε(SelectMany<TSource, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + var xs = default(IEnumerable<TResult>); + try + { + xs = _parent._selectorE(value); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + var e = default(IEnumerator<TResult>); + try + { + e = xs.GetEnumerator(); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + try + { + var hasNext = true; + while (hasNext) + { + hasNext = false; + var current = default(TResult); + + try + { + hasNext = e.MoveNext(); + if (hasNext) + current = e.Current; + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + if (hasNext) + base._observer.OnNext(current); + } + } + finally + { + if (e != null) + e.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + +#if !NO_TPL +#pragma warning disable 0420 + class τ : Sink<TResult>, IObserver<TSource> + { + private readonly SelectMany<TSource, TResult> _parent; + + public τ(SelectMany<TSource, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private CancellationDisposable _cancel; + private volatile int _count; + + public IDisposable Run() + { + _gate = new object(); + _cancel = new CancellationDisposable(); + _count = 1; + + return new CompositeDisposable(_parent._source.SubscribeSafe(this), _cancel); + } + + public void OnNext(TSource value) + { + var task = default(Task<TResult>); + try + { + Interlocked.Increment(ref _count); + task = _parent._selectorT(value, _cancel.Token); + } + catch (Exception ex) + { + lock (_gate) + { + base._observer.OnError(ex); + base.Dispose(); + } + + return; + } + + if (task.IsCompleted) + { + OnCompletedTask(task); + } + else + { + task.ContinueWith(OnCompletedTask); + } + } + + private void OnCompletedTask(Task<TResult> task) + { + switch (task.Status) + { + case TaskStatus.RanToCompletion: + { + lock (_gate) + base._observer.OnNext(task.Result); + + OnCompleted(); + } + break; + case TaskStatus.Faulted: + { + lock (_gate) + { + base._observer.OnError(task.Exception.InnerException); + base.Dispose(); + } + } + break; + case TaskStatus.Canceled: + { + if (!_cancel.IsDisposed) + { + lock (_gate) + { + base._observer.OnError(new TaskCanceledException(task)); + base.Dispose(); + } + } + } + break; + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + if (Interlocked.Decrement(ref _count) == 0) + { + lock (_gate) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + } +#pragma warning restore 0420 +#endif + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SequenceEqual.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SequenceEqual.cs new file mode 100644 index 0000000..4661f82 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SequenceEqual.cs @@ -0,0 +1,322 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class SequenceEqual<TSource> : Producer<bool> + { + private readonly IObservable<TSource> _first; + private readonly IObservable<TSource> _second; + private readonly IEnumerable<TSource> _secondE; + private readonly IEqualityComparer<TSource> _comparer; + + public SequenceEqual(IObservable<TSource> first, IObservable<TSource> second, IEqualityComparer<TSource> comparer) + { + _first = first; + _second = second; + _comparer = comparer; + } + + public SequenceEqual(IObservable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer) + { + _first = first; + _secondE = second; + _comparer = comparer; + } + + protected override IDisposable Run(IObserver<bool> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_second != null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else + { + var sink = new ε(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + + class _ : Sink<bool> + { + private readonly SequenceEqual<TSource> _parent; + + public _(SequenceEqual<TSource> parent, IObserver<bool> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private bool _donel; + private bool _doner; + private Queue<TSource> _ql; + private Queue<TSource> _qr; + + public IDisposable Run() + { + _gate = new object(); + _donel = false; + _doner = false; + _ql = new Queue<TSource>(); + _qr = new Queue<TSource>(); + + return new CompositeDisposable + { + _parent._first.SubscribeSafe(new F(this)), + _parent._second.SubscribeSafe(new S(this)) + }; + } + + class F : IObserver<TSource> + { + private readonly _ _parent; + + public F(_ parent) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + lock (_parent._gate) + { + if (_parent._qr.Count > 0) + { + var equal = false; + var v = _parent._qr.Dequeue(); + try + { + equal = _parent._parent._comparer.Equals(value, v); + } + catch (Exception exception) + { + _parent._observer.OnError(exception); + _parent.Dispose(); + return; + } + if (!equal) + { + _parent._observer.OnNext(false); + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + else if (_parent._doner) + { + _parent._observer.OnNext(false); + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + else + _parent._ql.Enqueue(value); + } + } + + public void OnError(Exception error) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + + public void OnCompleted() + { + lock (_parent._gate) + { + _parent._donel = true; + if (_parent._ql.Count == 0) + { + if (_parent._qr.Count > 0) + { + _parent._observer.OnNext(false); + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + else if (_parent._doner) + { + _parent._observer.OnNext(true); + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + } + } + } + + class S : IObserver<TSource> + { + private readonly _ _parent; + + public S(_ parent) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + lock (_parent._gate) + { + if (_parent._ql.Count > 0) + { + var equal = false; + var v = _parent._ql.Dequeue(); + try + { + equal = _parent._parent._comparer.Equals(v, value); + } + catch (Exception exception) + { + _parent._observer.OnError(exception); + _parent.Dispose(); + return; + } + if (!equal) + { + _parent._observer.OnNext(false); + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + else if (_parent._donel) + { + _parent._observer.OnNext(false); + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + else + _parent._qr.Enqueue(value); + } + } + + public void OnError(Exception error) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + + public void OnCompleted() + { + lock (_parent._gate) + { + _parent._doner = true; + if (_parent._qr.Count == 0) + { + if (_parent._ql.Count > 0) + { + _parent._observer.OnNext(false); + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + else if (_parent._donel) + { + _parent._observer.OnNext(true); + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + } + } + } + } + + class ε : Sink<bool>, IObserver<TSource> + { + private readonly SequenceEqual<TSource> _parent; + + public ε(SequenceEqual<TSource> parent, IObserver<bool> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private IEnumerator<TSource> _enumerator; + + public IDisposable Run() + { + // + // Notice the evaluation order of obtaining the enumerator and subscribing to the + // observable sequence is reversed compared to the operator's signature. This is + // required to make sure the enumerator is available as soon as the observer can + // be called. Otherwise, we end up having a race for the initialization and use + // of the _rightEnumerator field. + // + try + { + _enumerator = _parent._secondE.GetEnumerator(); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return Disposable.Empty; + } + + return new CompositeDisposable( + _parent._first.SubscribeSafe(this), + _enumerator + ); + } + + public void OnNext(TSource value) + { + var equal = false; + + try + { + if (_enumerator.MoveNext()) + { + var current = _enumerator.Current; + equal = _parent._comparer.Equals(value, current); + } + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + if (!equal) + { + base._observer.OnNext(false); + base._observer.OnCompleted(); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + var hasNext = false; + + try + { + hasNext = _enumerator.MoveNext(); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + base._observer.OnNext(!hasNext); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SingleAsync.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SingleAsync.cs new file mode 100644 index 0000000..19c07e5 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SingleAsync.cs @@ -0,0 +1,154 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class SingleAsync<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, bool> _predicate; + private readonly bool _throwOnEmpty; + + public SingleAsync(IObservable<TSource> source, Func<TSource, bool> predicate, bool throwOnEmpty) + { + _source = source; + _predicate = predicate; + _throwOnEmpty = throwOnEmpty; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_predicate != null) + { + var sink = new π(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly SingleAsync<TSource> _parent; + private TSource _value; + private bool _seenValue; + + public _(SingleAsync<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + + _value = default(TSource); + _seenValue = false; + } + + public void OnNext(TSource value) + { + if (_seenValue) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.MORE_THAN_ONE_ELEMENT)); + base.Dispose(); + return; + } + + _value = value; + _seenValue = true; + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_seenValue && _parent._throwOnEmpty) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS)); + } + else + { + base._observer.OnNext(_value); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + + class π : Sink<TSource>, IObserver<TSource> + { + private readonly SingleAsync<TSource> _parent; + private TSource _value; + private bool _seenValue; + + public π(SingleAsync<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + + _value = default(TSource); + _seenValue = false; + } + + public void OnNext(TSource value) + { + var b = false; + + try + { + b = _parent._predicate(value); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + if (b) + { + if (_seenValue) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.MORE_THAN_ONE_MATCHING_ELEMENT)); + base.Dispose(); + return; + } + + _value = value; + _seenValue = true; + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + if (!_seenValue && _parent._throwOnEmpty) + { + base._observer.OnError(new InvalidOperationException(Strings_Linq.NO_MATCHING_ELEMENTS)); + } + else + { + base._observer.OnNext(_value); + base._observer.OnCompleted(); + } + + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Skip.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Skip.cs new file mode 100644 index 0000000..a092d99 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Skip.cs @@ -0,0 +1,151 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Skip<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly int _count; + private readonly TimeSpan _duration; + internal readonly IScheduler _scheduler; + + public Skip(IObservable<TSource> source, int count) + { + _source = source; + _count = count; + } + + public Skip(IObservable<TSource> source, TimeSpan duration, IScheduler scheduler) + { + _source = source; + _duration = duration; + _scheduler = scheduler; + } + + public IObservable<TSource> Ω(int count) + { + // + // Sum semantics: + // + // xs --o--o--o--o--o--o--| xs --o--o--o--o--o--o--| + // xs.Skip(2) --x--x--o--o--o--o--| xs.Skip(3) --x--x--x--o--o--o--| + // xs.Skip(2).Skip(3) --------x--x--x--o--| xs.Skip(3).Skip(2) -----------x--x--o--| + // + return new Skip<TSource>(_source, _count + count); + } + + public IObservable<TSource> Ω(TimeSpan duration) + { + // + // Maximum semantics: + // + // t 0--1--2--3--4--5--6--7-> t 0--1--2--3--4--5--6--7-> + // + // xs --o--o--o--o--o--o--| xs --o--o--o--o--o--o--| + // xs.Skip(2s) xxxxxxx-o--o--o--o--| xs.Skip(3s) xxxxxxxxxx-o--o--o--| + // xs.Skip(2s).Skip(3s) xxxxxxxxxx-o--o--o--| xs.Skip(3s).Skip(2s) xxxxxxx----o--o--o--| + // + if (duration <= _duration) + return this; + else + return new Skip<TSource>(_source, duration, _scheduler); + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_scheduler == null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new τ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Skip<TSource> _parent; + private int _remaining; + + public _(Skip<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _remaining = _parent._count; + } + + public void OnNext(TSource value) + { + if (_remaining <= 0) + base._observer.OnNext(value); + else + _remaining--; + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class τ : Sink<TSource>, IObserver<TSource> + { + private readonly Skip<TSource> _parent; + private volatile bool _open; + + public τ(Skip<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + var t = _parent._scheduler.Schedule(_parent._duration, Tick); + var d = _parent._source.SubscribeSafe(this); + return new CompositeDisposable(t, d); + } + + private void Tick() + { + _open = true; + } + + public void OnNext(TSource value) + { + if (_open) + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SkipLast.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SkipLast.cs new file mode 100644 index 0000000..596f183 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SkipLast.cs @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive.Concurrency; + +namespace System.Reactive.Linq.Observαble +{ + class SkipLast<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly int _count; + private readonly TimeSpan _duration; + private readonly IScheduler _scheduler; + + public SkipLast(IObservable<TSource> source, int count) + { + _source = source; + _count = count; + } + + public SkipLast(IObservable<TSource> source, TimeSpan duration, IScheduler scheduler) + { + _source = source; + _duration = duration; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_scheduler == null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new τ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly SkipLast<TSource> _parent; + private Queue<TSource> _queue; + + public _(SkipLast<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _queue = new Queue<TSource>(); + } + + public void OnNext(TSource value) + { + _queue.Enqueue(value); + if (_queue.Count > _parent._count) + base._observer.OnNext(_queue.Dequeue()); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class τ : Sink<TSource>, IObserver<TSource> + { + private readonly SkipLast<TSource> _parent; + private Queue<System.Reactive.TimeInterval<TSource>> _queue; + + public τ(SkipLast<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _queue = new Queue<System.Reactive.TimeInterval<TSource>>(); + } + + private IStopwatch _watch; + + public IDisposable Run() + { + _watch = _parent._scheduler.StartStopwatch(); + + return _parent._source.SubscribeSafe(this); + } + + public void OnNext(TSource value) + { + var now = _watch.Elapsed; + _queue.Enqueue(new System.Reactive.TimeInterval<TSource>(value, now)); + while (_queue.Count > 0 && now - _queue.Peek().Interval >= _parent._duration) + base._observer.OnNext(_queue.Dequeue().Value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + var now = _watch.Elapsed; + while (_queue.Count > 0 && now - _queue.Peek().Interval >= _parent._duration) + base._observer.OnNext(_queue.Dequeue().Value); + + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SkipUntil.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SkipUntil.cs new file mode 100644 index 0000000..8d4c7a9 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SkipUntil.cs @@ -0,0 +1,210 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Threading; + +namespace System.Reactive.Linq.Observαble +{ + class SkipUntil<TSource, TOther> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly IObservable<TOther> _other; + + public SkipUntil(IObservable<TSource> source, IObservable<TOther> other) + { + _source = source; + _other = other; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource> + { + private readonly SkipUntil<TSource, TOther> _parent; + + public _(SkipUntil<TSource, TOther> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + var sourceObserver = new T(this); + var otherObserver = new O(this, sourceObserver); + + var sourceSubscription = _parent._source.SubscribeSafe(sourceObserver); + var otherSubscription = _parent._other.SubscribeSafe(otherObserver); + + sourceObserver.Disposable = sourceSubscription; + otherObserver.Disposable = otherSubscription; + + return new CompositeDisposable( + sourceSubscription, + otherSubscription + ); + } + + class T : IObserver<TSource> + { + private readonly _ _parent; + public volatile IObserver<TSource> _observer; + private readonly SingleAssignmentDisposable _subscription; + + public T(_ parent) + { + _parent = parent; + _observer = NopObserver<TSource>.Instance; + _subscription = new SingleAssignmentDisposable(); + } + + public IDisposable Disposable + { + set { _subscription.Disposable = value; } + } + + public void OnNext(TSource value) + { + _observer.OnNext(value); + } + + public void OnError(Exception error) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + + public void OnCompleted() + { + _observer.OnCompleted(); + _subscription.Dispose(); // We can't cancel the other stream yet, it may be on its way to dispatch an OnError message and we don't want to have a race. + } + } + + class O : IObserver<TOther> + { + private readonly _ _parent; + private readonly T _sourceObserver; + private readonly SingleAssignmentDisposable _subscription; + + public O(_ parent, T sourceObserver) + { + _parent = parent; + _sourceObserver = sourceObserver; + _subscription = new SingleAssignmentDisposable(); + } + + public IDisposable Disposable + { + set { _subscription.Disposable = value; } + } + + public void OnNext(TOther value) + { + _sourceObserver._observer = _parent._observer; + _subscription.Dispose(); + } + + public void OnError(Exception error) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + + public void OnCompleted() + { + _subscription.Dispose(); + } + } + } + } + + class SkipUntil<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly DateTimeOffset _startTime; + internal readonly IScheduler _scheduler; + + public SkipUntil(IObservable<TSource> source, DateTimeOffset startTime, IScheduler scheduler) + { + _source = source; + _startTime = startTime; + _scheduler = scheduler; + } + + public IObservable<TSource> Ω(DateTimeOffset startTime) + { + // + // Maximum semantics: + // + // t 0--1--2--3--4--5--6--7-> t 0--1--2--3--4--5--6--7-> + // + // xs --o--o--o--o--o--o--| xs --o--o--o--o--o--o--| + // xs.SU(5AM) xxxxxxxxxxxxxxxx-o--| xs.SU(3AM) xxxxxxxxxx-o--o--o--| + // xs.SU(5AM).SU(3AM) xxxxxxxxx--------o--| xs.SU(3AM).SU(5AM) xxxxxxxxxxxxxxxx-o--| + // + if (startTime <= _startTime) + return this; + else + return new SkipUntil<TSource>(_source, startTime, _scheduler); + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly SkipUntil<TSource> _parent; + private volatile bool _open; + + public _(SkipUntil<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + var t = _parent._scheduler.Schedule(_parent._startTime, Tick); + var d = _parent._source.SubscribeSafe(this); + return new CompositeDisposable(t, d); + } + + private void Tick() + { + _open = true; + } + + public void OnNext(TSource value) + { + if (_open) + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SkipWhile.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SkipWhile.cs new file mode 100644 index 0000000..e6e379d --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/SkipWhile.cs @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class SkipWhile<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, bool> _predicate; + private readonly Func<TSource, int, bool> _predicateI; + + public SkipWhile(IObservable<TSource> source, Func<TSource, bool> predicate) + { + _source = source; + _predicate = predicate; + } + + public SkipWhile(IObservable<TSource> source, Func<TSource, int, bool> predicate) + { + _source = source; + _predicateI = predicate; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_predicate != null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new τ(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly SkipWhile<TSource> _parent; + private bool _running; + + public _(SkipWhile<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _running = false; + } + + public void OnNext(TSource value) + { + if (!_running) + { + try + { + _running = !_parent._predicate(value); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + } + + if (_running) + { + base._observer.OnNext(value); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class τ : Sink<TSource>, IObserver<TSource> + { + private readonly SkipWhile<TSource> _parent; + private bool _running; + private int _index; + + public τ(SkipWhile<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _running = false; + _index = 0; + } + + public void OnNext(TSource value) + { + if (!_running) + { + try + { + _running = !_parent._predicateI(value, checked(_index++)); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + } + + if (_running) + { + base._observer.OnNext(value); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Sum.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Sum.cs new file mode 100644 index 0000000..6d51153 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Sum.cs @@ -0,0 +1,517 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class SumDouble : Producer<double> + { + private readonly IObservable<double> _source; + + public SumDouble(IObservable<double> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<double> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<double>, IObserver<double> + { + private double _sum; + + public _(IObserver<double> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0.0; + } + + public void OnNext(double value) + { + _sum += value; + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_sum); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class SumSingle : Producer<float> + { + private readonly IObservable<float> _source; + + public SumSingle(IObservable<float> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<float> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<float>, IObserver<float> + { + private double _sum; // This is what LINQ to Objects does! + + public _(IObserver<float> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0.0; // This is what LINQ to Objects does! + } + + public void OnNext(float value) + { + _sum += value; // This is what LINQ to Objects does! + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext((float)_sum); // This is what LINQ to Objects does! + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class SumDecimal : Producer<decimal> + { + private readonly IObservable<decimal> _source; + + public SumDecimal(IObservable<decimal> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<decimal> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<decimal>, IObserver<decimal> + { + private decimal _sum; + + public _(IObserver<decimal> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0M; + } + + public void OnNext(decimal value) + { + _sum += value; + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_sum); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class SumInt32 : Producer<int> + { + private readonly IObservable<int> _source; + + public SumInt32(IObservable<int> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<int> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<int>, IObserver<int> + { + private int _sum; + + public _(IObserver<int> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0; + } + + public void OnNext(int value) + { + try + { + checked + { + _sum += value; + } + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_sum); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class SumInt64 : Producer<long> + { + private readonly IObservable<long> _source; + + public SumInt64(IObservable<long> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<long> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<long>, IObserver<long> + { + private long _sum; + + public _(IObserver<long> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0L; + } + + public void OnNext(long value) + { + try + { + checked + { + _sum += value; + } + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_sum); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class SumDoubleNullable : Producer<double?> + { + private readonly IObservable<double?> _source; + + public SumDoubleNullable(IObservable<double?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<double?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<double?>, IObserver<double?> + { + private double _sum; + + public _(IObserver<double?> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0.0; + } + + public void OnNext(double? value) + { + if (value != null) + _sum += value.Value; + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_sum); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class SumSingleNullable : Producer<float?> + { + private readonly IObservable<float?> _source; + + public SumSingleNullable(IObservable<float?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<float?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<float?>, IObserver<float?> + { + private double _sum; // This is what LINQ to Objects does! + + public _(IObserver<float?> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0.0; // This is what LINQ to Objects does! + } + + public void OnNext(float? value) + { + if (value != null) + _sum += value.Value; // This is what LINQ to Objects does! + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext((float)_sum); // This is what LINQ to Objects does! + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class SumDecimalNullable : Producer<decimal?> + { + private readonly IObservable<decimal?> _source; + + public SumDecimalNullable(IObservable<decimal?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<decimal?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<decimal?>, IObserver<decimal?> + { + private decimal _sum; + + public _(IObserver<decimal?> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0M; + } + + public void OnNext(decimal? value) + { + if (value != null) + _sum += value.Value; + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_sum); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class SumInt32Nullable : Producer<int?> + { + private readonly IObservable<int?> _source; + + public SumInt32Nullable(IObservable<int?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<int?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<int?>, IObserver<int?> + { + private int _sum; + + public _(IObserver<int?> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0; + } + + public void OnNext(int? value) + { + try + { + checked + { + if (value != null) + _sum += value.Value; + } + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_sum); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class SumInt64Nullable : Producer<long?> + { + private readonly IObservable<long?> _source; + + public SumInt64Nullable(IObservable<long?> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<long?> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<long?>, IObserver<long?> + { + private long _sum; + + public _(IObserver<long?> observer, IDisposable cancel) + : base(observer, cancel) + { + _sum = 0L; + } + + public void OnNext(long? value) + { + try + { + checked + { + if (value != null) + _sum += value.Value; + } + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_sum); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Switch.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Switch.cs new file mode 100644 index 0000000..794b0d4 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Switch.cs @@ -0,0 +1,152 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Switch<TSource> : Producer<TSource> + { + private readonly IObservable<IObservable<TSource>> _sources; + + public Switch(IObservable<IObservable<TSource>> sources) + { + _sources = sources; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource>, IObserver<IObservable<TSource>> + { + private readonly Switch<TSource> _parent; + + public _(Switch<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private IDisposable _subscription; + private SerialDisposable _innerSubscription; + private bool _isStopped; + private ulong _latest; + private bool _hasLatest; + + public IDisposable Run() + { + _gate = new object(); + _innerSubscription = new SerialDisposable(); + _isStopped = false; + _latest = 0UL; + _hasLatest = false; + + var subscription = new SingleAssignmentDisposable(); + _subscription = subscription; + subscription.Disposable = _parent._sources.SubscribeSafe(this); + + return new CompositeDisposable(_subscription, _innerSubscription); + } + + public void OnNext(IObservable<TSource> value) + { + var id = default(ulong); + lock (_gate) + { + id = unchecked(++_latest); + _hasLatest = true; + } + + var d = new SingleAssignmentDisposable(); + _innerSubscription.Disposable = d; + d.Disposable = value.SubscribeSafe(new ι(this, id, d)); + } + + public void OnError(Exception error) + { + lock (_gate) + base._observer.OnError(error); + + base.Dispose(); + } + + public void OnCompleted() + { + lock (_gate) + { + _subscription.Dispose(); + + _isStopped = true; + if (!_hasLatest) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class ι : IObserver<TSource> + { + private readonly _ _parent; + private readonly ulong _id; + private readonly IDisposable _self; + + public ι(_ parent, ulong id, IDisposable self) + { + _parent = parent; + _id = id; + _self = self; + } + + public void OnNext(TSource value) + { + lock (_parent._gate) + { + if (_parent._latest == _id) + _parent._observer.OnNext(value); + } + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _self.Dispose(); + + if (_parent._latest == _id) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + } + + public void OnCompleted() + { + lock (_parent._gate) + { + _self.Dispose(); + + if (_parent._latest == _id) + { + _parent._hasLatest = false; + + if (_parent._isStopped) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + } + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Synchronize.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Synchronize.cs new file mode 100644 index 0000000..36985e9 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Synchronize.cs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class Synchronize<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly object _gate; + + public Synchronize(IObservable<TSource> source, object gate) + { + _source = source; + _gate = gate; + } + + public Synchronize(IObservable<TSource> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.Subscribe(sink); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Synchronize<TSource> _parent; + private object _gate; + + public _(Synchronize<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _gate = _parent._gate ?? new object(); + } + + public void OnNext(TSource value) + { + lock (_gate) + { + base._observer.OnNext(value); + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Take.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Take.cs new file mode 100644 index 0000000..738e7c0 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Take.cs @@ -0,0 +1,176 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Take<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly int _count; + private readonly TimeSpan _duration; + internal readonly IScheduler _scheduler; + + public Take(IObservable<TSource> source, int count) + { + _source = source; + _count = count; + } + + public Take(IObservable<TSource> source, TimeSpan duration, IScheduler scheduler) + { + _source = source; + _duration = duration; + _scheduler = scheduler; + } + + public IObservable<TSource> Ω(int count) + { + // + // Minimum semantics: + // + // xs --o--o--o--o--o--o--| xs --o--o--o--o--o--o--| + // xs.Take(5) --o--o--o--o--o| xs.Take(3) --o--o--o| + // xs.Take(5).Take(3) --o--o--o| xs.Take(3).Take(5) --o--o--o| + // + if (_count <= count) + return this; + else + return new Take<TSource>(_source, count); + } + + public IObservable<TSource> Ω(TimeSpan duration) + { + // + // Minimum semantics: + // + // t 0--1--2--3--4--5--6--7-> t 0--1--2--3--4--5--6--7-> + // + // xs --o--o--o--o--o--o--| xs --o--o--o--o--o--o--| + // xs.Take(5s) --o--o--o--o--o| xs.Take(3s) --o--o--o| + // xs.Take(5s).Take(3s) --o--o--o| xs.Take(3s).Take(5s) --o--o--o| + // + if (_duration <= duration) + return this; + else + return new Take<TSource>(_source, duration, _scheduler); + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_scheduler == null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new τ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Take<TSource> _parent; + private int _remaining; + + public _(Take<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _remaining = _parent._count; + } + + public void OnNext(TSource value) + { + if (_remaining > 0) + { + --_remaining; + base._observer.OnNext(value); + + if (_remaining == 0) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class τ : Sink<TSource>, IObserver<TSource> + { + private readonly Take<TSource> _parent; + + public τ(Take<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + + public IDisposable Run() + { + _gate = new object(); + + var t = _parent._scheduler.Schedule(_parent._duration, Tick); + var d = _parent._source.SubscribeSafe(this); + return new CompositeDisposable(t, d); + } + + private void Tick() + { + lock (_gate) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + + public void OnNext(TSource value) + { + lock (_gate) + { + base._observer.OnNext(value); + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TakeLast.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TakeLast.cs new file mode 100644 index 0000000..98bf40e --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TakeLast.cs @@ -0,0 +1,230 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class TakeLast<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly int _count; + private readonly TimeSpan _duration; + private readonly IScheduler _scheduler; + private readonly IScheduler _loopScheduler; + + public TakeLast(IObservable<TSource> source, int count, IScheduler loopScheduler) + { + _source = source; + _count = count; + _loopScheduler = loopScheduler; + } + + public TakeLast(IObservable<TSource> source, TimeSpan duration, IScheduler scheduler, IScheduler loopScheduler) + { + _source = source; + _duration = duration; + _scheduler = scheduler; + _loopScheduler = loopScheduler; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_scheduler == null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else + { + var sink = new τ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly TakeLast<TSource> _parent; + private Queue<TSource> _queue; + + public _(TakeLast<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _queue = new Queue<TSource>(); + } + + private SingleAssignmentDisposable _subscription; + private SingleAssignmentDisposable _loop; + + public IDisposable Run() + { + _subscription = new SingleAssignmentDisposable(); + _loop = new SingleAssignmentDisposable(); + + _subscription.Disposable = _parent._source.SubscribeSafe(this); + + return new CompositeDisposable(_subscription, _loop); + } + + public void OnNext(TSource value) + { + _queue.Enqueue(value); + if (_queue.Count > _parent._count) + _queue.Dequeue(); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + _subscription.Dispose(); + + var longRunning = _parent._loopScheduler.AsLongRunning(); + if (longRunning != null) + _loop.Disposable = longRunning.ScheduleLongRunning(Loop); + else + _loop.Disposable = _parent._loopScheduler.Schedule(LoopRec); + } + + private void LoopRec(Action recurse) + { + if (_queue.Count > 0) + { + base._observer.OnNext(_queue.Dequeue()); + recurse(); + } + else + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + + private void Loop(ICancelable cancel) + { + var n = _queue.Count; + + while (!cancel.IsDisposed) + { + if (n == 0) + { + base._observer.OnCompleted(); + break; + } + else + base._observer.OnNext(_queue.Dequeue()); + + n--; + } + + base.Dispose(); + } + } + + class τ : Sink<TSource>, IObserver<TSource> + { + private readonly TakeLast<TSource> _parent; + private Queue<System.Reactive.TimeInterval<TSource>> _queue; + + public τ(TakeLast<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _queue = new Queue<System.Reactive.TimeInterval<TSource>>(); + } + + private SingleAssignmentDisposable _subscription; + private SingleAssignmentDisposable _loop; + private IStopwatch _watch; + + public IDisposable Run() + { + _subscription = new SingleAssignmentDisposable(); + _loop = new SingleAssignmentDisposable(); + + _watch = _parent._scheduler.StartStopwatch(); + _subscription.Disposable = _parent._source.SubscribeSafe(this); + + return new CompositeDisposable(_subscription, _loop); + } + + public void OnNext(TSource value) + { + var now = _watch.Elapsed; + _queue.Enqueue(new System.Reactive.TimeInterval<TSource>(value, now)); + Trim(now); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + _subscription.Dispose(); + + var now = _watch.Elapsed; + Trim(now); + + var longRunning = _parent._loopScheduler.AsLongRunning(); + if (longRunning != null) + _loop.Disposable = longRunning.ScheduleLongRunning(Loop); + else + _loop.Disposable = _parent._loopScheduler.Schedule(LoopRec); + } + + private void LoopRec(Action recurse) + { + if (_queue.Count > 0) + { + base._observer.OnNext(_queue.Dequeue().Value); + recurse(); + } + else + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + + private void Loop(ICancelable cancel) + { + var n = _queue.Count; + + while (!cancel.IsDisposed) + { + if (n == 0) + { + base._observer.OnCompleted(); + break; + } + else + base._observer.OnNext(_queue.Dequeue().Value); + + n--; + } + + base.Dispose(); + } + + private void Trim(TimeSpan now) + { + while (_queue.Count > 0 && now - _queue.Peek().Interval >= _parent._duration) + _queue.Dequeue(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TakeLastBuffer.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TakeLastBuffer.cs new file mode 100644 index 0000000..71b36b0 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TakeLastBuffer.cs @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class TakeLastBuffer<TSource> : Producer<IList<TSource>> + { + private readonly IObservable<TSource> _source; + private readonly int _count; + private readonly TimeSpan _duration; + private readonly IScheduler _scheduler; + + public TakeLastBuffer(IObservable<TSource> source, int count) + { + _source = source; + _count = count; + } + + public TakeLastBuffer(IObservable<TSource> source, TimeSpan duration, IScheduler scheduler) + { + _source = source; + _duration = duration; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<IList<TSource>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_scheduler == null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new τ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + + class _ : Sink<IList<TSource>>, IObserver<TSource> + { + private readonly TakeLastBuffer<TSource> _parent; + private Queue<TSource> _queue; + + public _(TakeLastBuffer<TSource> parent, IObserver<IList<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _queue = new Queue<TSource>(); + } + + public void OnNext(TSource value) + { + _queue.Enqueue(value); + if (_queue.Count > _parent._count) + _queue.Dequeue(); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + var res = new List<TSource>(_queue.Count); + while (_queue.Count > 0) + res.Add(_queue.Dequeue()); + + base._observer.OnNext(res); + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class τ : Sink<IList<TSource>>, IObserver<TSource> + { + private readonly TakeLastBuffer<TSource> _parent; + private Queue<System.Reactive.TimeInterval<TSource>> _queue; + + public τ(TakeLastBuffer<TSource> parent, IObserver<IList<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _queue = new Queue<System.Reactive.TimeInterval<TSource>>(); + } + + private IStopwatch _watch; + + public IDisposable Run() + { + _watch = _parent._scheduler.StartStopwatch(); + + return _parent._source.SubscribeSafe(this); + } + + public void OnNext(TSource value) + { + var now = _watch.Elapsed; + _queue.Enqueue(new System.Reactive.TimeInterval<TSource>(value, now)); + Trim(now); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + var now = _watch.Elapsed; + Trim(now); + + var res = new List<TSource>(_queue.Count); + while (_queue.Count > 0) + res.Add(_queue.Dequeue().Value); + + base._observer.OnNext(res); + base._observer.OnCompleted(); + base.Dispose(); + } + + private void Trim(TimeSpan now) + { + while (_queue.Count > 0 && now - _queue.Peek().Interval >= _parent._duration) + _queue.Dequeue(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TakeUntil.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TakeUntil.cs new file mode 100644 index 0000000..eccc222 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TakeUntil.cs @@ -0,0 +1,258 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Threading; + +namespace System.Reactive.Linq.Observαble +{ + class TakeUntil<TSource, TOther> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly IObservable<TOther> _other; + + public TakeUntil(IObservable<TSource> source, IObservable<TOther> other) + { + _source = source; + _other = other; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource> + { + private readonly TakeUntil<TSource, TOther> _parent; + + public _(TakeUntil<TSource, TOther> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + var sourceObserver = new T(this); + var otherObserver = new O(this, sourceObserver); + + // COMPAT - Order of Subscribe calls per v1.0.10621 + var otherSubscription = _parent._other.SubscribeSafe(otherObserver); + otherObserver.Disposable = otherSubscription; + + var sourceSubscription = _parent._source.SubscribeSafe(sourceObserver); + + return new CompositeDisposable( + otherSubscription, + sourceSubscription + ); + } + + /* + * We tried a more fine-grained synchronization scheme to make TakeUntil more efficient, but + * this requires several CAS instructions, which quickly add up to being non-beneficial. + * + * Notice an approach where the "other" channel performs an Interlocked.Exchange operation on + * the _parent._observer field to substitute it with a NopObserver<TSource> doesn't work, + * because the "other" channel still need to send an OnCompleted message, which could happen + * concurrently with another message when the "source" channel has already read from the + * _parent._observer field between making the On* call. + * + * Fixing this issue requires an ownership transfer mechanism for channels to get exclusive + * access to the outgoing observer while dispatching a message. Doing this more fine-grained + * than using locks turns out to be tricky and doesn't reduce cost. + */ + class T : IObserver<TSource> + { + private readonly _ _parent; + public volatile bool _open; + + public T(_ parent) + { + _parent = parent; + _open = false; + } + + public void OnNext(TSource value) + { + if (_open) + { + _parent._observer.OnNext(value); + } + else + { + lock (_parent) + { + _parent._observer.OnNext(value); + } + } + } + + public void OnError(Exception error) + { + lock (_parent) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + lock (_parent) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + } + + class O : IObserver<TOther> + { + private readonly _ _parent; + private readonly T _sourceObserver; + private readonly SingleAssignmentDisposable _subscription; + + public O(_ parent, T sourceObserver) + { + _parent = parent; + _sourceObserver = sourceObserver; + _subscription = new SingleAssignmentDisposable(); + } + + public IDisposable Disposable + { + set { _subscription.Disposable = value; } + } + + public void OnNext(TOther value) + { + lock (_parent) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + } + } + + public void OnError(Exception error) + { + lock (_parent) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + lock (_parent) + { + _sourceObserver._open = true; + _subscription.Dispose(); + } + } + } + } + } + + class TakeUntil<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly DateTimeOffset _endTime; + internal readonly IScheduler _scheduler; + + public TakeUntil(IObservable<TSource> source, DateTimeOffset endTime, IScheduler scheduler) + { + _source = source; + _endTime = endTime; + _scheduler = scheduler; + } + + public IObservable<TSource> Ω(DateTimeOffset endTime) + { + // + // Minimum semantics: + // + // t 0--1--2--3--4--5--6--7-> t 0--1--2--3--4--5--6--7-> + // + // xs --o--o--o--o--o--o--| xs --o--o--o--o--o--o--| + // xs.TU(5AM) --o--o--o--o--o| xs.TU(3AM) --o--o--o| + // xs.TU(5AM).TU(3AM) --o--o--o| xs.TU(3AM).TU(5AM) --o--o--o| + // + if (_endTime <= endTime) + return this; + else + return new TakeUntil<TSource>(_source, endTime, _scheduler); + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly TakeUntil<TSource> _parent; + + public _(TakeUntil<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + + public IDisposable Run() + { + _gate = new object(); + + var t = _parent._scheduler.Schedule(_parent._endTime, Tick); + var d = _parent._source.SubscribeSafe(this); + return new CompositeDisposable(t, d); + } + + private void Tick() + { + lock (_gate) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + + public void OnNext(TSource value) + { + lock (_gate) + { + base._observer.OnNext(value); + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TakeWhile.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TakeWhile.cs new file mode 100644 index 0000000..30f63ca --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TakeWhile.cs @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class TakeWhile<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, bool> _predicate; + private readonly Func<TSource, int, bool> _predicateI; + + public TakeWhile(IObservable<TSource> source, Func<TSource, bool> predicate) + { + _source = source; + _predicate = predicate; + } + + public TakeWhile(IObservable<TSource> source, Func<TSource, int, bool> predicate) + { + _source = source; + _predicateI = predicate; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_predicate != null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new τ(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly TakeWhile<TSource> _parent; + private bool _running; + + public _(TakeWhile<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _running = true; + } + + public void OnNext(TSource value) + { + if (_running) + { + try + { + _running = _parent._predicate(value); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + if (_running) + { + base._observer.OnNext(value); + } + else + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class τ : Sink<TSource>, IObserver<TSource> + { + private readonly TakeWhile<TSource> _parent; + private bool _running; + private int _index; + + public τ(TakeWhile<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _running = true; + _index = 0; + } + + public void OnNext(TSource value) + { + if (_running) + { + try + { + _running = _parent._predicateI(value, checked(_index++)); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + if (_running) + { + base._observer.OnNext(value); + } + else + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Throttle.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Throttle.cs new file mode 100644 index 0000000..7b7ad86 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Throttle.cs @@ -0,0 +1,280 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Throttle<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly TimeSpan _dueTime; + private readonly IScheduler _scheduler; + + public Throttle(IObservable<TSource> source, TimeSpan dueTime, IScheduler scheduler) + { + _source = source; + _dueTime = dueTime; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Throttle<TSource> _parent; + + public _(Throttle<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private TSource _value; + private bool _hasValue; + private SerialDisposable _cancelable; + private ulong _id; + + public IDisposable Run() + { + _gate = new object(); + _value = default(TSource); + _hasValue = false; + _cancelable = new SerialDisposable(); + _id = 0UL; + + var subscription = _parent._source.SubscribeSafe(this); + + return new CompositeDisposable(subscription, _cancelable); + } + + public void OnNext(TSource value) + { + var currentid = default(ulong); + lock (_gate) + { + _hasValue = true; + _value = value; + _id = unchecked(_id + 1); + currentid = _id; + } + var d = new SingleAssignmentDisposable(); + _cancelable.Disposable = d; + d.Disposable = _parent._scheduler.Schedule(currentid, _parent._dueTime, Propagate); + } + + private IDisposable Propagate(IScheduler self, ulong currentid) + { + lock (_gate) + { + if (_hasValue && _id == currentid) + base._observer.OnNext(_value); + _hasValue = false; + } + + return Disposable.Empty; + } + + public void OnError(Exception error) + { + _cancelable.Dispose(); + + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + + _hasValue = false; + _id = unchecked(_id + 1); + } + } + + public void OnCompleted() + { + _cancelable.Dispose(); + + lock (_gate) + { + if (_hasValue) + base._observer.OnNext(_value); + + base._observer.OnCompleted(); + base.Dispose(); + + _hasValue = false; + _id = unchecked(_id + 1); + } + } + } + } + + class Throttle<TSource, TThrottle> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, IObservable<TThrottle>> _throttleSelector; + + public Throttle(IObservable<TSource> source, Func<TSource, IObservable<TThrottle>> throttleSelector) + { + _source = source; + _throttleSelector = throttleSelector; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Throttle<TSource, TThrottle> _parent; + + public _(Throttle<TSource, TThrottle> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private TSource _value; + private bool _hasValue; + private SerialDisposable _cancelable; + private ulong _id; + + public IDisposable Run() + { + _gate = new object(); + _value = default(TSource); + _hasValue = false; + _cancelable = new SerialDisposable(); + _id = 0UL; + + var subscription = _parent._source.SubscribeSafe(this); + + return new CompositeDisposable(subscription, _cancelable); + } + + public void OnNext(TSource value) + { + var throttle = default(IObservable<TThrottle>); + try + { + throttle = _parent._throttleSelector(value); + } + catch (Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + + return; + } + + ulong currentid; + lock (_gate) + { + _hasValue = true; + _value = value; + _id = unchecked(_id + 1); + currentid = _id; + } + + var d = new SingleAssignmentDisposable(); + _cancelable.Disposable = d; + d.Disposable = throttle.SubscribeSafe(new δ(this, value, currentid, d)); + } + + public void OnError(Exception error) + { + _cancelable.Dispose(); + + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + + _hasValue = false; + _id = unchecked(_id + 1); + } + } + + public void OnCompleted() + { + _cancelable.Dispose(); + + lock (_gate) + { + if (_hasValue) + base._observer.OnNext(_value); + + base._observer.OnCompleted(); + base.Dispose(); + + _hasValue = false; + _id = unchecked(_id + 1); + } + } + + class δ : IObserver<TThrottle> + { + private readonly _ _parent; + private readonly TSource _value; + private readonly ulong _currentid; + private readonly IDisposable _self; + + public δ(_ parent, TSource value, ulong currentid, IDisposable self) + { + _parent = parent; + _value = value; + _currentid = currentid; + _self = self; + } + + public void OnNext(TThrottle value) + { + lock (_parent._gate) + { + if (_parent._hasValue && _parent._id == _currentid) + _parent._observer.OnNext(_value); + + _parent._hasValue = false; + _self.Dispose(); + } + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + lock (_parent._gate) + { + if (_parent._hasValue && _parent._id == _currentid) + _parent._observer.OnNext(_value); + + _parent._hasValue = false; + _self.Dispose(); + } + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Throw.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Throw.cs new file mode 100644 index 0000000..3b10894 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Throw.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; + +namespace System.Reactive.Linq.Observαble +{ + class Throw<TResult> : Producer<TResult> + { + private readonly Exception _exception; + private readonly IScheduler _scheduler; + + public Throw(Exception exception, IScheduler scheduler) + { + _exception = exception; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TResult> + { + private readonly Throw<TResult> _parent; + + public _(Throw<TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + return _parent._scheduler.Schedule(Invoke); + } + + private void Invoke() + { + base._observer.OnError(_parent._exception); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TimeInterval.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TimeInterval.cs new file mode 100644 index 0000000..dacfa03 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/TimeInterval.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Diagnostics; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Threading; + +namespace System.Reactive.Linq.Observαble +{ + class TimeInterval<TSource> : Producer<System.Reactive.TimeInterval<TSource>> + { + private readonly IObservable<TSource> _source; + private readonly IScheduler _scheduler; + + public TimeInterval(IObservable<TSource> source, IScheduler scheduler) + { + _source = source; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<System.Reactive.TimeInterval<TSource>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<System.Reactive.TimeInterval<TSource>>, IObserver<TSource> + { + private readonly TimeInterval<TSource> _parent; + + public _(TimeInterval<TSource> parent, IObserver<System.Reactive.TimeInterval<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private IStopwatch _watch; + private TimeSpan _last; + + public IDisposable Run() + { + _watch = _parent._scheduler.StartStopwatch(); + _last = TimeSpan.Zero; + + return _parent._source.Subscribe(this); + } + + public void OnNext(TSource value) + { + var now = _watch.Elapsed; + var span = now.Subtract(_last); + _last = now; + base._observer.OnNext(new System.Reactive.TimeInterval<TSource>(value, span)); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Timeout.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Timeout.cs new file mode 100644 index 0000000..4896294 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Timeout.cs @@ -0,0 +1,432 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Timeout<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly TimeSpan? _dueTimeR; + private readonly DateTimeOffset? _dueTimeA; + private readonly IObservable<TSource> _other; + private readonly IScheduler _scheduler; + + public Timeout(IObservable<TSource> source, TimeSpan dueTime, IObservable<TSource> other, IScheduler scheduler) + { + _source = source; + _dueTimeR = dueTime; + _other = other; + _scheduler = scheduler; + } + + public Timeout(IObservable<TSource> source, DateTimeOffset dueTime, IObservable<TSource> other, IScheduler scheduler) + { + _source = source; + _dueTimeA = dueTime; + _other = other; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_dueTimeA.HasValue) + { + var sink = new α(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else + { + var sink = new ρ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + + class α : Sink<TSource>, IObserver<TSource> + { + private readonly Timeout<TSource> _parent; + + public α(Timeout<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private SerialDisposable _subscription; + private object _gate; + private bool _switched; + + public IDisposable Run() + { + _subscription = new SerialDisposable(); + var original = new SingleAssignmentDisposable(); + + _subscription.Disposable = original; + + _gate = new object(); + _switched = false; + + var timer = _parent._scheduler.Schedule(_parent._dueTimeA.Value, Timeout); + + original.Disposable = _parent._source.SubscribeSafe(this); + + return new CompositeDisposable(_subscription, timer); + } + + private void Timeout() + { + var timerWins = false; + + lock (_gate) + { + timerWins = !_switched; + _switched = true; + } + + if (timerWins) + _subscription.Disposable = _parent._other.SubscribeSafe(this.GetForwarder()); + } + + public void OnNext(TSource value) + { + lock (_gate) + { + if (!_switched) + base._observer.OnNext(value); + } + } + + public void OnError(Exception error) + { + var onErrorWins = false; + + lock (_gate) + { + onErrorWins = !_switched; + _switched = true; + } + + if (onErrorWins) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + var onCompletedWins = false; + + lock (_gate) + { + onCompletedWins = !_switched; + _switched = true; + } + + if (onCompletedWins) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class ρ : Sink<TSource>, IObserver<TSource> + { + private readonly Timeout<TSource> _parent; + + public ρ(Timeout<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private SerialDisposable _subscription; + private SerialDisposable _timer; + + private object _gate; + private ulong _id; + private bool _switched; + + public IDisposable Run() + { + _subscription = new SerialDisposable(); + _timer = new SerialDisposable(); + var original = new SingleAssignmentDisposable(); + + _subscription.Disposable = original; + + _gate = new object(); + _id = 0UL; + _switched = false; + + CreateTimer(); + + original.Disposable = _parent._source.SubscribeSafe(this); + + return new CompositeDisposable(_subscription, _timer); + } + + private void CreateTimer() + { + _timer.Disposable = _parent._scheduler.Schedule(_id, _parent._dueTimeR.Value, Timeout); + } + + private IDisposable Timeout(IScheduler _, ulong myid) + { + var timerWins = false; + + lock (_gate) + { + _switched = (_id == myid); + timerWins = _switched; + } + + if (timerWins) + _subscription.Disposable = _parent._other.SubscribeSafe(this.GetForwarder()); + + return Disposable.Empty; + } + + public void OnNext(TSource value) + { + var onNextWins = false; + + lock (_gate) + { + onNextWins = !_switched; + if (onNextWins) + { + _id = unchecked(_id + 1); + } + } + + if (onNextWins) + { + base._observer.OnNext(value); + CreateTimer(); + } + } + + public void OnError(Exception error) + { + var onErrorWins = false; + + lock (_gate) + { + onErrorWins = !_switched; + if (onErrorWins) + { + _id = unchecked(_id + 1); + } + } + + if (onErrorWins) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + var onCompletedWins = false; + + lock (_gate) + { + onCompletedWins = !_switched; + if (onCompletedWins) + { + _id = unchecked(_id + 1); + } + } + + if (onCompletedWins) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + } + + class Timeout<TSource, TTimeout> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly IObservable<TTimeout> _firstTimeout; + private readonly Func<TSource, IObservable<TTimeout>> _timeoutSelector; + private readonly IObservable<TSource> _other; + + public Timeout(IObservable<TSource> source, IObservable<TTimeout> firstTimeout, Func<TSource, IObservable<TTimeout>> timeoutSelector, IObservable<TSource> other) + { + _source = source; + _firstTimeout = firstTimeout; + _timeoutSelector = timeoutSelector; + _other = other; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Timeout<TSource, TTimeout> _parent; + + public _(Timeout<TSource, TTimeout> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private SerialDisposable _subscription; + private SerialDisposable _timer; + private object _gate; + private ulong _id; + private bool _switched; + + public IDisposable Run() + { + _subscription = new SerialDisposable(); + _timer = new SerialDisposable(); + var original = new SingleAssignmentDisposable(); + + _subscription.Disposable = original; + + _gate = new object(); + _id = 0UL; + _switched = false; + + SetTimer(_parent._firstTimeout); + + original.Disposable = _parent._source.SubscribeSafe(this); + + return new CompositeDisposable(_subscription, _timer); + } + + public void OnNext(TSource value) + { + if (ObserverWins()) + { + base._observer.OnNext(value); + + var timeout = default(IObservable<TTimeout>); + try + { + timeout = _parent._timeoutSelector(value); + } + catch (Exception error) + { + base._observer.OnError(error); + base.Dispose(); + return; + } + + SetTimer(timeout); + } + } + + public void OnError(Exception error) + { + if (ObserverWins()) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + if (ObserverWins()) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + + private void SetTimer(IObservable<TTimeout> timeout) + { + var myid = _id; + + var d = new SingleAssignmentDisposable(); + _timer.Disposable = d; + d.Disposable = timeout.SubscribeSafe(new τ(this, myid, d)); + } + + class τ : IObserver<TTimeout> + { + private readonly _ _parent; + private readonly ulong _id; + private readonly IDisposable _self; + + public τ(_ parent, ulong id, IDisposable self) + { + _parent = parent; + _id = id; + _self = self; + } + + public void OnNext(TTimeout value) + { + if (TimerWins()) + _parent._subscription.Disposable = _parent._parent._other.SubscribeSafe(_parent.GetForwarder()); + + _self.Dispose(); + } + + public void OnError(Exception error) + { + if (TimerWins()) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + if (TimerWins()) + _parent._subscription.Disposable = _parent._parent._other.SubscribeSafe(_parent.GetForwarder()); + } + + private bool TimerWins() + { + var res = false; + + lock (_parent._gate) + { + _parent._switched = (_parent._id == _id); + res = _parent._switched; + } + + return res; + } + } + + private bool ObserverWins() + { + var res = false; + + lock (_gate) + { + res = !_switched; + if (res) + { + _id = unchecked(_id + 1); + } + } + + return res; + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Timer.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Timer.cs new file mode 100644 index 0000000..22ee0df --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Timer.cs @@ -0,0 +1,264 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Diagnostics; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Threading; + +namespace System.Reactive.Linq.Observαble +{ + class Timer : Producer<long> + { + private readonly DateTimeOffset? _dueTimeA; + private readonly TimeSpan? _dueTimeR; + private readonly TimeSpan? _period; + private readonly IScheduler _scheduler; + + public Timer(DateTimeOffset dueTime, TimeSpan? period, IScheduler scheduler) + { + _dueTimeA = dueTime; + _period = period; + _scheduler = scheduler; + } + + public Timer(TimeSpan dueTime, TimeSpan? period, IScheduler scheduler) + { + _dueTimeR = dueTime; + _period = period; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<long> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_period.HasValue) + { + var sink = new π(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + + class _ : Sink<long> + { + private readonly Timer _parent; + + public _(Timer parent, IObserver<long> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + if (_parent._dueTimeA.HasValue) + { + return _parent._scheduler.Schedule(_parent._dueTimeA.Value, Invoke); + } + else + { + return _parent._scheduler.Schedule(_parent._dueTimeR.Value, Invoke); + } + } + + private void Invoke() + { + base._observer.OnNext(0); + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class π : Sink<long> + { + private readonly Timer _parent; + private readonly TimeSpan _period; + + public π(Timer parent, IObserver<long> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _period = _parent._period.Value; + } + + public IDisposable Run() + { + if (_parent._dueTimeA.HasValue) + { + var dueTime = _parent._dueTimeA.Value; + return _parent._scheduler.Schedule(default(object), dueTime, InvokeStart); + } + else + { + var dueTime = _parent._dueTimeR.Value; + + // + // Optimize for the case of Observable.Interval. + // + if (dueTime == _period) + { + return _parent._scheduler.SchedulePeriodic(0L, _period, (Func<long, long>)Tick); + } + + return _parent._scheduler.Schedule(default(object), dueTime, InvokeStart); + } + } + + // + // BREAKING CHANGE v2 > v1.x - No more correction for time drift based on absolute time. This + // didn't work for large period values anyway; the fractional + // error exceeded corrections. Also complicated dealing with system + // clock change conditions and caused numerous bugs. + // + // - For more precise scheduling, use a custom scheduler that measures TimeSpan values in a + // better way, e.g. spinning to make up for the last part of the period. Whether or not the + // values of the TimeSpan period match NT time or wall clock time is up to the scheduler. + // + // - For more accurate scheduling wrt the system clock, use Generate with DateTimeOffset time + // selectors. When the system clock changes, intervals will not be the same as diffs between + // consecutive absolute time values. The precision will be low (1s range by default). + // + private long Tick(long count) + { + base._observer.OnNext(count); + return unchecked(count + 1); + } + + private int _pendingTickCount; + private IDisposable _periodic; + + private IDisposable InvokeStart(IScheduler self, object state) + { + // + // Notice the first call to OnNext will introduce skew if it takes significantly long when + // using the following naive implementation: + // + // Code: base._observer.OnNext(0L); + // return self.SchedulePeriodicEmulated(1L, _period, (Func<long, long>)Tick); + // + // What we're saying here is that Observable.Timer(dueTime, period) is pretty much the same + // as writing Observable.Timer(dueTime).Concat(Observable.Interval(period)). + // + // Expected: dueTime + // | + // 0--period--1--period--2--period--3--period--4--... + // | + // +-OnNext(0L)-| + // + // Actual: dueTime + // | + // 0------------#--period--1--period--2--period--3--period--4--... + // | + // +-OnNext(0L)-| + // + // Different solutions for this behavior have different problems: + // + // 1. Scheduling the periodic job first and using an AsyncLock to serialize the OnNext calls + // has the drawback that InvokeStart may never return. This happens when every callback + // doesn't meet the period's deadline, hence the periodic job keeps queueing stuff up. In + // this case, InvokeStart stays the owner of the AsyncLock and the call to Wait will never + // return, thus not allowing any interleaving of work on this scheduler's logical thread. + // + // 2. Scheduling the periodic job first and using a (blocking) synchronization primitive to + // signal completion of the OnNext(0L) call to the Tick call requires quite a bit of state + // and careful handling of the case when OnNext(0L) throws. What's worse is the blocking + // behavior inside Tick. + // + // In order to avoid blocking behavior, we need a scheme much like SchedulePeriodic emulation + // where work to dispatch OnNext(n + 1) is delegated to a catch up loop in case OnNext(n) was + // still running. Because SchedulePeriodic emulation exhibits such behavior in all cases, we + // only need to deal with the overlap of OnNext(0L) with future periodic OnNext(n) dispatch + // jobs. In the worst case where every callback takes longer than the deadline implied by the + // period, the periodic job will just queue up work that's dispatched by the tail-recursive + // catch up loop. In the best case, all work will be dispatched on the periodic scheduler. + // + + // + // We start with one tick pending because we're about to start doing OnNext(0L). + // + _pendingTickCount = 1; + + var d = new SingleAssignmentDisposable(); + _periodic = d; + d.Disposable = self.SchedulePeriodic(1L, _period, (Func<long, long>)Tock); + + try + { + base._observer.OnNext(0L); + } + catch (Exception e) + { + d.Dispose(); + e.Throw(); + } + + // + // If the periodic scheduling job already ran before we finished dispatching the OnNext(0L) + // call, we'll find pendingTickCount to be > 1. In this case, we need to catch up by dispatching + // subsequent calls to OnNext as fast as possible, but without running a loop in order to ensure + // fair play with the scheduler. So, we run a tail-recursive loop in CatchUp instead. + // + if (Interlocked.Decrement(ref _pendingTickCount) > 0) + { + var c = new SingleAssignmentDisposable(); + c.Disposable = self.Schedule(1L, CatchUp); + + return new CompositeDisposable(2) { d, c }; + } + + return d; + } + + private long Tock(long count) + { + // + // Notice the handler for (emulated) periodic scheduling is non-reentrant. + // + // When there's no overlap with the OnNext(0L) call, the following code will cycle through + // pendingTickCount 0 -> 1 -> 0 for the remainder of the timer's execution. + // + // If there's overlap with the OnNext(0L) call, pendingTickCount will increase to record + // the number of catch up OnNext calls required, which will be dispatched by the recursive + // scheduling loop in CatchUp (which quits when it reaches 0 pending ticks). + // + if (Interlocked.Increment(ref _pendingTickCount) == 1) + { + base._observer.OnNext(count); + Interlocked.Decrement(ref _pendingTickCount); + } + + return unchecked(count + 1); + } + + private void CatchUp(long count, Action<long> recurse) + { + try + { + base._observer.OnNext(count); + } + catch (Exception e) + { + _periodic.Dispose(); + e.Throw(); + } + + // + // We can simply bail out if we decreased the tick count to 0. In that case, the Tock + // method will take over when it sees the 0 -> 1 transition. + // + if (Interlocked.Decrement(ref _pendingTickCount) > 0) + { + recurse(unchecked(count + 1)); + } + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Timestamp.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Timestamp.cs new file mode 100644 index 0000000..ed54678 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Timestamp.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Concurrency; + +namespace System.Reactive.Linq.Observαble +{ + class Timestamp<TSource> : Producer<Timestamped<TSource>> + { + private readonly IObservable<TSource> _source; + private readonly IScheduler _scheduler; + + public Timestamp(IObservable<TSource> source, IScheduler scheduler) + { + _source = source; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<Timestamped<TSource>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<Timestamped<TSource>>, IObserver<TSource> + { + private readonly Timestamp<TSource> _parent; + + public _(Timestamp<TSource> parent, IObserver<Timestamped<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + base._observer.OnNext(new Timestamped<TSource>(value, _parent._scheduler.Now)); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToArray.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToArray.cs new file mode 100644 index 0000000..ba039ae --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToArray.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; + +namespace System.Reactive.Linq.Observαble +{ + class ToArray<TSource> : Producer<TSource[]> + { + private readonly IObservable<TSource> _source; + + public ToArray(IObservable<TSource> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<TSource[]> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<TSource[]>, IObserver<TSource> + { + private List<TSource> _list; + + public _(IObserver<TSource[]> observer, IDisposable cancel) + : base(observer, cancel) + { + _list = new List<TSource>(); + } + + public void OnNext(TSource value) + { + _list.Add(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_list.ToArray()); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToDictionary.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToDictionary.cs new file mode 100644 index 0000000..919c1a1 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToDictionary.cs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; + +namespace System.Reactive.Linq.Observαble +{ + class ToDictionary<TSource, TKey, TElement> : Producer<IDictionary<TKey, TElement>> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, TKey> _keySelector; + private readonly Func<TSource, TElement> _elementSelector; + private readonly IEqualityComparer<TKey> _comparer; + + public ToDictionary(IObservable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer) + { + _source = source; + _keySelector = keySelector; + _elementSelector = elementSelector; + _comparer = comparer; + } + + protected override IDisposable Run(IObserver<IDictionary<TKey, TElement>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<IDictionary<TKey, TElement>>, IObserver<TSource> + { + private readonly ToDictionary<TSource, TKey, TElement> _parent; + private Dictionary<TKey, TElement> _dictionary; + + public _(ToDictionary<TSource, TKey, TElement> parent, IObserver<IDictionary<TKey, TElement>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _dictionary = new Dictionary<TKey, TElement>(_parent._comparer); + } + + public void OnNext(TSource value) + { + try + { + _dictionary.Add(_parent._keySelector(value), _parent._elementSelector(value)); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_dictionary); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToList.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToList.cs new file mode 100644 index 0000000..34956d4 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToList.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; + +namespace System.Reactive.Linq.Observαble +{ + class ToList<TSource> : Producer<IList<TSource>> + { + private readonly IObservable<TSource> _source; + + public ToList(IObservable<TSource> source) + { + _source = source; + } + + protected override IDisposable Run(IObserver<IList<TSource>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<IList<TSource>>, IObserver<TSource> + { + private List<TSource> _list; + + public _(IObserver<IList<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _list = new List<TSource>(); + } + + public void OnNext(TSource value) + { + _list.Add(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_list); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToLookup.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToLookup.cs new file mode 100644 index 0000000..55ff7d3 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToLookup.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Linq; + +namespace System.Reactive.Linq.Observαble +{ + class ToLookup<TSource, TKey, TElement> : Producer<ILookup<TKey, TElement>> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, TKey> _keySelector; + private readonly Func<TSource, TElement> _elementSelector; + private readonly IEqualityComparer<TKey> _comparer; + + public ToLookup(IObservable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer) + { + _source = source; + _keySelector = keySelector; + _elementSelector = elementSelector; + _comparer = comparer; + } + + protected override IDisposable Run(IObserver<ILookup<TKey, TElement>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + + class _ : Sink<ILookup<TKey, TElement>>, IObserver<TSource> + { + private readonly ToLookup<TSource, TKey, TElement> _parent; + private Lookup<TKey, TElement> _lookup; + + public _(ToLookup<TSource, TKey, TElement> parent, IObserver<ILookup<TKey, TElement>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _lookup = new Lookup<TKey, TElement>(_parent._comparer); + } + + public void OnNext(TSource value) + { + try + { + _lookup.Add(_parent._keySelector(value), _parent._elementSelector(value)); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnNext(_lookup); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToObservable.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToObservable.cs new file mode 100644 index 0000000..795d54d --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/ToObservable.cs @@ -0,0 +1,174 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class ToObservable<TSource> : Producer<TSource> + { + private readonly IEnumerable<TSource> _source; + private readonly IScheduler _scheduler; + + public ToObservable(IEnumerable<TSource> source, IScheduler scheduler) + { + _source = source; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource> + { + private readonly ToObservable<TSource> _parent; + + public _(ToObservable<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + var e = default(IEnumerator<TSource>); + try + { + e = _parent._source.GetEnumerator(); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return Disposable.Empty; + } + + var longRunning = _parent._scheduler.AsLongRunning(); + if (longRunning != null) + { + // + // Long-running schedulers have the contract they should *never* prevent + // the work from starting, such that the scheduled work has the chance + // to observe the cancellation and perform proper clean-up. In this case, + // we're sure Loop will be entered, allowing us to dispose the enumerator. + // + return longRunning.ScheduleLongRunning(e, Loop); + } + else + { + // + // We never allow the scheduled work to be cancelled. Instead, the flag + // is used to have LoopRec bail out and perform proper clean-up of the + // enumerator. + // + var flag = new BooleanDisposable(); + _parent._scheduler.Schedule(new State(flag, e), LoopRec); + return flag; + } + } + + class State + { + public readonly ICancelable flag; + public readonly IEnumerator<TSource> enumerator; + + public State(ICancelable flag, IEnumerator<TSource> enumerator) + { + this.flag = flag; + this.enumerator = enumerator; + } + } + + private void LoopRec(State state, Action<State> recurse) + { + var hasNext = false; + var ex = default(Exception); + var current = default(TSource); + + if (state.flag.IsDisposed) + { + state.enumerator.Dispose(); + return; + } + + try + { + hasNext = state.enumerator.MoveNext(); + if (hasNext) + current = state.enumerator.Current; + } + catch (Exception exception) + { + ex = exception; + } + + if (ex != null) + { + state.enumerator.Dispose(); + + base._observer.OnError(ex); + base.Dispose(); + return; + } + + if (!hasNext) + { + state.enumerator.Dispose(); + + base._observer.OnCompleted(); + base.Dispose(); + return; + } + + base._observer.OnNext(current); + recurse(state); + } + + private void Loop(IEnumerator<TSource> enumerator, ICancelable cancel) + { + while (!cancel.IsDisposed) + { + var hasNext = false; + var ex = default(Exception); + var current = default(TSource); + + try + { + hasNext = enumerator.MoveNext(); + if (hasNext) + current = enumerator.Current; + } + catch (Exception exception) + { + ex = exception; + } + + if (ex != null) + { + base._observer.OnError(ex); + break; + } + + if (!hasNext) + { + base._observer.OnCompleted(); + break; + } + + base._observer.OnNext(current); + } + + enumerator.Dispose(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Using.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Using.cs new file mode 100644 index 0000000..996ee0e --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Using.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + class Using<TSource, TResource> : Producer<TSource> + where TResource : IDisposable + { + private readonly Func<TResource> _resourceFactory; + private readonly Func<TResource, IObservable<TSource>> _observableFactory; + + public Using(Func<TResource> resourceFactory, Func<TResource, IObservable<TSource>> observableFactory) + { + _resourceFactory = resourceFactory; + _observableFactory = observableFactory; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Using<TSource, TResource> _parent; + + public _(Using<TSource, TResource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public IDisposable Run() + { + var source = default(IObservable<TSource>); + var disposable = Disposable.Empty; + try + { + var resource = _parent._resourceFactory(); + if (resource != null) + disposable = resource; + source = _parent._observableFactory(resource); + } + catch (Exception exception) + { + return new CompositeDisposable(Observable.Throw<TSource>(exception).SubscribeSafe(this), disposable); + } + + return new CompositeDisposable(source.SubscribeSafe(this), disposable); + } + + public void OnNext(TSource value) + { + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Where.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Where.cs new file mode 100644 index 0000000..a8eaa92 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Where.cs @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ + class Where<TSource> : Producer<TSource> + { + private readonly IObservable<TSource> _source; + private readonly Func<TSource, bool> _predicate; + private readonly Func<TSource, int, bool> _predicateI; + + public Where(IObservable<TSource> source, Func<TSource, bool> predicate) + { + _source = source; + _predicate = predicate; + } + + public Where(IObservable<TSource> source, Func<TSource, int, bool> predicate) + { + _source = source; + _predicateI = predicate; + } + + public IObservable<TSource> Ω(Func<TSource, bool> predicate) + { + if (_predicate != null) + return new Where<TSource>(_source, x => _predicate(x) && predicate(x)); + else + return new Where<TSource>(this, predicate); + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_predicate != null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + else + { + var sink = new τ(this, observer, cancel); + setSink(sink); + return _source.SubscribeSafe(sink); + } + } + + class _ : Sink<TSource>, IObserver<TSource> + { + private readonly Where<TSource> _parent; + + public _(Where<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + public void OnNext(TSource value) + { + var shouldRun = default(bool); + try + { + shouldRun = _parent._predicate(value); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + if (shouldRun) + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class τ : Sink<TSource>, IObserver<TSource> + { + private readonly Where<TSource> _parent; + private int _index; + + public τ(Where<TSource> parent, IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + _index = 0; + } + + public void OnNext(TSource value) + { + var shouldRun = default(bool); + try + { + shouldRun = _parent._predicateI(value, checked(_index++)); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return; + } + + if (shouldRun) + base._observer.OnNext(value); + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/While.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/While.cs new file mode 100644 index 0000000..178e4c8 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/While.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; + +namespace System.Reactive.Linq.Observαble +{ + class While<TSource> : Producer<TSource>, IConcatenatable<TSource> + { + private readonly Func<bool> _condition; + private readonly IObservable<TSource> _source; + + public While(Func<bool> condition, IObservable<TSource> source) + { + _condition = condition; + _source = source; + } + + protected override IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(observer, cancel); + setSink(sink); + return sink.Run(GetSources()); + } + + public IEnumerable<IObservable<TSource>> GetSources() + { + while (_condition()) + yield return _source; + } + + class _ : ConcatSink<TSource> + { + public _(IObserver<TSource> observer, IDisposable cancel) + : base(observer, cancel) + { + } + + public override void OnNext(TSource value) + { + base._observer.OnNext(value); + } + + public override void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + } + } +} +#endif diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Window.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Window.cs new file mode 100644 index 0000000..47059c7 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Window.cs @@ -0,0 +1,758 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Reactive.Subjects; +using System.Threading; + +namespace System.Reactive.Linq.Observαble +{ + class Window<TSource> : Producer<IObservable<TSource>> + { + private readonly IObservable<TSource> _source; + private readonly int _count; + private readonly int _skip; + + private readonly TimeSpan _timeSpan; + private readonly TimeSpan _timeShift; + private readonly IScheduler _scheduler; + + public Window(IObservable<TSource> source, int count, int skip) + { + _source = source; + _count = count; + _skip = skip; + } + + public Window(IObservable<TSource> source, TimeSpan timeSpan, TimeSpan timeShift, IScheduler scheduler) + { + _source = source; + _timeSpan = timeSpan; + _timeShift = timeShift; + _scheduler = scheduler; + } + + public Window(IObservable<TSource> source, TimeSpan timeSpan, int count, IScheduler scheduler) + { + _source = source; + _timeSpan = timeSpan; + _count = count; + _scheduler = scheduler; + } + + protected override IDisposable Run(IObserver<IObservable<TSource>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_scheduler == null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else if (_count > 0) + { + var sink = new μ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else + { + if (_timeSpan == _timeShift) + { + var sink = new π(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else + { + var sink = new τ(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + } + + class _ : Sink<IObservable<TSource>>, IObserver<TSource> + { + private readonly Window<TSource> _parent; + + public _(Window<TSource> parent, IObserver<IObservable<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private Queue<ISubject<TSource>> _queue; + private int _n; + private SingleAssignmentDisposable _m; + private RefCountDisposable _refCountDisposable; + + public IDisposable Run() + { + _queue = new Queue<ISubject<TSource>>(); + _n = 0; + _m = new SingleAssignmentDisposable(); + _refCountDisposable = new RefCountDisposable(_m); + + var firstWindow = CreateWindow(); + base._observer.OnNext(firstWindow); + + _m.Disposable = _parent._source.SubscribeSafe(this); + + return _refCountDisposable; + } + + private IObservable<TSource> CreateWindow() + { + var s = new Subject<TSource>(); + _queue.Enqueue(s); + return new WindowObservable<TSource>(s, _refCountDisposable); + } + + public void OnNext(TSource value) + { + foreach (var s in _queue) + s.OnNext(value); + + var c = _n - _parent._count + 1; + if (c >= 0 && c % _parent._skip == 0) + { + var s = _queue.Dequeue(); + s.OnCompleted(); + } + + _n++; + if (_n % _parent._skip == 0) + { + var newWindow = CreateWindow(); + base._observer.OnNext(newWindow); + } + } + + public void OnError(Exception error) + { + while (_queue.Count > 0) + _queue.Dequeue().OnError(error); + + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + while (_queue.Count > 0) + _queue.Dequeue().OnCompleted(); + + base._observer.OnCompleted(); + base.Dispose(); + } + } + + class τ : Sink<IObservable<TSource>>, IObserver<TSource> + { + private readonly Window<TSource> _parent; + + public τ(Window<TSource> parent, IObserver<IObservable<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private TimeSpan _totalTime; + private TimeSpan _nextShift; + private TimeSpan _nextSpan; + + private object _gate; + private Queue<ISubject<TSource>> _q; + + private SerialDisposable _timerD; + private RefCountDisposable _refCountDisposable; + + public IDisposable Run() + { + _totalTime = TimeSpan.Zero; + _nextShift = _parent._timeShift; + _nextSpan = _parent._timeSpan; + + _gate = new object(); + _q = new Queue<ISubject<TSource>>(); + + _timerD = new SerialDisposable(); + + var groupDisposable = new CompositeDisposable(2) { _timerD }; + _refCountDisposable = new RefCountDisposable(groupDisposable); + + CreateWindow(); + CreateTimer(); + + groupDisposable.Add(_parent._source.SubscribeSafe(this)); + + return _refCountDisposable; + } + + private void CreateWindow() + { + var s = new Subject<TSource>(); + _q.Enqueue(s); + base._observer.OnNext(new WindowObservable<TSource>(s, _refCountDisposable)); + } + + private void CreateTimer() + { + var m = new SingleAssignmentDisposable(); + _timerD.Disposable = m; + + var isSpan = false; + var isShift = false; + if (_nextSpan == _nextShift) + { + isSpan = true; + isShift = true; + } + else if (_nextSpan < _nextShift) + isSpan = true; + else + isShift = true; + + var newTotalTime = isSpan ? _nextSpan : _nextShift; + var ts = newTotalTime - _totalTime; + _totalTime = newTotalTime; + + if (isSpan) + _nextSpan += _parent._timeShift; + if (isShift) + _nextShift += _parent._timeShift; + + m.Disposable = _parent._scheduler.Schedule(new State { isSpan = isSpan, isShift = isShift }, ts, Tick); + } + + struct State + { + public bool isSpan; + public bool isShift; + } + + private IDisposable Tick(IScheduler self, State state) + { + lock (_gate) + { + // + // BREAKING CHANGE v2 > v1.x - Making behavior of sending OnCompleted to the window + // before sending out a new window consistent across all + // overloads of Window and Buffer. Before v2, the two + // operations below were reversed. + // + if (state.isSpan) + { + var s = _q.Dequeue(); + s.OnCompleted(); + } + + if (state.isShift) + { + CreateWindow(); + } + } + + CreateTimer(); + + return Disposable.Empty; + } + + public void OnNext(TSource value) + { + lock (_gate) + { + foreach (var s in _q) + s.OnNext(value); + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + foreach (var s in _q) + s.OnError(error); + + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + foreach (var s in _q) + s.OnCompleted(); + + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class π : Sink<IObservable<TSource>>, IObserver<TSource> + { + private readonly Window<TSource> _parent; + + public π(Window<TSource> parent, IObserver<IObservable<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private Subject<TSource> _subject; + private RefCountDisposable _refCountDisposable; + + public IDisposable Run() + { + _gate = new object(); + + var groupDisposable = new CompositeDisposable(2); + _refCountDisposable = new RefCountDisposable(groupDisposable); + + CreateWindow(); + + groupDisposable.Add(_parent._scheduler.SchedulePeriodic(_parent._timeSpan, Tick)); + groupDisposable.Add(_parent._source.SubscribeSafe(this)); + + return _refCountDisposable; + } + + private void Tick() + { + lock (_gate) + { + _subject.OnCompleted(); + CreateWindow(); + } + } + + private void CreateWindow() + { + _subject = new Subject<TSource>(); + base._observer.OnNext(new WindowObservable<TSource>(_subject, _refCountDisposable)); + } + + public void OnNext(TSource value) + { + lock (_gate) + { + _subject.OnNext(value); + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + _subject.OnError(error); + + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + _subject.OnCompleted(); + + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class μ : Sink<IObservable<TSource>>, IObserver<TSource> + { + private readonly Window<TSource> _parent; + + public μ(Window<TSource> parent, IObserver<IObservable<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private ISubject<TSource> _s; + private int _n; + private int _windowId; + + private SerialDisposable _timerD; + private RefCountDisposable _refCountDisposable; + + public IDisposable Run() + { + _gate = new object(); + _s = default(ISubject<TSource>); + _n = 0; + _windowId = 0; + + _timerD = new SerialDisposable(); + var groupDisposable = new CompositeDisposable(2) { _timerD }; + _refCountDisposable = new RefCountDisposable(groupDisposable); + + _s = new Subject<TSource>(); + base._observer.OnNext(new WindowObservable<TSource>(_s, _refCountDisposable)); + CreateTimer(0); + + groupDisposable.Add(_parent._source.SubscribeSafe(this)); + + return _refCountDisposable; + } + + private void CreateTimer(int id) + { + var m = new SingleAssignmentDisposable(); + _timerD.Disposable = m; + + m.Disposable = _parent._scheduler.Schedule(id, _parent._timeSpan, Tick); + } + + private IDisposable Tick(IScheduler self, int id) + { + var d = Disposable.Empty; + + var newId = 0; + lock (_gate) + { + if (id != _windowId) + return d; + + _n = 0; + newId = ++_windowId; + + _s.OnCompleted(); + _s = new Subject<TSource>(); + base._observer.OnNext(new WindowObservable<TSource>(_s, _refCountDisposable)); + } + + CreateTimer(newId); + + return d; + } + + public void OnNext(TSource value) + { + var newWindow = false; + var newId = 0; + + lock (_gate) + { + _s.OnNext(value); + + _n++; + if (_n == _parent._count) + { + newWindow = true; + _n = 0; + newId = ++_windowId; + + _s.OnCompleted(); + _s = new Subject<TSource>(); + base._observer.OnNext(new WindowObservable<TSource>(_s, _refCountDisposable)); + } + } + + if (newWindow) + CreateTimer(newId); + } + + public void OnError(Exception error) + { + lock (_gate) + { + _s.OnError(error); + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + _s.OnCompleted(); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + } + + class Window<TSource, TWindowClosing> : Producer<IObservable<TSource>> + { + private readonly IObservable<TSource> _source; + private readonly Func<IObservable<TWindowClosing>> _windowClosingSelector; + private readonly IObservable<TWindowClosing> _windowBoundaries; + + public Window(IObservable<TSource> source, Func<IObservable<TWindowClosing>> windowClosingSelector) + { + _source = source; + _windowClosingSelector = windowClosingSelector; + } + + public Window(IObservable<TSource> source, IObservable<TWindowClosing> windowBoundaries) + { + _source = source; + _windowBoundaries = windowBoundaries; + } + + protected override IDisposable Run(IObserver<IObservable<TSource>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_windowClosingSelector != null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else + { + var sink = new β(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + + class _ : Sink<IObservable<TSource>>, IObserver<TSource> + { + private readonly Window<TSource, TWindowClosing> _parent; + + public _(Window<TSource, TWindowClosing> parent, IObserver<IObservable<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private ISubject<TSource> _window; + private object _gate; + private AsyncLock _windowGate; + + private SerialDisposable _m; + private RefCountDisposable _refCountDisposable; + + public IDisposable Run() + { + _window = new Subject<TSource>(); + _gate = new object(); + _windowGate = new AsyncLock(); + + _m = new SerialDisposable(); + var groupDisposable = new CompositeDisposable(2) { _m }; + _refCountDisposable = new RefCountDisposable(groupDisposable); + + var window = new WindowObservable<TSource>(_window, _refCountDisposable); + base._observer.OnNext(window); + + groupDisposable.Add(_parent._source.SubscribeSafe(this)); + + _windowGate.Wait(CreateWindowClose); + + return _refCountDisposable; + } + + private void CreateWindowClose() + { + var windowClose = default(IObservable<TWindowClosing>); + try + { + windowClose = _parent._windowClosingSelector(); + } + catch (Exception exception) + { + lock (_gate) + { + base._observer.OnError(exception); + base.Dispose(); + } + return; + } + + var closingSubscription = new SingleAssignmentDisposable(); + _m.Disposable = closingSubscription; + closingSubscription.Disposable = windowClose.SubscribeSafe(new ω(this, closingSubscription)); + } + + private void CloseWindow(IDisposable closingSubscription) + { + closingSubscription.Dispose(); + + lock (_gate) + { + _window.OnCompleted(); + _window = new Subject<TSource>(); + + var window = new WindowObservable<TSource>(_window, _refCountDisposable); + base._observer.OnNext(window); + } + + _windowGate.Wait(CreateWindowClose); + } + + class ω : IObserver<TWindowClosing> + { + private readonly _ _parent; + private readonly IDisposable _self; + + public ω(_ parent, IDisposable self) + { + _parent = parent; + _self = self; + } + + public void OnNext(TWindowClosing value) + { + _parent.CloseWindow(_self); + } + + public void OnError(Exception error) + { + _parent.OnError(error); + } + + public void OnCompleted() + { + _parent.CloseWindow(_self); + } + } + + public void OnNext(TSource value) + { + lock (_gate) + { + _window.OnNext(value); + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + _window.OnError(error); + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + _window.OnCompleted(); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + class β : Sink<IObservable<TSource>>, IObserver<TSource> + { + private readonly Window<TSource, TWindowClosing> _parent; + + public β(Window<TSource, TWindowClosing> parent, IObserver<IObservable<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private ISubject<TSource> _window; + private object _gate; + + private RefCountDisposable _refCountDisposable; + + public IDisposable Run() + { + _window = new Subject<TSource>(); + _gate = new object(); + + var d = new CompositeDisposable(2); + _refCountDisposable = new RefCountDisposable(d); + + var window = new WindowObservable<TSource>(_window, _refCountDisposable); + base._observer.OnNext(window); + + d.Add(_parent._source.SubscribeSafe(this)); + d.Add(_parent._windowBoundaries.SubscribeSafe(new ω(this))); + + return _refCountDisposable; + } + + class ω : IObserver<TWindowClosing> + { + private readonly β _parent; + + public ω(β parent) + { + _parent = parent; + } + + public void OnNext(TWindowClosing value) + { + lock (_parent._gate) + { + _parent._window.OnCompleted(); + _parent._window = new Subject<TSource>(); + + var window = new WindowObservable<TSource>(_parent._window, _parent._refCountDisposable); + _parent._observer.OnNext(window); + } + } + + public void OnError(Exception error) + { + _parent.OnError(error); + } + + public void OnCompleted() + { + _parent.OnCompleted(); + } + } + + public void OnNext(TSource value) + { + lock (_gate) + { + _window.OnNext(value); + } + } + + public void OnError(Exception error) + { + lock (_gate) + { + _window.OnError(error); + base._observer.OnError(error); + base.Dispose(); + } + } + + public void OnCompleted() + { + lock (_gate) + { + _window.OnCompleted(); + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + } + + class WindowObservable<TSource> : AddRef<TSource> + { + public WindowObservable(IObservable<TSource> source, RefCountDisposable refCount) + : base(source, refCount) + { + } + } +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Zip.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Zip.cs new file mode 100644 index 0000000..bde6e29 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/Zip.cs @@ -0,0 +1,2326 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Disposables; + +namespace System.Reactive.Linq.Observαble +{ + #region Binary + + class Zip<TFirst, TSecond, TResult> : Producer<TResult> + { + private readonly IObservable<TFirst> _first; + private readonly IObservable<TSecond> _second; + private readonly IEnumerable<TSecond> _secondE; + private readonly Func<TFirst, TSecond, TResult> _resultSelector; + + public Zip(IObservable<TFirst> first, IObservable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector) + { + _first = first; + _second = second; + _resultSelector = resultSelector; + } + + public Zip(IObservable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector) + { + _first = first; + _secondE = second; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + if (_second != null) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + else + { + var sink = new ε(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + } + + class _ : Sink<TResult> + { + private readonly Zip<TFirst, TSecond, TResult> _parent; + + public _(Zip<TFirst, TSecond, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + + public IDisposable Run() + { + _gate = new object(); + + var fstSubscription = new SingleAssignmentDisposable(); + var sndSubscription = new SingleAssignmentDisposable(); + + var fstO = new F(this, fstSubscription); + var sndO = new S(this, sndSubscription); + + fstO.Other = sndO; + sndO.Other = fstO; + + fstSubscription.Disposable = _parent._first.SubscribeSafe(fstO); + sndSubscription.Disposable = _parent._second.SubscribeSafe(sndO); + + return new CompositeDisposable(fstSubscription, sndSubscription, fstO, sndO); + } + + class F : IObserver<TFirst>, IDisposable + { + private readonly _ _parent; + private readonly IDisposable _self; + private S _other; + private Queue<TFirst> _queue; + + public F(_ parent, IDisposable self) + { + _parent = parent; + _self = self; + _queue = new Queue<TFirst>(); + } + + public S Other { set { _other = value; } } + + public Queue<TFirst> Queue { get { return _queue; } } + public bool Done { get; private set; } + + public void OnNext(TFirst value) + { + lock (_parent._gate) + { + if (_other.Queue.Count > 0) + { + var r = _other.Queue.Dequeue(); + + var res = default(TResult); + try + { + res = _parent._parent._resultSelector(value, r); + } + catch (Exception ex) + { + _parent._observer.OnError(ex); + _parent.Dispose(); + return; + } + + _parent._observer.OnNext(res); + } + else + { + if (_other.Done) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + return; + } + + _queue.Enqueue(value); + } + } + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + lock (_parent._gate) + { + Done = true; + + if (_other.Done) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + return; + } + else + { + _self.Dispose(); + } + } + } + + public void Dispose() + { + _queue.Clear(); + } + } + + class S : IObserver<TSecond>, IDisposable + { + private readonly _ _parent; + private readonly IDisposable _self; + private F _other; + private Queue<TSecond> _queue; + + public S(_ parent, IDisposable self) + { + _parent = parent; + _self = self; + _queue = new Queue<TSecond>(); + } + + public F Other { set { _other = value; } } + + public Queue<TSecond> Queue { get { return _queue; } } + public bool Done { get; private set; } + + public void OnNext(TSecond value) + { + lock (_parent._gate) + { + if (_other.Queue.Count > 0) + { + var l = _other.Queue.Dequeue(); + + var res = default(TResult); + try + { + res = _parent._parent._resultSelector(l, value); + } + catch (Exception ex) + { + _parent._observer.OnError(ex); + _parent.Dispose(); + return; + } + + _parent._observer.OnNext(res); + } + else + { + if (_other.Done) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + return; + } + + _queue.Enqueue(value); + } + } + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _parent._observer.OnError(error); + _parent.Dispose(); + } + } + + public void OnCompleted() + { + lock (_parent._gate) + { + Done = true; + + if (_other.Done) + { + _parent._observer.OnCompleted(); + _parent.Dispose(); + return; + } + else + { + _self.Dispose(); + } + } + } + + public void Dispose() + { + _queue.Clear(); + } + } + } + + class ε : Sink<TResult>, IObserver<TFirst> + { + private readonly Zip<TFirst, TSecond, TResult> _parent; + + public ε(Zip<TFirst, TSecond, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private IEnumerator<TSecond> _rightEnumerator; + + public IDisposable Run() + { + // + // Notice the evaluation order of obtaining the enumerator and subscribing to the + // observable sequence is reversed compared to the operator's signature. This is + // required to make sure the enumerator is available as soon as the observer can + // be called. Otherwise, we end up having a race for the initialization and use + // of the _rightEnumerator field. + // + try + { + _rightEnumerator = _parent._secondE.GetEnumerator(); + } + catch (Exception exception) + { + base._observer.OnError(exception); + base.Dispose(); + return Disposable.Empty; + } + + var leftSubscription = _parent._first.SubscribeSafe(this); + + return new CompositeDisposable(leftSubscription, _rightEnumerator); + } + + public void OnNext(TFirst value) + { + var hasNext = false; + try + { + hasNext = _rightEnumerator.MoveNext(); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + if (hasNext) + { + var right = default(TSecond); + try + { + right = _rightEnumerator.Current; + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + TResult result; + try + { + result = _parent._resultSelector(value, right); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + base._observer.OnNext(result); + } + else + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + + public void OnError(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void OnCompleted() + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + #endregion + + #region [3,16]-ary + + /* The following code is generated by a tool checked in to $/.../Source/Tools/CodeGenerators. */ + + #region Zip auto-generated code (6/10/2012 8:13:21 PM) + + class Zip<T1, T2, T3, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly Func<T1, T2, T3, TResult> _resultSelector; + + public Zip(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, Func<T1, T2, T3, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : ZipSink<TResult> + { + private readonly Zip<T1, T2, T3, TResult> _parent; + + public _(Zip<T1, T2, T3, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(3, observer, cancel) + { + _parent = parent; + } + + private ZipObserver<T1> _observer1; + private ZipObserver<T2> _observer2; + private ZipObserver<T3> _observer3; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[3]; + for (int i = 0; i < 3; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new ZipObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new ZipObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new ZipObserver<T3>(_gate, this, 2, subscriptions[2]); + + base.Queues[0] = _observer1.Values; + base.Queues[1] = _observer2.Values; + base.Queues[2] = _observer3.Values; + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + + return new CompositeDisposable(subscriptions) + { + Disposable.Create(() => + { + _observer1.Values.Clear(); + _observer2.Values.Clear(); + _observer3.Values.Clear(); + }) + }; + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Values.Dequeue(), _observer2.Values.Dequeue(), _observer3.Values.Dequeue()); + } + } + } + + class Zip<T1, T2, T3, T4, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly Func<T1, T2, T3, T4, TResult> _resultSelector; + + public Zip(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, Func<T1, T2, T3, T4, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : ZipSink<TResult> + { + private readonly Zip<T1, T2, T3, T4, TResult> _parent; + + public _(Zip<T1, T2, T3, T4, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(4, observer, cancel) + { + _parent = parent; + } + + private ZipObserver<T1> _observer1; + private ZipObserver<T2> _observer2; + private ZipObserver<T3> _observer3; + private ZipObserver<T4> _observer4; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[4]; + for (int i = 0; i < 4; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new ZipObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new ZipObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new ZipObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new ZipObserver<T4>(_gate, this, 3, subscriptions[3]); + + base.Queues[0] = _observer1.Values; + base.Queues[1] = _observer2.Values; + base.Queues[2] = _observer3.Values; + base.Queues[3] = _observer4.Values; + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + + return new CompositeDisposable(subscriptions) + { + Disposable.Create(() => + { + _observer1.Values.Clear(); + _observer2.Values.Clear(); + _observer3.Values.Clear(); + _observer4.Values.Clear(); + }) + }; + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Values.Dequeue(), _observer2.Values.Dequeue(), _observer3.Values.Dequeue(), _observer4.Values.Dequeue()); + } + } + } + +#if !NO_LARGEARITY + class Zip<T1, T2, T3, T4, T5, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly Func<T1, T2, T3, T4, T5, TResult> _resultSelector; + + public Zip(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, Func<T1, T2, T3, T4, T5, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : ZipSink<TResult> + { + private readonly Zip<T1, T2, T3, T4, T5, TResult> _parent; + + public _(Zip<T1, T2, T3, T4, T5, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(5, observer, cancel) + { + _parent = parent; + } + + private ZipObserver<T1> _observer1; + private ZipObserver<T2> _observer2; + private ZipObserver<T3> _observer3; + private ZipObserver<T4> _observer4; + private ZipObserver<T5> _observer5; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[5]; + for (int i = 0; i < 5; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new ZipObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new ZipObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new ZipObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new ZipObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new ZipObserver<T5>(_gate, this, 4, subscriptions[4]); + + base.Queues[0] = _observer1.Values; + base.Queues[1] = _observer2.Values; + base.Queues[2] = _observer3.Values; + base.Queues[3] = _observer4.Values; + base.Queues[4] = _observer5.Values; + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + + return new CompositeDisposable(subscriptions) + { + Disposable.Create(() => + { + _observer1.Values.Clear(); + _observer2.Values.Clear(); + _observer3.Values.Clear(); + _observer4.Values.Clear(); + _observer5.Values.Clear(); + }) + }; + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Values.Dequeue(), _observer2.Values.Dequeue(), _observer3.Values.Dequeue(), _observer4.Values.Dequeue(), _observer5.Values.Dequeue()); + } + } + } + + class Zip<T1, T2, T3, T4, T5, T6, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly Func<T1, T2, T3, T4, T5, T6, TResult> _resultSelector; + + public Zip(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, Func<T1, T2, T3, T4, T5, T6, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : ZipSink<TResult> + { + private readonly Zip<T1, T2, T3, T4, T5, T6, TResult> _parent; + + public _(Zip<T1, T2, T3, T4, T5, T6, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(6, observer, cancel) + { + _parent = parent; + } + + private ZipObserver<T1> _observer1; + private ZipObserver<T2> _observer2; + private ZipObserver<T3> _observer3; + private ZipObserver<T4> _observer4; + private ZipObserver<T5> _observer5; + private ZipObserver<T6> _observer6; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[6]; + for (int i = 0; i < 6; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new ZipObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new ZipObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new ZipObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new ZipObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new ZipObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new ZipObserver<T6>(_gate, this, 5, subscriptions[5]); + + base.Queues[0] = _observer1.Values; + base.Queues[1] = _observer2.Values; + base.Queues[2] = _observer3.Values; + base.Queues[3] = _observer4.Values; + base.Queues[4] = _observer5.Values; + base.Queues[5] = _observer6.Values; + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + + return new CompositeDisposable(subscriptions) + { + Disposable.Create(() => + { + _observer1.Values.Clear(); + _observer2.Values.Clear(); + _observer3.Values.Clear(); + _observer4.Values.Clear(); + _observer5.Values.Clear(); + _observer6.Values.Clear(); + }) + }; + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Values.Dequeue(), _observer2.Values.Dequeue(), _observer3.Values.Dequeue(), _observer4.Values.Dequeue(), _observer5.Values.Dequeue(), _observer6.Values.Dequeue()); + } + } + } + + class Zip<T1, T2, T3, T4, T5, T6, T7, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, TResult> _resultSelector; + + public Zip(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, Func<T1, T2, T3, T4, T5, T6, T7, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : ZipSink<TResult> + { + private readonly Zip<T1, T2, T3, T4, T5, T6, T7, TResult> _parent; + + public _(Zip<T1, T2, T3, T4, T5, T6, T7, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(7, observer, cancel) + { + _parent = parent; + } + + private ZipObserver<T1> _observer1; + private ZipObserver<T2> _observer2; + private ZipObserver<T3> _observer3; + private ZipObserver<T4> _observer4; + private ZipObserver<T5> _observer5; + private ZipObserver<T6> _observer6; + private ZipObserver<T7> _observer7; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[7]; + for (int i = 0; i < 7; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new ZipObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new ZipObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new ZipObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new ZipObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new ZipObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new ZipObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new ZipObserver<T7>(_gate, this, 6, subscriptions[6]); + + base.Queues[0] = _observer1.Values; + base.Queues[1] = _observer2.Values; + base.Queues[2] = _observer3.Values; + base.Queues[3] = _observer4.Values; + base.Queues[4] = _observer5.Values; + base.Queues[5] = _observer6.Values; + base.Queues[6] = _observer7.Values; + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + + return new CompositeDisposable(subscriptions) + { + Disposable.Create(() => + { + _observer1.Values.Clear(); + _observer2.Values.Clear(); + _observer3.Values.Clear(); + _observer4.Values.Clear(); + _observer5.Values.Clear(); + _observer6.Values.Clear(); + _observer7.Values.Clear(); + }) + }; + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Values.Dequeue(), _observer2.Values.Dequeue(), _observer3.Values.Dequeue(), _observer4.Values.Dequeue(), _observer5.Values.Dequeue(), _observer6.Values.Dequeue(), _observer7.Values.Dequeue()); + } + } + } + + class Zip<T1, T2, T3, T4, T5, T6, T7, T8, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> _resultSelector; + + public Zip(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : ZipSink<TResult> + { + private readonly Zip<T1, T2, T3, T4, T5, T6, T7, T8, TResult> _parent; + + public _(Zip<T1, T2, T3, T4, T5, T6, T7, T8, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(8, observer, cancel) + { + _parent = parent; + } + + private ZipObserver<T1> _observer1; + private ZipObserver<T2> _observer2; + private ZipObserver<T3> _observer3; + private ZipObserver<T4> _observer4; + private ZipObserver<T5> _observer5; + private ZipObserver<T6> _observer6; + private ZipObserver<T7> _observer7; + private ZipObserver<T8> _observer8; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[8]; + for (int i = 0; i < 8; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new ZipObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new ZipObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new ZipObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new ZipObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new ZipObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new ZipObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new ZipObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new ZipObserver<T8>(_gate, this, 7, subscriptions[7]); + + base.Queues[0] = _observer1.Values; + base.Queues[1] = _observer2.Values; + base.Queues[2] = _observer3.Values; + base.Queues[3] = _observer4.Values; + base.Queues[4] = _observer5.Values; + base.Queues[5] = _observer6.Values; + base.Queues[6] = _observer7.Values; + base.Queues[7] = _observer8.Values; + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + + return new CompositeDisposable(subscriptions) + { + Disposable.Create(() => + { + _observer1.Values.Clear(); + _observer2.Values.Clear(); + _observer3.Values.Clear(); + _observer4.Values.Clear(); + _observer5.Values.Clear(); + _observer6.Values.Clear(); + _observer7.Values.Clear(); + _observer8.Values.Clear(); + }) + }; + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Values.Dequeue(), _observer2.Values.Dequeue(), _observer3.Values.Dequeue(), _observer4.Values.Dequeue(), _observer5.Values.Dequeue(), _observer6.Values.Dequeue(), _observer7.Values.Dequeue(), _observer8.Values.Dequeue()); + } + } + } + + class Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> _resultSelector; + + public Zip(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : ZipSink<TResult> + { + private readonly Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> _parent; + + public _(Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(9, observer, cancel) + { + _parent = parent; + } + + private ZipObserver<T1> _observer1; + private ZipObserver<T2> _observer2; + private ZipObserver<T3> _observer3; + private ZipObserver<T4> _observer4; + private ZipObserver<T5> _observer5; + private ZipObserver<T6> _observer6; + private ZipObserver<T7> _observer7; + private ZipObserver<T8> _observer8; + private ZipObserver<T9> _observer9; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[9]; + for (int i = 0; i < 9; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new ZipObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new ZipObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new ZipObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new ZipObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new ZipObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new ZipObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new ZipObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new ZipObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new ZipObserver<T9>(_gate, this, 8, subscriptions[8]); + + base.Queues[0] = _observer1.Values; + base.Queues[1] = _observer2.Values; + base.Queues[2] = _observer3.Values; + base.Queues[3] = _observer4.Values; + base.Queues[4] = _observer5.Values; + base.Queues[5] = _observer6.Values; + base.Queues[6] = _observer7.Values; + base.Queues[7] = _observer8.Values; + base.Queues[8] = _observer9.Values; + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + + return new CompositeDisposable(subscriptions) + { + Disposable.Create(() => + { + _observer1.Values.Clear(); + _observer2.Values.Clear(); + _observer3.Values.Clear(); + _observer4.Values.Clear(); + _observer5.Values.Clear(); + _observer6.Values.Clear(); + _observer7.Values.Clear(); + _observer8.Values.Clear(); + _observer9.Values.Clear(); + }) + }; + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Values.Dequeue(), _observer2.Values.Dequeue(), _observer3.Values.Dequeue(), _observer4.Values.Dequeue(), _observer5.Values.Dequeue(), _observer6.Values.Dequeue(), _observer7.Values.Dequeue(), _observer8.Values.Dequeue(), _observer9.Values.Dequeue()); + } + } + } + + class Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly IObservable<T10> _source10; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> _resultSelector; + + public Zip(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, IObservable<T10> source10, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _source10 = source10; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : ZipSink<TResult> + { + private readonly Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> _parent; + + public _(Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(10, observer, cancel) + { + _parent = parent; + } + + private ZipObserver<T1> _observer1; + private ZipObserver<T2> _observer2; + private ZipObserver<T3> _observer3; + private ZipObserver<T4> _observer4; + private ZipObserver<T5> _observer5; + private ZipObserver<T6> _observer6; + private ZipObserver<T7> _observer7; + private ZipObserver<T8> _observer8; + private ZipObserver<T9> _observer9; + private ZipObserver<T10> _observer10; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[10]; + for (int i = 0; i < 10; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new ZipObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new ZipObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new ZipObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new ZipObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new ZipObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new ZipObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new ZipObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new ZipObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new ZipObserver<T9>(_gate, this, 8, subscriptions[8]); + _observer10 = new ZipObserver<T10>(_gate, this, 9, subscriptions[9]); + + base.Queues[0] = _observer1.Values; + base.Queues[1] = _observer2.Values; + base.Queues[2] = _observer3.Values; + base.Queues[3] = _observer4.Values; + base.Queues[4] = _observer5.Values; + base.Queues[5] = _observer6.Values; + base.Queues[6] = _observer7.Values; + base.Queues[7] = _observer8.Values; + base.Queues[8] = _observer9.Values; + base.Queues[9] = _observer10.Values; + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + subscriptions[9].Disposable = _parent._source10.SubscribeSafe(_observer10); + + return new CompositeDisposable(subscriptions) + { + Disposable.Create(() => + { + _observer1.Values.Clear(); + _observer2.Values.Clear(); + _observer3.Values.Clear(); + _observer4.Values.Clear(); + _observer5.Values.Clear(); + _observer6.Values.Clear(); + _observer7.Values.Clear(); + _observer8.Values.Clear(); + _observer9.Values.Clear(); + _observer10.Values.Clear(); + }) + }; + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Values.Dequeue(), _observer2.Values.Dequeue(), _observer3.Values.Dequeue(), _observer4.Values.Dequeue(), _observer5.Values.Dequeue(), _observer6.Values.Dequeue(), _observer7.Values.Dequeue(), _observer8.Values.Dequeue(), _observer9.Values.Dequeue(), _observer10.Values.Dequeue()); + } + } + } + + class Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly IObservable<T10> _source10; + private readonly IObservable<T11> _source11; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult> _resultSelector; + + public Zip(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, IObservable<T10> source10, IObservable<T11> source11, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _source10 = source10; + _source11 = source11; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : ZipSink<TResult> + { + private readonly Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult> _parent; + + public _(Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(11, observer, cancel) + { + _parent = parent; + } + + private ZipObserver<T1> _observer1; + private ZipObserver<T2> _observer2; + private ZipObserver<T3> _observer3; + private ZipObserver<T4> _observer4; + private ZipObserver<T5> _observer5; + private ZipObserver<T6> _observer6; + private ZipObserver<T7> _observer7; + private ZipObserver<T8> _observer8; + private ZipObserver<T9> _observer9; + private ZipObserver<T10> _observer10; + private ZipObserver<T11> _observer11; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[11]; + for (int i = 0; i < 11; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new ZipObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new ZipObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new ZipObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new ZipObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new ZipObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new ZipObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new ZipObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new ZipObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new ZipObserver<T9>(_gate, this, 8, subscriptions[8]); + _observer10 = new ZipObserver<T10>(_gate, this, 9, subscriptions[9]); + _observer11 = new ZipObserver<T11>(_gate, this, 10, subscriptions[10]); + + base.Queues[0] = _observer1.Values; + base.Queues[1] = _observer2.Values; + base.Queues[2] = _observer3.Values; + base.Queues[3] = _observer4.Values; + base.Queues[4] = _observer5.Values; + base.Queues[5] = _observer6.Values; + base.Queues[6] = _observer7.Values; + base.Queues[7] = _observer8.Values; + base.Queues[8] = _observer9.Values; + base.Queues[9] = _observer10.Values; + base.Queues[10] = _observer11.Values; + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + subscriptions[9].Disposable = _parent._source10.SubscribeSafe(_observer10); + subscriptions[10].Disposable = _parent._source11.SubscribeSafe(_observer11); + + return new CompositeDisposable(subscriptions) + { + Disposable.Create(() => + { + _observer1.Values.Clear(); + _observer2.Values.Clear(); + _observer3.Values.Clear(); + _observer4.Values.Clear(); + _observer5.Values.Clear(); + _observer6.Values.Clear(); + _observer7.Values.Clear(); + _observer8.Values.Clear(); + _observer9.Values.Clear(); + _observer10.Values.Clear(); + _observer11.Values.Clear(); + }) + }; + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Values.Dequeue(), _observer2.Values.Dequeue(), _observer3.Values.Dequeue(), _observer4.Values.Dequeue(), _observer5.Values.Dequeue(), _observer6.Values.Dequeue(), _observer7.Values.Dequeue(), _observer8.Values.Dequeue(), _observer9.Values.Dequeue(), _observer10.Values.Dequeue(), _observer11.Values.Dequeue()); + } + } + } + + class Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly IObservable<T10> _source10; + private readonly IObservable<T11> _source11; + private readonly IObservable<T12> _source12; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult> _resultSelector; + + public Zip(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, IObservable<T10> source10, IObservable<T11> source11, IObservable<T12> source12, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _source10 = source10; + _source11 = source11; + _source12 = source12; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : ZipSink<TResult> + { + private readonly Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult> _parent; + + public _(Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(12, observer, cancel) + { + _parent = parent; + } + + private ZipObserver<T1> _observer1; + private ZipObserver<T2> _observer2; + private ZipObserver<T3> _observer3; + private ZipObserver<T4> _observer4; + private ZipObserver<T5> _observer5; + private ZipObserver<T6> _observer6; + private ZipObserver<T7> _observer7; + private ZipObserver<T8> _observer8; + private ZipObserver<T9> _observer9; + private ZipObserver<T10> _observer10; + private ZipObserver<T11> _observer11; + private ZipObserver<T12> _observer12; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[12]; + for (int i = 0; i < 12; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new ZipObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new ZipObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new ZipObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new ZipObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new ZipObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new ZipObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new ZipObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new ZipObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new ZipObserver<T9>(_gate, this, 8, subscriptions[8]); + _observer10 = new ZipObserver<T10>(_gate, this, 9, subscriptions[9]); + _observer11 = new ZipObserver<T11>(_gate, this, 10, subscriptions[10]); + _observer12 = new ZipObserver<T12>(_gate, this, 11, subscriptions[11]); + + base.Queues[0] = _observer1.Values; + base.Queues[1] = _observer2.Values; + base.Queues[2] = _observer3.Values; + base.Queues[3] = _observer4.Values; + base.Queues[4] = _observer5.Values; + base.Queues[5] = _observer6.Values; + base.Queues[6] = _observer7.Values; + base.Queues[7] = _observer8.Values; + base.Queues[8] = _observer9.Values; + base.Queues[9] = _observer10.Values; + base.Queues[10] = _observer11.Values; + base.Queues[11] = _observer12.Values; + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + subscriptions[9].Disposable = _parent._source10.SubscribeSafe(_observer10); + subscriptions[10].Disposable = _parent._source11.SubscribeSafe(_observer11); + subscriptions[11].Disposable = _parent._source12.SubscribeSafe(_observer12); + + return new CompositeDisposable(subscriptions) + { + Disposable.Create(() => + { + _observer1.Values.Clear(); + _observer2.Values.Clear(); + _observer3.Values.Clear(); + _observer4.Values.Clear(); + _observer5.Values.Clear(); + _observer6.Values.Clear(); + _observer7.Values.Clear(); + _observer8.Values.Clear(); + _observer9.Values.Clear(); + _observer10.Values.Clear(); + _observer11.Values.Clear(); + _observer12.Values.Clear(); + }) + }; + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Values.Dequeue(), _observer2.Values.Dequeue(), _observer3.Values.Dequeue(), _observer4.Values.Dequeue(), _observer5.Values.Dequeue(), _observer6.Values.Dequeue(), _observer7.Values.Dequeue(), _observer8.Values.Dequeue(), _observer9.Values.Dequeue(), _observer10.Values.Dequeue(), _observer11.Values.Dequeue(), _observer12.Values.Dequeue()); + } + } + } + + class Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly IObservable<T10> _source10; + private readonly IObservable<T11> _source11; + private readonly IObservable<T12> _source12; + private readonly IObservable<T13> _source13; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult> _resultSelector; + + public Zip(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, IObservable<T10> source10, IObservable<T11> source11, IObservable<T12> source12, IObservable<T13> source13, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _source10 = source10; + _source11 = source11; + _source12 = source12; + _source13 = source13; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : ZipSink<TResult> + { + private readonly Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult> _parent; + + public _(Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(13, observer, cancel) + { + _parent = parent; + } + + private ZipObserver<T1> _observer1; + private ZipObserver<T2> _observer2; + private ZipObserver<T3> _observer3; + private ZipObserver<T4> _observer4; + private ZipObserver<T5> _observer5; + private ZipObserver<T6> _observer6; + private ZipObserver<T7> _observer7; + private ZipObserver<T8> _observer8; + private ZipObserver<T9> _observer9; + private ZipObserver<T10> _observer10; + private ZipObserver<T11> _observer11; + private ZipObserver<T12> _observer12; + private ZipObserver<T13> _observer13; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[13]; + for (int i = 0; i < 13; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new ZipObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new ZipObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new ZipObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new ZipObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new ZipObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new ZipObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new ZipObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new ZipObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new ZipObserver<T9>(_gate, this, 8, subscriptions[8]); + _observer10 = new ZipObserver<T10>(_gate, this, 9, subscriptions[9]); + _observer11 = new ZipObserver<T11>(_gate, this, 10, subscriptions[10]); + _observer12 = new ZipObserver<T12>(_gate, this, 11, subscriptions[11]); + _observer13 = new ZipObserver<T13>(_gate, this, 12, subscriptions[12]); + + base.Queues[0] = _observer1.Values; + base.Queues[1] = _observer2.Values; + base.Queues[2] = _observer3.Values; + base.Queues[3] = _observer4.Values; + base.Queues[4] = _observer5.Values; + base.Queues[5] = _observer6.Values; + base.Queues[6] = _observer7.Values; + base.Queues[7] = _observer8.Values; + base.Queues[8] = _observer9.Values; + base.Queues[9] = _observer10.Values; + base.Queues[10] = _observer11.Values; + base.Queues[11] = _observer12.Values; + base.Queues[12] = _observer13.Values; + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + subscriptions[9].Disposable = _parent._source10.SubscribeSafe(_observer10); + subscriptions[10].Disposable = _parent._source11.SubscribeSafe(_observer11); + subscriptions[11].Disposable = _parent._source12.SubscribeSafe(_observer12); + subscriptions[12].Disposable = _parent._source13.SubscribeSafe(_observer13); + + return new CompositeDisposable(subscriptions) + { + Disposable.Create(() => + { + _observer1.Values.Clear(); + _observer2.Values.Clear(); + _observer3.Values.Clear(); + _observer4.Values.Clear(); + _observer5.Values.Clear(); + _observer6.Values.Clear(); + _observer7.Values.Clear(); + _observer8.Values.Clear(); + _observer9.Values.Clear(); + _observer10.Values.Clear(); + _observer11.Values.Clear(); + _observer12.Values.Clear(); + _observer13.Values.Clear(); + }) + }; + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Values.Dequeue(), _observer2.Values.Dequeue(), _observer3.Values.Dequeue(), _observer4.Values.Dequeue(), _observer5.Values.Dequeue(), _observer6.Values.Dequeue(), _observer7.Values.Dequeue(), _observer8.Values.Dequeue(), _observer9.Values.Dequeue(), _observer10.Values.Dequeue(), _observer11.Values.Dequeue(), _observer12.Values.Dequeue(), _observer13.Values.Dequeue()); + } + } + } + + class Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly IObservable<T10> _source10; + private readonly IObservable<T11> _source11; + private readonly IObservable<T12> _source12; + private readonly IObservable<T13> _source13; + private readonly IObservable<T14> _source14; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult> _resultSelector; + + public Zip(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, IObservable<T10> source10, IObservable<T11> source11, IObservable<T12> source12, IObservable<T13> source13, IObservable<T14> source14, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _source10 = source10; + _source11 = source11; + _source12 = source12; + _source13 = source13; + _source14 = source14; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : ZipSink<TResult> + { + private readonly Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult> _parent; + + public _(Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(14, observer, cancel) + { + _parent = parent; + } + + private ZipObserver<T1> _observer1; + private ZipObserver<T2> _observer2; + private ZipObserver<T3> _observer3; + private ZipObserver<T4> _observer4; + private ZipObserver<T5> _observer5; + private ZipObserver<T6> _observer6; + private ZipObserver<T7> _observer7; + private ZipObserver<T8> _observer8; + private ZipObserver<T9> _observer9; + private ZipObserver<T10> _observer10; + private ZipObserver<T11> _observer11; + private ZipObserver<T12> _observer12; + private ZipObserver<T13> _observer13; + private ZipObserver<T14> _observer14; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[14]; + for (int i = 0; i < 14; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new ZipObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new ZipObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new ZipObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new ZipObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new ZipObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new ZipObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new ZipObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new ZipObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new ZipObserver<T9>(_gate, this, 8, subscriptions[8]); + _observer10 = new ZipObserver<T10>(_gate, this, 9, subscriptions[9]); + _observer11 = new ZipObserver<T11>(_gate, this, 10, subscriptions[10]); + _observer12 = new ZipObserver<T12>(_gate, this, 11, subscriptions[11]); + _observer13 = new ZipObserver<T13>(_gate, this, 12, subscriptions[12]); + _observer14 = new ZipObserver<T14>(_gate, this, 13, subscriptions[13]); + + base.Queues[0] = _observer1.Values; + base.Queues[1] = _observer2.Values; + base.Queues[2] = _observer3.Values; + base.Queues[3] = _observer4.Values; + base.Queues[4] = _observer5.Values; + base.Queues[5] = _observer6.Values; + base.Queues[6] = _observer7.Values; + base.Queues[7] = _observer8.Values; + base.Queues[8] = _observer9.Values; + base.Queues[9] = _observer10.Values; + base.Queues[10] = _observer11.Values; + base.Queues[11] = _observer12.Values; + base.Queues[12] = _observer13.Values; + base.Queues[13] = _observer14.Values; + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + subscriptions[9].Disposable = _parent._source10.SubscribeSafe(_observer10); + subscriptions[10].Disposable = _parent._source11.SubscribeSafe(_observer11); + subscriptions[11].Disposable = _parent._source12.SubscribeSafe(_observer12); + subscriptions[12].Disposable = _parent._source13.SubscribeSafe(_observer13); + subscriptions[13].Disposable = _parent._source14.SubscribeSafe(_observer14); + + return new CompositeDisposable(subscriptions) + { + Disposable.Create(() => + { + _observer1.Values.Clear(); + _observer2.Values.Clear(); + _observer3.Values.Clear(); + _observer4.Values.Clear(); + _observer5.Values.Clear(); + _observer6.Values.Clear(); + _observer7.Values.Clear(); + _observer8.Values.Clear(); + _observer9.Values.Clear(); + _observer10.Values.Clear(); + _observer11.Values.Clear(); + _observer12.Values.Clear(); + _observer13.Values.Clear(); + _observer14.Values.Clear(); + }) + }; + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Values.Dequeue(), _observer2.Values.Dequeue(), _observer3.Values.Dequeue(), _observer4.Values.Dequeue(), _observer5.Values.Dequeue(), _observer6.Values.Dequeue(), _observer7.Values.Dequeue(), _observer8.Values.Dequeue(), _observer9.Values.Dequeue(), _observer10.Values.Dequeue(), _observer11.Values.Dequeue(), _observer12.Values.Dequeue(), _observer13.Values.Dequeue(), _observer14.Values.Dequeue()); + } + } + } + + class Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly IObservable<T10> _source10; + private readonly IObservable<T11> _source11; + private readonly IObservable<T12> _source12; + private readonly IObservable<T13> _source13; + private readonly IObservable<T14> _source14; + private readonly IObservable<T15> _source15; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult> _resultSelector; + + public Zip(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, IObservable<T10> source10, IObservable<T11> source11, IObservable<T12> source12, IObservable<T13> source13, IObservable<T14> source14, IObservable<T15> source15, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _source10 = source10; + _source11 = source11; + _source12 = source12; + _source13 = source13; + _source14 = source14; + _source15 = source15; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : ZipSink<TResult> + { + private readonly Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult> _parent; + + public _(Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(15, observer, cancel) + { + _parent = parent; + } + + private ZipObserver<T1> _observer1; + private ZipObserver<T2> _observer2; + private ZipObserver<T3> _observer3; + private ZipObserver<T4> _observer4; + private ZipObserver<T5> _observer5; + private ZipObserver<T6> _observer6; + private ZipObserver<T7> _observer7; + private ZipObserver<T8> _observer8; + private ZipObserver<T9> _observer9; + private ZipObserver<T10> _observer10; + private ZipObserver<T11> _observer11; + private ZipObserver<T12> _observer12; + private ZipObserver<T13> _observer13; + private ZipObserver<T14> _observer14; + private ZipObserver<T15> _observer15; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[15]; + for (int i = 0; i < 15; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new ZipObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new ZipObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new ZipObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new ZipObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new ZipObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new ZipObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new ZipObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new ZipObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new ZipObserver<T9>(_gate, this, 8, subscriptions[8]); + _observer10 = new ZipObserver<T10>(_gate, this, 9, subscriptions[9]); + _observer11 = new ZipObserver<T11>(_gate, this, 10, subscriptions[10]); + _observer12 = new ZipObserver<T12>(_gate, this, 11, subscriptions[11]); + _observer13 = new ZipObserver<T13>(_gate, this, 12, subscriptions[12]); + _observer14 = new ZipObserver<T14>(_gate, this, 13, subscriptions[13]); + _observer15 = new ZipObserver<T15>(_gate, this, 14, subscriptions[14]); + + base.Queues[0] = _observer1.Values; + base.Queues[1] = _observer2.Values; + base.Queues[2] = _observer3.Values; + base.Queues[3] = _observer4.Values; + base.Queues[4] = _observer5.Values; + base.Queues[5] = _observer6.Values; + base.Queues[6] = _observer7.Values; + base.Queues[7] = _observer8.Values; + base.Queues[8] = _observer9.Values; + base.Queues[9] = _observer10.Values; + base.Queues[10] = _observer11.Values; + base.Queues[11] = _observer12.Values; + base.Queues[12] = _observer13.Values; + base.Queues[13] = _observer14.Values; + base.Queues[14] = _observer15.Values; + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + subscriptions[9].Disposable = _parent._source10.SubscribeSafe(_observer10); + subscriptions[10].Disposable = _parent._source11.SubscribeSafe(_observer11); + subscriptions[11].Disposable = _parent._source12.SubscribeSafe(_observer12); + subscriptions[12].Disposable = _parent._source13.SubscribeSafe(_observer13); + subscriptions[13].Disposable = _parent._source14.SubscribeSafe(_observer14); + subscriptions[14].Disposable = _parent._source15.SubscribeSafe(_observer15); + + return new CompositeDisposable(subscriptions) + { + Disposable.Create(() => + { + _observer1.Values.Clear(); + _observer2.Values.Clear(); + _observer3.Values.Clear(); + _observer4.Values.Clear(); + _observer5.Values.Clear(); + _observer6.Values.Clear(); + _observer7.Values.Clear(); + _observer8.Values.Clear(); + _observer9.Values.Clear(); + _observer10.Values.Clear(); + _observer11.Values.Clear(); + _observer12.Values.Clear(); + _observer13.Values.Clear(); + _observer14.Values.Clear(); + _observer15.Values.Clear(); + }) + }; + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Values.Dequeue(), _observer2.Values.Dequeue(), _observer3.Values.Dequeue(), _observer4.Values.Dequeue(), _observer5.Values.Dequeue(), _observer6.Values.Dequeue(), _observer7.Values.Dequeue(), _observer8.Values.Dequeue(), _observer9.Values.Dequeue(), _observer10.Values.Dequeue(), _observer11.Values.Dequeue(), _observer12.Values.Dequeue(), _observer13.Values.Dequeue(), _observer14.Values.Dequeue(), _observer15.Values.Dequeue()); + } + } + } + + class Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> : Producer<TResult> + { + private readonly IObservable<T1> _source1; + private readonly IObservable<T2> _source2; + private readonly IObservable<T3> _source3; + private readonly IObservable<T4> _source4; + private readonly IObservable<T5> _source5; + private readonly IObservable<T6> _source6; + private readonly IObservable<T7> _source7; + private readonly IObservable<T8> _source8; + private readonly IObservable<T9> _source9; + private readonly IObservable<T10> _source10; + private readonly IObservable<T11> _source11; + private readonly IObservable<T12> _source12; + private readonly IObservable<T13> _source13; + private readonly IObservable<T14> _source14; + private readonly IObservable<T15> _source15; + private readonly IObservable<T16> _source16; + private readonly Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> _resultSelector; + + public Zip(IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, IObservable<T4> source4, IObservable<T5> source5, IObservable<T6> source6, IObservable<T7> source7, IObservable<T8> source8, IObservable<T9> source9, IObservable<T10> source10, IObservable<T11> source11, IObservable<T12> source12, IObservable<T13> source13, IObservable<T14> source14, IObservable<T15> source15, IObservable<T16> source16, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> resultSelector) + { + _source1 = source1; + _source2 = source2; + _source3 = source3; + _source4 = source4; + _source5 = source5; + _source6 = source6; + _source7 = source7; + _source8 = source8; + _source9 = source9; + _source10 = source10; + _source11 = source11; + _source12 = source12; + _source13 = source13; + _source14 = source14; + _source15 = source15; + _source16 = source16; + _resultSelector = resultSelector; + } + + protected override IDisposable Run(IObserver<TResult> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : ZipSink<TResult> + { + private readonly Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> _parent; + + public _(Zip<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> parent, IObserver<TResult> observer, IDisposable cancel) + : base(16, observer, cancel) + { + _parent = parent; + } + + private ZipObserver<T1> _observer1; + private ZipObserver<T2> _observer2; + private ZipObserver<T3> _observer3; + private ZipObserver<T4> _observer4; + private ZipObserver<T5> _observer5; + private ZipObserver<T6> _observer6; + private ZipObserver<T7> _observer7; + private ZipObserver<T8> _observer8; + private ZipObserver<T9> _observer9; + private ZipObserver<T10> _observer10; + private ZipObserver<T11> _observer11; + private ZipObserver<T12> _observer12; + private ZipObserver<T13> _observer13; + private ZipObserver<T14> _observer14; + private ZipObserver<T15> _observer15; + private ZipObserver<T16> _observer16; + + public IDisposable Run() + { + var subscriptions = new SingleAssignmentDisposable[16]; + for (int i = 0; i < 16; i++) + subscriptions[i] = new SingleAssignmentDisposable(); + + _observer1 = new ZipObserver<T1>(_gate, this, 0, subscriptions[0]); + _observer2 = new ZipObserver<T2>(_gate, this, 1, subscriptions[1]); + _observer3 = new ZipObserver<T3>(_gate, this, 2, subscriptions[2]); + _observer4 = new ZipObserver<T4>(_gate, this, 3, subscriptions[3]); + _observer5 = new ZipObserver<T5>(_gate, this, 4, subscriptions[4]); + _observer6 = new ZipObserver<T6>(_gate, this, 5, subscriptions[5]); + _observer7 = new ZipObserver<T7>(_gate, this, 6, subscriptions[6]); + _observer8 = new ZipObserver<T8>(_gate, this, 7, subscriptions[7]); + _observer9 = new ZipObserver<T9>(_gate, this, 8, subscriptions[8]); + _observer10 = new ZipObserver<T10>(_gate, this, 9, subscriptions[9]); + _observer11 = new ZipObserver<T11>(_gate, this, 10, subscriptions[10]); + _observer12 = new ZipObserver<T12>(_gate, this, 11, subscriptions[11]); + _observer13 = new ZipObserver<T13>(_gate, this, 12, subscriptions[12]); + _observer14 = new ZipObserver<T14>(_gate, this, 13, subscriptions[13]); + _observer15 = new ZipObserver<T15>(_gate, this, 14, subscriptions[14]); + _observer16 = new ZipObserver<T16>(_gate, this, 15, subscriptions[15]); + + base.Queues[0] = _observer1.Values; + base.Queues[1] = _observer2.Values; + base.Queues[2] = _observer3.Values; + base.Queues[3] = _observer4.Values; + base.Queues[4] = _observer5.Values; + base.Queues[5] = _observer6.Values; + base.Queues[6] = _observer7.Values; + base.Queues[7] = _observer8.Values; + base.Queues[8] = _observer9.Values; + base.Queues[9] = _observer10.Values; + base.Queues[10] = _observer11.Values; + base.Queues[11] = _observer12.Values; + base.Queues[12] = _observer13.Values; + base.Queues[13] = _observer14.Values; + base.Queues[14] = _observer15.Values; + base.Queues[15] = _observer16.Values; + + subscriptions[0].Disposable = _parent._source1.SubscribeSafe(_observer1); + subscriptions[1].Disposable = _parent._source2.SubscribeSafe(_observer2); + subscriptions[2].Disposable = _parent._source3.SubscribeSafe(_observer3); + subscriptions[3].Disposable = _parent._source4.SubscribeSafe(_observer4); + subscriptions[4].Disposable = _parent._source5.SubscribeSafe(_observer5); + subscriptions[5].Disposable = _parent._source6.SubscribeSafe(_observer6); + subscriptions[6].Disposable = _parent._source7.SubscribeSafe(_observer7); + subscriptions[7].Disposable = _parent._source8.SubscribeSafe(_observer8); + subscriptions[8].Disposable = _parent._source9.SubscribeSafe(_observer9); + subscriptions[9].Disposable = _parent._source10.SubscribeSafe(_observer10); + subscriptions[10].Disposable = _parent._source11.SubscribeSafe(_observer11); + subscriptions[11].Disposable = _parent._source12.SubscribeSafe(_observer12); + subscriptions[12].Disposable = _parent._source13.SubscribeSafe(_observer13); + subscriptions[13].Disposable = _parent._source14.SubscribeSafe(_observer14); + subscriptions[14].Disposable = _parent._source15.SubscribeSafe(_observer15); + subscriptions[15].Disposable = _parent._source16.SubscribeSafe(_observer16); + + return new CompositeDisposable(subscriptions) + { + Disposable.Create(() => + { + _observer1.Values.Clear(); + _observer2.Values.Clear(); + _observer3.Values.Clear(); + _observer4.Values.Clear(); + _observer5.Values.Clear(); + _observer6.Values.Clear(); + _observer7.Values.Clear(); + _observer8.Values.Clear(); + _observer9.Values.Clear(); + _observer10.Values.Clear(); + _observer11.Values.Clear(); + _observer12.Values.Clear(); + _observer13.Values.Clear(); + _observer14.Values.Clear(); + _observer15.Values.Clear(); + _observer16.Values.Clear(); + }) + }; + } + + protected override TResult GetResult() + { + return _parent._resultSelector(_observer1.Values.Dequeue(), _observer2.Values.Dequeue(), _observer3.Values.Dequeue(), _observer4.Values.Dequeue(), _observer5.Values.Dequeue(), _observer6.Values.Dequeue(), _observer7.Values.Dequeue(), _observer8.Values.Dequeue(), _observer9.Values.Dequeue(), _observer10.Values.Dequeue(), _observer11.Values.Dequeue(), _observer12.Values.Dequeue(), _observer13.Values.Dequeue(), _observer14.Values.Dequeue(), _observer15.Values.Dequeue(), _observer16.Values.Dequeue()); + } + } + } + +#endif + + #endregion + + #region Helpers for n-ary overloads + + interface IZip + { + void Next(int index); + void Fail(Exception error); + void Done(int index); + } + + abstract class ZipSink<TResult> : Sink<TResult>, IZip + { + protected readonly object _gate; + + private readonly ICollection[] _queues; + private readonly bool[] _isDone; + + public ZipSink(int arity, IObserver<TResult> observer, IDisposable cancel) + : base(observer, cancel) + { + _gate = new object(); + + _isDone = new bool[arity]; + _queues = new ICollection[arity]; + } + + public ICollection[] Queues + { + get { return _queues; } + } + + public void Next(int index) + { + var hasValueAll = true; + foreach (var queue in _queues) + { + if (queue.Count == 0) + { + hasValueAll = false; + break; + } + } + + if (hasValueAll) + { + var res = default(TResult); + try + { + res = GetResult(); + } + catch (Exception ex) + { + base._observer.OnError(ex); + base.Dispose(); + return; + } + + base._observer.OnNext(res); + } + else + { + var allOthersDone = true; + for (int i = 0; i < _isDone.Length; i++) + { + if (i != index && !_isDone[i]) + { + allOthersDone = false; + break; + } + } + + if (allOthersDone) + { + base._observer.OnCompleted(); + base.Dispose(); + } + } + } + + protected abstract TResult GetResult(); + + public void Fail(Exception error) + { + base._observer.OnError(error); + base.Dispose(); + } + + public void Done(int index) + { + _isDone[index] = true; + + var allDone = true; + foreach (var isDone in _isDone) + { + if (!isDone) + { + allDone = false; + break; + } + } + + if (allDone) + { + base._observer.OnCompleted(); + base.Dispose(); + return; + } + } + } + + class ZipObserver<T> : IObserver<T> + { + private readonly object _gate; + private readonly IZip _parent; + private readonly int _index; + private readonly IDisposable _self; + private readonly Queue<T> _values; + + public ZipObserver(object gate, IZip parent, int index, IDisposable self) + { + _gate = gate; + _parent = parent; + _index = index; + _self = self; + _values = new Queue<T>(); + } + + public Queue<T> Values + { + get { return _values; } + } + + public void OnNext(T value) + { + lock (_gate) + { + _values.Enqueue(value); + _parent.Next(_index); + } + } + + public void OnError(Exception error) + { + _self.Dispose(); + + lock (_gate) + { + _parent.Fail(error); + } + } + + public void OnCompleted() + { + _self.Dispose(); + + lock (_gate) + { + _parent.Done(_index); + } + } + } + + #endregion + + #endregion + + #region N-ary + + class Zip<TSource> : Producer<IList<TSource>> + { + private readonly IEnumerable<IObservable<TSource>> _sources; + + public Zip(IEnumerable<IObservable<TSource>> sources) + { + _sources = sources; + } + + protected override IDisposable Run(IObserver<IList<TSource>> observer, IDisposable cancel, Action<IDisposable> setSink) + { + var sink = new _(this, observer, cancel); + setSink(sink); + return sink.Run(); + } + + class _ : Sink<IList<TSource>> + { + private readonly Zip<TSource> _parent; + + public _(Zip<TSource> parent, IObserver<IList<TSource>> observer, IDisposable cancel) + : base(observer, cancel) + { + _parent = parent; + } + + private object _gate; + private Queue<TSource>[] _queues; + private bool[] _isDone; + private IDisposable[] _subscriptions; + + public IDisposable Run() + { + var srcs = _parent._sources.ToArray(); + + var N = srcs.Length; + + _queues = new Queue<TSource>[N]; + for (int i = 0; i < N; i++) + _queues[i] = new Queue<TSource>(); + + _isDone = new bool[N]; + + _subscriptions = new SingleAssignmentDisposable[N]; + + _gate = new object(); + + for (int i = 0; i < N; i++) + { + var j = i; + + var d = new SingleAssignmentDisposable(); + _subscriptions[j] = d; + + var o = new O(this, j); + d.Disposable = srcs[j].SubscribeSafe(o); + } + + return new CompositeDisposable(_subscriptions) { Disposable.Create(() => { foreach (var q in _queues) q.Clear(); }) }; + } + + private void OnNext(int index, TSource value) + { + lock (_gate) + { + _queues[index].Enqueue(value); + + if (_queues.All(q => q.Count > 0)) + { + var res = _queues.Select(q => q.Dequeue()).ToList(); + base._observer.OnNext(res); + } + else if (_isDone.Where((x, i) => i != index).All(Stubs<bool>.I)) + { + base._observer.OnCompleted(); + base.Dispose(); + return; + } + } + } + + private void OnError(Exception error) + { + lock (_gate) + { + base._observer.OnError(error); + base.Dispose(); + } + } + + private void OnCompleted(int index) + { + lock (_gate) + { + _isDone[index] = true; + + if (_isDone.All(Stubs<bool>.I)) + { + base._observer.OnCompleted(); + base.Dispose(); + return; + } + else + { + _subscriptions[index].Dispose(); + } + } + } + + class O : IObserver<TSource> + { + private readonly _ _parent; + private readonly int _index; + + public O(_ parent, int index) + { + _parent = parent; + _index = index; + } + + public void OnNext(TSource value) + { + _parent.OnNext(_index, value); + } + + public void OnError(Exception error) + { + _parent.OnError(error); + } + + public void OnCompleted() + { + _parent.OnCompleted(_index); + } + } + } + } + + #endregion +} +#endif
\ No newline at end of file diff --git a/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/_.cs b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/_.cs new file mode 100644 index 0000000..d4b0dbc --- /dev/null +++ b/Rx/NET/Source/System.Reactive.Linq/Reactive/Linq/Observable/_.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !NO_PERF +using System; + +namespace System.Reactive.Linq.Observαble +{ +} +#endif |