/*
This file is part of Reactive Cascade which is released under The MIT License.
See license.md , https://github.com/futurice/cascade and http://reactivecascade.com for details.
This is open source for the common good. Please contribute improvements by pull request or contact paulirotta@gmail.com
*/
package com.reactivecascade.util;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import com.reactivecascade.Async;
import com.reactivecascade.i.IAltFuture;
import com.reactivecascade.i.NotCallOrigin;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Treat the system UI thread as an ExecutorService
* <p>
* This is done to allow UI codes to be part of UI {@link com.reactivecascade.i.IThreadType}
* so that {@link IAltFuture} objects can be easily defined to
* run on the UI thread in {@link Async#UI} vs background worker threads.
* <p>
* Since the system UI thread runs forever, not all {@link java.util.concurrent.ExecutorService}
* items, for example related to lifecycle. make sense to implement.
*/
public final class UIExecutorService extends Origin implements ExecutorService {
@NonNull
private final Handler mHandler;
public UIExecutorService(@NonNull Handler handler) {
this.mHandler = handler;
}
@Override // ExecutorService
public void shutdown() {
RCLog.i(this, "shutdown() called on UiAsync default ExecutorService");
throw new UnsupportedOperationException("Shutdown() called on UiAsync default ExecutorService");
}
@NonNull
@Override // ExecutorService
public List<Runnable> shutdownNow() {
RCLog.i(this, "shutdownNow() called on UiAsync default ExecutorService");
throw new UnsupportedOperationException("ShutdownNow() called on UiAsync default ExecutorService");
}
@Override // ExecutorService
public boolean isShutdown() {
return false;
}
@Override // ExecutorService
public boolean isTerminated() {
return false;
}
@Override // ExecutorService
public boolean awaitTermination(long timeout,
@NonNull TimeUnit unit) throws InterruptedException {
RCLog.i(this, "awaitTermination() called on UiAsync default ExecutorService");
throw new UnsupportedOperationException("awaitTermination() called on UiAsync default ExecutorService");
}
@NonNull
@Override // ExecutorService
public <T> Future<T> submit(@NonNull Callable<T> callable) {
FutureTask<T> future = new FutureTask<>(callable);
execute(future);
return future;
}
@NonNull
@Override // ExecutorService
public <T> Future<T> submit(@NonNull Runnable runnable,
@NonNull T result) {
FutureTask<T> future = new FutureTask<>(
() -> {
runnable.run();
return result;
});
execute(future);
return future;
}
@NonNull
@NotCallOrigin
@Override // ExecutorService
public Future submit(@NonNull Runnable runnable) {
if (runnable instanceof RunnableFuture) {
mHandler.post(runnable);
return (Future) runnable;
}
FutureTask<Object> future = new FutureTask<>(new Callable<Object>() {
@Override
@NotCallOrigin
@Nullable
public Object call() throws Exception {
runnable.run();
return null;
}
});
mHandler.post(future);
return future;
}
@NonNull
@Override // ExecutorService
@WorkerThread
public <T> List<Future<T>> invokeAll(@NonNull Collection<? extends Callable<T>> callables) throws InterruptedException, NullPointerException, RejectedExecutionException {
final ArrayList<Future<T>> futures = new ArrayList<>(callables.size());
if (callables.size() > 0) {
for (Callable<T> callable : callables) {
futures.add(submit(callable));
}
try {
futures.get(futures.size() - 1).get();
} catch (ExecutionException e) {
RCLog.throwRuntimeException(this, "Can not get() last element of invokeAll()", e);
}
}
return futures;
}
@NonNull
@Override // ExecutorService
@WorkerThread
public <T> List<Future<T>> invokeAll(@NonNull Collection<? extends Callable<T>> callables,
long timeout,
@NonNull TimeUnit unit) throws InterruptedException, NullPointerException, RejectedExecutionException {
final ArrayList<Future<T>> futures = new ArrayList<>(callables.size());
if (callables.size() > 0) {
for (Callable<T> callable : callables) {
futures.add(submit(callable));
}
try {
futures.get(futures.size() - 1).get(timeout, unit);
} catch (ExecutionException e) {
RCLog.throwRuntimeException(this, "Can not get() last element of invokeAll()", e);
} catch (TimeoutException e) {
RCLog.throwRuntimeException(this, "Timeout waiting to get() last element of invokeAll()", e);
}
}
return futures;
}
@Override // ExecutorService
@WorkerThread
@NonNull
public <T> T invokeAny(@NonNull Collection<? extends Callable<T>> callables) throws InterruptedException, NullPointerException, RejectedExecutionException, ExecutionException {
ArrayList<Future<T>> futures = new ArrayList<>(callables.size());
if (callables.size() == 0) {
throw new NullPointerException("Empty list can not invokeAny() as there is no from to return");
}
for (final Callable<T> callable : callables) {
futures.add(submit(callable));
}
return futures.get(0).get();
}
@NonNull
@Override // ExecutorService
@WorkerThread
public <T> T invokeAny(@NonNull Collection<? extends Callable<T>> callables,
long timeout,
@NonNull TimeUnit unit) throws InterruptedException, NullPointerException, RejectedExecutionException, TimeoutException, ExecutionException {
ArrayList<Future<T>> futures = new ArrayList<>(callables.size());
if (callables.size() == 0) {
throw new NullPointerException("Empty list can not invokeAny() as there is no from to return");
}
for (Callable<T> callable : callables) {
futures.add(submit(callable));
}
return futures.get(0).get(timeout, unit);
}
@Override // ExecutorService
public void execute(@NonNull Runnable command) {
if (!mHandler.post(command)) {
RCLog.throwIllegalStateException(this, "Can not Handler.post() to UIThread in this Context right now, probably app is shutting down");
}
}
}