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

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

/**
 * 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.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.function.Supplier;

/**
 * Lazily loaded reference that is not constructed until required. This class is
 * used to maintain a reference to an object that is expensive to create and
 * must be constructed once and once only. This reference behaves as though the
 * final keyword has been used (you cannot reset it once it has
 * been constructed). Object creation is guaranteed to be thread-safe and the
 * first thread that calls {@link #get()} will be the one that creates it.
 * 

* Usage: clients need to implement the {@link #create()} method to return the * object this reference will hold. *

* For instance: * *

 * final LazyReference<MyObject> ref = new LazyReference() {
 *   protected MyObject create() throws Exception {
 *     // Do expensive object construction here
 *     return new MyObject();
 *   }
 * };
 * 
* * Then call {@link #get()} to get a reference to the referenced object: * *
 * MyObject myLazyLoadedObject = ref.get()
 * 
* * NOTE: Interruption policy is that if you want to be cancellable while waiting * for another thread to create the value, instead of calling {@link #get()} * call {@link #getInterruptibly()}. However, If your {@link #create()} method * is interrupted and throws an {@link java.lang.InterruptedException}, it is * treated as an application exception and will be the causal exception inside * the runtime * {@link io.atlassian.util.concurrent.LazyReference.InitializationException} * that {@link #get()} or {@link #getInterruptibly()} throws and your * {@link #create()} will not be called again. *

* This class is NOT {@link java.io.Serializable}. *

* Implementation note. This class extends {@link java.lang.ref.WeakReference} * as {@link java.lang.ref.Reference} does not have a public constructor. * WeakReference is preferable as it does not have any members and therefore * doesn't increase the memory footprint. As we never pass a referent through to * the super-class and override {@link #get()}, the garbage collection semantics * of WeakReference are irrelevant. The referenced object will not become * eligible for GC unless the object holding the reference to this object is * collectible. * * @param the type of the contained element. */ @ThreadSafe public abstract class LazyReference extends WeakReference implements Supplier { private final Sync sync = new Sync(); /** *

* Constructor for LazyReference. *

*/ public LazyReference() { super(null); } /** * The object factory method, guaranteed to be called once and only once. * * @return the object that {@link #get()} and {@link #getInterruptibly()} will * return. * @throws java.lang.Exception if anything goes wrong, rethrown as an * InitializationException from {@link #get()} and {@link #getInterruptibly()} */ protected abstract T create() throws Exception; /** * {@inheritDoc} * * Get the lazily loaded reference in a non-cancellable manner. If your * create() method throws an Exception calls to * get() will throw an InitializationException which wraps the * previously thrown exception. */ @Override public final T get() { boolean interrupted = false; try { while (true) { try { return getInterruptibly(); } catch (final InterruptedException ignore) { // ignore and try again interrupted = true; } } } finally { if (interrupted) { Thread.currentThread().interrupt(); } } } /** * Get the lazily loaded reference in a cancellable manner. If your * create() method throws an Exception, calls to * get() will throw a RuntimeException which wraps the previously * thrown exception. * * @return the object that {@link #create()} created. * @throws io.atlassian.util.concurrent.LazyReference.InitializationException * if the {@link #create()} method throws an exception. The * {@link io.atlassian.util.concurrent.LazyReference.InitializationException#getCause()} * will contain the exception thrown by the {@link #create()} method * @throws java.lang.InterruptedException If the calling thread is Interrupted * while waiting for another thread to create the value (if the creating * thread is interrupted while blocking on something, the * {@link java.lang.InterruptedException} will be thrown as the causal * exception of the * {@link io.atlassian.util.concurrent.LazyReference.InitializationException} * to everybody calling this method). */ public final T getInterruptibly() throws InterruptedException { if (!sync.isDone()) { sync.run(); } try { return sync.get(); } catch (final ExecutionException e) { throw new InitializationException(e); } } /** * Has the {@link #create()} reference been initialized. * * @return true if the task is complete */ public final boolean isInitialized() { return sync.isDone(); } /** * Cancel the initializing operation if it has not already run. Will try and * interrupt if it is currently running. */ public final void cancel() { sync.cancel(true); } /** * If the factory {@link LazyReference#create()} method threw an exception, * this wraps it. */ public static class InitializationException extends RuntimeException { private static final long serialVersionUID = 3638376010285456759L; InitializationException(final ExecutionException e) { super((e.getCause() != null) ? e.getCause() : e); } } static final class State { static final int INIT = 0; static final int RUNNING = 1; static final int RAN = 2; static final int CANCELLED = 4; } /** * Synchronization control for LazyReference. Note that this must be a * non-static inner class in order to invoke the protected create * method. Taken from FutureTask AQS implementation and pruned to be as * compact as possible. * * Uses AQS sync state to represent run status. */ private final class Sync extends AbstractQueuedSynchronizer { static final int IGNORED = 0; /** * only here to shut up the compiler warnings, the outer class is NOT * serializable */ private static final long serialVersionUID = -1645412544240373524L; /** The result to return from get() */ private T result; /** The exception to throw from get() */ private Throwable exception; /** * The thread running task. When nulled after set/cancel, this indicates * that the results are accessible. Must be volatile, to ensure visibility * upon completion. */ private volatile Thread runner; private boolean ranOrCancelled(final int state) { return (state & (State.RAN | State.CANCELLED)) != State.INIT; } /** * Implements AQS base acquire to succeed if ran or cancelled */ @Override protected int tryAcquireShared(final int ignore) { return isDone() ? 1 : -1; } /** * Implements AQS base release to always signal after setting final done * status by nulling runner thread. */ @Override protected boolean tryReleaseShared(final int ignore) { runner = null; return true; } boolean isDone() { return ranOrCancelled(getState()) && (runner == null); } T get() throws InterruptedException, ExecutionException { acquireSharedInterruptibly(IGNORED); if (getState() == State.CANCELLED) { throw new CancellationException(); } if (exception != null) { throw new ExecutionException(exception); } return result; } void set(final T v) { for (;;) { final int s = getState(); if (s == State.RAN) { return; } if (s == State.CANCELLED) { // aggressively release to set runner to null, // in case we are racing with a cancel request // that will try to interrupt runner releaseShared(IGNORED); return; } if (compareAndSetState(s, State.RAN)) { result = v; releaseShared(IGNORED); return; } } } void setException(final Throwable t) { for (;;) { final int s = getState(); if (s == State.RAN) { return; } if (s == State.CANCELLED) { // aggressively release to set runner to null, // in case we are racing with a cancel request // that will try to interrupt runner releaseShared(0); return; } if (compareAndSetState(s, State.RAN)) { exception = t; result = null; releaseShared(0); return; } } } void cancel(final boolean mayInterruptIfRunning) { for (;;) { final int s = getState(); if (ranOrCancelled(s)) { return; } if (compareAndSetState(s, State.CANCELLED)) { break; } } if (mayInterruptIfRunning) { final Thread r = runner; if (r != null) { r.interrupt(); } } releaseShared(IGNORED); } void run() { if ((getState() != State.INIT) || !compareAndSetState(State.INIT, State.RUNNING)) { if (runner == Thread.currentThread()) { throw new IllegalMonitorStateException("Not reentrant!"); } return; } try { runner = Thread.currentThread(); set(create()); } catch (final Throwable ex) { setException(ex); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy