/* * Copyright (c) 2011-2017 Pivotal Software Inc, All Rights Reserved. * * 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 reactor.core; import java.util.Objects; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.Supplier; import reactor.core.publisher.Operators; /** * A {@link Disposable} container that allows updating/replacing its inner Disposable * atomically and with respect of disposing the container itself. * * Utility methods to work with {@link Disposable} atomically as well as disposable * wrappers for multiple disposables. * * @author Simon Baslé * @author David Karnok */ public class Disposables { /** * A singleton {@link Disposable} that represents a disposed instance. Should not be * leaked to clients. */ static final Disposable DISPOSED = new Disposable() { @Override public void dispose() { //NO-OP } @Override public boolean isDisposed() { return true; } }; /** * Atomically set the field to a {@link Disposable} and dispose the old content. * * @param updater the target field updater * @param holder the target instance holding the field * @param newValue the new Disposable to set * @return true if successful, false if the field contains the {@link #DISPOSED} instance. */ public static <T> boolean set(AtomicReferenceFieldUpdater<T, Disposable> updater, T holder, Disposable newValue) { for (;;) { Disposable current = updater.get(holder); if (current == DISPOSED) { if (newValue != null) { newValue.dispose(); } return false; } if (updater.compareAndSet(holder, current, newValue)) { if (current != null) { current.dispose(); } return true; } } } /** * Atomically set the field to the given non-null {@link Disposable} and return true, * or return false if the field is non-null. * If the target field contains the common {@link #DISPOSED} instance, the supplied disposable * is disposed. If the field contains other non-null {@link Disposable}, an {@link IllegalStateException} * is signalled to the {@link Operators#onErrorDropped(Throwable)} hook. * * @param updater the target field updater * @param holder the target instance holding the field * @param newValue the new Disposable to set, not null * @return true if the operation succeeded, false */ public static <T> boolean setOnce(AtomicReferenceFieldUpdater<T, Disposable> updater, T holder, Disposable newValue) { Objects.requireNonNull(newValue, "newValue is null"); if (!updater.compareAndSet(holder, null, newValue)) { newValue.dispose(); if (updater.get(holder) != DISPOSED) { reportDisposableSet(); } return false; } return true; } private static void reportDisposableSet() { Operators.onErrorDropped(new IllegalStateException("Disposable already set")); } /** * Atomically replace the {@link Disposable} in the field with the given new Disposable * but do not dispose the old one. * * @param updater the target field updater * @param holder the target instance holding the field * @param newValue the new Disposable to set, null allowed * @return true if the operation succeeded, false if the target field contained * the common {@link #DISPOSED} instance and the given disposable is not null but is disposed. */ public static <T> boolean replace(AtomicReferenceFieldUpdater<T, Disposable> updater, T holder, Disposable newValue) { for (;;) { Disposable current = updater.get(holder); if (current == DISPOSED) { if (newValue != null) { newValue.dispose(); } return false; } if (updater.compareAndSet(holder, current, newValue)) { return true; } } } /** * Atomically dispose the {@link Disposable} in the field if not already disposed. * * @param updater the target field updater * @param holder the target instance holding the field * @return true if the {@link Disposable} held by the field was properly disposed */ public static <T> boolean dispose(AtomicReferenceFieldUpdater<T, Disposable> updater, T holder) { Disposable current = updater.get(holder); Disposable d = DISPOSED; if (current != d) { current = updater.getAndSet(holder, d); if (current != d) { if (current != null) { current.dispose(); } return true; } } return false; } /** * Verify that current is null and next is not null, otherwise signal a * {@link NullPointerException} to the {@link Operators#onErrorDropped(Throwable)} * hook and return false. * * @param current the current {@link Disposable}, expected to be null * @param next the next {@link Disposable}, expected to be non-null * @return true if the validation succeeded */ public static boolean validate(Disposable current, Disposable next) { if (next == null) { Operators.onErrorDropped(new NullPointerException("next is null")); return false; } if (current != null) { next.dispose(); reportDisposableSet(); return false; } return true; } /** * Atomically try to set the given {@link Disposable} on the field if it is null or * disposes it if the field contains {@link #DISPOSED}. * * @param updater the target field updater * @param holder the target instance holding the field * @param newValue the disposable to set * @return true if successful, false otherwise */ public static <T> boolean trySet(AtomicReferenceFieldUpdater<T, Disposable> updater, T holder, Disposable newValue) { if (!updater.compareAndSet(holder, null, newValue)) { if (updater.get(holder) == DISPOSED) { newValue.dispose(); } return false; } return true; } /** * Check if the given {@link Disposable} is the singleton {@link #DISPOSED}. * * @param d the disposable to check * @return true if d is {@link #DISPOSED} */ public static boolean isDisposed(Disposable d) { return d == DISPOSED; } /** * A {@link Disposable} container that allows updating/replacing its inner Disposable * atomically and with respect of disposing the container itself. * * Includes static utility methods to work with Disposable atomically. * * @author Simon Baslé * @author David Karnok */ public static final class SequentialDisposable implements Disposable, Supplier<Disposable> { volatile Disposable inner; static final AtomicReferenceFieldUpdater<SequentialDisposable, Disposable> INNER = AtomicReferenceFieldUpdater.newUpdater(SequentialDisposable.class, Disposable.class, "inner"); /** * Construct an empty {@link SequentialDisposable}. */ public SequentialDisposable() { // nothing to do } /** * Construct a {@link SequentialDisposable} with the initial {@link Disposable} provided. * * @param initial the initial disposable, null allowed */ public SequentialDisposable(Disposable initial) { inner = initial; } /** * Atomically set the next {@link Disposable} on this container and dispose the previous * one (if any). If the container has been disposed, fall back to disposing {@code next}. * * @param next the {@link Disposable} to set, may be null * @return true if the operation succeeded, false if the container has been disposed * @see #replace(Disposable) */ public boolean update(Disposable next) { return Disposables.set(INNER, this, next); } /** * Atomically set the next {@link Disposable} on this container but don't dispose the previous * one (if any). If the container has been disposed, fall back to disposing {@code next}. * * @param next the {@link Disposable} to set, may be null * @return true if the operation succeeded, false if the container has been disposed * @see #update(Disposable) */ public boolean replace(Disposable next) { return Disposables.replace(INNER, this, next); } @Override public Disposable get() { return inner; } @Override public void dispose() { Disposables.dispose(INNER, this); } @Override public boolean isDisposed() { return Disposables.isDisposed(INNER.get(this)); } } }