package com.github.kmkt.util.concurrent; import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * スレッド間の単純な値受け渡しに用いる Future * FutureTask の Runnable 部分を必要としない場面などに * * License : MIT License * * @see java.util.concurrent.Future * @param <V> */ public class SimpleFuture<V> implements Future<V> { protected final CountDownLatch latch = new CountDownLatch(1); protected volatile boolean cancelled = false; protected volatile boolean accepted = false; protected V result = null; protected Throwable exception = null; /** * Future で受け渡すオブジェクトを設定する * @param obj * @return 受け付けられた場合は true<br> 既に受け付け済み、又は cancel 済みの場合は false */ public boolean set(V obj) { synchronized(this) { if (accepted || cancelled) { return false; } accepted = true; result = obj; } latch.countDown(); // release latch done(); return true; } /** * Future で受け渡す例外を設定する * 例外が設定された場合、get は設定された例外をラップした ExecutionException を起こす * @param t * @return 受け付けられた場合は true<br> 既に受け付け済み、又は cancel 済みの場合は false */ public boolean setException(Throwable t) { synchronized(this) { if (accepted || cancelled) { return false; } accepted = true; exception = t; } latch.countDown(); // release latch done(); return true; } /** * @see java.util.concurrent.Future#cancel(boolean) * @param mayInterruptIfRunning 無視されます * @return @see java.util.concurrent.Future#cancel(boolean) */ @Override public boolean cancel(boolean mayInterruptIfRunning) { synchronized(this) { if (accepted || cancelled) { return false; } cancelled = true; } latch.countDown(); // release latch done(); return false; } /** * Future の状態が done (isDone が true) になる際に呼び出される protected メソッド * デフォルトは空実装 */ protected void done() { } /** * @see java.util.concurrent.Future#get() */ @Override public V get() throws InterruptedException, ExecutionException { if (!accepted && !cancelled) { latch.await(); // wait to release latch } if (cancelled) { throw new CancellationException(); } if (exception != null) { throw new ExecutionException(exception); } return result; } /** * @see java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit) */ @Override public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (!accepted && !cancelled) { if (!latch.await(timeout, unit)) // wait to release latch throw new TimeoutException(); } if (cancelled) { throw new CancellationException(); } if (exception != null) { throw new ExecutionException(exception); } return result; } /** * Future に値、例外が設定されるか、cancel されるまで待機する * @throws InterruptedException */ public void await() throws InterruptedException { latch.await(); // wait to release latch } /** * Future の状態が確定(値、例外が設定されるか、cancel されるか)するか、タイムアウトするまで待機する * @param timeout 待機する最長時間 * @param unit timeout 引数の時間単位 * @return Future の状態が確定した場合は true タイムアウトした場合は false * @throws InterruptedException */ public boolean await(long timeout, TimeUnit unit) throws InterruptedException { return latch.await(timeout, unit); // wait to release latch } /** * @see java.util.concurrent.Future#isCancelled() */ @Override public boolean isCancelled() { return cancelled; } /** * @see java.util.concurrent.Future#isDone() */ @Override public boolean isDone() { return (cancelled || accepted); } /** * 例外が設定されたか否かを取得する * true を返す場合、 get を呼ぶと ExecutionException が生じる * @return */ public boolean hasException() { return (accepted && exception != null); } }