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

io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper Maven / Gradle / Ivy

There is a newer version: 3.1.9
Show newest version
/*
 * Copyright (c) 2016-present, RxJava Contributors.
 *
 * 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 io.reactivex.rxjava3.internal.subscriptions;

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

import org.reactivestreams.Subscription;

import io.reactivex.rxjava3.exceptions.ProtocolViolationException;
import io.reactivex.rxjava3.internal.util.BackpressureHelper;
import io.reactivex.rxjava3.plugins.RxJavaPlugins;

/**
 * Utility methods to validate Subscriptions in the various onSubscribe calls.
 */
public enum SubscriptionHelper implements Subscription {
    /**
     * Represents a cancelled Subscription.
     * 

Don't leak this instance! */ CANCELLED ; @Override public void request(long n) { // deliberately ignored } @Override public void cancel() { // deliberately ignored } /** * Verifies that current is null, next is not null, otherwise signals errors * to the RxJavaPlugins and returns false. * @param current the current Subscription, expected to be null * @param next the next Subscription, expected to be non-null * @return true if the validation succeeded */ public static boolean validate(Subscription current, Subscription next) { if (next == null) { RxJavaPlugins.onError(new NullPointerException("next is null")); return false; } if (current != null) { next.cancel(); reportSubscriptionSet(); return false; } return true; } /** * Reports that the subscription is already set to the RxJavaPlugins error handler, * which is an indication of a onSubscribe management bug. */ public static void reportSubscriptionSet() { RxJavaPlugins.onError(new ProtocolViolationException("Subscription already set!")); } /** * Validates that the n is positive. * @param n the request amount * @return false if n is non-positive. */ public static boolean validate(long n) { if (n <= 0) { RxJavaPlugins.onError(new IllegalArgumentException("n > 0 required but it was " + n)); return false; } return true; } /** * Reports to the plugin error handler that there were more values produced than requested, which * is a sign of internal backpressure handling bug. * @param n the overproduction amount */ public static void reportMoreProduced(long n) { RxJavaPlugins.onError(new ProtocolViolationException("More produced than requested: " + n)); } /** * Atomically sets the subscription on the field and cancels the * previous subscription if any. * @param field the target field to set the new subscription on * @param s the new subscription * @return true if the operation succeeded, false if the target field * holds the {@link #CANCELLED} instance. * @see #replace(AtomicReference, Subscription) */ public static boolean set(AtomicReference field, Subscription s) { for (;;) { Subscription current = field.get(); if (current == CANCELLED) { if (s != null) { s.cancel(); } return false; } if (field.compareAndSet(current, s)) { if (current != null) { current.cancel(); } return true; } } } /** * Atomically sets the subscription on the field if it is still null. *

If the field is not null and doesn't contain the {@link #CANCELLED} * instance, the {@link #reportSubscriptionSet()} is called. * @param field the target field * @param s the new subscription to set * @return true if the operation succeeded, false if the target field was not null. */ public static boolean setOnce(AtomicReference field, Subscription s) { Objects.requireNonNull(s, "s is null"); if (!field.compareAndSet(null, s)) { s.cancel(); if (field.get() != CANCELLED) { reportSubscriptionSet(); } return false; } return true; } /** * Atomically sets the subscription on the field but does not * cancel the previous subscription. * @param field the target field to set the new subscription on * @param s the new subscription * @return true if the operation succeeded, false if the target field * holds the {@link #CANCELLED} instance. * @see #set(AtomicReference, Subscription) */ public static boolean replace(AtomicReference field, Subscription s) { for (;;) { Subscription current = field.get(); if (current == CANCELLED) { if (s != null) { s.cancel(); } return false; } if (field.compareAndSet(current, s)) { return true; } } } /** * Atomically swaps in the common cancelled subscription instance * and cancels the previous subscription if any. * @param field the target field to dispose the contents of * @return true if the swap from the non-cancelled instance to the * common cancelled instance happened in the caller's thread (allows * further one-time actions). */ public static boolean cancel(AtomicReference field) { Subscription current = field.get(); if (current != CANCELLED) { current = field.getAndSet(CANCELLED); if (current != CANCELLED) { if (current != null) { current.cancel(); } return true; } } return false; } /** * Atomically sets the new Subscription on the field and requests any accumulated amount * from the requested field. * @param field the target field for the new Subscription * @param requested the current requested amount * @param s the new Subscription, not null (verified) * @return true if the Subscription was set the first time */ public static boolean deferredSetOnce(AtomicReference field, AtomicLong requested, Subscription s) { if (SubscriptionHelper.setOnce(field, s)) { long r = requested.getAndSet(0L); if (r != 0L) { s.request(r); } return true; } return false; } /** * Atomically requests from the Subscription in the field if not null, otherwise accumulates * the request amount in the requested field to be requested once the field is set to non-null. * @param field the target field that may already contain a Subscription * @param requested the current requested amount * @param n the request amount, positive (verified) */ public static void deferredRequest(AtomicReference field, AtomicLong requested, long n) { Subscription s = field.get(); if (s != null) { s.request(n); } else { if (SubscriptionHelper.validate(n)) { BackpressureHelper.add(requested, n); s = field.get(); if (s != null) { long r = requested.getAndSet(0L); if (r != 0L) { s.request(r); } } } } } /** * Atomically sets the subscription on the field if it is still null and issues a positive request * to the given {@link Subscription}. *

* If the field is not null and doesn't contain the {@link #CANCELLED} * instance, the {@link #reportSubscriptionSet()} is called. * @param field the target field * @param s the new subscription to set * @param request the amount to request, positive (not verified) * @return true if the operation succeeded, false if the target field was not null. * @since 2.1.11 */ public static boolean setOnce(AtomicReference field, Subscription s, long request) { if (setOnce(field, s)) { s.request(request); return true; } return false; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy