package org.webpieces.util.threading;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import org.webpieces.util.logging.Logger;
import org.webpieces.util.logging.LoggerFactory;
public class SessionExecutorImplOld implements SessionExecutor {
private static final Logger log = LoggerFactory.getLogger(SessionExecutorImpl.class);
private Executor executor;
private Map<Object, List<Runnable>> cachedRunnables = new HashMap<>();
private int counter;
private Set<Object> currentlyRunning = new HashSet<>();
public SessionExecutorImplOld(Executor executor) {
this.executor = executor;
}
@Override
public <T> CompletableFuture<T> executeCall(Object key, Callable<CompletableFuture<T>> callable) {
CompletableFuture<T> future = new CompletableFuture<T>();
FutureRunnable<T> r = new FutureRunnable<>(callable, future);
execute(key, r);
return future;
}
private class FutureRunnable<T> implements Runnable {
private Callable<CompletableFuture<T>> callable;
private CompletableFuture<T> future;
public FutureRunnable(Callable<CompletableFuture<T>> callable, CompletableFuture<T> future) {
this.callable = callable;
this.future = future;
}
@Override
public void run() {
try {
CompletableFuture<T> result = callable.call();
result.handle((r, t) -> {
if(t != null) {
future.completeExceptionally(t);
}
future.complete(r);
return null;
});
} catch(Throwable e) {
future.completeExceptionally(e);
}
}
}
@Override
public void execute(Object key, Runnable r) {
synchronized(this) {
if(currentlyRunning.contains(key)) {
cacheRunnable(key, new RunnableWithKey(key, r));
return;
} else {
currentlyRunning.add(key);
}
if(counter >= 10000)
log.warn("Session executor is falling behind on incoming data, possibly add back pressure", new RuntimeException());
}
executor.execute(new RunnableWithKey(key, r));
}
private void executeNext(Object key) {
Runnable nextRunnable = null;
synchronized (this) {
List<Runnable> list = cachedRunnables.get(key);
if(list == null) {
currentlyRunning.remove(key);
return;
}
nextRunnable = list.remove(0);
counter--;
if(list.isEmpty()) {
cachedRunnables.remove(key);
}
}
executor.execute(nextRunnable);
}
private class RunnableWithKey implements Runnable {
private Runnable runnable;
private Object key;
public RunnableWithKey(Object key, Runnable r) {
this.key = key;
this.runnable = r;
}
@Override
public void run() {
try {
runnable.run();
} catch(Throwable e) {
log.error("Uncaught Exception", e);
} finally {
executeNext(key);
}
}
}
private synchronized void cacheRunnable(Object key, Runnable r) {
List<Runnable> list = cachedRunnables.get(key);
if(list == null) {
list = new LinkedList<>();
cachedRunnables.put(key, list);
}
list.add(r);
counter++;
}
}