/*
* 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));
}
}
}