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

com.firefly.utils.concurrent.SharedBlockingCallback Maven / Gradle / Ivy

There is a newer version: 5.0.2
Show newest version
package com.firefly.utils.concurrent;

import java.io.Closeable;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Provides a reusable {@link Callback} that can block the thread while waiting
 * to be completed.
 * 

* A typical usage pattern is: * *

 * void someBlockingCall(Object... args) throws IOException {
 * 	try (Blocker blocker = sharedBlockingCallback.acquire()) {
 * 		someAsyncCall(args, blocker);
 * 		blocker.block();
 * 	}
 * }
 * 
*/ public class SharedBlockingCallback { private static Throwable IDLE = new Throwable() { private static final long serialVersionUID = 2839201304149405751L; @Override public String toString() { return "IDLE"; } }; private static Throwable SUCCEEDED = new Throwable() { private static final long serialVersionUID = 3201333456423359501L; @Override public String toString() { return "SUCCEEDED"; } }; private static Throwable FAILED = new Throwable() { private static final long serialVersionUID = 7378407146943258362L; @Override public String toString() { return "FAILED"; } }; private final ReentrantLock _lock = new ReentrantLock(); private final Condition _idle = _lock.newCondition(); private final Condition _complete = _lock.newCondition(); private Blocker _blocker = new Blocker(); protected long getIdleTimeout() { return -1; } public Blocker acquire() throws IOException { _lock.lock(); long idle = getIdleTimeout(); try { while (_blocker._state != IDLE) { if (idle > 0 && (idle < Long.MAX_VALUE / 2)) { // Wait a little bit longer than the blocker might block if (!_idle.await(idle * 2, TimeUnit.MILLISECONDS)) throw new IOException(new TimeoutException()); } else _idle.await(); } _blocker._state = null; } catch (final InterruptedException e) { throw new InterruptedIOException(); } finally { _lock.unlock(); } return _blocker; } protected void notComplete(Blocker blocker) { } /** * A Closeable Callback. Uses the auto close mechanism to check block has * been called OK. *

* Implements {@link Callback.NonBlocking} because calls to this callback do * not blocak, rather they wakeup the thread that is blocked in * {@link #block()} */ public class Blocker implements Callback.NonBlocking, Closeable { private Throwable _state = IDLE; protected Blocker() { } @Override public void succeeded() { _lock.lock(); try { if (_state == null) { _state = SUCCEEDED; _complete.signalAll(); } else throw new IllegalStateException(_state); } finally { _lock.unlock(); } } @Override public void failed(Throwable cause) { _lock.lock(); try { if (_state == null) { if (cause == null) _state = FAILED; else if (cause instanceof BlockerTimeoutException) // Not this blockers timeout _state = new IOException(cause); else _state = cause; _complete.signalAll(); } else throw new IllegalStateException(_state); } finally { _lock.unlock(); } } /** * Block until the Callback has succeeded or failed and after the return * leave in the state to allow reuse. This is useful for code that wants * to repeatable use a FutureCallback to convert an asynchronous API to * a blocking API. * * @throws IOException * if exception was caught during blocking, or callback was * cancelled */ public void block() throws IOException { _lock.lock(); long idle = getIdleTimeout(); try { while (_state == null) { if (idle > 0 && (idle < Long.MAX_VALUE / 2)) { // Wait a little bit longer than expected callback idle // timeout if (!_complete.await(idle + idle / 2, TimeUnit.MILLISECONDS)) // The callback has not arrived in sufficient time. // We will synthesize a TimeoutException _state = new BlockerTimeoutException(); } else { _complete.await(); } } if (_state == SUCCEEDED) return; if (_state == IDLE) throw new IllegalStateException("IDLE"); if (_state instanceof IOException) throw (IOException) _state; if (_state instanceof CancellationException) throw (CancellationException) _state; if (_state instanceof RuntimeException) throw (RuntimeException) _state; if (_state instanceof Error) throw (Error) _state; throw new IOException(_state); } catch (final InterruptedException e) { throw new InterruptedIOException(); } finally { _lock.unlock(); } } /** * Check the Callback has succeeded or failed and after the return leave * in the state to allow reuse. */ @Override public void close() { _lock.lock(); try { if (_state == IDLE) throw new IllegalStateException("IDLE"); if (_state == null) notComplete(this); } finally { try { // If the blocker timed itself out, remember the state if (_state instanceof BlockerTimeoutException) // and create a new Blocker _blocker = new Blocker(); else // else reuse Blocker _state = IDLE; _idle.signalAll(); _complete.signalAll(); } finally { _lock.unlock(); } } } @Override public String toString() { _lock.lock(); try { return String.format("%s@%x{%s}", Blocker.class.getSimpleName(), hashCode(), _state); } finally { _lock.unlock(); } } } private static class BlockerTimeoutException extends TimeoutException { private static final long serialVersionUID = -3538089426850786553L; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy