/*******************************************************************************
* Copyright (c) 2011, 2014 Wind River Systems, Inc. and others. All rights reserved.
* This program and the accompanying materials are made available under the terms
* of the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.te.runtime.concurrent.executors;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.tcf.te.runtime.concurrent.factories.SingleThreadThreadFactory;
import org.eclipse.tcf.te.runtime.concurrent.interfaces.INestableExecutor;
import org.eclipse.tcf.te.runtime.concurrent.interfaces.ISingleThreadedExecutor;
/**
* A single threaded executor service implementation.
*/
public class SingleThreadedExecutorService extends AbstractDelegatingExecutorService implements ISingleThreadedExecutor, INestableExecutor {
/**
* A single threaded executor implementation.
*/
protected class SingleThreadedExecutor extends ThreadPoolExecutor implements INestableExecutor {
// The current nesting depth
private final AtomicInteger currentNestingDepth = new AtomicInteger(0);
/**
* Constructor.
*
* @param threadFactory
* The thread factory instance. Must not be <code>null</code>.
*
* @throws NullPointerException
* if threadFactory is <code>null</code>.
*/
public SingleThreadedExecutor(ThreadFactory threadFactory) {
this(threadFactory, new LinkedBlockingQueue<Runnable>());
}
/**
* Constructor.
* <p>
* Private constructor to catch the work queue instance passed into the
* {@link ThreadPoolExecutor} constructor.
*
* @param threadFactory
* The thread factory instance. Must not be <code>null</code>.
* @param workQueue
* The work queue instance. Must not be <code>null</code>.
*/
private SingleThreadedExecutor(ThreadFactory threadFactory, BlockingQueue<Runnable> workQueue) {
super(1, 1, 0L, TimeUnit.NANOSECONDS, workQueue, threadFactory);
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.concurrent.interfaces.INestableExecutor#getMaxDepth()
*/
@Override
public int getMaxDepth() {
return 1;
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.concurrent.interfaces.INestableExecutor#readAndExecute()
*/
@Override
public boolean readAndExecute() {
// Method is callable from the executor thread only
if (!isExecutorThread()) {
throw new IllegalStateException("Must be called from within the executor thread!"); //$NON-NLS-1$
}
BlockingQueue<Runnable> queue = getQueue();
// If the work queue is empty, there is nothing to do
if (!queue.isEmpty()) {
// Work queue not empty, check if we reached the maximum nesting
// depth
if (currentNestingDepth.get() >= getMaxDepth()) {
throw new IllegalStateException("Maximum nesting depth exceeded!"); //$NON-NLS-1$
}
// Get the next work item to do
Runnable runnable = null;
try {
// Double check that the queue is not empty, we desire to
// avoid
// blocking here!
if (!queue.isEmpty()) {
runnable = queue.take();
}
} catch (InterruptedException e) { /* ignored on purpose */ }
if (runnable != null) {
// Increase the nesting depth
currentNestingDepth.incrementAndGet();
try {
// Execute the runnable
runnable.run();
} finally {
// Decrease nesting depth
currentNestingDepth.decrementAndGet();
}
}
}
return !queue.isEmpty();
}
/* (non-Javadoc)
* @see java.util.concurrent.ThreadPoolExecutor#afterExecute(java.lang.Runnable, java.lang.Throwable)
*/
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t != null)
logException(t);
}
}
// Internal reference to the one shot thread factory instance
private SingleThreadThreadFactory threadFactory;
/**
* Constructor.
*/
public SingleThreadedExecutorService() {
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.concurrent.executors.AbstractDelegatingExecutorService#createExecutorServiceDelegate()
*/
@Override
protected ExecutorService createExecutorServiceDelegate() {
threadFactory = new SingleThreadThreadFactory(getThreadPoolNamePrefix());
return new SingleThreadedExecutor(threadFactory);
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.concurrent.interfaces.ISingleThreadedExecutor#isExecutorThread()
*/
@Override
public final boolean isExecutorThread() {
return isExecutorThread(Thread.currentThread());
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.concurrent.interfaces.ISingleThreadedExecutor#isExecutorThread(java.lang.Thread)
*/
@Override
public final boolean isExecutorThread(Thread thread) {
if (thread != null && threadFactory != null) {
return thread.equals(threadFactory.getThread());
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.concurrent.interfaces.INestableExecutor#getMaxDepth()
*/
@Override
public int getMaxDepth() {
if (!(getExecutorServiceDelegate() instanceof INestableExecutor)) {
throw new UnsupportedOperationException("Executor service delegate must implement INestableExecutor"); //$NON-NLS-1$
}
return ((INestableExecutor) getExecutorServiceDelegate()).getMaxDepth();
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.concurrent.interfaces.INestableExecutor#readAndExecute()
*/
@Override
public boolean readAndExecute() {
if (!(getExecutorServiceDelegate() instanceof INestableExecutor)) {
throw new UnsupportedOperationException("Executor service delegate must implement INestableExecutor"); //$NON-NLS-1$
}
return ((INestableExecutor) getExecutorServiceDelegate()).readAndExecute();
}
}