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

com.hierynomus.protocol.commons.concurrent.Promise Maven / Gradle / Ivy

There is a newer version: 0.13.0
Show newest version
/*
 * Copyright (C)2016 - SMBJ Contributors
 *
 * 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 com.hierynomus.protocol.commons.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 promised 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 promise, e.g. checking if a value is delivered and if it is not then setting it, the * associated lock for the promise should be acquired while doing so. */ public class Promise { private final Logger logger = LoggerFactory.getLogger(getClass()); private final String name; private final ExceptionWrapper wrapper; private final ReentrantLock lock; private final Condition cond; private V val; private T pendingEx; /** * Creates this promise with given {@code name} and exception {@code wrapper}. Allocates a new {@link * java.util.concurrent.locks.Lock lock} object for this promise. * * @param name name of this promise * @param wrapper {@link ExceptionWrapper} that will be used for chaining exceptions */ public Promise(String name, ExceptionWrapper wrapper) { this(name, wrapper, null); } /** * Creates this promise with given {@code name}, exception {@code wrapper}, and associated {@code lock}. * * @param name name of this promise * @param wrapper {@link ExceptionWrapper} that will be used for chaining exceptions * @param lock lock to use */ public Promise(String name, ExceptionWrapper wrapper, ReentrantLock lock) { this.name = name; this.wrapper = wrapper; this.lock = lock == null ? new ReentrantLock() : lock; this.cond = this.lock.newCondition(); } /** * Set this promise's value to {@code val}. Any waiters will be delivered this value. * * @param val the value */ public void deliver(V val) { lock.lock(); try { logger.debug("Setting << {} >> to `{}`", name, val); this.val = val; cond.signalAll(); } finally { lock.unlock(); } } /** * Queues error that will be thrown in any waiting thread or any thread that attempts to wait on this promise * hereafter. * * @param e the error */ public void deliverError(Throwable e) { lock.lock(); try { pendingEx = wrapper.wrap(e); cond.signalAll(); } finally { lock.unlock(); } } /** * Clears this promise by setting its value and queued exception to {@code null}. */ public void clear() { lock.lock(); try { pendingEx = null; deliver(null); } finally { lock.unlock(); } } /** * Wait indefinitely for this promise's value to be deliver. * * @return the value * @throws T in case another thread informs the promise of an error meanwhile */ public V retrieve() throws T { return tryRetrieve(0, TimeUnit.SECONDS); } /** * Wait for {@code timeout} duration for this promise's value to be deliver. * * @param timeout the timeout * @param unit time unit for the timeout * @return the value * @throws T in case another thread informs the promise of an error meanwhile, or the timeout expires */ public V retrieve(long timeout, TimeUnit unit) throws T { final V value = tryRetrieve(timeout, unit); if (value == null) { throw wrapper.wrap(new TimeoutException("Timeout expired")); } else { return value; } } /** * Wait for {@code timeout} duration for this promise's value to be deliver. *

* If the value is not deliver by the time the timeout expires, returns {@code null}. * * @param timeout the timeout * @param unit time unit for the timeout * @return the value or {@code null} * @throws T in case another thread informs the promise of an error meanwhile */ public V tryRetrieve(long timeout, TimeUnit unit) throws T { lock.lock(); try { if (pendingEx != null) { throw pendingEx; } if (val != null) { return val; } logger.debug("Awaiting << {} >>", name); if (timeout == 0) { while (val == null && pendingEx == null) { cond.await(); } } else { if (!cond.await(timeout, unit)) { return null; } } if (pendingEx != null) { logger.error("<< {} >> woke to: {}", name, pendingEx); throw pendingEx; } return val; } catch (InterruptedException ie) { throw wrapper.wrap(ie); } finally { lock.unlock(); } } /** * @return whether this promise has a value delivered, and no error waiting to pop. */ public boolean isDelivered() { lock.lock(); try { return pendingEx == null && val != null; } finally { lock.unlock(); } } /** * @return whether this promise has been delivered an error. */ public boolean inError() { lock.lock(); try { return pendingEx != null; } finally { lock.unlock(); } } /** * @return whether this promise was fulfilled with either a value or an error. */ public boolean isFulfilled() { lock.lock(); try { return pendingEx != null || val != null; } finally { lock.unlock(); } } /** * @return whether this promise has threads waiting on it. */ public boolean hasWaiters() { lock.lock(); try { return lock.hasWaiters(cond); } finally { lock.unlock(); } } /** * Acquire the lock associated with this promise. */ public void lock() { lock.lock(); } /** * Release the lock associated with this promise. */ public void unlock() { lock.unlock(); } @Override public String toString() { return name; } public AFuture future() { return new PromiseBackedFuture<>(this); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy