com.gemstone.gemfire.distributed.internal.FunctionExecutionPooledExecutor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gemfire-core Show documentation
Show all versions of gemfire-core Show documentation
SnappyData store based off Pivotal GemFireXD
The newest version!
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* Licensed 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. See accompanying
* LICENSE file.
*/
package com.gemstone.gemfire.distributed.internal;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadFactory;
import com.gemstone.gemfire.SystemFailure;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.gemfire.internal.concurrent.SynchronousQueueNoSpin;
import java.util.List;
/**
* A ThreadPoolExecutor with stat support. This executor also has a
* buffer that rejected executions spill over into.
*
* This executor has also been modified to handle rejected execution in
* one of three ways:
* If the executor is for function execution we ignore size caps on the thread pool
* If the executor has a SynchronousQueue or SynchronousQueueNoSpin then
* rejected executions are run in the current thread.
* Otherwise a thread is started in the pool that buffers the rejected
* tasks and puts them back into the executor.
*
* TODO this is a version of PooledExecutorWithDMStats that was spun off
* in cedar_dev_Oct12 as a measure to fix bug #46438. It should be cleaned
* up to not have code paths for !forFnExecution since it is only used
* for function execution. This was not done for the 8.0 release because
* of code freeze.
*
* @author darrel
*
*/
public class FunctionExecutionPooledExecutor extends ThreadPoolExecutor {
protected final PoolStatHelper stats;
/**
* Create a new pool
**/
public FunctionExecutionPooledExecutor(BlockingQueue q, int maxPoolSize, PoolStatHelper stats, ThreadFactory tf, int msTimeout, RejectedExecutionHandler reh) {
super(getCorePoolSize(maxPoolSize), maxPoolSize,
msTimeout, TimeUnit.MILLISECONDS,
q, tf, reh);
// if (getCorePoolSize() != 0 && getCorePoolSize() == getMaximumPoolSize()) {
// allowCoreThreadTimeOut(true); // deadcoded for 1.5
// }
this.stats = stats;
}
/**
* Used to buffer up tasks that would be have been rejected.
* Only used (i.e. non-null) if constructor queue is not a SynchronousQueue.
*/
protected BlockingQueue bufferQueue;
/**
* Used to consume items off the bufferQueue and put them into the pools
* synchronous queue.
* Only used (i.e. non-null) if constructor queue is not a SynchronousQueue.
*/
Thread bufferConsumer;
private static BlockingQueue initQ(BlockingQueue q) {
if (q instanceof SynchronousQueue || q instanceof SynchronousQueueNoSpin) {
return q;
} else {
return new SynchronousQueueNoSpin();
}
}
private static RejectedExecutionHandler initREH(
final BlockingQueue q, boolean forFnExec)
{
if (forFnExec) {
return new RejectedExecutionHandler() {
public void rejectedExecution(final Runnable r,
ThreadPoolExecutor executor)
{
if (executor.isShutdown()) {
throw new RejectedExecutionException(
LocalizedStrings.PooledExecutorWithDMStats_EXECUTOR_HAS_BEEN_SHUTDOWN
.toLocalizedString());
}
else {
// System.out.println("Asif: Rejection called");
if (Thread.currentThread() == ((FunctionExecutionPooledExecutor)executor).bufferConsumer) {
Thread th = executor.getThreadFactory().newThread(
(new Runnable() {
public void run()
{
r.run();
}
}));
th.start();
}
else {
try {
q.put(r);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
// this thread is being shutdown so just return;
return;
}
}
}
}
};
}
else {
if (q instanceof SynchronousQueue || q instanceof SynchronousQueueNoSpin) {
return new CallerRunsPolicy();
// return new BlockHandler();
}
else {
// create a thread that takes from bufferQueue and puts into result
return new BufferHandler();
}
}
}
public FunctionExecutionPooledExecutor(BlockingQueue q, int maxPoolSize, PoolStatHelper stats, ThreadFactory tf, int msTimeout, final boolean forFnExec) {
this(initQ(q), maxPoolSize, stats, tf, msTimeout, initREH(q,forFnExec));
final int retryFor = Integer.getInteger("gemfire.RETRY_INTERVAL", 5000).intValue();
if (!(q instanceof SynchronousQueue)) {
this.bufferQueue = q;
// create a thread that takes from bufferQueue and puts into result
final BlockingQueue takeQueue = q;
final BlockingQueue putQueue = getQueue();
Runnable r = new Runnable() {
public void run() {
try {
for (;;) {
SystemFailure.checkFailure();
Runnable task = takeQueue.take();
if(forFnExec) {
if(!putQueue.offer(task,retryFor , TimeUnit.MILLISECONDS)){
submit(task);
}
}else {
putQueue.put(task);
}
}
}
catch (InterruptedException ie) {
Thread.currentThread().interrupt();
// this thread is being shutdown so just return;
return;
}
}
};
this.bufferConsumer = tf.newThread(r);
this.bufferConsumer.start();
}
}
@Override
public void shutdown() {
try {
super.shutdown();
}
finally {
terminated();
}
}
@Override
protected void terminated() {
if (this.bufferConsumer != null) {
this.bufferConsumer.interrupt();
}
super.terminated();
}
@Override
public List shutdownNow() {
terminated();
List l = super.shutdownNow();
if (this.bufferQueue != null) {
this.bufferQueue.drainTo(l);
}
return l;
}
/**
* Sets timeout to IDLE_THREAD_TIMEOUT
*/
public FunctionExecutionPooledExecutor(BlockingQueue q, int poolSize, PoolStatHelper stats, ThreadFactory tf) {
/**
* How long an idle thread will wait, in milliseconds, before it is removed
* from its thread pool. Default is (30000 * 60) ms (30 minutes).
* It is not static so it can be set at runtime and pick up different values.
*/
this(q, poolSize, stats, tf, Integer.getInteger("gemfire.IDLE_THREAD_TIMEOUT", 30000*60), false /* not for fn exec*/);
}
public FunctionExecutionPooledExecutor(BlockingQueue q, int poolSize, PoolStatHelper stats, ThreadFactory tf, boolean forFnExec) {
/**
* How long an idle thread will wait, in milliseconds, before it is removed
* from its thread pool. Default is (30000 * 60) ms (30 minutes).
* It is not static so it can be set at runtime and pick up different values.
*/
this(q, poolSize, stats, tf, Integer.getInteger("gemfire.IDLE_THREAD_TIMEOUT", 30000*60), forFnExec);
}
/**
* Default timeout with no stats.
*/
public FunctionExecutionPooledExecutor(BlockingQueue q, int poolSize, ThreadFactory tf) {
this(q, poolSize, null/*no stats*/, tf);
}
@Override
protected final void beforeExecute(Thread t, Runnable r) {
if (this.stats != null) {
this.stats.startJob();
}
}
@Override
protected final void afterExecute(Runnable r, Throwable ex) {
if (this.stats != null) {
this.stats.endJob();
}
}
private static int getCorePoolSize(int maxSize) {
if (maxSize == Integer.MAX_VALUE) {
return 0;
} else {
return 1;
// int result = Runtime.getRuntime().availableProcessors();
// if (result < 2) {
// result = 2;
// }
// if (result > maxSize) {
// result = maxSize;
// }
// return result;
}
}
/**
* This guy does a put which will just wait until the queue has room.
*/
public static class BlockHandler implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (executor.isShutdown()) {
throw new RejectedExecutionException(LocalizedStrings.PooledExecutorWithDMStats_EXECUTOR_HAS_BEEN_SHUTDOWN.toLocalizedString());
} else {
try {
executor.getQueue().put(r);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
RejectedExecutionException e = new RejectedExecutionException(LocalizedStrings.PooledExecutorWithDMStats_INTERRUPTED.toLocalizedString());
e.initCause(ie);
throw e;
}
}
}
}
/**
* This guy fronts a synchronous queue, that is owned by the parent
* ThreadPoolExecutor, with a the client supplied BlockingQueue that
* supports storage (the buffer queue).
* A dedicated thread is used to consume off the buffer queue and put
* into the synchronous queue.
*/
public static class BufferHandler implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (executor.isShutdown()) {
throw new RejectedExecutionException(LocalizedStrings.PooledExecutorWithDMStats_EXECUTOR_HAS_BEEN_SHUTDOWN.toLocalizedString());
} else {
try {
FunctionExecutionPooledExecutor pool = (FunctionExecutionPooledExecutor)executor;
pool.bufferQueue.put(r);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
RejectedExecutionException e = new RejectedExecutionException(LocalizedStrings.PooledExecutorWithDMStats_INTERRUPTED.toLocalizedString());
e.initCause(ie);
throw e;
}
}
}
}
}