/// <summary> /// Atomically set the next ISubscription on this arbiter. /// </summary> /// <param name="s">The new ISubscription instance.</param> public void Set(ISubscription s) { if (Volatile.Read(ref wip) == 0 && Interlocked.CompareExchange(ref wip, 1, 0) == 0) { var c = current; long r = requested; if (SubscriptionHelper.IsCancelled(c)) { s?.Cancel(); if (Interlocked.Decrement(ref wip) == 0) { return; } } else { c?.Cancel(); current = s; if (Interlocked.Decrement(ref wip) != 0) { Drain(); } if (r != 0L) { s?.Request(r); } } } else { ISubscription c = Interlocked.Exchange(ref missedSubscription, s); c?.Cancel(); if (Interlocked.Increment(ref wip) != 1) { return; } Drain(); } }
void Drain() { long requestAmount = 0L; ISubscription requestTarget = null; int missed = 1; for (;;) { long mRequested = Volatile.Read(ref missedRequested); if (mRequested != 0L) { mRequested = Interlocked.Exchange(ref missedRequested, 0L); } long mProduced = Volatile.Read(ref missedProduced); if (mProduced != 0L) { mProduced = Interlocked.Exchange(ref missedProduced, 0L); } long r = requested; if (r != long.MaxValue) { long u = BackpressureHelper.AddCap(r, mRequested); if (u != long.MaxValue) { long v = u - mProduced; if (v < 0L) { ExceptionHelper.OnErrorDropped(new InvalidOperationException("More produced than requested: " + v)); v = 0L; } requested = v; r = v; } else { requested = u; r = u; } } ISubscription mSubscription = Volatile.Read(ref missedSubscription); if (mSubscription != null) { mSubscription = Interlocked.Exchange(ref missedSubscription, null); } var c = current; if (SubscriptionHelper.IsCancelled(c)) { mSubscription?.Cancel(); } else { if (mSubscription != null) { current?.Cancel(); current = mSubscription; if (r != 0L) { requestAmount = BackpressureHelper.AddCap(requestAmount, r); requestTarget = mSubscription; } } else if (mRequested != 0) { requestAmount = BackpressureHelper.AddCap(requestAmount, mRequested); requestTarget = current; } } missed = QueueDrainHelper.Leave(ref wip, missed); if (missed == 0) { if (requestAmount != 0L) { requestTarget?.Request(requestAmount); } break; } } }