internal void Drain() { if (Interlocked.Increment(ref wip) != 1) { return; } var missed = 1; var observers = this.observers; var n = observers.Length; var downstream = this.downstream; var delayErrors = this.delayErrors; var hasValues = this.hasValues; for (; ;) { for (; ;) { if (Volatile.Read(ref disposed)) { Array.Clear(values, 0, values.Length); break; } if (!delayErrors) { var ex = Volatile.Read(ref error); if (ex != null) { downstream.OnError(ex); DisposeAll(); continue; } } int ready = 0; bool done = false; for (int i = 0; i < n; i++) { if (hasValues[i]) { ready++; } else { var inner = Volatile.Read(ref observers[i]); if (inner != null) { var d = inner.IsDone(); var q = Volatile.Read(ref inner.queue); var v = default(T); var success = false; try { if (q != null) { v = q.TryPoll(out success); } } catch (Exception ex) { if (delayErrors) { inner.Dispose(); ExceptionHelper.AddException(ref error, ex); inner.SetDone(); d = true; } else { Interlocked.CompareExchange(ref error, ex, null); ex = Volatile.Read(ref error); downstream.OnError(ex); DisposeAll(); continue; } } if (d && !success) { done = true; break; } if (success) { hasValues[i] = true; values[i] = v; ready++; } } } } if (done) { var ex = Volatile.Read(ref error); if (ex != null) { downstream.OnError(ex); } else { downstream.OnCompleted(); } DisposeAll(); continue; } if (ready == n) { var vals = values; values = new T[n]; Array.Clear(hasValues, 0, hasValues.Length); var result = default(R); try { result = mapper(vals); } catch (Exception ex) { if (delayErrors) { ExceptionHelper.AddException(ref error, ex); ex = ExceptionHelper.Terminate(ref error); downstream.OnError(ex); DisposeAll(); } else { if (Interlocked.CompareExchange(ref error, ex, null) == null) { downstream.OnError(ex); DisposeAll(); } } continue; } downstream.OnNext(result); } else { break; } } missed = Interlocked.Add(ref wip, -missed); if (missed == 0) { break; } } }
void DrainLoop() { var missed = 1; var delayErrors = this.delayErrors; var downstream = this.downstream; for (; ;) { for (; ;) { var a = Volatile.Read(ref active); if (a == DISPOSED) { break; } if (!delayErrors) { var ex = Volatile.Read(ref errors); if (ex != null) { downstream.OnError(ex); Dispose(); break; } } bool d = Volatile.Read(ref done); if (d && a == null) { var ex = ExceptionHelper.Terminate(ref errors); if (ex == null) { downstream.OnCompleted(); } else { downstream.OnError(ex); } Dispose(); break; } if (a == null) { break; } var innerDone = a.IsDone(); var q = a.GetQueue(); var v = default(R); var empty = true; if (q != null) { v = q.TryPoll(out var success); empty = !success; } if (innerDone && empty) { Interlocked.CompareExchange(ref active, null, a); } else if (empty) { break; } else { downstream.OnNext(v); } } missed = Interlocked.Add(ref wip, -missed); if (missed == 0) { break; } } }
internal void Drain() { if (Interlocked.Increment(ref wip) == 1) { var q = queue; var delayErrors = this.delayErrors; for (; ;) { if (disposed) { while (q.TryDequeue(out var _)) { ; } } else { if (!active) { var d = Volatile.Read(ref done); if (d && !delayErrors) { var ex = Volatile.Read(ref errors); if (ex != null) { downstream.OnError(ex); disposed = true; continue; } } bool empty = !q.TryDequeue(out var v); if (d && empty) { var ex = ExceptionHelper.Terminate(ref errors); if (ex != null) { downstream.OnError(ex); } else { downstream.OnCompleted(); } disposed = true; continue; } if (!empty) { var c = default(ICompletableSource); try { c = RequireNonNullRef(mapper(v), "The mapper returned a null ICompletableSource"); } catch (Exception ex) { DisposableHelper.Dispose(ref upstream); ExceptionHelper.AddException(ref errors, ex); ex = ExceptionHelper.Terminate(ref errors); downstream.OnError(ex); disposed = true; continue; } active = true; c.Subscribe(inner); } } } if (Interlocked.Decrement(ref wip) == 0) { break; } } } }