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

hu.akarnokd.rxjava3.interop.SubscriptionHelper Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2016-2020 David Karnok
 *
 * 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.rxjava3.interop;

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

import org.reactivestreams.Subscription;

import io.reactivex.rxjava3.exceptions.ProtocolViolationException;
import io.reactivex.rxjava3.plugins.RxJavaPlugins;

/**
 * Utility methods to validate Subscriptions in the various onSubscribe calls.
 */
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; } /** * 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 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 - 2025 Weber Informatics LLC | Privacy Policy