io.fabric8.utils.SerialExecutorService Maven / Gradle / Ivy
/**
* Copyright 2005-2016 Red Hat, Inc.
*
* Red Hat licenses this file to you under the Apache License, version
* 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package io.fabric8.utils;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* An ExecutorService which ensures serial execution of the Runnable
* objects which it is asked to execute. By default it delegates
* execution of those tasks to a thread pool, but can be configured
* to use any Executor.
*/
public class SerialExecutorService extends AbstractExecutorService {
static long THREAD_POOL_KEEP_ALIVE = Integer.getInteger("io.fabric8.utils.THREAD_POOL_KEEP_ALIVE", 5000);
static final ThreadGroup group = new ThreadGroup("Fabric Tasks");
static final Executor threadPool = new Executor() {
SynchronousQueue queue = new SynchronousQueue();
@Override
public void execute(final Runnable task) {
if (task == null) {
throw new NullPointerException();
}
// Lets try to give the task to a running thread..
if (!queue.offer(task)) {
// Existing thread did not take the task, so spin
// up a thread to execute the task..
new Thread(group, "Fabric Task") {
@Override
public void run() {
while (true) {
Runnable task;
try {
task = queue.poll(THREAD_POOL_KEEP_ALIVE, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
return;
}
if (task == null) {
return;
}
task.run();
}
}
}.start();
// Now wait till a thread picks it up..
try {
queue.put(task);
} catch (InterruptedException e) {
throw new RejectedExecutionException(e);
}
}
}
};
protected Executor target;
protected volatile String label;
protected AtomicBoolean shutdown = new AtomicBoolean(false);
protected AtomicBoolean terminated = new AtomicBoolean(false);
protected CountDownLatch terminatedLatch = new CountDownLatch(1);
protected final AtomicBoolean triggered = new AtomicBoolean();
protected final ConcurrentLinkedQueue externalQueue = new ConcurrentLinkedQueue();
protected final LinkedList localQueue = new LinkedList();
protected final ThreadLocal draining = new ThreadLocal();
protected final Runnable drainTask = new Runnable() {
public void run() {
drain();
}
};
public SerialExecutorService() {
this("");
}
public SerialExecutorService(String label) {
this(threadPool, label);
}
public SerialExecutorService(Executor target) {
this(target, "");
}
public SerialExecutorService(Executor target, String label) {
this.target = target;
this.label = label;
}
/**
* Queues the runnable for execution.
* @param runnable
*/
@Override
public void execute(Runnable runnable) {
if (runnable == null)
throw new NullPointerException("runnable cannot be null");
if (shutdown.get())
throw new RejectedExecutionException("shutdown");
if (isDraining()) {
localQueue.add(runnable);
} else {
externalQueue.add(runnable);
triggerDrain();
}
}
/**
* Executes the runnable. This method blocks until it has been
* executed.
* @param runnable
*/
public void executeAndDrain(Runnable runnable) {
if (runnable == null)
throw new NullPointerException("runnable cannot be null");
if (shutdown.get())
throw new RejectedExecutionException("shutdown");
if (isDraining()) {
runnable.run();
} else {
externalQueue.add(runnable);
drain();
}
}
protected void triggerDrain() {
if (triggered.compareAndSet(false, true)) {
target.execute(drainTask);
}
}
/**
* This method blocks until all previously queued Runnable objects are run.
*/
synchronized public void drain() {
draining.set(Boolean.TRUE);
try {
boolean drained = false;
while (!drained) {
Runnable runnable = localQueue.poll();
if (runnable == null) {
runnable = externalQueue.poll();
}
if (runnable == null) {
drained = true;
} else {
try {
runnable.run();
} catch (Throwable e) {
Thread thread = Thread.currentThread();
thread.getUncaughtExceptionHandler().uncaughtException(thread, e);
}
}
}
} finally {
draining.remove();
triggered.set(false);
if (!externalQueue.isEmpty()) {
triggerDrain();
}
}
}
@Override
public void shutdown() {
if (shutdown.compareAndSet(false, true)) {
externalQueue.add(new Runnable() {
@Override
public void run() {
terminated.set(true);
terminatedLatch.countDown();
}
});
triggerDrain();
}
}
@Override
public List shutdownNow() {
shutdown();
return Collections.EMPTY_LIST;
}
public boolean isDraining() {
return draining.get() == Boolean.TRUE;
}
@Override
public boolean isShutdown() {
return shutdown.get();
}
@Override
public boolean isTerminated() {
return terminated.get();
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
return terminatedLatch.await(timeout, unit);
}
@Override
public String toString() {
return label;
}
public Executor getTarget() {
return target;
}
public void setTarget(Executor target) {
this.target = target;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy