
io.machinecode.then.core.DeferredImpl Maven / Gradle / Ivy
package io.machinecode.then.core;
import io.machinecode.then.api.Deferred;
import io.machinecode.then.api.FailureException;
import io.machinecode.then.api.ListenerException;
import io.machinecode.then.api.OnCancel;
import io.machinecode.then.api.OnComplete;
import io.machinecode.then.api.OnProgress;
import io.machinecode.then.api.OnReject;
import io.machinecode.then.api.OnResolve;
import io.machinecode.then.api.Promise;
import org.jboss.logging.Logger;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
/**
* A thread safe {@link Deferred} implementation that silently drops multiple calls to terminal methods.
*
* It will not report progress to a listener if the listener is added after the call to {@link #progress(Object)}
*
* @author Brent Douglas
* @since 1.0
*/
public class DeferredImpl implements Deferred {
private static final Logger log = Logger.getLogger(DeferredImpl.class);
protected static final byte ON_RESOLVE = 100;
protected static final byte ON_REJECT = 101;
protected static final byte ON_CANCEL = 102;
protected static final byte ON_COMPLETE = 103;
protected static final byte ON_PROGRESS = 104;
protected static final byte ON_GET = 105;
protected volatile byte state = PENDING;
protected T value;
protected F failure;
private final ReentrantLock _lock = new ReentrantLock();
private final Condition _condition = _lock.newCondition();
private Event[] _events;
private int _pos = 0;
private static class Event {
final byte event;
final Object value;
private Event(final byte event, final Object value) {
this.event = event;
this.value = value;
}
}
protected void _addEvent(final byte event, final Object that) {
if (_pos >= _events.length) {
final Event[] events = new Event[_events.length + 1];
System.arraycopy(_events, 0, events, 0, _pos);
_events = events;
}
_events[_pos++] = new Event(event, that);
}
@SuppressWarnings("unchecked")
protected List _getEvents(final byte event) {
final List list = new LinkedList<>();
for (int i = 0; i < _pos; ++i) {
if (event == _events[i].event) {
list.add((X) _events[i].value);
}
}
return list;
}
public DeferredImpl() {
this(2);
}
public DeferredImpl(final int hint) {
this._events = new Event[hint];
}
protected void _lock() {
while (!_lock.tryLock()) {}
}
protected void _unlock() {
_lock.unlock();
}
protected void _await() throws InterruptedException {
_condition.await();
}
protected boolean _await(final long timeout, final TimeUnit unit) throws InterruptedException {
return _condition.await(timeout, unit);
}
protected void _signalAll() {
_condition.signalAll();
}
protected boolean setValue(final T value) {
switch (this.state) {
case RESOLVED:
case REJECTED:
case CANCELLED:
return true;
}
this.value = value;
this.state = RESOLVED;
return false;
}
protected boolean setFailure(final F failure) {
switch (this.state) {
case RESOLVED:
case REJECTED:
case CANCELLED:
return true;
}
this.failure = failure;
this.state = REJECTED;
return false;
}
protected boolean setCancelled() {
switch (this.state) {
case CANCELLED:
case REJECTED:
case RESOLVED:
return true;
}
this.state = CANCELLED;
return false;
}
@Override
public Promise promise() {
return this;
}
@Override
public void resolve(final T value) throws ListenerException {
log().tracef(getResolveLogMessage(), value);
final List> onResolves;
final List onCompletes;
final int state;
_lock();
try {
if (setValue(value)) {
return;
}
onResolves = this._getEvents(ON_RESOLVE);
onCompletes = this._getEvents(ON_COMPLETE);
state = this.state;
} finally {
_unlock();
}
ListenerException exception = null;
for (final OnResolve on : onResolves) {
try {
on.resolve(this.value);
} catch (final Throwable e) {
if (exception == null) {
exception = new ListenerException(Messages.format("THEN-000300.promise.on.resolve.exception"), e);
} else {
exception.addSuppressed(e);
}
}
}
for (final OnComplete on : onCompletes) {
try {
on.complete(state);
} catch (final Throwable e) {
if (exception == null) {
exception = new ListenerException(Messages.format("THEN-000303.promise.on.complete.exception"), e);
} else {
exception.addSuppressed(e);
}
}
}
_lock();
try {
_signalAll();
} finally {
_unlock();
}
if (exception != null) {
throw exception;
}
}
@Override
public void reject(final F failure) {
log().tracef(getRejectLogMessage(), failure);
final List> onRejects;
final List onCompletes;
final int state;
_lock();
try {
if (setFailure(failure)) {
return;
}
onRejects = this._getEvents(ON_REJECT);
onCompletes = this._getEvents(ON_COMPLETE);
state = this.state;
} finally {
_unlock();
}
ListenerException exception = null;
for (final OnReject then : onRejects) {
try {
then.reject(this.failure);
} catch (final Throwable e) {
if (exception == null) {
exception = new ListenerException(Messages.format("THEN-000301.promise.on.reject.exception"), e);
} else {
exception.addSuppressed(e);
}
}
}
for (final OnComplete on : onCompletes) {
try {
on.complete(state);
} catch (final Throwable e) {
if (exception == null) {
exception = new ListenerException(Messages.format("THEN-000303.promise.on.complete.exception"), e);
} else {
exception.addSuppressed(e);
}
}
}
_lock();
try {
_signalAll();
} finally {
_unlock();
}
if (exception != null) {
throw exception;
}
}
@Override
public void progress(final P that) {
log().tracef(getProgressLogMessage(), that);
final List> onProgresses;
_lock();
try {
onProgresses = this._getEvents(ON_PROGRESS);
} finally {
_unlock();
}
ListenerException exception = null;
for (final OnProgress then : onProgresses) {
try {
then.progress(that);
} catch (final Throwable e) {
if (exception == null) {
exception = new ListenerException(Messages.format("THEN-000304.promise.on.progress.exception"), e);
} else {
exception.addSuppressed(e);
}
}
}
}
@Override
public boolean cancel(final boolean mayInterruptIfRunning) throws ListenerException {
log().tracef(getCancelLogMessage());
final List onCancels;
final List onCompletes;
final int state;
_lock();
try {
if (setCancelled()) {
return isCancelled();
}
onCancels = this._getEvents(ON_CANCEL);
onCompletes = this._getEvents(ON_COMPLETE);
state = this.state;
} finally {
_unlock();
}
ListenerException exception = null;
for (final OnCancel then : onCancels) {
try {
then.cancel(mayInterruptIfRunning);
} catch (final Throwable e) {
if (exception == null) {
exception = new ListenerException(Messages.format("THEN-000302.promise.on.cancel.exception"), e);
} else {
exception.addSuppressed(e);
}
}
}
for (final OnComplete on : onCompletes) {
try {
on.complete(state);
} catch (final Throwable e) {
if (exception == null) {
exception = new ListenerException(Messages.format("THEN-000303.promise.on.complete.exception"), e);
} else {
exception.addSuppressed(e);
}
}
}
_lock();
try {
_signalAll();
} finally {
_unlock();
}
if (exception != null) {
throw exception;
}
return true;
}
@Override
public boolean isDone() {
switch (this.state) {
case RESOLVED:
case REJECTED:
case CANCELLED:
return true;
default:
return false;
}
}
@Override
public boolean isCancelled() {
return this.state == CANCELLED;
}
@Override
public boolean isRejected() {
return this.state == REJECTED;
}
@Override
public boolean isResolved() {
return this.state == RESOLVED;
}
@Override
public Deferred onResolve(final OnResolve then) {
if (then == null) {
throw new IllegalArgumentException(Messages.format("THEN-000400.promise.argument.required", "onResolve"));
}
boolean run = false;
_lock();
try {
switch (this.state) {
case REJECTED:
case CANCELLED:
return this;
case RESOLVED:
run = true;
case PENDING:
default:
_addEvent(ON_RESOLVE, then);
}
} finally {
_unlock();
}
if (run) {
then.resolve(this.value);
}
return this;
}
@Override
public Deferred onReject(final OnReject then) {
if (then == null) {
throw new IllegalArgumentException(Messages.format("THEN-000400.promise.argument.required", "onReject"));
}
boolean run = false;
_lock();
try {
switch (this.state) {
case RESOLVED:
case CANCELLED:
return this;
case REJECTED:
run = true;
case PENDING:
default:
_addEvent(ON_REJECT, then);
}
} finally {
_unlock();
}
if (run) {
then.reject(this.failure);
}
return this;
}
@Override
public Deferred onCancel(final OnCancel then) {
if (then == null) {
throw new IllegalArgumentException(Messages.format("THEN-000400.promise.argument.required", "onCancel"));
}
boolean run = false;
_lock();
try {
switch (this.state) {
case RESOLVED:
case REJECTED:
return this;
case CANCELLED:
run = true;
case PENDING:
default:
_addEvent(ON_CANCEL, then);
}
} finally {
_unlock();
}
if (run) {
then.cancel(true);
}
return this;
}
@Override
public Deferred onComplete(final OnComplete then) {
if (then == null) {
throw new IllegalArgumentException(Messages.format("THEN-000400.promise.argument.required", "onComplete"));
}
boolean run = false;
_lock();
try {
switch (this.state) {
case REJECTED:
case CANCELLED:
case RESOLVED:
run = true;
case PENDING:
default:
_addEvent(ON_COMPLETE, then);
}
} finally {
_unlock();
}
if (run) {
then.complete(this.state);
}
return this;
}
@Override
public Deferred onProgress(final OnProgress then) {
if (then == null) {
throw new IllegalArgumentException(Messages.format("THEN-000400.promise.argument.required", "onProgress"));
}
_lock();
try {
_addEvent(ON_PROGRESS, then);
} finally {
_unlock();
}
return this;
}
@Override
public Deferred onGet(final Future> then) {
if (then == null) {
throw new IllegalArgumentException(Messages.format("THEN-000400.promise.argument.required", "onGet"));
}
_lock();
try {
_addEvent(ON_GET, then);
} finally {
_unlock();
}
return this;
}
@Override
public T get() throws InterruptedException, ExecutionException {
return _get();
}
protected T _get() throws InterruptedException, ExecutionException {
if (Thread.interrupted()) {
throw new InterruptedException(getInterruptedExceptionMessage());
}
final List> onGets;
final int state;
_lock();
try {
loop: do {
switch (this.state) {
case CANCELLED:
case REJECTED:
case RESOLVED:
break loop;
}
_await();
_signalAll();
} while (true);
state = this.state;
onGets = this._getEvents(ON_GET);
} finally {
_unlock();
}
switch (state) {
case CANCELLED:
throw _onGet(onGets, new CancellationException(Messages.format("THEN-000202.promise.cancelled")));
case REJECTED:
final String msg = Messages.format("THEN-000201.promise.rejected");
throw _onGet(onGets, new ExecutionException(msg, _getFailureCause(msg)));
case RESOLVED:
_onGet(onGets, null);
return value;
default:
throw new IllegalStateException(Messages.format("THEN-000200.promise.illegal.state", _stateToString(state)));
}
}
@Override
public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return _get(timeout, unit);
}
protected T _get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
if (Thread.interrupted()) {
throw new InterruptedException(getInterruptedExceptionMessage());
}
final List> onGets;
final long end = System.currentTimeMillis() + unit.toMillis(timeout);
final byte state;
_lock();
try {
loop: do {
switch (this.state) {
case CANCELLED:
case REJECTED:
case RESOLVED:
break loop;
}
if (!_await(_tryTimeout(end), MILLISECONDS)) {
throw new TimeoutException(getTimeoutExceptionMessage());
}
_signalAll();
} while (true);
state = this.state;
onGets = this._getEvents(ON_GET);
} finally {
_unlock();
}
switch (state) {
case CANCELLED:
throw _onTimedGet(onGets, end, new CancellationException(Messages.format("THEN-000202.promise.cancelled")));
case REJECTED:
final String msg = Messages.format("THEN-000201.promise.rejected");
throw _onTimedGet(onGets, end, new ExecutionException(msg, _getFailureCause(msg)));
case RESOLVED:
_onTimedGet(onGets, end, null);
return value;
default:
throw new IllegalStateException(Messages.format("THEN-000200.promise.illegal.state", _stateToString(state)));
}
}
protected X _onGet(final List> onGets, X exception) throws ExecutionException, InterruptedException {
for (final Future> then : onGets) {
try {
then.get();
} catch (final Throwable e) {
if (exception != null) {
exception.addSuppressed(e);
}
log.tracef(e, Messages.get("THEN-000401.promise.get.exception"));
}
}
return exception;
}
protected X _onTimedGet(final List> onGets, final long end, final X exception) throws TimeoutException, ExecutionException, InterruptedException {
for (final Future> then : onGets) {
try {
then.get(_tryTimeout(end), MILLISECONDS);
} catch (final Throwable e) {
if (exception != null) {
exception.addSuppressed(e);
}
log.tracef(e, Messages.get("THEN-000401.promise.get.exception"));
}
}
return exception;
}
protected Throwable _getFailureCause(final String msg) {
return failure instanceof Throwable
? (Throwable)failure
: new FailureException(msg, failure);
}
protected long _tryTimeout(final long end) throws TimeoutException {
final long timeout = end - System.currentTimeMillis();
if (timeout <= 0) {
throw new TimeoutException(getTimeoutExceptionMessage());
}
return timeout;
}
protected String getResolveLogMessage() {
return Messages.get("THEN-000000.promise.resolve");
}
protected String getRejectLogMessage() {
return Messages.get("THEN-000001.promise.reject");
}
protected String getCancelLogMessage() {
return Messages.get("THEN-000002.promise.cancel");
}
protected String getProgressLogMessage() {
return Messages.get("THEN-000003.promise.progress");
}
protected String getTimeoutExceptionMessage() {
return Messages.get("THEN-000100.promise.timeout");
}
protected String getInterruptedExceptionMessage() {
return Messages.format("THEN-000101.promise.interrupted");
}
protected Logger log() {
return log;
}
protected String _stateToString(final int state) {
switch (state) {
case PENDING: return "PENDING";
case RESOLVED: return "RESOLVED";
case REJECTED: return "REJECTED";
case CANCELLED: return "CANCELLED";
default: return "UNKNOWN";
}
}
}