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

no.digipost.concurrent.OneTimeToggle Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) Posten Norge AS
 *
 * 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 no.digipost.concurrent;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;

/**
 * A kind of specialized variant of {@link AtomicBoolean} which
 * can only be toggled once. There is no way to "untoggle" it.
 * Successive attempts to toggle it with {@link #now()} will have no effect,
 * but it is possible to enforce a fail-fast toggle only once policy using
 * {@link #nowOrIfAlreadyThenThrow(Supplier)} which will throw an exception
 * if the toggle happens several times.
 *
 * @see TargetState
 */
public final class OneTimeToggle implements TargetState {

    private final AtomicBoolean toggled = new AtomicBoolean(false);

    /**
     * Toggle it! This will make {@link #yet()} return true.
     */
    public void now() {
        toggled.set(true);
    }

    /**
     * Toggle it, or throw exception if the toggle has already been done, for instance
     * by another thread.
     *
     * @param  the exception type that this method may throw.
     * @param exceptionSupplier supply exception to throw if it was already toggled.
     */
    public  void nowOrIfAlreadyThenThrow(Supplier exceptionSupplier) throws E {
        E exceptionIfAlreadyToggled = toggleAndExecuteIfPreviouslyWas(true, exceptionSupplier);
        if (exceptionIfAlreadyToggled != null) {
            throw exceptionIfAlreadyToggled;
        }
    }

    @Override
    public boolean yet() {
        return toggled.get();
    }

    /**
     * Toggle it, to make {@link #yet()} return true, and if
     * a toggle was actually done (i.e. the {@code OneTimeToggle} has not already been toggled),
     * execute the provided action. This can be used to facilitate at-most-once
     * execution semantics, even in a multi-threaded context.
     *
     * @param action the action to run if the toggle was actually switched.
     */
    public void nowAndUnlessAlreadyToggled(Runnable action) {
        toggleAndExecuteIfPreviouslyWas(false, () -> {
            action.run();
            return null;
        });
    }

    /**
     * Toggle it, to make {@link #yet()} return true, and if
     * a toggle was actually done (i.e. the {@code OneTimeToggle} has not already been toggled),
     * use the given supplier to resolve a value. This can be used to facilitate at-most-once
     * execution semantics, even in a multi-threaded context.
     * 

* The given supplier may resolve a {@code null} value, and in that case the return * value from this method will be indistinguishable from an invocation on an already toggled * {@code OneTimeToggle}. * * @param the type of the value resolved by the given supplier. * @param supplier the supplier to resolve a value if the toggle was actually switched. * * @return An {@link Optional} containing the value from the given supplier, if it was * executed, otherwise always {@link Optional#empty()}. */ public Optional nowAndUnlessAlreadyToggled(Supplier supplier) { return Optional.ofNullable(toggleAndExecuteIfPreviouslyWas(false, supplier)); } private T toggleAndExecuteIfPreviouslyWas(boolean existing, Supplier supplier) { if (toggled.getAndSet(true) == existing) { return supplier.get(); } else { return null; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy