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

io.atlassian.util.concurrent.BlockingReference Maven / Gradle / Ivy

Go to download

This project contains utility classes that are used by various products and projects inside Atlassian and may have some utility to the world at large.

There is a newer version: 4.0.1
Show newest version
/**
 * Copyright 2008 Atlassian Pty Ltd 
 * 
 * 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 io.atlassian.util.concurrent;

import net.jcip.annotations.ThreadSafe;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import static java.util.Objects.requireNonNull;

/**
 * A Reference with queue semantics where the current reference may be retrieved
 * or taken instead, and if there is no current element then it will be block
 * until the reference becomes available. This is somewhat analogous to a single
 * element {@link java.util.concurrent.BlockingQueue}.
 * 

* Note: this class does not support null elements being {@link #set(Object)} * and will throw an exception. If the internal reference is null, then calls to * {@link #take()} or {@link #take(long, TimeUnit)} will block. *

* This class is most suited to {@link #newSRSW() SRSW} or {@link #newMRSW() * MRSW} usage. Multiple writers will overwrite each other's elements and the * chosen value will be arbitrary in the absence of any external consensus. If * multiple readers are waiting to {@link #take()} a value, one reader will be * arbitrarily chosen (similar to * {@link java.util.concurrent.locks.Condition#signal()}). Multiple readers can * however {@link #get()} the current value if it is not null, but they may see * the current value more than once. If multiple readers attempt to * {@link #get()} a value from the SRSW reference and it is not yet present then * only one waiting thread may be notified, please use the MRSW version for this * case. *

* This implementation has been optimized for SRSW performance with * {@link #set(Object)}/{@link #take()} pairs. *

* This class is explicit in that it handles take/get separately. * * @param the value type * @see BlockingQueue */ @ThreadSafe public class BlockingReference { // // static factory methods // /** * Create a BlockingReference best suited to single-reader/single-writer * situations. In a MRSW case this instance may get missed signals if multiple * reader threads are all waiting on the value. * * @return a {@link io.atlassian.util.concurrent.BlockingReference} object. */ public static BlockingReference newSRSW() { return newSRSW(null); } /** * Create a BlockingReference best suited to single-reader/single-writer * situations. In a MRSW case this instance may get missed signals if multiple * reader threads are all waiting on the value. * * @param initialValue the initial value * @param a V value to reference. * @return a {@link io.atlassian.util.concurrent.BlockingReference}. */ public static BlockingReference newSRSW(final V initialValue) { return new BlockingReference(new BooleanLatch(), initialValue); } /** * Create a BlockingReference best suited to multi-reader/single-writer * situations. In a SRSW case this instance may not perform quite as well. * * @return a {@link io.atlassian.util.concurrent.BlockingReference}. */ public static BlockingReference newMRSW() { return newMRSW(null); } /** * Create a BlockingReference best suited to multi-reader/single-writer * situations. In a SRSW case this instance may not perform quite as well. * * @param initialValue the initial value * @param a V value to reference. * @return a {@link io.atlassian.util.concurrent.BlockingReference}. */ public static BlockingReference newMRSW(final V initialValue) { return new BlockingReference(new PhasedLatch() { /* * Workaround for the fact that take() always calls await. Calling await() * on a phased latch by default waits on the next phase (after the * current one). We need to make sure we await on the previous phase * instead so we remember the previous phase. */ private final AtomicInteger currentPhase = new AtomicInteger(super.getPhase()); @Override public synchronized int getPhase() { try { return currentPhase.get(); } finally { currentPhase.set(super.getPhase()); } } }, initialValue); } // // instance vars // private final AtomicReference ref = new AtomicReference(); private final ReusableLatch latch; // // ctors // BlockingReference(final ReusableLatch latch, final V initialValue) { this.latch = latch; internalSet(initialValue); } // /CLOVER:OFF /** * Creates a new SRSW BlockingReference. * * @deprecated use {@link #newSRSW()} instead. */ @Deprecated public BlockingReference() { this(new BooleanLatch(), null); } /** * Creates a new SRSW BlockingReference. * * @deprecated use {@link #newSRSW()} instead. * @param value a V object. */ @Deprecated public BlockingReference(@NotNull final V value) { this(new BooleanLatch(), value); } // /CLOVER:ON // // methods // /** * Takes the current element if it is not null and replaces it with null. If * the current element is null then wait until it becomes non-null. *

* If the current thread: *

    *
  • has its interrupted status set on entry to this method; or *
  • is {@link java.lang.Thread#interrupt() interrupted} while waiting, *
* then {@link java.lang.InterruptedException} is thrown and the current * thread's interrupted status is cleared. * * @return the current element * @throws java.lang.InterruptedException if the current thread is interrupted * while waiting */ @NotNull public final V take() throws InterruptedException { V result = null; while (result == null) { latch.await(); result = ref.getAndSet(null); } return result; } /** * Takes the current element if it is not null and replaces it with null. If * the current element is null then wait until it becomes non-null. The method * will throw a {@link java.util.concurrent.TimeoutException} if the timeout * is reached before an element becomes available. *

* If the current thread: *

    *
  • has its interrupted status set on entry to this method; or *
  • is {@link java.lang.Thread#interrupt() interrupted} while waiting, *
* then {@link java.lang.InterruptedException} is thrown and the current * thread's interrupted status is cleared. * * @param time the maximum time to wait * @param unit the time unit of the {@code timeout} argument * @return the current element * @throws java.lang.InterruptedException if the current thread is interrupted * while waiting * @throws java.util.concurrent.TimeoutException if the timeout is reached * without another thread having called {@link #set(Object)}. */ @NotNull public final V take(final long time, final TimeUnit unit) throws TimeoutException, InterruptedException { final Timeout timeout = Timeout.getNanosTimeout(time, unit); V result = null; while (result == null) { timeout.await(latch); result = ref.getAndSet(null); } return result; } /** * Gets the current element if it is not null, if it is null then this method * blocks and waits until it is not null. Unlike {@link #take()} it does not * reset the current element to null. *

* If the current thread: *

    *
  • has its interrupted status set on entry to this method; or *
  • is {@link java.lang.Thread#interrupt() interrupted} while waiting, *
* then {@link java.lang.InterruptedException} is thrown and the current * thread's interrupted status is cleared. * * @return the current element * @throws java.lang.InterruptedException if the current thread is interrupted * while waiting */ @NotNull public final V get() throws InterruptedException { V result = ref.get(); while (result == null) { latch.await(); result = ref.get(); } return result; } /** * Gets the current element if it is not null, if it is null then this method * blocks and waits until it is not null. Unlike {@link #take()} it does not * reset the current element to null. *

* If the current thread: *

    *
  • has its interrupted status set on entry to this method; or *
  • is {@link java.lang.Thread#interrupt() interrupted} while waiting, *
* then {@link java.lang.InterruptedException} is thrown and the current * thread's interrupted status is cleared. * * @return the current element * @throws java.util.concurrent.TimeoutException if the timeout is reached * without another thread having called {@link #set(Object)}. * @throws java.lang.InterruptedException if the current thread is interrupted * while waiting * @param time a long. * @param unit a {@link java.util.concurrent.TimeUnit}. */ @NotNull public final V get(final long time, @NotNull final TimeUnit unit) throws TimeoutException, InterruptedException { final Timeout timeout = Timeout.getNanosTimeout(time, unit); V result = ref.get(); while (result == null) { timeout.await(latch); result = ref.get(); } return result; } /** * Set the value of this reference. This method is lock-free. A thread waiting * in {@link #take()} or {@link #take(long, TimeUnit)} will be released and * given this value. * * @param value the new value. */ public final void set(@NotNull final V value) { requireNonNull(value, "value"); internalSet(value); } /** * Whether or not the current value is null or not. If this is true and the * next call to {@link #take()} or {@link #take(long, TimeUnit)} will not * block. * * @return true if the current reference is null. */ public final boolean isEmpty() { return peek() == null; } /** * Return the current value whether is null or not. If this is true and the * next call to {@link #take()} or {@link #take(long, TimeUnit)} will not * block. * * @return the current reference or null if there is none. */ @Nullable public final V peek() { return ref.get(); } /** * Clear the current reference. */ public final void clear() { internalSet(null); } // // private // /** * Set the value * * @param value maybe null */ private final void internalSet(@Nullable final V value) { ref.set(value); latch.release(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy