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

net.schmizz.concurrent.Future Maven / Gradle / Ivy

There is a newer version: 0.10.0
Show newest version
/*
 * Copyright 2010 Shikhar Bhushan
 *
 * 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 net.schmizz.concurrent;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Represents future data of the parameterized type {@code V} and allows waiting on it. An exception may also be
 * delivered to a waiter, and will be of the parameterized type {@code T}.
 * 

* For atomic operations on a future, e.g. checking if a value is set and if it is not then setting it - in other words, * Compare-And-Set type operations - the associated lock for the future should be acquired while doing so. */ public class Future { private final Logger log = LoggerFactory.getLogger(getClass()); private final String name; private final ExceptionChainer chainer; private final ReentrantLock lock; private final Condition cond; private V val; private T pendingEx; /** * Creates this future with given {@code name} and exception {@code chainer}. Allocates a new {@link * java.util.concurrent.locks.Lock lock} object for this future. * * @param name name of this future * @param chainer {@link ExceptionChainer} that will be used for chaining exceptions */ public Future(String name, ExceptionChainer chainer) { this(name, chainer, null); } /** * Creates this future with given {@code name}, exception {@code chainer}, and associated {@code lock}. * * @param name name of this future * @param chainer {@link ExceptionChainer} that will be used for chaining exceptions * @param lock lock to use */ public Future(String name, ExceptionChainer chainer, ReentrantLock lock) { this.name = name; this.chainer = chainer; this.lock = lock == null ? new ReentrantLock() : lock; this.cond = this.lock.newCondition(); } /** * Set this future's value to {@code val}. Any waiters will be delivered this value. * * @param val the value */ public void set(V val) { lock(); try { log.debug("Setting <<{}>> to `{}`", name, val); this.val = val; cond.signalAll(); } finally { unlock(); } } /** * Queues error that will be thrown in any waiting thread or any thread that attempts to wait on this future * hereafter. * * @param e the error */ public void error(Throwable e) { lock(); try { pendingEx = chainer.chain(e); cond.signalAll(); } finally { unlock(); } } /** Clears this future by setting its value and queued exception to {@code null}. */ public void clear() { lock(); try { pendingEx = null; set(null); } finally { unlock(); } } /** * Wait indefinitely for this future's value to be set. * * @return the value * * @throws T in case another thread informs the future of an error meanwhile */ public V get() throws T { return get(0, TimeUnit.SECONDS); } /** * Wait for {@code timeout} duration for this future's value to be set. * * @param timeout the timeout * @param unit time unit for the timeout * * @return the value * * @throws T in case another thread informs the future of an error meanwhile, or the timeout expires */ public V get(long timeout, TimeUnit unit) throws T { lock(); try { if (pendingEx != null) throw pendingEx; if (val != null) return val; log.debug("Awaiting <<{}>>", name); while (val == null && pendingEx == null) if (timeout == 0) cond.await(); else if (!cond.await(timeout, unit)) throw chainer.chain(new TimeoutException("Timeout expired")); if (pendingEx != null) { log.error("<<{}>> woke to: {}", name, pendingEx.toString()); throw pendingEx; } return val; } catch (InterruptedException ie) { throw chainer.chain(ie); } finally { unlock(); } } /** @return whether this future has a value set, and no error waiting to pop. */ public boolean isSet() { lock(); try { return pendingEx == null && val != null; } finally { unlock(); } } /** @return whether this future currently has an error set. */ public boolean hasError() { lock(); try { return pendingEx != null; } finally { unlock(); } } /** @return whether this future has threads waiting on it. */ public boolean hasWaiters() { lock(); try { return lock.hasWaiters(cond); } finally { unlock(); } } /** * Lock using the associated lock. Use as part of a {@code try-finally} construct in conjunction with {@link * #unlock()}. */ public void lock() { lock.lock(); } /** * Unlock using the associated lock. Use as part of a {@code try-finally} construct in conjunction with {@link * #lock()}. */ public void unlock() { lock.unlock(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy