org.infinispan.util.concurrent.BlockingTaskAwareExecutorServiceImpl Maven / Gradle / Ivy
package org.infinispan.util.concurrent;
import org.infinispan.util.TimeService;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
/**
* A special executor service that accepts a {@code BlockingRunnable}. This special runnable gives hints about the code
* to be running in order to avoiding put a runnable that will block the thread. In this way, only when the runnable
* says that is ready, it is sent to the real executor service
*
* @author Pedro Ruivo
* @since 5.3
*/
public class BlockingTaskAwareExecutorServiceImpl extends AbstractExecutorService implements BlockingTaskAwareExecutorService {
private static final Log log = LogFactory.getLog(BlockingTaskAwareExecutorServiceImpl.class);
private final BlockingQueue blockedTasks;
private final ExecutorService executorService;
private final TimeService timeService;
private volatile boolean shutdown;
public BlockingTaskAwareExecutorServiceImpl(ExecutorService executorService, TimeService timeService) {
this.blockedTasks = new LinkedBlockingQueue();
this.executorService = executorService;
this.timeService = timeService;
this.shutdown = false;
}
@Override
public final void execute(BlockingRunnable runnable) {
if (shutdown) {
throw new RejectedExecutionException("Executor Service is already shutdown");
}
if (runnable.isReady()) {
doExecute(runnable);
} else {
blockedTasks.offer(runnable);
//case: T1 is adding a task and T2 is releasing one. problem to solve:
//T1: is adding a new task. runnable.isReady() returns false
//T2: meanwhile, T2 releases a resources that is blocking the T1's runnable
//T2: also, T2 invokes checkForReadyTasks(), that does nothing because the queue is empty
//T1: continues and add the runnable to the queue
//problem: if no more interaction with this object is done, the runnable will be kept in the queue forever.
boolean checkPendingTasks;
synchronized (blockedTasks) {
//to synchronize with the thread invoking checkForReadyTasks();
checkPendingTasks = runnable.isReady();
}
if (checkPendingTasks) {
checkForReadyTasks();
}
}
if (log.isTraceEnabled()) {
log.tracef("Added a new task: %s task(s) are waiting", blockedTasks.size());
}
}
@Override
public void shutdown() {
shutdown = true;
}
@Override
public List shutdownNow() {
shutdown = true;
List runnableList = new LinkedList();
runnableList.addAll(executorService.shutdownNow());
runnableList.addAll(blockedTasks);
return runnableList;
}
@Override
public boolean isShutdown() {
return shutdown;
}
@Override
public boolean isTerminated() {
return shutdown && blockedTasks.isEmpty() && executorService.isTerminated();
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
final long endTime = timeService.expectedEndTime(timeout, unit);
synchronized (blockedTasks) {
long waitTime = timeService.remainingTime(endTime, TimeUnit.MILLISECONDS);
while (!blockedTasks.isEmpty() && waitTime > 0) {
wait(waitTime);
}
}
return isTerminated();
}
@Override
public final void checkForReadyTasks() {
List runnableReadyList = new ArrayList(blockedTasks.size());
synchronized (blockedTasks) {
for (Iterator iterator = blockedTasks.iterator(); iterator.hasNext(); ) {
BlockingRunnable runnable = iterator.next();
if (runnable.isReady()) {
iterator.remove();
runnableReadyList.add(runnable);
}
}
}
if (log.isTraceEnabled()) {
log.tracef("Tasks executed=%s, still pending=%s", runnableReadyList.size(), blockedTasks.size());
}
for (BlockingRunnable runnable : runnableReadyList) {
doExecute(runnable);
}
}
@Override
public void execute(Runnable command) {
if (shutdown) {
throw new RejectedExecutionException("Executor Service is already shutdown");
}
executorService.execute(command);
}
private void doExecute(BlockingRunnable runnable) {
try {
executorService.execute(runnable);
} catch (RejectedExecutionException rejected) {
//put it back!
blockedTasks.offer(runnable);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy