All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hu.akarnokd.rxjava2.internal.util.QueueDrainHelper Maven / Gradle / Ivy

There is a newer version: 2.0.0-RC3
Show newest version
/**
 * Copyright 2015 David Karnok and Netflix, Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
 * the License for the specific language governing permissions and limitations under the License.
 */
package hu.akarnokd.rxjava2.internal.util;

import java.util.Queue;
import java.util.concurrent.atomic.*;

import org.reactivestreams.Subscriber;

import hu.akarnokd.rxjava2.NbpObservable.NbpSubscriber;
import hu.akarnokd.rxjava2.disposables.Disposable;
import hu.akarnokd.rxjava2.functions.BooleanSupplier;

/**
 * Utility class to help with the queue-drain serialization idiom.
 */
public enum QueueDrainHelper {
    ;

    /**
     * A fast-path queue-drain serialization logic.
     * 

The decrementing of the state is left to the drain callback. * @param the instance type * @param updater the field updater * @param instance the instance holding the field * @param fastPath called if the instance is uncontended. * @param queue called if the instance is contended to queue up work * @param drain called if the instance transitions to the drain state successfully */ public static void queueDrain(AtomicIntegerFieldUpdater updater, T instance, Runnable fastPath, Runnable queue, Runnable drain) { if (updater.get(instance) == 0 && updater.compareAndSet(instance, 0, 1)) { fastPath.run(); if (updater.decrementAndGet(instance) == 0) { return; } } else { queue.run(); if (updater.getAndIncrement(instance) != 0) { return; } } drain.run(); } /** * A fast-path queue-drain serialization logic with the ability to leave the state * in fastpath/drain mode or not continue after the call to queue. *

The decrementing of the state is left to the drain callback. * @param the instance type * @param updater the field updater * @param instance the instance holding the field * @param fastPath * @param queue * @param drain */ public static void queueDrainIf(AtomicIntegerFieldUpdater updater, T instance, BooleanSupplier fastPath, BooleanSupplier queue, Runnable drain) { if (updater.get(instance) == 0 && updater.compareAndSet(instance, 0, 1)) { if (fastPath.getAsBoolean()) { return; } if (updater.decrementAndGet(instance) == 0) { return; } } else { if (queue.getAsBoolean()) { return; } if (updater.getAndIncrement(instance) != 0) { return; } } drain.run(); } /** * A fast-path queue-drain serialization logic where the drain is looped until * the instance state reaches 0 again. * @param the instance type * @param updater the field updater * @param instance the instance holding the field * @param fastPath * @param queue * @param drain */ public static void queueDrainLoop(AtomicIntegerFieldUpdater updater, T instance, Runnable fastPath, Runnable queue, Runnable drain) { if (updater.get(instance) == 0 && updater.compareAndSet(instance, 0, 1)) { fastPath.run(); if (updater.decrementAndGet(instance) == 0) { return; } } else { queue.run(); if (updater.getAndIncrement(instance) != 0) { return; } } int missed = 1; for (;;) { drain.run(); missed = updater.addAndGet(instance, -missed); if (missed == 0) { return; } } } /** * A fast-path queue-drain serialization logic with looped drain call and the ability to leave the state * in fastpath/drain mode or not continue after the call to queue. * @param the instance type * @param updater the field updater * @param instance the instance holding the field * @param fastPath * @param queue * @param drain */ public static void queueDrainLoopIf(AtomicIntegerFieldUpdater updater, T instance, BooleanSupplier fastPath, BooleanSupplier queue, BooleanSupplier drain) { if (updater.get(instance) == 0 && updater.compareAndSet(instance, 0, 1)) { if (fastPath.getAsBoolean()) { return; } if (updater.decrementAndGet(instance) == 0) { return; } } else { if (queue.getAsBoolean()) { return; } if (updater.getAndIncrement(instance) != 0) { return; } } int missed = 1; for (;;) { if (drain.getAsBoolean()) { return; } missed = updater.addAndGet(instance, -missed); if (missed == 0) { return; } } } /** * A fast-path queue-drain serialization logic. *

The decrementing of the state is left to the drain callback. * @param the instance type * @param instance * @param fastPath called if the instance is uncontended. * @param queue called if the instance is contended to queue up work * @param drain called if the instance transitions to the drain state successfully */ public static void queueDrain(AtomicInteger instance, Runnable fastPath, Runnable queue, Runnable drain) { if (instance.get() == 0 && instance.compareAndSet(0, 1)) { fastPath.run(); if (instance.decrementAndGet() == 0) { return; } } else { queue.run(); if (instance.getAndIncrement() != 0) { return; } } drain.run(); } /** * A fast-path queue-drain serialization logic with the ability to leave the state * in fastpath/drain mode or not continue after the call to queue. *

The decrementing of the state is left to the drain callback. * @param the instance type * @param instance * @param fastPath * @param queue * @param drain */ public static void queueDrainIf(AtomicInteger instance, BooleanSupplier fastPath, BooleanSupplier queue, Runnable drain) { if (instance.get() == 0 && instance.compareAndSet(0, 1)) { if (fastPath.getAsBoolean()) { return; } if (instance.decrementAndGet() == 0) { return; } } else { if (queue.getAsBoolean()) { return; } if (instance.getAndIncrement() != 0) { return; } } drain.run(); } /** * A fast-path queue-drain serialization logic where the drain is looped until * the instance state reaches 0 again. * @param the instance type * @param instance * @param fastPath * @param queue * @param drain */ public static void queueDrainLoop(AtomicInteger instance, Runnable fastPath, Runnable queue, Runnable drain) { if (instance.get() == 0 && instance.compareAndSet(0, 1)) { fastPath.run(); if (instance.decrementAndGet() == 0) { return; } } else { queue.run(); if (instance.getAndIncrement() != 0) { return; } } int missed = 1; for (;;) { drain.run(); missed = instance.addAndGet(-missed); if (missed == 0) { return; } } } /** * A fast-path queue-drain serialization logic with looped drain call and the ability to leave the state * in fastpath/drain mode or not continue after the call to queue. * @param the instance type * @param instance * @param fastPath * @param queue * @param drain */ public static void queueDrainLoopIf(AtomicInteger instance, BooleanSupplier fastPath, BooleanSupplier queue, BooleanSupplier drain) { if (instance.get() == 0 && instance.compareAndSet(0, 1)) { if (fastPath.getAsBoolean()) { return; } if (instance.decrementAndGet() == 0) { return; } } else { if (queue.getAsBoolean()) { return; } if (instance.getAndIncrement() != 0) { return; } } int missed = 1; for (;;) { if (drain.getAsBoolean()) { return; } missed = instance.addAndGet(-missed); if (missed == 0) { return; } } } public static void drainLoop(Queue q, Subscriber a, boolean delayError, QueueDrain qd) { int missed = 1; for (;;) { if (checkTerminated(qd.done(), q.isEmpty(), a, delayError, q, qd)) { return; } long r = qd.requested(); boolean unbounded = r == Long.MAX_VALUE; long e = 0L; while (e != r) { boolean d = qd.done(); T v = q.poll(); boolean empty = v == null; if (checkTerminated(d, empty, a, delayError, q, qd)) { return; } if (empty) { break; } if (qd.accept(a, v)) { e++; } } if (e != 0L && !unbounded) { qd.produced(e); } missed = qd.leave(-missed); if (missed == 0) { break; } } } /** * Drain the queue but give up with an error if there aren't enough requests. * @param the queue value type * @param the emission value type * @param q the queue * @param a the subscriber * @param delayError true if errors should be delayed after all normal items * @param dispose the disposable to call when termination happens and cleanup is necessary * @param qd the QueueDrain instance that gives status information to the drain logic */ public static void drainMaxLoop(Queue q, Subscriber a, boolean delayError, Disposable dispose, QueueDrain qd) { int missed = 1; for (;;) { for (;;) { boolean d = qd.done(); T v = q.poll(); boolean empty = v == null; if (checkTerminated(d, empty, a, delayError, q, qd)) { if (dispose != null) { dispose.dispose(); } return; } if (empty) { break; } long r = qd.requested(); if (r != 0L) { if (qd.accept(a, v)) { if (r != Long.MAX_VALUE) { r = qd.produced(1); } } } else { q.clear(); if (dispose != null) { dispose.dispose(); } a.onError(new IllegalStateException("Could not emit value due to lack of requests.")); return; } } missed = qd.leave(-missed); if (missed == 0) { break; } } } public static boolean checkTerminated(boolean d, boolean empty, Subscriber s, boolean delayError, Queue q, QueueDrain qd) { if (qd.cancelled()) { q.clear(); return true; } if (d) { if (delayError) { if (empty) { Throwable err = qd.error(); if (err != null) { s.onError(err); } else { s.onComplete(); } return true; } } else { Throwable err = qd.error(); if (err != null) { q.clear(); s.onError(err); return true; } else if (empty) { s.onComplete(); return true; } } } return false; } public static void drainLoop(Queue q, NbpSubscriber a, boolean delayError, Disposable dispose, NbpQueueDrain qd) { int missed = 1; for (;;) { if (checkTerminated(qd.done(), q.isEmpty(), a, delayError, q, dispose, qd)) { return; } for (;;) { boolean d = qd.done(); T v = q.poll(); boolean empty = v == null; if (checkTerminated(d, empty, a, delayError, q, dispose, qd)) { return; } if (empty) { break; } qd.accept(a, v); } missed = qd.leave(-missed); if (missed == 0) { break; } } } public static boolean checkTerminated(boolean d, boolean empty, NbpSubscriber s, boolean delayError, Queue q, Disposable disposable, NbpQueueDrain qd) { if (qd.cancelled()) { q.clear(); disposable.dispose(); return true; } if (d) { if (delayError) { if (empty) { disposable.dispose(); Throwable err = qd.error(); if (err != null) { s.onError(err); } else { s.onComplete(); } return true; } } else { Throwable err = qd.error(); if (err != null) { q.clear(); disposable.dispose(); s.onError(err); return true; } else if (empty) { disposable.dispose(); s.onComplete(); return true; } } } return false; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy