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

hu.akarnokd.rxjava2.internal.util.TerminalAtomicsHelper 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.concurrent.atomic.*;

import hu.akarnokd.rxjava2.functions.*;

/**
 * Helper class to handle copy-on-write arrays plus terminal state for arbitrary references.
 */
public enum TerminalAtomicsHelper {
    ;
    
    /**
     * Atomically swaps in the terminalValue and calls the given callback if not already terminated.
     * @param  the instance type
     * @param  the reference type
     * @param updater
     * @param instance
     * @param terminalValue
     * @param onTerminate
     */
    public static  void terminate(AtomicReferenceFieldUpdater updater, 
            T instance, U terminalValue, Consumer onTerminate) {
        U a = updater.get(instance);
        if (a != terminalValue) {
            a = updater.getAndSet(instance, terminalValue);
            if (a != terminalValue && a != null) {
                onTerminate.accept(a);
            }
        }
    }
    
    /**
     * Atomically swaps in the terminalValue and calls the given callback if not already terminated.
     * @param  the reference type
     * @param instance
     * @param terminalValue
     * @param onTerminate
     */
    public static  void terminate(AtomicReference instance, 
            U terminalValue, Consumer onTerminate) {
        U a = instance.get();
        if (a != terminalValue) {
            a = instance.getAndSet(terminalValue);
            if (a != terminalValue && a != null) {
                onTerminate.accept(a);
            }
        }
    }
    
    /**
     * Atomically swaps in the terminal value and returns the previous value.
     * @param  the instance type
     * @param  the reference type
     * @param updater
     * @param instance
     * @param terminalValue
     * @return the last value or the terminal value
     */
    public static  U terminate(AtomicReferenceFieldUpdater updater, 
            T instance, U terminalValue) {
        U a = updater.get(instance);
        if (a != terminalValue) {
            a = updater.getAndSet(instance, terminalValue);
        }
        return a;
    }
    
    /**
     * Atomically swaps in the terminal value and returns the previous value.
     * @param  the reference type
     * @param instance
     * @param terminalValue
     * @return the last value or the terminal value
     */
    public static  U terminate(AtomicReference instance, 
            U terminalValue) {
        U a = instance.get();
        if (a != terminalValue) {
            a = instance.getAndSet(terminalValue);
        }
        return a;
    }
    
    /**
     * Atomically replaces the contents of the instance and calls the callback with the old value
     * or calls the callback with the new value if the instance holds the terminal value.
     * @param  the instance type
     * @param  the reference type
     * @param updater
     * @param instance
     * @param newValue
     * @param terminalValue
     * @param onTerminate
     * @return false if the instance holds the terminal value
     * @see #update(AtomicReferenceFieldUpdater, Object, Object, Object, Consumer)
     */
    public static  boolean set(AtomicReferenceFieldUpdater updater, T instance, 
            U newValue, U terminalValue, Consumer onTerminate) {
        for (;;) {
            U a = updater.get(instance);
            if (a == terminalValue) {
                if (newValue != null) {
                    onTerminate.accept(newValue);
                }
                return false;
            }
            if (updater.compareAndSet(instance, a, newValue)) {
                if (a != null) {
                    onTerminate.accept(a);
                }
                return true;
            }
        }
    }

    /**
     * Atomically replaces the contents of the instance and calls the callback with the old value
     * or calls the callback with the new value if the instance holds the terminal value.
     * @param  the instance type
     * @param  the reference type
     * @param instance
     * @param newValue
     * @param terminalValue
     * @param onTerminate
     * @return false if the instance holds the terminal value
     * @see #update(AtomicReference, Object, Object, Consumer)
     */
    public static  boolean set(AtomicReference instance, 
            U newValue, U terminalValue, Consumer onTerminate) {
        for (;;) {
            U a = instance.get();
            if (a == terminalValue) {
                if (newValue != null) {
                    onTerminate.accept(newValue);
                }
                return false;
            }
            if (instance.compareAndSet(a, newValue)) {
                if (a != null) {
                    onTerminate.accept(a);
                }
                return true;
            }
        }
    }

    /**
     * Atomically replaces the content of the instance but calls the callback only if the instance holds
     * the terminal value.
     * @param  the instance type
     * @param  the reference type
     * @param updater
     * @param instance
     * @param newValue
     * @param terminalValue
     * @param onTerminate
     * @return false if the instance holds the terminal value
     * @see #set(AtomicReferenceFieldUpdater, Object, Object, Object, Consumer)
     */
    public static  boolean update(AtomicReferenceFieldUpdater updater, T instance, 
            U newValue, U terminalValue, Consumer onTerminate) {
        for (;;) {
            U a = updater.get(instance);
            if (a == terminalValue) {
                if (newValue != null) {
                    onTerminate.accept(newValue);
                }
                return false;
            }
            if (updater.compareAndSet(instance, a, newValue)) {
                return true;
            }
        }
    }

    /**
     * Atomically replaces the content of the instance but calls the callback only if the instance holds
     * the terminal value.
     * @param  the instance type
     * @param  the reference type
     * @param instance
     * @param newValue
     * @param terminalValue
     * @param onTerminate
     * @return false if the instance holds the terminal value
     * @see #set(AtomicReference, Object, Object, Consumer)
     */
    public static  boolean update(AtomicReference instance, 
            U newValue, U terminalValue, Consumer onTerminate) {
        for (;;) {
            U a = instance.get();
            if (a == terminalValue) {
                if (newValue != null) {
                    onTerminate.accept(newValue);
                }
                return false;
            }
            if (instance.compareAndSet(a, newValue)) {
                return true;
            }
        }
    }

    /**
     * Atomically replaces the array in instance with a new array that contains the newValue as well
     * or returns false if the instance holds the terminalValue.
     * @param  the instance type
     * @param  the reference type
     * @param updater
     * @param instance
     * @param newValue
     * @param terminalValue
     * @param arraySupplier
     * @return true if the add succeeded, false if the instance has terminated
     */
    public static  boolean add(AtomicReferenceFieldUpdater updater, T instance,
            U newValue, U[] terminalValue, IntFunction arraySupplier) {
        for (;;) {
            U[] a = updater.get(instance);
            if (a == terminalValue) {
                return false;
            }
            int n = a.length;
            U[] b = arraySupplier.apply(n + 1);
            System.arraycopy(a, 0, b, 0, n);
            b[n] = newValue;
            if (updater.compareAndSet(instance, a, b)) {
                return true;
            }
        }
    }
    
    /**
     * Atomically replaces the array in instance with a new array that contains the newValue as well
     * or returns false if the instance holds the terminalValue.
     * @param  the reference type
     * @param instance
     * @param newValue
     * @param terminalValue
     * @param arraySupplier
     * @return true if the add succeeded, false if the instance has terminated
     */
    public static  boolean add(AtomicReference instance,
            U newValue, U[] terminalValue, IntFunction arraySupplier) {
        for (;;) {
            U[] a = instance.get();
            if (a == terminalValue) {
                return false;
            }
            int n = a.length;
            U[] b = arraySupplier.apply(n + 1);
            System.arraycopy(a, 0, b, 0, n);
            b[n] = newValue;
            if (instance.compareAndSet(a, b)) {
                return true;
            }
        }
    }
    
    /**
     * Atomically replaces the array in instance with a new array that doesn't contain the
     * given value or returns false if the instance holds the terminal state or an empty array
     * or the value is not in the array.
     * @param  the instance type
     * @param  the reference type
     * @param updater
     * @param instance
     * @param value
     * @param terminalValue
     * @param zeroArray
     * @param arraySupplier
     * @return true if the remove succeeded, false if the instance has terminated
     */
    public static  boolean remove(AtomicReferenceFieldUpdater updater, T instance,
            U value, U[] terminalValue, U[] zeroArray, IntFunction arraySupplier) {
        for (;;) {
            U[] a = updater.get(instance);
            if (a == terminalValue) {
                return false;
            }
            int n = a.length;
            if (n == 0) {
                return false;
            }
            int j = -1;
            for (int i = 0; i < n; i++) {
                if (a[i] == value) {
                    j = i;
                    break;
                }
            }
            if (j < 0) {
                return false;
            }
            U[] b;
            if (n == 1) {
                b = zeroArray;
            } else {
                b = arraySupplier.apply(n - 1);
                System.arraycopy(a, 0, b, 0, j);
                System.arraycopy(a, j + 1, b, j, n - j - 1);
            }
            if (updater.compareAndSet(instance, a, b)) {
                return true;
            }
        }
    }
    
    /**
     * Atomically replaces the array in instance with a new array that doesn't contain the
     * given value or returns false if the instance holds the terminal state or an empty array
     * or the value is not in the array.
     * @param  the reference type
     * @param reference
     * @param value
     * @param terminalValue
     * @param zeroArray
     * @param arraySupplier
     * @return true if the remove succeeded, false if the instance has terminated
     */
    public static  boolean remove(AtomicReference reference,
            U value, U[] terminalValue, U[] zeroArray, IntFunction arraySupplier) {
        for (;;) {
            U[] a = reference.get();
            if (a == terminalValue) {
                return false;
            }
            int n = a.length;
            if (n == 0) {
                return false;
            }
            int j = -1;
            for (int i = 0; i < n; i++) {
                if (a[i] == value) {
                    j = i;
                    break;
                }
            }
            if (j < 0) {
                return false;
            }
            U[] b;
            if (n == 1) {
                b = zeroArray;
            } else {
                b = arraySupplier.apply(n - 1);
                System.arraycopy(a, 0, b, 0, j);
                System.arraycopy(a, j + 1, b, j, n - j - 1);
            }
            if (reference.compareAndSet(a, b)) {
                return true;
            }
        }
    }
}