public void Subscribe(ISignalObserver <T> observer) { var ccd = new CacheConsumerDisposable(observer, this, head); observer.OnSubscribe(ccd); Add(ccd); if (ccd.IsDisposed()) { Remove(ccd); return; } if (Volatile.Read(ref once) == 0 && Interlocked.CompareExchange(ref once, 1, 0) == 0) { cancel?.Invoke(this); source.Subscribe(this); } Drain(ccd); }
void Drain(CacheConsumerDisposable ccd) { if (Interlocked.Increment(ref ccd.wip) != 1) { return; } var missed = 1; var downstream = ccd.downstream; for (; ;) { var i = ccd.index; var n = ccd.node; var o = ccd.offset; if (n != null) { var a = n.items; var cap = a.Length; for (; ;) { if (ccd.IsDisposed()) { n = null; break; } var ex = Volatile.Read(ref terminated); var s = Volatile.Read(ref size); bool empty = i == s; if (ex != null && empty) { if (ex == ExceptionHelper.TERMINATED) { downstream.OnCompleted(); } else { downstream.OnError(ex); } n = null; break; } if (empty) { break; } if (o == cap) { var b = n.next; n = b; a = b.items; o = 0; } var v = a[o]; downstream.OnNext(v); i++; o++; } ccd.index = i; ccd.node = n; ccd.offset = o; } missed = Interlocked.Add(ref ccd.wip, -missed); if (missed == 0) { break; } } }