internal void SubscribeAll(IEnumerable <ICompletableSource> sources) { Interlocked.Increment(ref active); try { foreach (var src in sources) { var inner = new MergeInnerObserver(this); if (!Add(inner)) { break; } Interlocked.Increment(ref active); src.Subscribe(inner); } } catch (Exception ex) { if (delayErrors) { ExceptionHelper.AddException(ref errors, ex); } else { Interlocked.CompareExchange(ref errors, ex, null); } } Interlocked.Decrement(ref active); Drain(); }
internal void SubscribeAll(ICompletableSource[] sources) { Interlocked.Exchange(ref active, sources.Length); foreach (var src in sources) { var inner = new MergeInnerObserver(this); if (!Add(inner)) { break; } src.Subscribe(inner); } }
void Remove(MergeInnerObserver inner) { if (Volatile.Read(ref disposed)) { return; } lock (this) { if (Volatile.Read(ref disposed)) { return; } observers.Remove(inner); } }
void InnerError(MergeInnerObserver sender, Exception ex) { Remove(sender); if (delayErrors) { ExceptionHelper.AddException(ref errors, ex); Interlocked.Decrement(ref active); Drain(); } else { if (Interlocked.CompareExchange(ref errors, ex, null) == null) { Drain(); } } }
protected bool Add(MergeInnerObserver inner) { if (Volatile.Read(ref disposed)) { return(false); } lock (this) { if (Volatile.Read(ref disposed)) { return(false); } observers.Add(inner); } return(true); }
internal override void Drain() { if (Interlocked.Increment(ref wip) != 1) { return; } var missed = 1; var sources = this.sources; var n = sources.Length; var maxConcurrency = this.maxConcurrency; for (; ;) { if (!Volatile.Read(ref disposed)) { if (!delayErrors) { var ex = Volatile.Read(ref errors); if (ex != null) { base.Dispose(); downstream.OnError(ex); continue; } } if (index < n && Volatile.Read(ref active) < maxConcurrency) { var src = sources[index++]; if (src == null) { var ex = new NullReferenceException("The ICompletableSource at index " + (index - 1) + " is null"); if (delayErrors) { index = n; ExceptionHelper.AddException(ref errors, ex); } else { Interlocked.CompareExchange(ref errors, ex, null); } continue; } var inner = new MergeInnerObserver(this); if (Add(inner)) { Interlocked.Increment(ref active); src.Subscribe(inner); } continue; } var d = Volatile.Read(ref active) == 0; var empty = index == n; if (d && empty) { var ex = Volatile.Read(ref errors); if (ex != null) { Volatile.Write(ref disposed, true); downstream.OnError(ex); } else { Volatile.Write(ref disposed, true); downstream.OnCompleted(); } } } missed = Interlocked.Add(ref wip, -missed); if (missed == 0) { break; } } }
internal override void Drain() { if (Interlocked.Increment(ref wip) != 1) { return; } var missed = 1; var sources = this.sources; var maxConcurrency = this.maxConcurrency; for (; ;) { if (Volatile.Read(ref disposed)) { if (!done) { done = true; sources.Dispose(); } } else { if (!delayErrors) { var ex = Volatile.Read(ref errors); if (ex != null) { base.Dispose(); downstream.OnError(ex); continue; } } if (!done && Volatile.Read(ref active) < maxConcurrency) { var src = default(ICompletableSource); var empty = true; try { if (sources.MoveNext()) { empty = false; src = RequireNonNullRef(sources.Current, "The IEnumerator returned a null ICompletableSource"); } } catch (Exception ex) { if (delayErrors) { ExceptionHelper.AddException(ref errors, ex); } else { Interlocked.CompareExchange(ref errors, ex, null); continue; } } if (empty) { done = true; sources.Dispose(); } else { var inner = new MergeInnerObserver(this); if (Add(inner)) { Interlocked.Increment(ref active); src.Subscribe(inner); } continue; } } var d = Volatile.Read(ref active) == 0; if (d && done) { var ex = Volatile.Read(ref errors); if (ex != null) { Volatile.Write(ref disposed, true); downstream.OnError(ex); } else { Volatile.Write(ref disposed, true); downstream.OnCompleted(); } } } missed = Interlocked.Add(ref wip, -missed); if (missed == 0) { break; } } }
void InnerCompleted(MergeInnerObserver sender) { Remove(sender); Interlocked.Decrement(ref active); Drain(); }