callable, final long delay, final TimeUnit unit) {
return delegate.schedule(callable, delay, unit);
}
public ScheduledFuture> scheduleAtFixedRate(final Runnable command, final long initialDelay, final long period, final TimeUnit unit) {
return delegate.scheduleAtFixedRate(command, initialDelay, period, unit);
}
public ScheduledFuture> scheduleWithFixedDelay(final Runnable command, final long initialDelay, final long delay, final TimeUnit unit) {
return delegate.scheduleWithFixedDelay(command, initialDelay, delay, unit);
}
} jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/DiscardingExecutor.java 0000664 0000000 0000000 00000002103 14303134447 0027744 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.util.concurrent.Executor;
class DiscardingExecutor implements Executor {
static final DiscardingExecutor INSTANCE = new DiscardingExecutor();
private DiscardingExecutor() {
}
public void execute(final Runnable command) {
// nothing
}
public String toString() {
return "Discarding executor";
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/EnhancedQueueExecutor.java 0000664 0000000 0000000 00000407332 14303134447 0030424 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.Thread.currentThread;
import static java.security.AccessController.doPrivileged;
import static java.security.AccessController.getContext;
import static java.util.concurrent.locks.LockSupport.parkNanos;
import static java.util.concurrent.locks.LockSupport.unpark;
import static org.jboss.threads.JBossExecutors.unsafe;
import java.lang.management.ManagementFactory;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import org.jboss.threads.management.ManageableThreadPoolExecutorService;
import org.jboss.threads.management.StandardThreadPoolMXBean;
import org.wildfly.common.Assert;
import org.wildfly.common.cpu.ProcessorInfo;
/**
* A task-or-thread queue backed thread pool executor service. Tasks are added in a FIFO manner, and consumers in a LIFO manner.
* Threads are only ever created in the event that there are no idle threads available to service a task, which, when
* combined with the LIFO-based consumer behavior, means that the thread pool will generally trend towards the minimum
* necessary size. In addition, the optional {@linkplain #setGrowthResistance(float) growth resistance feature} can
* be used to further govern the thread pool size.
*
* Additionally, this thread pool implementation supports scheduling of tasks.
* The scheduled tasks will execute on the main pool.
*
* New instances of this thread pool are created by constructing and configuring a {@link Builder} instance, and calling
* its {@link Builder#build() build()} method.
*
* @author David M. Lloyd
*/
public final class EnhancedQueueExecutor extends EnhancedQueueExecutorBase6 implements ManageableThreadPoolExecutorService, ScheduledExecutorService {
private static final Thread[] NO_THREADS = new Thread[0];
static {
Version.getVersionString();
MBeanUnregisterAction.forceInit();
}
/*
┌──────────────────────────┐
│ Explanation of operation │
└──────────────────────────┘
The primary mechanism of this executor is the special linked list/stack. This list has the following properties:
• Multiple node types:
◦ Task nodes (TaskNode), which have the following properties:
▪ Strongly connected (no task is ever removed from the middle of the list; tail can always be found by following .next pointers)
▪ FIFO (insertion at tail.next, "removal" at head.next)
▪ Head and tail always refer to TaskNodes; head.next/tail.next are the "true" action points for all node types
▪ At least one dead task node is always in the list, thus the task is cleared after execution to avoid leaks
◦ Waiting consumer thread nodes (PoolThreadNode), which have the following properties:
▪ LIFO/FILO (insertion and removal at tail.next)
▪ Carrier for task handoff between producer and waiting consumer
◦ Waiting termination (awaitTermination) thread nodes (TerminateWaiterNode), which have the following properties:
▪ Append-only (insertion at tail.next.next...next)
▪ All removed at once when termination is complete
▪ If a thread stops waiting, the node remains (but its thread field is cleared to prevent (best-effort) useless unparks)
▪ Once cleared, the thread field can never be reinitialized
• Concurrently accessed (multiple threads may read the list and update the head and tail pointers)
• TaskNode.next may be any node type or null
• PoolThreadNode.next may only be another PoolThreadNode or null
• TerminateWaiterNode.next may only be another TerminateWaiterNode or null
The secondary mechanism is the thread status atomic variable. It is logically a structure with the following fields:
• Current thread count (currentSizeOf(), withCurrentSize())
• Core pool size (coreSizeOf(), withCoreSize())
• Max pool size (maxSizeOf(), withMaxSize())
• Shutdown-requested flag (isShutdownRequested(), withShutdownRequested())
• Shutdown-with-interrupt requested flag (isShutdownInterrupt(), withShutdownInterrupt())
• Shutdown-completed flag (isShutdownComplete(), withShutdownComplete())
• The decision to create a new thread is affected by whether the number of active threads is less than the core size;
if not, then the growth resistance factor is applied to decide whether the task should be enqueued or a new thread begun.
Note: the default growth resistance factor is 0% resistance.
The final mechanism is the queue status atomic variable. It is logically a structure with the following fields:
• Current queue size (currentQueueSizeOf(), withCurrentQueueSize())
• Queue size limit (maxQueueSizeOf(), withMaxQueueSize())
*/
// =======================================================
// Optimization control flags
// =======================================================
/**
* A global hint which establishes whether it is recommended to disable uses of {@code EnhancedQueueExecutor}.
* This hint defaults to {@code false} but can be changed to {@code true} by setting the {@code jboss.threads.eqe.disable}
* property to {@code true} before this class is initialized.
*/
public static final boolean DISABLE_HINT = readBooleanPropertyPrefixed("disable", false);
/**
* Update the summary statistics.
*/
static final boolean UPDATE_STATISTICS = readBooleanPropertyPrefixed("statistics", false);
/**
* Maintain an estimate of the number of threads which are currently doing work on behalf of the thread pool.
*/
static final boolean UPDATE_ACTIVE_COUNT =
UPDATE_STATISTICS || readBooleanPropertyPrefixed("statistics.active-count", false);
/**
* Suppress queue limit and size tracking for performance.
*/
static final boolean NO_QUEUE_LIMIT = readBooleanPropertyPrefixed("unlimited-queue", false);
/**
* Set the default value for whether an mbean is to be auto-registered for the thread pool.
*/
static final boolean REGISTER_MBEAN = readBooleanPropertyPrefixed("register-mbean", true);
/**
* Set to enable or disable MBean registration.
*/
static final boolean DISABLE_MBEAN = readBooleanPropertyPrefixed("disable-mbean", readProperty("org.graalvm.nativeimage.imagecode", null) != null);
/**
* The number of times a thread should spin/yield before actually parking.
*/
static final int PARK_SPINS = readIntPropertyPrefixed("park-spins", ProcessorInfo.availableProcessors() == 1 ? 0 : 1 << 7);
/**
* The yield ratio expressed as the number of spins that should yield.
*/
static final int YIELD_FACTOR = max(min(readIntPropertyPrefixed("park-yields", 1), PARK_SPINS), 0);
// =======================================================
// Constants
// =======================================================
static final Executor DEFAULT_HANDLER = JBossExecutors.rejectingExecutor();
// =======================================================
// Immutable configuration fields
// =======================================================
/**
* The thread factory.
*/
private final ThreadFactory threadFactory;
/**
* The approximate set of pooled threads.
*/
private final Set runningThreads = Collections.newSetFromMap(new ConcurrentHashMap<>());
/**
* The management bean instance.
*/
private final MXBeanImpl mxBean;
/**
* The MBean registration handle (if any).
*/
private final Object handle;
/**
* The access control context of the creating thread.
* Will be set to null when the MBean is not registered.
*/
private volatile AccessControlContext acc;
/**
* The context handler for the user-defined context.
*/
private final ContextHandler> contextHandler;
/**
* The task for scheduled execution.
*/
private final SchedulerTask schedulerTask = new SchedulerTask();
/**
* The scheduler thread.
*/
private final Thread schedulerThread;
// =======================================================
// Current state fields
// =======================================================
/**
* The linked list of threads waiting for termination of this thread pool.
*/
@SuppressWarnings("unused") // used by field updater
volatile Waiter terminationWaiters;
/**
* Queue size:
*
* Bit 00..1F: current queue length
* Bit 20..3F: queue limit
*
*/
@SuppressWarnings("unused") // used by field updater
volatile long queueSize;
/**
* The thread keep-alive timeout value.
*/
volatile long timeoutNanos;
/**
* A resistance factor applied after the core pool is full; values applied here will cause that fraction
* of submissions to create new threads when no idle thread is available. A value of {@code 0.0f} implies that
* threads beyond the core size should be created as aggressively as threads within it; a value of {@code 1.0f}
* implies that threads beyond the core size should never be created.
*/
volatile float growthResistance;
/**
* The handler for tasks which cannot be accepted by the executor.
*/
volatile Executor handoffExecutor;
/**
* The handler for uncaught exceptions which occur during user tasks.
*/
volatile Thread.UncaughtExceptionHandler exceptionHandler;
/**
* The termination task to execute when the thread pool exits.
*/
volatile Runnable terminationTask;
// =======================================================
// Statistics fields and counters
// =======================================================
/**
* The peak number of threads ever created by this pool.
*/
@SuppressWarnings("unused") // used by field updater
volatile int peakThreadCount;
/**
* The approximate peak queue size.
*/
@SuppressWarnings("unused") // used by field updater
volatile int peakQueueSize;
private final LongAdder submittedTaskCounter = new LongAdder();
private final LongAdder completedTaskCounter = new LongAdder();
private final LongAdder rejectedTaskCounter = new LongAdder();
private final LongAdder spinMisses = new LongAdder();
/**
* The current active number of threads.
*/
@SuppressWarnings("unused")
volatile int activeCount;
// =======================================================
// Updaters
// =======================================================
private static final long terminationWaitersOffset;
private static final long queueSizeOffset;
private static final long peakThreadCountOffset;
private static final long activeCountOffset;
private static final long peakQueueSizeOffset;
static {
try {
terminationWaitersOffset = unsafe.objectFieldOffset(EnhancedQueueExecutor.class.getDeclaredField("terminationWaiters"));
queueSizeOffset = unsafe.objectFieldOffset(EnhancedQueueExecutor.class.getDeclaredField("queueSize"));
peakThreadCountOffset = unsafe.objectFieldOffset(EnhancedQueueExecutor.class.getDeclaredField("peakThreadCount"));
activeCountOffset = unsafe.objectFieldOffset(EnhancedQueueExecutor.class.getDeclaredField("activeCount"));
peakQueueSizeOffset = unsafe.objectFieldOffset(EnhancedQueueExecutor.class.getDeclaredField("peakQueueSize"));
} catch (NoSuchFieldException e) {
throw new NoSuchFieldError(e.getMessage());
}
}
// =======================================================
// Thread state field constants
// =======================================================
private static final long TS_THREAD_CNT_MASK = 0b1111_1111_1111_1111_1111L; // 20 bits, can be shifted as needed
// shift amounts
private static final long TS_CURRENT_SHIFT = 0;
private static final long TS_CORE_SHIFT = 20;
private static final long TS_MAX_SHIFT = 40;
private static final long TS_ALLOW_CORE_TIMEOUT = 1L << 60;
private static final long TS_SHUTDOWN_REQUESTED = 1L << 61;
private static final long TS_SHUTDOWN_INTERRUPT = 1L << 62;
@SuppressWarnings("NumericOverflow")
private static final long TS_SHUTDOWN_COMPLETE = 1L << 63;
private static final int EXE_OK = 0;
private static final int EXE_REJECT_QUEUE_FULL = 1;
private static final int EXE_REJECT_SHUTDOWN = 2;
private static final int EXE_CREATE_THREAD = 3;
private static final int AT_YES = 0;
private static final int AT_NO = 1;
private static final int AT_SHUTDOWN = 2;
// =======================================================
// Marker objects
// =======================================================
static final QNode TERMINATE_REQUESTED = new TerminateWaiterNode();
static final QNode TERMINATE_COMPLETE = new TerminateWaiterNode();
static final Waiter TERMINATE_COMPLETE_WAITER = new Waiter(null);
static final Runnable WAITING = new NullRunnable();
static final Runnable GAVE_UP = new NullRunnable();
static final Runnable ACCEPTED = new NullRunnable();
static final Runnable EXIT = new NullRunnable();
// =======================================================
// Constructor
// =======================================================
static final AtomicInteger sequence = new AtomicInteger(1);
EnhancedQueueExecutor(final Builder builder) {
super();
int maxSize = builder.getMaximumPoolSize();
int coreSize = min(builder.getCorePoolSize(), maxSize);
this.handoffExecutor = builder.getHandoffExecutor();
this.exceptionHandler = builder.getExceptionHandler();
this.threadFactory = builder.getThreadFactory();
this.schedulerThread = threadFactory.newThread(schedulerTask);
String schedulerName = this.schedulerThread.getName();
this.schedulerThread.setName(schedulerName + " (scheduler)");
this.terminationTask = builder.getTerminationTask();
this.growthResistance = builder.getGrowthResistance();
this.contextHandler = builder.getContextHandler();
final Duration keepAliveTime = builder.getKeepAliveTime();
// initial dead node
// thread stat
threadStatus = withCoreSize(withMaxSize(withAllowCoreTimeout(0L, builder.allowsCoreThreadTimeOut()), maxSize), coreSize);
timeoutNanos = TimeUtil.clampedPositiveNanos(keepAliveTime);
queueSize = withMaxQueueSize(withCurrentQueueSize(0L, 0), builder.getMaximumQueueSize());
mxBean = new MXBeanImpl();
if (! DISABLE_MBEAN && builder.isRegisterMBean()) {
this.acc = getContext();
final String configuredName = builder.getMBeanName();
final String finalName = configuredName != null ? configuredName : "threadpool-" + sequence.getAndIncrement();
handle = doPrivileged(new MBeanRegisterAction(finalName, mxBean), acc);
} else {
handle = null;
this.acc = null;
}
}
static final class MBeanRegisterAction implements PrivilegedAction {
private final String finalName;
private final MXBeanImpl mxBean;
MBeanRegisterAction(final String finalName, final MXBeanImpl mxBean) {
this.finalName = finalName;
this.mxBean = mxBean;
}
public ObjectInstance run() {
try {
final Hashtable table = new Hashtable<>();
table.put("name", ObjectName.quote(finalName));
table.put("type", "thread-pool");
return ManagementFactory.getPlatformMBeanServer().registerMBean(mxBean, new ObjectName("jboss.threads", table));
} catch (Throwable ignored) {
}
return null;
}
}
// =======================================================
// Builder
// =======================================================
/**
* The builder class for an {@code EnhancedQueueExecutor}. All the fields are initialized to sensible defaults for
* a small thread pool.
*/
public static final class Builder {
private ThreadFactory threadFactory = Executors.defaultThreadFactory();
private Runnable terminationTask = NullRunnable.getInstance();
private Executor handoffExecutor = DEFAULT_HANDLER;
private Thread.UncaughtExceptionHandler exceptionHandler = JBossExecutors.loggingExceptionHandler();
private int coreSize = 16;
private int maxSize = 64;
private Duration keepAliveTime = Duration.ofSeconds(30);
private float growthResistance;
private boolean allowCoreTimeOut;
private int maxQueueSize = Integer.MAX_VALUE;
private boolean registerMBean = REGISTER_MBEAN;
private String mBeanName;
private ContextHandler> contextHandler = ContextHandler.NONE;
/**
* Construct a new instance.
*/
public Builder() {}
/**
* Get the configured thread factory.
*
* @return the configured thread factory (not {@code null})
*/
public ThreadFactory getThreadFactory() {
return threadFactory;
}
/**
* Set the configured thread factory.
*
* @param threadFactory the configured thread factory (must not be {@code null})
* @return this builder
*/
public Builder setThreadFactory(final ThreadFactory threadFactory) {
Assert.checkNotNullParam("threadFactory", threadFactory);
this.threadFactory = threadFactory;
return this;
}
/**
* Get the termination task. By default, an empty {@code Runnable} is used.
*
* @return the termination task (not {@code null})
*/
public Runnable getTerminationTask() {
return terminationTask;
}
/**
* Set the termination task.
*
* @param terminationTask the termination task (must not be {@code null})
* @return this builder
*/
public Builder setTerminationTask(final Runnable terminationTask) {
Assert.checkNotNullParam("terminationTask", terminationTask);
this.terminationTask = terminationTask;
return this;
}
/**
* Get the core pool size. This is the size below which new threads will always be created if no idle threads
* are available. If the pool size reaches the core size but has not yet reached the maximum size, a resistance
* factor will be applied to each task submission which determines whether the task should be queued or a new
* thread started.
*
* @return the core pool size
* @see EnhancedQueueExecutor#getCorePoolSize()
*/
public int getCorePoolSize() {
return coreSize;
}
/**
* Set the core pool size. If the configured maximum pool size is less than the configured core size, the
* core size will be reduced to match the maximum size when the thread pool is constructed.
*
* @param coreSize the core pool size (must be greater than or equal to 0, and less than 2^20)
* @return this builder
* @see EnhancedQueueExecutor#setCorePoolSize(int)
*/
public Builder setCorePoolSize(final int coreSize) {
Assert.checkMinimumParameter("coreSize", 0, coreSize);
Assert.checkMaximumParameter("coreSize", TS_THREAD_CNT_MASK, coreSize);
this.coreSize = coreSize;
return this;
}
/**
* Get the maximum pool size. This is the absolute upper limit to the size of the thread pool.
*
* @return the maximum pool size
* @see EnhancedQueueExecutor#getMaximumPoolSize()
*/
public int getMaximumPoolSize() {
return maxSize;
}
/**
* Set the maximum pool size. If the configured maximum pool size is less than the configured core size, the
* core size will be reduced to match the maximum size when the thread pool is constructed.
*
* @param maxSize the maximum pool size (must be greater than or equal to 0, and less than 2^20)
* @return this builder
* @see EnhancedQueueExecutor#setMaximumPoolSize(int)
*/
public Builder setMaximumPoolSize(final int maxSize) {
Assert.checkMinimumParameter("maxSize", 0, maxSize);
Assert.checkMaximumParameter("maxSize", TS_THREAD_CNT_MASK, maxSize);
this.maxSize = maxSize;
return this;
}
/**
* Get the thread keep-alive time. This is the amount of time (in the configured time unit) that idle threads
* will wait for a task before exiting.
*
* @return the thread keep-alive time duration
*/
public Duration getKeepAliveTime() {
return keepAliveTime;
}
/**
* Get the thread keep-alive time. This is the amount of time (in the configured time unit) that idle threads
* will wait for a task before exiting.
*
* @param keepAliveUnits the time keepAliveUnits of the keep-alive time (must not be {@code null})
* @return the thread keep-alive time
* @see EnhancedQueueExecutor#getKeepAliveTime(TimeUnit)
* @deprecated Use {@link #getKeepAliveTime()} instead.
*/
@Deprecated
public long getKeepAliveTime(TimeUnit keepAliveUnits) {
Assert.checkNotNullParam("keepAliveUnits", keepAliveUnits);
final long secondsPart = keepAliveUnits.convert(keepAliveTime.getSeconds(), TimeUnit.SECONDS);
final long nanoPart = keepAliveUnits.convert(keepAliveTime.getNano(), TimeUnit.NANOSECONDS);
final long sum = secondsPart + nanoPart;
return sum < 0 ? Long.MAX_VALUE : sum;
}
/**
* Set the thread keep-alive time.
*
* @param keepAliveTime the thread keep-alive time (must not be {@code null})
*/
public Builder setKeepAliveTime(final Duration keepAliveTime) {
Assert.checkNotNullParam("keepAliveTime", keepAliveTime);
this.keepAliveTime = keepAliveTime;
return this;
}
/**
* Set the thread keep-alive time.
*
* @param keepAliveTime the thread keep-alive time (must be greater than 0)
* @param keepAliveUnits the time keepAliveUnits of the keep-alive time (must not be {@code null})
* @return this builder
* @see EnhancedQueueExecutor#setKeepAliveTime(long, TimeUnit)
* @deprecated Use {@link #setKeepAliveTime(Duration)} instead.
*/
@Deprecated
public Builder setKeepAliveTime(final long keepAliveTime, final TimeUnit keepAliveUnits) {
Assert.checkMinimumParameter("keepAliveTime", 1L, keepAliveTime);
Assert.checkNotNullParam("keepAliveUnits", keepAliveUnits);
this.keepAliveTime = Duration.of(keepAliveTime, JDKSpecific.timeToTemporal(keepAliveUnits));
return this;
}
/**
* Get the thread pool growth resistance. This is the average fraction of submitted tasks that will be enqueued (instead
* of causing a new thread to start) when there are no idle threads and the pool size is equal to or greater than
* the core size (but still less than the maximum size). A value of {@code 0.0} indicates that tasks should
* not be enqueued until the pool is completely full; a value of {@code 1.0} indicates that tasks should always
* be enqueued until the queue is completely full.
*
* @return the thread pool growth resistance
* @see EnhancedQueueExecutor#getGrowthResistance()
*/
public float getGrowthResistance() {
return growthResistance;
}
/**
* Set the thread pool growth resistance.
*
* @param growthResistance the thread pool growth resistance (must be in the range {@code 0.0f ≤ n ≤ 1.0f})
* @return this builder
* @see #getGrowthResistance()
* @see EnhancedQueueExecutor#setGrowthResistance(float)
*/
public Builder setGrowthResistance(final float growthResistance) {
Assert.checkMinimumParameter("growthResistance", 0.0f, growthResistance);
Assert.checkMaximumParameter("growthResistance", 1.0f, growthResistance);
this.growthResistance = growthResistance;
return this;
}
/**
* Determine whether core threads are allowed to time out. A "core thread" is defined as any thread in the pool
* when the pool size is below the pool's {@linkplain #getCorePoolSize() core pool size}.
*
* @return {@code true} if core threads are allowed to time out, {@code false} otherwise
* @see EnhancedQueueExecutor#allowsCoreThreadTimeOut()
*/
public boolean allowsCoreThreadTimeOut() {
return allowCoreTimeOut;
}
/**
* Establish whether core threads are allowed to time out. A "core thread" is defined as any thread in the pool
* when the pool size is below the pool's {@linkplain #getCorePoolSize() core pool size}.
*
* @param allowCoreTimeOut {@code true} if core threads are allowed to time out, {@code false} otherwise
* @return this builder
* @see EnhancedQueueExecutor#allowCoreThreadTimeOut(boolean)
*/
public Builder allowCoreThreadTimeOut(final boolean allowCoreTimeOut) {
this.allowCoreTimeOut = allowCoreTimeOut;
return this;
}
/**
* Get the maximum queue size. If the queue is full and a task cannot be immediately accepted, rejection will result.
*
* @return the maximum queue size
* @see EnhancedQueueExecutor#getMaximumQueueSize()
*/
public int getMaximumQueueSize() {
return maxQueueSize;
}
/**
* Set the maximum queue size.
* This has no impact when {@code jboss.threads.eqe.unlimited-queue} is set.
*
* @param maxQueueSize the maximum queue size (must be ≥ 0)
* @return this builder
* @see EnhancedQueueExecutor#setMaximumQueueSize(int)
*/
public Builder setMaximumQueueSize(final int maxQueueSize) {
Assert.checkMinimumParameter("maxQueueSize", 0, maxQueueSize);
Assert.checkMaximumParameter("maxQueueSize", Integer.MAX_VALUE, maxQueueSize);
this.maxQueueSize = maxQueueSize;
return this;
}
/**
* Get the handoff executor.
*
* @return the handoff executor (not {@code null})
*/
public Executor getHandoffExecutor() {
return handoffExecutor;
}
/**
* Set the handoff executor.
*
* @param handoffExecutor the handoff executor (must not be {@code null})
* @return this builder
*/
public Builder setHandoffExecutor(final Executor handoffExecutor) {
Assert.checkNotNullParam("handoffExecutor", handoffExecutor);
this.handoffExecutor = handoffExecutor;
return this;
}
/**
* Get the uncaught exception handler.
*
* @return the uncaught exception handler (not {@code null})
*/
public Thread.UncaughtExceptionHandler getExceptionHandler() {
return exceptionHandler;
}
/**
* Set the uncaught exception handler.
*
* @param exceptionHandler the uncaught exception handler (must not be {@code null})
* @return this builder
*/
public Builder setExceptionHandler(final Thread.UncaughtExceptionHandler exceptionHandler) {
this.exceptionHandler = exceptionHandler;
return this;
}
/**
* Construct the executor from the configured parameters.
*
* @return the executor, which will be active and ready to accept tasks (not {@code null})
*/
public EnhancedQueueExecutor build() {
return new EnhancedQueueExecutor(this);
}
/**
* Determine whether an MBean should automatically be registered for this pool.
*
* @return {@code true} if an MBean is to be auto-registered; {@code false} otherwise
*/
public boolean isRegisterMBean() {
return registerMBean;
}
/**
* Establish whether an MBean should automatically be registered for this pool.
*
* @param registerMBean {@code true} if an MBean is to be auto-registered; {@code false} otherwise
* @return this builder
*/
public Builder setRegisterMBean(final boolean registerMBean) {
this.registerMBean = registerMBean;
return this;
}
/**
* Get the overridden MBean name.
*
* @return the overridden MBean name, or {@code null} if a default name should be generated
*/
public String getMBeanName() {
return mBeanName;
}
/**
* Set the overridden MBean name.
*
* @param mBeanName the overridden MBean name, or {@code null} if a default name should be generated
* @return this builder
*/
public Builder setMBeanName(final String mBeanName) {
this.mBeanName = mBeanName;
return this;
}
/**
* Get the context handler for the user-defined context.
*
* @return the context handler for the user-defined context (not {@code null})
*/
public ContextHandler> getContextHandler() {
return contextHandler;
}
/**
* Set the context handler for the user-defined context.
*
* @param contextHandler the context handler for the user-defined context
* @return this builder
*/
public Builder setContextHandler(final ContextHandler> contextHandler) {
Assert.checkNotNullParam("contextHandler", contextHandler);
this.contextHandler = contextHandler;
return this;
}
}
// =======================================================
// ExecutorService
// =======================================================
/**
* Execute a task.
*
* @param runnable the task to execute (must not be {@code null})
*/
public void execute(Runnable runnable) {
Assert.checkNotNullParam("runnable", runnable);
final Task realRunnable = new Task(runnable, contextHandler.captureContext());
int result;
result = tryExecute(realRunnable);
boolean ok = false;
if (result == EXE_OK) {
// last check to ensure that there is at least one existent thread to avoid rare thread timeout race condition
if (currentSizeOf(threadStatus) == 0 && tryAllocateThread(0.0f) == AT_YES && ! doStartThread(null)) {
deallocateThread();
}
if (UPDATE_STATISTICS) submittedTaskCounter.increment();
return;
} else if (result == EXE_CREATE_THREAD) try {
ok = doStartThread(realRunnable);
} finally {
if (! ok) deallocateThread();
} else {
if (UPDATE_STATISTICS) rejectedTaskCounter.increment();
if (result == EXE_REJECT_SHUTDOWN) {
rejectShutdown(realRunnable);
} else {
assert result == EXE_REJECT_QUEUE_FULL;
rejectQueueFull(realRunnable);
}
}
}
/**
* Request that shutdown be initiated for this thread pool. This is equivalent to calling
* {@link #shutdown(boolean) shutdown(false)}; see that method for more information.
*/
public void shutdown() {
shutdown(false);
}
/**
* Attempt to stop the thread pool immediately by interrupting all running threads and de-queueing all pending
* tasks. The thread pool might not be fully stopped when this method returns, if a currently running task
* does not respect interruption.
*
* @return a list of pending tasks (not {@code null})
*/
public List shutdownNow() {
shutdown(true);
final ArrayList list = new ArrayList<>();
TaskNode head = this.head;
QNode headNext;
for (;;) {
headNext = head.getNext();
if (headNext == head) {
// a racing consumer has already consumed it (and moved head)
head = this.head;
continue;
}
if (headNext instanceof TaskNode) {
TaskNode taskNode = (TaskNode) headNext;
if (compareAndSetHead(head, taskNode)) {
// save from GC nepotism
head.setNextOrdered(head);
if (! NO_QUEUE_LIMIT) decreaseQueueSize();
head = taskNode;
list.add(taskNode.task.handoff());
}
// retry
} else {
// no more tasks;
return list;
}
}
}
/**
* Determine whether shutdown was requested on this thread pool.
*
* @return {@code true} if shutdown was requested, {@code false} otherwise
*/
public boolean isShutdown() {
return isShutdownRequested(threadStatus);
}
/**
* Determine whether shutdown has completed on this thread pool.
*
* @return {@code true} if shutdown has completed, {@code false} otherwise
*/
public boolean isTerminated() {
return isShutdownComplete(threadStatus);
}
/**
* Wait for the thread pool to complete termination. If the timeout expires before the thread pool is terminated,
* {@code false} is returned. If the calling thread is interrupted before the thread pool is terminated, then
* an {@link InterruptedException} is thrown.
*
* @param timeout the amount of time to wait (must be ≥ 0)
* @param unit the unit of time to use for waiting (must not be {@code null})
* @return {@code true} if the thread pool terminated within the given timeout, {@code false} otherwise
* @throws InterruptedException if the calling thread was interrupted before either the time period elapsed or the pool terminated successfully
*/
public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException {
Assert.checkMinimumParameter("timeout", 0, timeout);
Assert.checkNotNullParam("unit", unit);
if (timeout > 0) {
final Thread thread = Thread.currentThread();
if (runningThreads.contains(thread) || thread == schedulerThread) {
throw Messages.msg.cannotAwaitWithin();
}
Waiter waiters = this.terminationWaiters;
if (waiters == TERMINATE_COMPLETE_WAITER) {
return true;
}
final Waiter waiter = new Waiter(waiters);
waiter.setThread(currentThread());
while (! compareAndSetTerminationWaiters(waiters, waiter)) {
waiters = this.terminationWaiters;
if (waiters == TERMINATE_COMPLETE_WAITER) {
return true;
}
waiter.setNext(waiters);
}
try {
parkNanos(this, unit.toNanos(timeout));
} finally {
// prevent future spurious unparks without sabotaging the queue's integrity
waiter.setThread(null);
}
}
if (Thread.interrupted()) throw new InterruptedException();
return isTerminated();
}
// =======================================================
// ScheduledExecutorService
// =======================================================
public ScheduledFuture> schedule(final Runnable command, final long delay, final TimeUnit unit) {
startScheduleThread();
return schedulerTask.schedule(new RunnableScheduledFuture(command, delay, unit));
}
public ScheduledFuture schedule(final Callable callable, final long delay, final TimeUnit unit) {
startScheduleThread();
return schedulerTask.schedule(new CallableScheduledFuture(callable, delay, unit));
}
public ScheduledFuture> scheduleAtFixedRate(final Runnable command, final long initialDelay, final long period, final TimeUnit unit) {
startScheduleThread();
return schedulerTask.schedule(new FixedRateRunnableScheduledFuture(command, initialDelay, period, unit));
}
public ScheduledFuture> scheduleWithFixedDelay(final Runnable command, final long initialDelay, final long delay, final TimeUnit unit) {
startScheduleThread();
return schedulerTask.schedule(new FixedDelayRunnableScheduledFuture(command, initialDelay, delay, unit));
}
private void startScheduleThread() {
// this should be fairly quick...
if (schedulerThread.getState() == Thread.State.NEW) try {
schedulerThread.start();
} catch (IllegalThreadStateException ignored) {
// make sure it's race-proof
}
}
// =======================================================
// Management
// =======================================================
public StandardThreadPoolMXBean getThreadPoolMXBean() {
return mxBean;
}
// =======================================================
// Other public API
// =======================================================
/**
* Initiate shutdown of this thread pool. After this method is called, no new tasks will be accepted. Once
* the last task is complete, the thread pool will be terminated and its
* {@linkplain Builder#setTerminationTask(Runnable) termination task}
* will be called. Calling this method more than once has no additional effect, unless all previous calls
* had the {@code interrupt} parameter set to {@code false} and the subsequent call sets it to {@code true}, in
* which case all threads in the pool will be interrupted.
*
* @param interrupt {@code true} to request that currently-running tasks be interrupted; {@code false} otherwise
*/
public void shutdown(boolean interrupt) {
long oldStatus, newStatus;
// state change sh1:
// threadStatus ← threadStatus | shutdown
// succeeds: -
// preconditions:
// none (thread status may be in any state)
// postconditions (succeed):
// (always) ShutdownRequested set in threadStatus
// (maybe) ShutdownInterrupted set in threadStatus (depends on parameter)
// (maybe) ShutdownComplete set in threadStatus (if currentSizeOf() == 0)
// (maybe) exit if no change
// postconditions (fail): -
// post-actions (fail):
// repeat state change until success or return
do {
oldStatus = threadStatus;
newStatus = withShutdownRequested(oldStatus);
if (interrupt) newStatus = withShutdownInterrupt(newStatus);
if (currentSizeOf(oldStatus) == 0) newStatus = withShutdownComplete(newStatus);
if (newStatus == oldStatus) return;
} while (! compareAndSetThreadStatus(oldStatus, newStatus));
assert oldStatus != newStatus;
if (isShutdownRequested(newStatus) != isShutdownRequested(oldStatus)) {
assert ! isShutdownRequested(oldStatus); // because it can only ever be set, not cleared
// we initiated shutdown
// terminate the scheduler
schedulerTask.shutdown();
// clear out all consumers and append a dummy waiter node
TaskNode tail = this.tail;
QNode tailNext;
// a marker to indicate that termination was requested
for (;;) {
tailNext = tail.getNext();
if (tailNext instanceof TaskNode) {
tail = (TaskNode) tailNext;
} else if (tailNext instanceof PoolThreadNode || tailNext == null) {
// remove the entire chain from this point
PoolThreadNode node = (PoolThreadNode) tailNext;
// state change sh2:
// tail.next ← terminateNode(null)
// succeeds: sh1
// preconditions:
// threadStatus contains shutdown
// tail(snapshot) is a task node
// tail(snapshot).next is a (list of) pool thread node(s) or null
// postconditions (succeed):
// tail(snapshot).next is TERMINATE_REQUESTED
if (tail.compareAndSetNext(node, TERMINATE_REQUESTED)) {
// got it!
// state change sh3:
// node.task ← EXIT
// preconditions:
// none (node task may be in any state)
// postconditions (succeed):
// task is EXIT
// postconditions (fail):
// no change (repeat loop)
while (node != null) {
node.compareAndSetTask(WAITING, EXIT);
node.unpark();
node = node.getNext();
}
// success; exit loop
break;
}
// repeat loop (failed CAS)
} else if (tailNext instanceof TerminateWaiterNode) {
// theoretically impossible, but it means we have nothing else to do
break;
} else {
throw Assert.unreachableCode();
}
}
}
if (isShutdownInterrupt(newStatus) != isShutdownInterrupt(oldStatus)) {
assert ! isShutdownInterrupt(oldStatus); // because it can only ever be set, not cleared
// we initiated interrupt, so interrupt all currently active threads
for (Thread thread : runningThreads) {
thread.interrupt();
}
}
if (isShutdownComplete(newStatus) != isShutdownComplete(oldStatus)) {
assert ! isShutdownComplete(oldStatus); // because it can only ever be set, not cleared
completeTermination();
}
}
/**
* Determine if this thread pool is in the process of terminating but has not yet completed.
*
* @return {@code true} if the thread pool is terminating, or {@code false} if the thread pool is not terminating or has completed termination
*/
public boolean isTerminating() {
final long threadStatus = this.threadStatus;
return isShutdownRequested(threadStatus) && ! isShutdownComplete(threadStatus);
}
/**
* Start an idle core thread. Normally core threads are only begun when a task was submitted to the executor
* but no thread is immediately available to handle the task.
*
* @return {@code true} if a core thread was started, or {@code false} if all core threads were already running or
* if the thread failed to be created for some other reason
*/
public boolean prestartCoreThread() {
if (tryAllocateThread(1.0f) != AT_YES) return false;
if (doStartThread(null)) return true;
deallocateThread();
return false;
}
/**
* Start all core threads. Calls {@link #prestartCoreThread()} in a loop until it returns {@code false}.
*
* @return the number of core threads created
*/
public int prestartAllCoreThreads() {
int cnt = 0;
while (prestartCoreThread()) cnt ++;
return cnt;
}
// =======================================================
// Tuning & configuration parameters (run time)
// =======================================================
/**
* Get the thread pool growth resistance. This is the average fraction of submitted tasks that will be enqueued (instead
* of causing a new thread to start) when there are no idle threads and the pool size is equal to or greater than
* the core size (but still less than the maximum size). A value of {@code 0.0} indicates that tasks should
* not be enqueued until the pool is completely full; a value of {@code 1.0} indicates that tasks should always
* be enqueued until the queue is completely full.
*
* @return the configured growth resistance factor
* @see Builder#getGrowthResistance() Builder.getGrowthResistance()
*/
public float getGrowthResistance() {
return growthResistance;
}
/**
* Set the growth resistance factor.
*
* @param growthResistance the thread pool growth resistance (must be in the range {@code 0.0f ≤ n ≤ 1.0f})
* @see Builder#setGrowthResistance(float) Builder.setGrowthResistance()
*/
public void setGrowthResistance(final float growthResistance) {
Assert.checkMinimumParameter("growthResistance", 0.0f, growthResistance);
Assert.checkMaximumParameter("growthResistance", 1.0f, growthResistance);
this.growthResistance = growthResistance;
}
/**
* Get the core pool size. This is the size below which new threads will always be created if no idle threads
* are available. If the pool size reaches the core size but has not yet reached the maximum size, a resistance
* factor will be applied to each task submission which determines whether the task should be queued or a new
* thread started.
*
* @return the core pool size
* @see Builder#getCorePoolSize() Builder.getCorePoolSize()
*/
public int getCorePoolSize() {
return coreSizeOf(threadStatus);
}
/**
* Set the core pool size. If the configured maximum pool size is less than the configured core size, the
* core size will be reduced to match the maximum size when the thread pool is constructed.
*
* @param corePoolSize the core pool size (must be greater than or equal to 0, and less than 2^20)
* @see Builder#setCorePoolSize(int) Builder.setCorePoolSize()
*/
public void setCorePoolSize(final int corePoolSize) {
Assert.checkMinimumParameter("corePoolSize", 0, corePoolSize);
Assert.checkMaximumParameter("corePoolSize", TS_THREAD_CNT_MASK, corePoolSize);
long oldVal, newVal;
do {
oldVal = threadStatus;
if (corePoolSize > maxSizeOf(oldVal)) {
// automatically bump up max size to match
newVal = withCoreSize(withMaxSize(oldVal, corePoolSize), corePoolSize);
} else {
newVal = withCoreSize(oldVal, corePoolSize);
}
} while (! compareAndSetThreadStatus(oldVal, newVal));
if (maxSizeOf(newVal) < maxSizeOf(oldVal) || coreSizeOf(newVal) < coreSizeOf(oldVal)) {
// poke all the threads to try to terminate any excess eagerly
for (Thread activeThread : runningThreads) {
unpark(activeThread);
}
}
}
/**
* Get the maximum pool size. This is the absolute upper limit to the size of the thread pool.
*
* @return the maximum pool size
* @see Builder#getMaximumPoolSize() Builder.getMaximumPoolSize()
*/
public int getMaximumPoolSize() {
return maxSizeOf(threadStatus);
}
/**
* Set the maximum pool size. If the configured maximum pool size is less than the configured core size, the
* core size will be reduced to match the maximum size when the thread pool is constructed.
*
* @param maxPoolSize the maximum pool size (must be greater than or equal to 0, and less than 2^20)
* @see Builder#setMaximumPoolSize(int) Builder.setMaximumPoolSize()
*/
public void setMaximumPoolSize(final int maxPoolSize) {
Assert.checkMinimumParameter("maxPoolSize", 0, maxPoolSize);
Assert.checkMaximumParameter("maxPoolSize", TS_THREAD_CNT_MASK, maxPoolSize);
long oldVal, newVal;
do {
oldVal = threadStatus;
if (maxPoolSize < coreSizeOf(oldVal)) {
// automatically bump down core size to match
newVal = withCoreSize(withMaxSize(oldVal, maxPoolSize), maxPoolSize);
} else {
newVal = withMaxSize(oldVal, maxPoolSize);
}
} while (! compareAndSetThreadStatus(oldVal, newVal));
if (maxSizeOf(newVal) < maxSizeOf(oldVal) || coreSizeOf(newVal) < coreSizeOf(oldVal)) {
// poke all the threads to try to terminate any excess eagerly
for (Thread activeThread : runningThreads) {
unpark(activeThread);
}
}
}
/**
* Determine whether core threads are allowed to time out. A "core thread" is defined as any thread in the pool
* when the pool size is below the pool's {@linkplain #getCorePoolSize() core pool size}.
*
* @return {@code true} if core threads are allowed to time out, {@code false} otherwise
* @see Builder#allowsCoreThreadTimeOut() Builder.allowsCoreThreadTimeOut()
*/
public boolean allowsCoreThreadTimeOut() {
return isAllowCoreTimeout(threadStatus);
}
/**
* Establish whether core threads are allowed to time out. A "core thread" is defined as any thread in the pool
* when the pool size is below the pool's {@linkplain #getCorePoolSize() core pool size}.
*
* @param value {@code true} if core threads are allowed to time out, {@code false} otherwise
* @see Builder#allowCoreThreadTimeOut(boolean) Builder.allowCoreThreadTimeOut()
*/
public void allowCoreThreadTimeOut(boolean value) {
long oldVal, newVal;
do {
oldVal = threadStatus;
newVal = withAllowCoreTimeout(oldVal, value);
if (oldVal == newVal) return;
} while (! compareAndSetThreadStatus(oldVal, newVal));
if (value) {
// poke all the threads to try to terminate any excess eagerly
for (Thread activeThread : runningThreads) {
unpark(activeThread);
}
}
}
/**
* Get the thread keep-alive time. This is the minimum length of time that idle threads should remain until they exit.
* Unless core threads are allowed to time out, threads will only exit if the current thread count exceeds the core
* limit.
*
* @param keepAliveUnits the unit in which the result should be expressed (must not be {@code null})
* @return the amount of time (will be greater than zero)
* @see Builder#getKeepAliveTime(TimeUnit) Builder.getKeepAliveTime()
* @deprecated Use {@link #getKeepAliveTime()} instead.
*/
@Deprecated
public long getKeepAliveTime(TimeUnit keepAliveUnits) {
Assert.checkNotNullParam("keepAliveUnits", keepAliveUnits);
return keepAliveUnits.convert(timeoutNanos, TimeUnit.NANOSECONDS);
}
/**
* Get the thread keep-alive time. This is the minimum length of time that idle threads should remain until they exit.
* Unless core threads are allowed to time out, threads will only exit if the current thread count exceeds the core
* limit.
*
* @return the amount of time (will be greater than zero)
* @see Builder#getKeepAliveTime() Builder.getKeepAliveTime()
*/
public Duration getKeepAliveTime() {
return Duration.of(timeoutNanos, ChronoUnit.NANOS);
}
/**
* Set the thread keep-alive time. This is the minimum length of time that idle threads should remain until they exit.
* Unless core threads are allowed to time out, threads will only exit if the current thread count exceeds the core
* limit.
*
* @param keepAliveTime the thread keep-alive time (must be > 0)
* @param keepAliveUnits the unit in which the value is expressed (must not be {@code null})
* @see Builder#setKeepAliveTime(long, TimeUnit) Builder.setKeepAliveTime()
* @deprecated Use {@link #setKeepAliveTime(Duration)} instead.
*/
@Deprecated
public void setKeepAliveTime(final long keepAliveTime, final TimeUnit keepAliveUnits) {
Assert.checkMinimumParameter("keepAliveTime", 1L, keepAliveTime);
Assert.checkNotNullParam("keepAliveUnits", keepAliveUnits);
timeoutNanos = max(1L, keepAliveUnits.toNanos(keepAliveTime));
}
/**
* Set the thread keep-alive time. This is the minimum length of time that idle threads should remain until they exit.
* Unless core threads are allowed to time out, threads will only exit if the current thread count exceeds the core
* limit.
*
* @param keepAliveTime the thread keep-alive time (must not be {@code null})
* @see Builder#setKeepAliveTime(Duration) Builder.setKeepAliveTime()
*/
public void setKeepAliveTime(final Duration keepAliveTime) {
Assert.checkNotNullParam("keepAliveTime", keepAliveTime);
timeoutNanos = TimeUtil.clampedPositiveNanos(keepAliveTime);
}
/**
* Get the maximum queue size. If the queue is full and a task cannot be immediately accepted, rejection will result.
*
* @return the maximum queue size
* @see Builder#getMaximumQueueSize() Builder.getMaximumQueueSize()
*/
public int getMaximumQueueSize() {
return maxQueueSizeOf(queueSize);
}
/**
* Set the maximum queue size. If the new maximum queue size is smaller than the current queue size, there is no
* effect other than preventing tasks from being enqueued until the size decreases below the maximum again.
*
* @param maxQueueSize the maximum queue size (must be ≥ 0)
* @see Builder#setMaximumQueueSize(int) Builder.setMaximumQueueSize()
*/
public void setMaximumQueueSize(final int maxQueueSize) {
Assert.checkMinimumParameter("maxQueueSize", 0, maxQueueSize);
Assert.checkMaximumParameter("maxQueueSize", Integer.MAX_VALUE, maxQueueSize);
if (NO_QUEUE_LIMIT) return;
long oldVal;
do {
oldVal = queueSize;
} while (! compareAndSetQueueSize(oldVal, withMaxQueueSize(oldVal, maxQueueSize)));
}
/**
* Get the executor to delegate to in the event of task rejection.
*
* @return the executor to delegate to in the event of task rejection (not {@code null})
*/
public Executor getHandoffExecutor() {
return handoffExecutor;
}
/**
* Set the executor to delegate to in the event of task rejection.
*
* @param handoffExecutor the executor to delegate to in the event of task rejection (must not be {@code null})
*/
public void setHandoffExecutor(final Executor handoffExecutor) {
Assert.checkNotNullParam("handoffExecutor", handoffExecutor);
this.handoffExecutor = handoffExecutor;
}
/**
* Get the exception handler to use for uncaught exceptions.
*
* @return the exception handler to use for uncaught exceptions (not {@code null})
*/
public Thread.UncaughtExceptionHandler getExceptionHandler() {
return exceptionHandler;
}
/**
* Set the exception handler to use for uncaught exceptions.
*
* @param exceptionHandler the exception handler to use for uncaught exceptions (must not be {@code null})
*/
public void setExceptionHandler(final Thread.UncaughtExceptionHandler exceptionHandler) {
Assert.checkNotNullParam("exceptionHandler", exceptionHandler);
this.exceptionHandler = exceptionHandler;
}
/**
* Set the termination task, overwriting any previous setting.
*
* @param terminationTask the termination task, or {@code null} to perform no termination task
*/
public void setTerminationTask(final Runnable terminationTask) {
this.terminationTask = terminationTask;
}
// =======================================================
// Statistics & metrics API
// =======================================================
/**
* Get an estimate of the current queue size.
*
* @return an estimate of the current queue size or -1 when {@code jboss.threads.eqe.unlimited-queue} is enabled
*/
public int getQueueSize() {
return NO_QUEUE_LIMIT ? -1 : currentQueueSizeOf(queueSize);
}
/**
* Get an estimate of the peak number of threads that the pool has ever held.
*
* @return an estimate of the peak number of threads that the pool has ever held
*/
public int getLargestPoolSize() {
return UPDATE_STATISTICS ? peakThreadCount : -1;
}
/**
* Get an estimate of the number of threads which are currently doing work on behalf of the thread pool.
*
* @return the active count estimate or -1 when {@code jboss.threads.eqe.statistics.active-count} is disabled
*/
public int getActiveCount() {
return UPDATE_ACTIVE_COUNT ? activeCount : -1;
}
/**
* Get an estimate of the peak size of the queue.
*
* return an estimate of the peak size of the queue or -1 when {@code jboss.threads.eqe.statistics}
* is disabled or {@code jboss.threads.eqe.unlimited-queue} is enabled
*/
public int getLargestQueueSize() {
return UPDATE_STATISTICS && !NO_QUEUE_LIMIT ? peakQueueSize : -1;
}
/**
* Get an estimate of the total number of tasks ever submitted to this thread pool.
*
* @return an estimate of the total number of tasks ever submitted to this thread pool
* or -1 when {@code jboss.threads.eqe.statistics} is disabled
*/
public long getSubmittedTaskCount() {
return UPDATE_STATISTICS ? submittedTaskCounter.longValue() : -1;
}
/**
* Get an estimate of the total number of tasks ever rejected by this thread pool for any reason.
*
* @return an estimate of the total number of tasks ever rejected by this thread pool
* or -1 when {@code jboss.threads.eqe.statistics} is disabled
*/
public long getRejectedTaskCount() {
return UPDATE_STATISTICS ? rejectedTaskCounter.longValue() : -1;
}
/**
* Get an estimate of the number of tasks completed by this thread pool.
*
* @return an estimate of the number of tasks completed by this thread pool
* or -1 when {@code jboss.threads.eqe.statistics} is disabled
*/
public long getCompletedTaskCount() {
return UPDATE_STATISTICS ? completedTaskCounter.longValue() : -1;
}
/**
* Get an estimate of the current number of active threads in the pool.
*
* @return an estimate of the current number of active threads in the pool
*/
public int getPoolSize() {
return currentSizeOf(threadStatus);
}
/**
* Get an array containing an approximate snapshot of the currently running threads in
* this executor.
*
* @return an array of running (unterminated) threads (not {@code null})
*/
public Thread[] getRunningThreads() {
return runningThreads.toArray(NO_THREADS);
}
static class MBeanUnregisterAction implements PrivilegedAction {
static void forceInit() {
}
private final Object handle;
MBeanUnregisterAction(final Object handle) {
this.handle = handle;
}
public Void run() {
try {
ManagementFactory.getPlatformMBeanServer().unregisterMBean(((ObjectInstance) handle).getObjectName());
} catch (Throwable ignored) {
}
return null;
}
}
// =======================================================
// Pooled thread body
// =======================================================
final class ThreadBody implements Runnable {
private Task initialTask;
ThreadBody(final Task initialTask) {
this.initialTask = initialTask;
}
/**
* Execute the body of the thread. On entry the thread is added to the {@link #runningThreads} set, and on
* exit it is removed.
*/
public void run() {
final Thread currentThread = Thread.currentThread();
final LongAdder spinMisses = EnhancedQueueExecutor.this.spinMisses;
runningThreads.add(currentThread);
// run the initial task
nullToNop(getAndClearInitialTask()).run();
// Eagerly allocate a PoolThreadNode for the next time it's needed
PoolThreadNode nextPoolThreadNode = new PoolThreadNode(currentThread);
// main loop
QNode node;
processingQueue: for (;;) {
node = getOrAddNode(nextPoolThreadNode);
if (node instanceof TaskNode) {
// task node was removed
((TaskNode) node).getAndClearTask().run();
continue;
} else if (node == nextPoolThreadNode) {
// pool thread node was added
final PoolThreadNode newNode = nextPoolThreadNode;
// nextPoolThreadNode has been added to the queue, a new node is required for next time.
nextPoolThreadNode = new PoolThreadNode(currentThread);
// at this point, we are registered into the queue
long start = System.nanoTime();
long elapsed = 0L;
waitingForTask: for (;;) {
Runnable task = newNode.getTask();
assert task != ACCEPTED && task != GAVE_UP && task != null;
if (task != WAITING && task != EXIT) {
if (newNode.compareAndSetTask(task, ACCEPTED)) {
// we have a task to run, so run it and then abandon the node
task.run();
// rerun outer
continue processingQueue;
}
// we had a task to run, but we failed to CAS it for some reason, so retry
if (UPDATE_STATISTICS) spinMisses.increment();
continue waitingForTask;
} else {
final long timeoutNanos = EnhancedQueueExecutor.this.timeoutNanos;
long oldVal = threadStatus;
if (elapsed >= timeoutNanos || task == EXIT || currentSizeOf(oldVal) > maxSizeOf(oldVal)) {
// try to exit this thread, if we are allowed
if (task == EXIT ||
isShutdownRequested(oldVal) ||
isAllowCoreTimeout(oldVal) ||
currentSizeOf(oldVal) > coreSizeOf(oldVal)
) {
if (newNode.compareAndSetTask(task, GAVE_UP)) {
for (;;) {
if (tryDeallocateThread(oldVal)) {
// clear to exit.
runningThreads.remove(currentThread);
return;
}
if (UPDATE_STATISTICS) spinMisses.increment();
oldVal = threadStatus;
}
//throw Assert.unreachableCode();
}
continue waitingForTask;
} else {
if (elapsed >= timeoutNanos) {
newNode.park(EnhancedQueueExecutor.this);
} else {
newNode.park(EnhancedQueueExecutor.this, timeoutNanos - elapsed);
}
Thread.interrupted();
elapsed = System.nanoTime() - start;
// retry inner
continue waitingForTask;
}
//throw Assert.unreachableCode();
} else {
assert task == WAITING;
newNode.park(EnhancedQueueExecutor.this, timeoutNanos - elapsed);
Thread.interrupted();
elapsed = System.nanoTime() - start;
// retry inner
continue waitingForTask;
}
//throw Assert.unreachableCode();
}
//throw Assert.unreachableCode();
} // :waitingForTask
//throw Assert.unreachableCode();
} else {
assert node instanceof TerminateWaiterNode;
// we're shutting down!
runningThreads.remove(currentThread);
deallocateThread();
return;
}
//throw Assert.unreachableCode();
} // :processingQueue
//throw Assert.unreachableCode();
}
private QNode getOrAddNode(PoolThreadNode nextPoolThreadNode) {
TaskNode head;
QNode headNext;
for (;;) {
head = EnhancedQueueExecutor.this.head;
headNext = head.getNext();
// headNext == head can happen if another consumer has already consumed head:
// retry with a fresh head
if (headNext != head) {
if (headNext instanceof TaskNode) {
TaskNode taskNode = (TaskNode) headNext;
if (compareAndSetHead(head, taskNode)) {
// save from GC Nepotism: generational GCs don't like
// cross-generational references, so better to "clean-up" head::next
// to save dragging head::next into the old generation.
// Clean-up cannot just null out next
head.setNextOrdered(head);
if (!NO_QUEUE_LIMIT) decreaseQueueSize();
return taskNode;
}
} else if (headNext instanceof PoolThreadNode || headNext == null) {
nextPoolThreadNode.setNextRelaxed(headNext);
if (head.compareAndSetNext(headNext, nextPoolThreadNode)) {
return nextPoolThreadNode;
} else if (headNext != null) {
// GC Nepotism:
// save dragging headNext into old generation
// (although being a PoolThreadNode it won't make a big difference)
nextPoolThreadNode.setNextRelaxed(null);
}
} else {
assert headNext instanceof TerminateWaiterNode;
return headNext;
}
}
if (UPDATE_STATISTICS) spinMisses.increment();
}
}
private Runnable getAndClearInitialTask() {
Runnable initial = initialTask;
this.initialTask = null;
return initial;
}
}
// =======================================================
// Thread starting
// =======================================================
/**
* Allocate a new thread.
*
* @param growthResistance the growth resistance to apply
* @return {@code AT_YES} if a thread is allocated; {@code AT_NO} if a thread was not allocated; {@code AT_SHUTDOWN} if the pool is being shut down
*/
int tryAllocateThread(final float growthResistance) {
int oldSize;
long oldStat;
for (;;) {
oldStat = threadStatus;
if (isShutdownRequested(oldStat)) {
return AT_SHUTDOWN;
}
oldSize = currentSizeOf(oldStat);
if (oldSize >= maxSizeOf(oldStat)) {
// max threads already reached
return AT_NO;
}
if (oldSize >= coreSizeOf(oldStat) && oldSize > 0) {
// core threads are reached; check resistance factor (if no threads are running, then always start a thread)
if (growthResistance != 0.0f && (growthResistance == 1.0f || ThreadLocalRandom.current().nextFloat() < growthResistance)) {
// do not create a thread this time
return AT_NO;
}
}
// try to increase
final int newSize = oldSize + 1;
// state change ex3:
// threadStatus.size ← threadStatus(snapshot).size + 1
// cannot succeed: sh1
// succeeds: -
// preconditions:
// ! threadStatus(snapshot).shutdownRequested
// threadStatus(snapshot).size < threadStatus(snapshot).maxSize
// threadStatus(snapshot).size < threadStatus(snapshot).coreSize || random < growthResistance
// post-actions (fail):
// retry whole loop
if (compareAndSetThreadStatus(oldStat, withCurrentSize(oldStat, newSize))) {
// increment peak thread count
if (UPDATE_STATISTICS) {
int oldVal;
do {
oldVal = peakThreadCount;
if (oldVal >= newSize) break;
} while (! compareAndSetPeakThreadCount(oldVal, newSize));
}
return AT_YES;
}
if (UPDATE_STATISTICS) spinMisses.increment();
}
}
/**
* Roll back a thread allocation, possibly terminating the pool. Only call after {@link #tryAllocateThread(float)} returns {@link #AT_YES}.
*/
void deallocateThread() {
long oldStat;
do {
oldStat = threadStatus;
} while (! tryDeallocateThread(oldStat));
}
/**
* Try to roll back a thread allocation, possibly running the termination task if the pool would be terminated
* by last thread exit.
*
* @param oldStat the {@code threadStatus} to CAS
* @return {@code true} if the thread was deallocated, or {@code false} to retry with a new {@code oldStat}
*/
boolean tryDeallocateThread(long oldStat) {
// roll back our thread allocation attempt
// state change ex4:
// threadStatus.size ← threadStatus.size - 1
// succeeds: ex3
// preconditions:
// threadStatus.size > 0
long newStat = withCurrentSize(oldStat, currentSizeOf(oldStat) - 1);
if (currentSizeOf(newStat) == 0 && isShutdownRequested(oldStat)) {
newStat = withShutdownComplete(newStat);
}
if (! compareAndSetThreadStatus(oldStat, newStat)) return false;
if (isShutdownComplete(newStat)) {
completeTermination();
}
return true;
}
/**
* Start an allocated thread.
*
* @param runnable the task or {@code null}
* @return {@code true} if the thread was started, {@code false} otherwise
* @throws RejectedExecutionException if {@code runnable} is not {@code null} and the thread could not be created or started
*/
boolean doStartThread(Task runnable) throws RejectedExecutionException {
Thread thread;
try {
thread = threadFactory.newThread(new ThreadBody(runnable));
} catch (Throwable t) {
if (runnable != null) {
if (UPDATE_STATISTICS) rejectedTaskCounter.increment();
rejectException(runnable, t);
}
return false;
}
if (thread == null) {
if (runnable != null) {
if (UPDATE_STATISTICS) rejectedTaskCounter.increment();
rejectNoThread(runnable);
}
return false;
}
try {
thread.start();
} catch (Throwable t) {
if (runnable != null) {
if (UPDATE_STATISTICS) rejectedTaskCounter.increment();
rejectException(runnable, t);
}
return false;
}
return true;
}
// =======================================================
// Task submission
// =======================================================
private int tryExecute(final Task runnable) {
QNode tailNext;
TaskNode tail = this.tail;
TaskNode node = null;
for (;;) {
tailNext = tail.getNext();
if (tailNext == tail) {
// tail is already consumed, retry with new tail(snapshot)
} else if (tailNext == null) {
// no consumers available; maybe we can start one
int tr = tryAllocateThread(growthResistance);
if (tr == AT_YES) {
return EXE_CREATE_THREAD;
}
if (tr == AT_SHUTDOWN) {
return EXE_REJECT_SHUTDOWN;
}
assert tr == AT_NO;
// no; try to enqueue
if (!NO_QUEUE_LIMIT && !increaseQueueSize()) {
// queue is full
// OK last effort to create a thread, disregarding growth limit
tr = tryAllocateThread(0.0f);
if (tr == AT_YES) {
return EXE_CREATE_THREAD;
}
if (tr == AT_SHUTDOWN) {
return EXE_REJECT_SHUTDOWN;
}
assert tr == AT_NO;
return EXE_REJECT_QUEUE_FULL;
}
// queue size increased successfully; we can add to the list
if (node == null) {
// avoid re-allocating TaskNode instances on subsequent iterations
node = new TaskNode(runnable);
}
// state change ex5:
// tail(snapshot).next ← new task node
// cannot succeed: sh2
// preconditions:
// tail(snapshot).next = null
// ex3 failed precondition
// queue size increased to accommodate node
// postconditions (success):
// tail(snapshot).next = new task node
if (tail.compareAndSetNext(null, node)) {
// try to update tail to the new node; if this CAS fails then tail already points at past the node
// this is because tail can only ever move forward, and the task list is always strongly connected
compareAndSetTail(tail, node);
return EXE_OK;
}
// we failed; we have to drop the queue size back down again to compensate before we can retry
if (!NO_QUEUE_LIMIT) decreaseQueueSize();
} else if (tailNext instanceof PoolThreadNode) {
final QNode tailNextNext = tailNext.getNext();
// state change ex1:
// tail(snapshot).next ← tail(snapshot).next(snapshot).next(snapshot)
// succeeds: -
// cannot succeed: sh2
// preconditions:
// tail(snapshot) is a dead TaskNode
// tail(snapshot).next is PoolThreadNode
// tail(snapshot).next.next* is PoolThreadNode or null
// additional success postconditions: -
// failure postconditions: -
// post-actions (succeed):
// run state change ex2
// post-actions (fail):
// retry with new tail(snapshot)
if (tail.compareAndSetNext(tailNext, tailNextNext)) {
assert tail instanceof TaskNode;
PoolThreadNode consumerNode = (PoolThreadNode) tailNext;
// state change ex2:
// tail(snapshot).next(snapshot).task ← runnable
// succeeds: ex1
// preconditions:
// tail(snapshot).next(snapshot).task = WAITING
// post-actions (succeed):
// unpark thread and return
// post-actions (fail):
// retry outer with new tail(snapshot)
if (consumerNode.compareAndSetTask(WAITING, runnable)) {
// GC Nepotism:
// We can save consumerNode::next from being dragged into
// old generation, if possible
consumerNode.compareAndSetNext(tailNextNext, null);
consumerNode.unpark();
return EXE_OK;
}
// otherwise the consumer gave up or was exited already, so fall out and...
}
} else if (tailNext instanceof TaskNode) {
TaskNode tailNextTaskNode = (TaskNode) tailNext;
// Opportunistically update tail to the next node. If this operation has been handled by
// another thread we fall back to the loop and try again instead of duplicating effort.
if (compareAndSetTail(tail, tailNextTaskNode)) {
tail = tailNextTaskNode;
// bypass the on-spin-miss path because we've updated the next task node to tailNextTaskNode.
continue;
}
} else {
// no consumers are waiting and the tail(snapshot).next node is non-null and not a task node, therefore it must be a...
assert tailNext instanceof TerminateWaiterNode;
// shutting down
return EXE_REJECT_SHUTDOWN;
}
// retry with new tail(snapshot)
if (UPDATE_STATISTICS) spinMisses.increment();
tail = this.tail;
}
// not reached
}
// =======================================================
// Termination task
// =======================================================
void completeTermination() {
// be kind and un-interrupt the thread for the termination task
boolean intr = Thread.interrupted();
try {
final Runnable terminationTask = JBossExecutors.classLoaderPreservingTask(this.terminationTask);
this.terminationTask = null;
try {
terminationTask.run();
} catch (Throwable t) {
try {
exceptionHandler.uncaughtException(Thread.currentThread(), t);
} catch (Throwable ignored) {
// nothing else we can safely do here
}
}
// notify all waiters
Waiter waiters = getAndSetTerminationWaiters(TERMINATE_COMPLETE_WAITER);
while (waiters != null) {
unpark(waiters.getThread());
waiters = waiters.getNext();
}
tail.setNext(TERMINATE_COMPLETE);
if (!DISABLE_MBEAN) {
//The check for DISABLE_MBEAN is redundant as acc would be null,
//but GraalVM needs the hint so to not make JMX reachable.
if (this.acc != null) {
final Object handle = this.handle;
if (handle != null) {
intr = intr || Thread.interrupted();
doPrivileged(new MBeanUnregisterAction(handle), acc);
}
this.acc = null;
}
}
} finally {
if (intr) {
Thread.currentThread().interrupt();
}
}
}
// =======================================================
// Compare-and-set operations
// =======================================================
void incrementActiveCount() {
unsafe.getAndAddInt(this, activeCountOffset, 1);
}
void decrementActiveCount() {
unsafe.getAndAddInt(this, activeCountOffset, -1);
}
boolean compareAndSetPeakThreadCount(final int expect, final int update) {
return unsafe.compareAndSwapInt(this, peakThreadCountOffset, expect, update);
}
boolean compareAndSetPeakQueueSize(final int expect, final int update) {
return unsafe.compareAndSwapInt(this, peakQueueSizeOffset, expect, update);
}
boolean compareAndSetQueueSize(final long expect, final long update) {
return unsafe.compareAndSwapLong(this, queueSizeOffset, expect, update);
}
boolean compareAndSetTerminationWaiters(final Waiter expect, final Waiter update) {
return unsafe.compareAndSwapObject(this, terminationWaitersOffset, expect, update);
}
Waiter getAndSetTerminationWaiters(final Waiter update) {
return (Waiter) unsafe.getAndSetObject(this, terminationWaitersOffset, update);
}
// =======================================================
// Queue size operations
// =======================================================
boolean increaseQueueSize() {
long oldVal = queueSize;
int oldSize = currentQueueSizeOf(oldVal);
if (oldSize >= maxQueueSizeOf(oldVal)) {
// reject
return false;
}
int newSize = oldSize + 1;
while (! compareAndSetQueueSize(oldVal, withCurrentQueueSize(oldVal, newSize))) {
if (UPDATE_STATISTICS) spinMisses.increment();
oldVal = queueSize;
oldSize = currentQueueSizeOf(oldVal);
if (oldSize >= maxQueueSizeOf(oldVal)) {
// reject
return false;
}
newSize = oldSize + 1;
}
if (UPDATE_STATISTICS) {
do {
// oldSize now represents the old peak size
oldSize = peakQueueSize;
if (newSize <= oldSize) break;
} while (! compareAndSetPeakQueueSize(oldSize, newSize));
}
return true;
}
void decreaseQueueSize() {
long oldVal;
oldVal = queueSize;
assert currentQueueSizeOf(oldVal) > 0;
while (! compareAndSetQueueSize(oldVal, withCurrentQueueSize(oldVal, currentQueueSizeOf(oldVal) - 1))) {
if (UPDATE_STATISTICS) spinMisses.increment();
oldVal = queueSize;
assert currentQueueSizeOf(oldVal) > 0;
}
}
// =======================================================
// Inline Functions
// =======================================================
static int currentQueueSizeOf(long queueSize) {
return (int) (queueSize & 0x7fff_ffff);
}
static long withCurrentQueueSize(long queueSize, int current) {
assert current >= 0;
return queueSize & 0xffff_ffff_0000_0000L | current;
}
static int maxQueueSizeOf(long queueSize) {
return (int) (queueSize >>> 32 & 0x7fff_ffff);
}
static long withMaxQueueSize(long queueSize, int max) {
assert max >= 0;
return queueSize & 0xffff_ffffL | (long)max << 32;
}
static int coreSizeOf(long status) {
return (int) (status >>> TS_CORE_SHIFT & TS_THREAD_CNT_MASK);
}
static int maxSizeOf(long status) {
return (int) (status >>> TS_MAX_SHIFT & TS_THREAD_CNT_MASK);
}
static int currentSizeOf(long status) {
return (int) (status >>> TS_CURRENT_SHIFT & TS_THREAD_CNT_MASK);
}
static long withCoreSize(long status, int newCoreSize) {
assert 0 <= newCoreSize && newCoreSize <= TS_THREAD_CNT_MASK;
return status & ~(TS_THREAD_CNT_MASK << TS_CORE_SHIFT) | (long)newCoreSize << TS_CORE_SHIFT;
}
static long withCurrentSize(long status, int newCurrentSize) {
assert 0 <= newCurrentSize && newCurrentSize <= TS_THREAD_CNT_MASK;
return status & ~(TS_THREAD_CNT_MASK << TS_CURRENT_SHIFT) | (long)newCurrentSize << TS_CURRENT_SHIFT;
}
static long withMaxSize(long status, int newMaxSize) {
assert 0 <= newMaxSize && newMaxSize <= TS_THREAD_CNT_MASK;
return status & ~(TS_THREAD_CNT_MASK << TS_MAX_SHIFT) | (long)newMaxSize << TS_MAX_SHIFT;
}
static long withShutdownRequested(final long status) {
return status | TS_SHUTDOWN_REQUESTED;
}
static long withShutdownComplete(final long status) {
return status | TS_SHUTDOWN_COMPLETE;
}
static long withShutdownInterrupt(final long status) {
return status | TS_SHUTDOWN_INTERRUPT;
}
static long withAllowCoreTimeout(final long status, final boolean allowed) {
return allowed ? status | TS_ALLOW_CORE_TIMEOUT : status & ~TS_ALLOW_CORE_TIMEOUT;
}
static boolean isShutdownRequested(final long status) {
return (status & TS_SHUTDOWN_REQUESTED) != 0;
}
static boolean isShutdownComplete(final long status) {
return (status & TS_SHUTDOWN_COMPLETE) != 0;
}
static boolean isShutdownInterrupt(final long threadStatus) {
return (threadStatus & TS_SHUTDOWN_INTERRUPT) != 0;
}
static boolean isAllowCoreTimeout(final long oldVal) {
return (oldVal & TS_ALLOW_CORE_TIMEOUT) != 0;
}
// =======================================================
// Static configuration
// =======================================================
// =======================================================
// Utilities
// =======================================================
void rejectException(final Task task, final Throwable cause) {
try {
handoffExecutor.execute(task.handoff());
} catch (Throwable t) {
t.addSuppressed(cause);
throw t;
}
}
void rejectNoThread(final Task task) {
try {
handoffExecutor.execute(task.handoff());
} catch (Throwable t) {
t.addSuppressed(new RejectedExecutionException("No threads available"));
throw t;
}
}
void rejectQueueFull(final Task task) {
try {
handoffExecutor.execute(task.handoff());
} catch (Throwable t) {
t.addSuppressed(new RejectedExecutionException("Queue is full"));
throw t;
}
}
void rejectShutdown(final Task task) {
try {
handoffExecutor.execute(task.handoff());
} catch (Throwable t) {
t.addSuppressed(new RejectedExecutionException("Executor is being shut down"));
throw t;
}
}
static Runnable nullToNop(final Runnable task) {
return task == null ? NullRunnable.getInstance() : task;
}
// =======================================================
// Node classes
// =======================================================
abstract static class QNode {
private static final long nextOffset;
static {
try {
nextOffset = unsafe.objectFieldOffset(QNode.class.getDeclaredField("next"));
} catch (NoSuchFieldException e) {
throw new NoSuchFieldError(e.getMessage());
}
}
@SuppressWarnings("unused")
private volatile QNode next;
boolean compareAndSetNext(QNode expect, QNode update) {
return unsafe.compareAndSwapObject(this, nextOffset, expect, update);
}
QNode getNext() {
return next;
}
void setNext(final QNode node) {
next = node;
}
void setNextRelaxed(final QNode node) {
unsafe.putObject(this, nextOffset, node);
}
void setNextOrdered(final QNode node) {
unsafe.putOrderedObject(this, nextOffset, node);
}
}
/** Padding between PoolThreadNode task and parked fields and QNode.next */
static abstract class PoolThreadNodeBase extends QNode {
/**
* Padding fields.
*/
@SuppressWarnings("unused")
int p00, p01, p02, p03,
p04, p05, p06, p07,
p08, p09, p0A, p0B,
p0C, p0D, p0E, p0F;
PoolThreadNodeBase() {}
}
static final class PoolThreadNode extends PoolThreadNodeBase {
/**
* Thread is running normally.
*/
private static final int STATE_NORMAL = 0;
/**
* Thread is parked (or about to park), and unpark call is necessary to wake the thread
*/
private static final int STATE_PARKED = 1;
/**
* The thread has been unparked, any thread that is spinning or about to park will return,
* if not thread is currently spinning the next thread that attempts to spin will immediately return
*/
private static final int STATE_UNPARKED = 2;
private static final long taskOffset;
private static final long parkedOffset;
static {
try {
taskOffset = unsafe.objectFieldOffset(PoolThreadNode.class.getDeclaredField("task"));
parkedOffset = unsafe.objectFieldOffset(PoolThreadNode.class.getDeclaredField("parked"));
} catch (NoSuchFieldException e) {
throw new NoSuchFieldError(e.getMessage());
}
}
private final Thread thread;
@SuppressWarnings("unused")
private volatile Runnable task;
/**
* The park state, see the STATE_ constants for the meanings of each value
*/
@SuppressWarnings("unused")
private volatile int parked;
PoolThreadNode(final Thread thread) {
this.thread = thread;
task = WAITING;
}
boolean compareAndSetTask(final Runnable expect, final Runnable update) {
return task == expect && unsafe.compareAndSwapObject(this, taskOffset, expect, update);
}
Runnable getTask() {
return task;
}
PoolThreadNode getNext() {
return (PoolThreadNode) super.getNext();
}
void park(EnhancedQueueExecutor enhancedQueueExecutor) {
int spins = PARK_SPINS;
if (parked == STATE_UNPARKED && unsafe.compareAndSwapInt(this, parkedOffset, STATE_UNPARKED, STATE_NORMAL)) {
return;
}
while (spins > 0) {
if (spins < YIELD_FACTOR) {
Thread.yield();
} else {
JDKSpecific.onSpinWait();
}
spins--;
if (parked == STATE_UNPARKED && unsafe.compareAndSwapInt(this, parkedOffset, STATE_UNPARKED, STATE_NORMAL)) {
return;
}
}
if (parked == STATE_NORMAL && unsafe.compareAndSwapInt(this, parkedOffset, STATE_NORMAL, STATE_PARKED)) try {
LockSupport.park(enhancedQueueExecutor);
} finally {
// parked can be STATE_PARKED or STATE_UNPARKED, cannot possibly be STATE_NORMAL.
// if it's STATE_PARKED, we'd go back to NORMAL since we're not parking anymore.
// if it's STATE_UNPARKED, we can still go back to NORMAL because all of our preconditions will be rechecked anyway.
parked = STATE_NORMAL;
}
}
void park(EnhancedQueueExecutor enhancedQueueExecutor, long nanos) {
long remaining;
int spins = PARK_SPINS;
if (spins > 0) {
long start = System.nanoTime();
//note that we don't check the nanotime while spinning
//as spin time is short and for our use cases it does not matter if the time
//overruns a bit (as the nano time is for thread timeout) we just spin then check
//to keep performance consistent between the two versions.
if (parked == STATE_UNPARKED && unsafe.compareAndSwapInt(this, parkedOffset, STATE_UNPARKED, STATE_NORMAL)) {
return;
}
while (spins > 0) {
if (spins < YIELD_FACTOR) {
Thread.yield();
} else {
JDKSpecific.onSpinWait();
}
if (parked == STATE_UNPARKED && unsafe.compareAndSwapInt(this, parkedOffset, STATE_UNPARKED, STATE_NORMAL)) {
return;
}
spins--;
}
remaining = nanos - (System.nanoTime() - start);
if (remaining < 0) {
return;
}
} else {
remaining = nanos;
}
if (parked == STATE_NORMAL && unsafe.compareAndSwapInt(this, parkedOffset, STATE_NORMAL, STATE_PARKED)) try {
LockSupport.parkNanos(enhancedQueueExecutor, remaining);
} finally {
// parked can be STATE_PARKED or STATE_UNPARKED, cannot possibly be STATE_NORMAL.
// if it's STATE_PARKED, we'd go back to NORMAL since we're not parking anymore.
// if it's STATE_UNPARKED, we can still go back to NORMAL because all of our preconditions will be rechecked anyway.
parked = STATE_NORMAL;
}
}
void unpark() {
if (parked == STATE_NORMAL && unsafe.compareAndSwapInt(this, parkedOffset, STATE_NORMAL, STATE_UNPARKED)) {
return;
}
LockSupport.unpark(thread);
}
}
static final class TerminateWaiterNode extends QNode {
}
static final class TaskNode extends QNode {
Task task;
TaskNode(final Task task) {
// we always start task nodes with a {@code null} next
this.task = task;
}
Runnable getAndClearTask() {
Runnable result = task;
task = null;
return result;
}
}
// =======================================================
// Management bean implementation
// =======================================================
final class MXBeanImpl implements StandardThreadPoolMXBean {
MXBeanImpl() {
}
public float getGrowthResistance() {
return EnhancedQueueExecutor.this.getGrowthResistance();
}
public void setGrowthResistance(final float value) {
EnhancedQueueExecutor.this.setGrowthResistance(value);
}
public boolean isGrowthResistanceSupported() {
return true;
}
public int getCorePoolSize() {
return EnhancedQueueExecutor.this.getCorePoolSize();
}
public void setCorePoolSize(final int corePoolSize) {
EnhancedQueueExecutor.this.setCorePoolSize(corePoolSize);
}
public boolean isCorePoolSizeSupported() {
return true;
}
public boolean prestartCoreThread() {
return EnhancedQueueExecutor.this.prestartCoreThread();
}
public int prestartAllCoreThreads() {
return EnhancedQueueExecutor.this.prestartAllCoreThreads();
}
public boolean isCoreThreadPrestartSupported() {
return true;
}
public int getMaximumPoolSize() {
return EnhancedQueueExecutor.this.getMaximumPoolSize();
}
public void setMaximumPoolSize(final int maxPoolSize) {
EnhancedQueueExecutor.this.setMaximumPoolSize(maxPoolSize);
}
public int getPoolSize() {
return EnhancedQueueExecutor.this.getPoolSize();
}
public int getLargestPoolSize() {
return EnhancedQueueExecutor.this.getLargestPoolSize();
}
public int getActiveCount() {
return EnhancedQueueExecutor.this.getActiveCount();
}
public boolean isAllowCoreThreadTimeOut() {
return EnhancedQueueExecutor.this.allowsCoreThreadTimeOut();
}
public void setAllowCoreThreadTimeOut(final boolean value) {
EnhancedQueueExecutor.this.allowCoreThreadTimeOut(value);
}
public long getKeepAliveTimeSeconds() {
return EnhancedQueueExecutor.this.getKeepAliveTime().getSeconds();
}
public void setKeepAliveTimeSeconds(final long seconds) {
EnhancedQueueExecutor.this.setKeepAliveTime(Duration.of(seconds, ChronoUnit.SECONDS));
}
public int getMaximumQueueSize() {
return EnhancedQueueExecutor.this.getMaximumQueueSize();
}
public void setMaximumQueueSize(final int size) {
EnhancedQueueExecutor.this.setMaximumQueueSize(size);
}
public int getQueueSize() {
return EnhancedQueueExecutor.this.getQueueSize();
}
public int getLargestQueueSize() {
return EnhancedQueueExecutor.this.getLargestQueueSize();
}
public boolean isQueueBounded() {
return ! NO_QUEUE_LIMIT;
}
public boolean isQueueSizeModifiable() {
return ! NO_QUEUE_LIMIT;
}
public boolean isShutdown() {
return EnhancedQueueExecutor.this.isShutdown();
}
public boolean isTerminating() {
return EnhancedQueueExecutor.this.isTerminating();
}
public boolean isTerminated() {
return EnhancedQueueExecutor.this.isTerminated();
}
public long getSubmittedTaskCount() {
return EnhancedQueueExecutor.this.getSubmittedTaskCount();
}
public long getRejectedTaskCount() {
return EnhancedQueueExecutor.this.getRejectedTaskCount();
}
public long getCompletedTaskCount() {
return EnhancedQueueExecutor.this.getCompletedTaskCount();
}
public long getSpinMissCount() {
return EnhancedQueueExecutor.this.spinMisses.longValue();
}
}
// =======================================================
// Basic task wrapper
// =======================================================
final class Task implements Runnable {
private final Runnable delegate;
private final ClassLoader contextClassLoader;
private final Object context;
Task(Runnable delegate, final Object context) {
Assert.checkNotNullParam("delegate", delegate);
this.delegate = delegate;
this.context = context;
this.contextClassLoader = JBossExecutors.getContextClassLoader(Thread.currentThread());
}
@Override
@SuppressWarnings("rawtypes")
public void run() {
if (isShutdownInterrupt(threadStatus)) {
Thread.currentThread().interrupt();
} else {
Thread.interrupted();
}
if (UPDATE_ACTIVE_COUNT) incrementActiveCount();
final Thread currentThread = Thread.currentThread();
final ClassLoader old = JBossExecutors.getAndSetContextClassLoader(currentThread, contextClassLoader);
try {
((ContextHandler)contextHandler).runWith(delegate, context);
} catch (Throwable t) {
try {
exceptionHandler.uncaughtException(Thread.currentThread(), t);
} catch (Throwable ignored) {
// nothing else we can safely do here
}
} finally {
JBossExecutors.setContextClassLoader(currentThread, old);
}
Thread.interrupted();
if (UPDATE_ACTIVE_COUNT) {
decrementActiveCount();
if (UPDATE_STATISTICS) {
completedTaskCounter.increment();
}
}
}
/**
* Extracts the original runnable without EQE-specific state updating. This runnable does retain the original
* context classloader from the submitting thread.
*/
Runnable handoff() {
return new ContextClassLoaderSavingRunnable(contextClassLoader, delegate);
}
@Override
public String toString() {
return "Task{delegate=" + delegate + ", contextClassLoader=" + contextClassLoader + '}';
}
}
// =======================================================
// Scheduled future tasks
// =======================================================
static final int ASF_ST_WAITING = 0;
static final int ASF_ST_CANCELLED = 1;
static final int ASF_ST_SUBMITTED = 2;
static final int ASF_ST_RUNNING = 3;
static final int ASF_ST_FINISHED = 4;
static final int ASF_ST_FAILED = 5;
static final int ASF_ST_REJECTED = 6;
static final AbstractScheduledFuture>[] NO_FUTURES = new AbstractScheduledFuture>[0];
static final AtomicLong SCHEDULED_TASK_SEQ = new AtomicLong();
/**
* An implementation of {@link ScheduledFuture} which is wrapped by {@link Task}.
*
* @param the result type
*/
abstract class AbstractScheduledFuture implements ScheduledFuture, Runnable {
final long seq = SCHEDULED_TASK_SEQ.getAndIncrement();
/**
* The task which is wrapping this one.
*/
final Task wrappingTask;
/**
* The scheduled time for this task, in nanoseconds since the scheduler thread was born.
* Can be mutated in subclasses if the task is recurring, but only under lock.
*/
volatile long when;
/**
* The state of this task; one of {@code ASF_ST_*}.
*/
volatile int state = ASF_ST_WAITING;
/**
* The actual result; only valid in {@code ASF_ST_FINISHED} (where it is of type {@code V}),
* or in {@code ASF_ST_FAILED} (where it is of type {@code Throwable}),
* or in {@code ASF_ST_REJECTED} (where it is of type {@code RejectedExecutionException}).
*/
volatile Object result;
/**
* The thread which is currently live for this task.
*/
Thread liveThread;
AbstractScheduledFuture(long delay, TimeUnit unit) {
when = Math.addExact(schedulerTask.age(), unit.toNanos(delay));
wrappingTask = new Task(this, contextHandler.captureContext());
}
public int compareTo(final Delayed o) {
return o instanceof AbstractScheduledFuture> ? compareTo((AbstractScheduledFuture>) o) : wrongType();
}
public int compareTo(final AbstractScheduledFuture> other) {
int cmp = Long.compare(when, other.when);
if (cmp == 0) cmp = Long.compare(seq, other.seq);
return cmp;
}
public long getDelay(final TimeUnit unit) {
return unit.convert(Math.max(0, when - schedulerTask.age()), TimeUnit.NANOSECONDS);
}
public boolean isCancelled() {
return state == ASF_ST_CANCELLED;
}
public boolean isDone() {
int state = this.state;
return state == ASF_ST_FINISHED || state == ASF_ST_FAILED || state == ASF_ST_CANCELLED || state == ASF_ST_REJECTED;
}
public boolean cancel(final boolean mayInterruptIfRunning) {
int state;
synchronized (this) {
state = this.state;
switch (state) {
case ASF_ST_WAITING:
case ASF_ST_SUBMITTED: {
this.state = ASF_ST_CANCELLED;
return true;
}
case ASF_ST_RUNNING: {
if (mayInterruptIfRunning) {
liveThread.interrupt();
}
return false;
}
case ASF_ST_CANCELLED: {
return true;
}
default: {
return false;
}
}
}
}
public V get() throws InterruptedException, ExecutionException {
int state;
synchronized (this) {
for (;;) {
state = this.state;
switch (state) {
case ASF_ST_WAITING:
case ASF_ST_SUBMITTED:
case ASF_ST_RUNNING: {
wait();
break;
}
case ASF_ST_CANCELLED: {
throw new CancellationException("Task was cancelled");
}
case ASF_ST_REJECTED: {
throw new ExecutionException("Task failed due to rejection", (RejectedExecutionException) result);
}
case ASF_ST_FAILED: {
throw new ExecutionException((Throwable) result);
}
case ASF_ST_FINISHED: {
//noinspection unchecked
return (V) result;
}
}
}
}
}
public V get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
long remaining = unit.toNanos(timeout);
long start = System.nanoTime();
int state;
synchronized (this) {
for (;;) {
state = this.state;
switch (state) {
case ASF_ST_WAITING:
case ASF_ST_SUBMITTED:
case ASF_ST_RUNNING: {
if (remaining <= 0) {
throw new TimeoutException();
}
wait(remaining / 1_000_000L, (int) (remaining % 1_000_000));
break;
}
case ASF_ST_CANCELLED: {
throw new CancellationException("Task was cancelled");
}
case ASF_ST_REJECTED: {
throw new ExecutionException("Task failed due to rejection", (RejectedExecutionException) result);
}
case ASF_ST_FAILED: {
throw new ExecutionException((Throwable) result);
}
case ASF_ST_FINISHED: {
//noinspection unchecked
return (V) result;
}
}
long newStart = System.nanoTime();
long elapsed = newStart - start;
remaining -= elapsed;
start = newStart;
}
}
}
public void run() {
stateTest: synchronized (this) {
switch (state) {
case ASF_ST_SUBMITTED: {
this.state = ASF_ST_RUNNING;
liveThread = currentThread();
break stateTest;
}
case ASF_ST_CANCELLED: {
// cancelled after submit but before it was run
return;
}
case ASF_ST_FAILED:
case ASF_ST_REJECTED: {
// a recurring task terminated abruptly, but was still found in the schedule
return;
}
default: {
// invalid state
fail(badState());
return;
}
}
}
try {
finish(performTask());
} catch (Throwable t) {
fail(t);
}
}
void submit() {
synchronized (this) {
stateTest: switch (state) {
case ASF_ST_WAITING: {
this.state = ASF_ST_SUBMITTED;
//noinspection UnnecessaryLabelOnBreakStatement
break stateTest;
}
case ASF_ST_CANCELLED: {
// do not actually submit
return;
}
case ASF_ST_FAILED:
case ASF_ST_REJECTED: {
// a recurring task terminated abruptly, but was still found in the schedule
return;
}
default: {
// invalid state
fail(badState());
return;
}
}
}
try {
/* copied from {@link #execute(Runnable)} */
int result = tryExecute(wrappingTask);
boolean ok = false;
if (result == EXE_OK) {
// last check to ensure that there is at least one existent thread to avoid rare thread timeout race condition
if (currentSizeOf(threadStatus) == 0 && tryAllocateThread(0.0f) == AT_YES && ! doStartThread(null)) {
deallocateThread();
}
if (UPDATE_STATISTICS) submittedTaskCounter.increment();
return;
} else if (result == EXE_CREATE_THREAD) try {
ok = doStartThread(wrappingTask);
} finally {
if (! ok) deallocateThread();
} else {
if (UPDATE_STATISTICS) rejectedTaskCounter.increment();
if (result == EXE_REJECT_SHUTDOWN) {
rejectShutdown(wrappingTask);
} else {
assert result == EXE_REJECT_QUEUE_FULL;
rejectQueueFull(wrappingTask);
}
}
} catch (RejectedExecutionException e) {
reject(e);
} catch (Throwable t) {
reject(new RejectedExecutionException("Task submission failed", t));
}
}
IllegalStateException badState() {
return new IllegalStateException("Task was not in expected state");
}
void reject(RejectedExecutionException e) {
synchronized (this) {
switch (state) {
case ASF_ST_SUBMITTED: {
result = e;
this.state = ASF_ST_REJECTED;
liveThread = null;
notifyAll();
return;
}
default: {
// invalid state
fail(badState());
return;
}
}
}
}
void fail(Throwable t) {
synchronized (this) {
switch (state) {
case ASF_ST_WAITING:
case ASF_ST_SUBMITTED:
case ASF_ST_RUNNING: {
result = t;
this.state = ASF_ST_FAILED;
liveThread = null;
notifyAll();
return;
}
case ASF_ST_CANCELLED:
case ASF_ST_FINISHED:
case ASF_ST_FAILED:
case ASF_ST_REJECTED: {
// ignore the failure, though we're likely in an invalid state
return;
}
}
}
}
void finish(V result) {
// overridden in subclasses where the task repeats
synchronized (this) {
switch (state) {
case ASF_ST_RUNNING: {
this.result = result;
this.state = ASF_ST_FINISHED;
liveThread = null;
notifyAll();
return;
}
default: {
// invalid state
fail(badState());
return;
}
}
}
}
abstract V performTask() throws Exception;
public String toString() {
return toString(new StringBuilder()).toString();
}
StringBuilder toString(StringBuilder b) {
return b.append("future result of ");
}
}
static int wrongType() throws ClassCastException {
throw new ClassCastException("Wrong task type for comparison");
}
final class RunnableScheduledFuture extends AbstractScheduledFuture {
final Runnable runnable;
RunnableScheduledFuture(final Runnable runnable, final long delay, final TimeUnit unit) {
super(delay, unit);
this.runnable = runnable;
}
Void performTask() {
runnable.run();
return null;
}
StringBuilder toString(final StringBuilder b) {
return super.toString(b).append(runnable);
}
}
final class CallableScheduledFuture extends AbstractScheduledFuture {
final Callable callable;
CallableScheduledFuture(final Callable callable, final long delay, final TimeUnit unit) {
super(delay, unit);
this.callable = callable;
}
V performTask() throws Exception {
return callable.call();
}
StringBuilder toString(final StringBuilder b) {
return super.toString(b).append(callable);
}
}
abstract class RepeatingScheduledFuture extends AbstractScheduledFuture {
final long period;
RepeatingScheduledFuture(final long delay, final long period, final TimeUnit unit) {
super(delay, unit);
this.period = unit.toNanos(period);
}
/**
* Adjust the time of this future for resubmission, after the task has run successfully.
*/
abstract void adjustTime();
public void run() {
super.run();
// if an exception is thrown, we will have failed already anyway
adjustTime();
synchronized (this) {
switch (state) {
case ASF_ST_RUNNING: {
state = ASF_ST_WAITING;
schedulerTask.schedule(this);
return;
}
default: {
// in all other cases, we failed so the task should not be rescheduled
return;
}
}
}
}
void finish(final V result) {
// repeating tasks never actually finish
}
StringBuilder toString(final StringBuilder b) {
return super.toString(b.append("repeating "));
}
}
final class FixedRateRunnableScheduledFuture extends RepeatingScheduledFuture {
final Runnable runnable;
FixedRateRunnableScheduledFuture(final Runnable runnable, final long delay, final long period, final TimeUnit unit) {
super(delay, period, unit);
this.runnable = runnable;
}
void adjustTime() {
// if this results in a time in the past, the next run will happen immediately
this.when += period;
}
Void performTask() {
runnable.run();
return null;
}
StringBuilder toString(final StringBuilder b) {
return super.toString(b).append(runnable);
}
}
final class FixedDelayRunnableScheduledFuture extends RepeatingScheduledFuture {
final Runnable runnable;
FixedDelayRunnableScheduledFuture(final Runnable runnable, final long delay, final long period, final TimeUnit unit) {
super(delay, period, unit);
this.runnable = runnable;
}
void adjustTime() {
this.when = schedulerTask.age() + period;
}
Void performTask() {
runnable.run();
return null;
}
StringBuilder toString(final StringBuilder b) {
return super.toString(b).append(runnable);
}
}
// =======================================================
// Scheduler task thread worker
// =======================================================
final class SchedulerTask implements Runnable {
final long startMark = System.nanoTime();
final ReentrantLock ql = new ReentrantLock();
final Condition qc = ql.newCondition();
// todo: switch to array queue on a more optimistic day
// protected by {@link #ql}
ScheduledFutureQueue q = new TreeSetQueue();
boolean shutdownDetected;
void shutdown() {
ql.lock();
try {
shutdownDetected = true;
qc.signal();
} finally {
ql.unlock();
}
}
public void run() {
ScheduledFutureQueue q = this.q;
AbstractScheduledFuture>[] remainingFutures;
AbstractScheduledFuture> first;
long startMark = this.startMark;
outerLoop: for (;;) {
ql.lock();
try {
innerLoop: for (;;) {
long now = System.nanoTime();
if (shutdownDetected) {
// drop all tasks and return
remainingFutures = q.toArray();
q.clear();
break outerLoop;
} else if (q.isEmpty()) try {
qc.await();
} catch (InterruptedException ignored) {
// clear interrupt status
continue innerLoop;
} else {
first = q.first();
long firstWhen = first.when;
long currentWhen = max(0, now - startMark);
if (firstWhen <= currentWhen) {
// it's ready; run it outside of the lock
q.pollFirst();
//noinspection UnnecessaryLabelOnBreakStatement
break innerLoop;
} else {
long waitTime = firstWhen - currentWhen;
try {
qc.awaitNanos(waitTime);
} catch (InterruptedException e) {
// clear interrupt status
continue innerLoop;
}
}
}
}
} finally {
ql.unlock();
}
// outside of lock; `break innerLoop` goes ↓ here
first.submit();
// continue loop to find the next task
}
// ↓ `break outerLoop` goes here ↓
if (remainingFutures.length > 0) {
for (AbstractScheduledFuture> future : remainingFutures) {
future.cancel(true);
}
}
return;
}
> F schedule(final F item) {
Task wrappingTask = item.wrappingTask;
if (item.when <= age()) {
// just submit it now
item.submit();
return item;
}
ql.lock();
try {
if (shutdownDetected) {
rejectShutdown(wrappingTask);
return item;
}
// check to see if we need to wake up the scheduler
boolean first;
for (;;) try {
first = q.insertAndCheckForFirst(item);
break;
} catch (QueueFullException ignored) {
q = q.grow();
}
if (first) {
// the delay time has changed, so wake up the waiter
qc.signal();
}
return item;
} finally {
ql.unlock();
}
}
long age() {
return System.nanoTime() - startMark;
}
}
// =======================================================
// Schedule queue API & implementations
// =======================================================
interface ScheduledFutureQueue {
AbstractScheduledFuture>[] toArray();
void clear();
boolean isEmpty();
int size();
AbstractScheduledFuture> first();
@SuppressWarnings("UnusedReturnValue") // must match signature for TreeSet
AbstractScheduledFuture> pollFirst();
/**
* Insert the item in order, checking to see if it was added as the first item.
*
* @param item the item to insert (must not be {@code null})
* @return {@code true} if the item is first, {@code false} otherwise
* @throws QueueFullException if the queue is full; it must be recreated in this case
*/
boolean insertAndCheckForFirst(AbstractScheduledFuture> item) throws QueueFullException;
/**
* Get a new queue with the same contents as this one, but with a larger capacity.
*
* @return the grown queue
*/
ScheduledFutureQueue grow();
}
static final class ArrayQueue implements ScheduledFutureQueue {
final AbstractScheduledFuture>[] array;
// the removal point (lowest+least index)
int head;
// the number of elements
int size;
ArrayQueue(int capacity) {
// next power of two
capacity = Integer.highestOneBit(Math.max(capacity, 2) - 1) << 1;
array = new AbstractScheduledFuture>[capacity];
}
private ArrayQueue(final ArrayQueue original, final int newCapacity) {
assert Integer.bitCount(newCapacity) == 1;
array = original.toArray(newCapacity);
head = 0;
size = original.size;
}
public AbstractScheduledFuture>[] toArray() {
return toArray(size());
}
public AbstractScheduledFuture>[] toArray(int size) {
int head = this.head;
int end = head + size;
AbstractScheduledFuture>[] copy = Arrays.copyOfRange(array, head, end);
if (end > array.length) {
// copy the wrapped elements
System.arraycopy(array, 0, copy, size - (array.length - head), size - array.length);
}
return copy;
}
public void clear() {
Arrays.fill(array, null);
head = size = 0;
}
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
public AbstractScheduledFuture> first() {
if (size == 0) {
throw new NoSuchElementException();
}
return array[head];
}
public AbstractScheduledFuture> pollFirst() {
if (size == 0) {
throw new NoSuchElementException();
}
int head = this.head;
AbstractScheduledFuture> item = array[head];
array[head] = null;
this.size --;
int mask = array.length - 1;
this.head = head + 1 & mask;
return item;
}
public boolean insertAndCheckForFirst(final AbstractScheduledFuture> item) {
// find the insertion point
int size = this.size;
AbstractScheduledFuture>[] array = this.array;
int arrayLen = array.length;
if (size == arrayLen) {
throw new QueueFullException();
}
int mask = arrayLen - 1;
int idx = 0;
int high = size - 1;
// at this point and onwards, there is definitely space in the array
int head = this.head;
while (idx <= high) {
int mid = (idx + high) >>> 1;
AbstractScheduledFuture> testVal = array[head + mid & mask];
int cmp = testVal.compareTo(item);
if (cmp < 0) {
idx = mid + 1;
} else if (cmp > 0) {
high = mid - 1;
} else {
// we found this task already present in the queue (should never happen)
return false;
}
}
return insertAt(idx, item);
}
/**
* Move all elements starting at the given index forward to make space at that position, wrapping if needed.
*
* @param idx the element index relative to {@code head} to open up
*/
void moveForward(final int idx, final AbstractScheduledFuture> storeVal) {
AbstractScheduledFuture>[] array = this.array;
int size = this.size;
int moveCnt = size - idx;
int arrayLength = array.length;
int mask = arrayLength - 1;
int head = this.head;
int start = head + idx;
// TODO:
// - change this to three calls to System.arraycopy
// - one for the already-wrapped portion
// - one for the portion that is being newly wrapped
// - one for the leading (pre-wrap) portion
for (int i = moveCnt - 1; i >= 0; i --) {
int pos = start + i;
array[pos + 1 & mask] = array[pos & mask];
}
array[start & mask] = storeVal;
}
/**
* Move all elements starting before the given index backward to make space at that position, wrapping if needed.
*
* @param idx the element index relative to {@code head} to open up
*/
void moveBackward(final int idx, final AbstractScheduledFuture> storeVal) {
AbstractScheduledFuture>[] array = this.array;
int size = this.size;
int moveCnt = size - idx + 1;
int arrayLength = array.length;
int mask = arrayLength - 1;
int head = this.head;
int start = head + idx - 1;
// TODO:
// - change this to three calls to System.arraycopy
// - one for the leading (pre-wrap) portion
// - one for the portion that is being newly de-wrapped
// - one for the already-wrapped portion
for (int i = moveCnt - 1; i >= 0; i --) {
int pos = start - i;
array[pos - 1 & mask] = array[pos & mask];
}
array[start & mask] = storeVal;
this.head = head - 1 & mask;
}
boolean insertAt(final int idx, final AbstractScheduledFuture> item) {
// this is a separate method for easier testing of the arraycopy algebraic mayhem
int size = this.size;
// no matter what, we're growing by one
this.size = size + 1;
int halfSize = size + 1 >> 1;
if (idx >= halfSize) {
moveForward(idx, item);
} else {
moveBackward(idx, item);
}
return idx == 0;
}
public ScheduledFutureQueue grow() {
// todo: calibrate this threshold
if (array.length >= 256) {
return new TreeSetQueue(this);
} else {
return new ArrayQueue(this, array.length << 1);
}
}
// test points for white-box unit tests
int testPoint_arrayLength() {
return array.length;
}
int testPoint_head() {
return head;
}
void testPoint_setHead(int newHead) {
head = newHead;
}
void testPoint_setSize(int newSize) {
size = newSize;
}
AbstractScheduledFuture> testPoint_getArrayItem(int index) {
return array[index & array.length - 1];
}
AbstractScheduledFuture> testPoint_setArrayItem(int index, AbstractScheduledFuture> item) {
try {
return array[index & array.length - 1];
} finally {
array[index & array.length - 1] = item;
}
}
}
@SuppressWarnings("serial")
static class TreeSetQueue extends TreeSet> implements ScheduledFutureQueue {
TreeSetQueue(final ScheduledFutureQueue original) {
Collections.addAll(this, original.toArray());
}
TreeSetQueue() {
}
public AbstractScheduledFuture>[] toArray() {
return super.toArray(NO_FUTURES);
}
public boolean insertAndCheckForFirst(final AbstractScheduledFuture> item) {
add(item);
return item == first();
}
public ScheduledFutureQueue grow() {
return this;
}
}
@SuppressWarnings("serial")
static final class QueueFullException extends RuntimeException {
QueueFullException() {
super(null, null, false, false);
}
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/EnhancedQueueExecutorBase0.java 0000664 0000000 0000000 00000004535 14303134447 0031275 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import static java.security.AccessController.doPrivileged;
import java.security.PrivilegedAction;
import java.util.concurrent.AbstractExecutorService;
/**
* EQE base class: shared utilities and initial padding.
*/
abstract class EnhancedQueueExecutorBase0 extends AbstractExecutorService {
/**
* Padding fields.
*/
@SuppressWarnings("unused")
int p00, p01, p02, p03,
p04, p05, p06, p07,
p08, p09, p0A, p0B,
p0C, p0D, p0E, p0F;
EnhancedQueueExecutorBase0() {}
static int readIntPropertyPrefixed(String name, int defVal) {
try {
return Integer.parseInt(readPropertyPrefixed(name, Integer.toString(defVal)));
} catch (NumberFormatException ignored) {
return defVal;
}
}
static boolean readBooleanPropertyPrefixed(String name, boolean defVal) {
return Boolean.parseBoolean(readPropertyPrefixed(name, Boolean.toString(defVal)));
}
static String readPropertyPrefixed(String name, String defVal) {
return readProperty("jboss.threads.eqe." + name, defVal);
}
static String readProperty(String name, String defVal) {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
return doPrivileged(new PrivilegedAction() {
public String run() {
return readPropertyRaw(name, defVal);
}
});
} else {
return readPropertyRaw(name, defVal);
}
}
static String readPropertyRaw(final String name, final String defVal) {
return System.getProperty(name, defVal);
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/EnhancedQueueExecutorBase1.java 0000664 0000000 0000000 00000003677 14303134447 0031304 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import static org.jboss.threads.JBossExecutors.unsafe;
import org.wildfly.common.annotation.NotNull;
/**
* EQE base class: tail section.
*/
abstract class EnhancedQueueExecutorBase1 extends EnhancedQueueExecutorBase0 {
static final long tailOffset;
static {
try {
tailOffset = unsafe.objectFieldOffset(EnhancedQueueExecutorBase1.class.getDeclaredField("tail"));
} catch (NoSuchFieldException e) {
throw new NoSuchFieldError(e.getMessage());
}
}
/**
* The node preceding the tail node; this field is not {@code null}. This
* is the insertion point for tasks (and the removal point for waiting threads).
*/
@NotNull
@SuppressWarnings("unused") // used by field updater
volatile EnhancedQueueExecutor.TaskNode tail;
EnhancedQueueExecutorBase1() {}
// =======================================================
// Compare-and-set operations
// =======================================================
boolean compareAndSetTail(final EnhancedQueueExecutor.TaskNode expect, final EnhancedQueueExecutor.TaskNode update) {
return tail == expect && unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/EnhancedQueueExecutorBase2.java 0000664 0000000 0000000 00000002061 14303134447 0031267 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
/**
* EQE base class: padding.
*/
abstract class EnhancedQueueExecutorBase2 extends EnhancedQueueExecutorBase1 {
/**
* Padding fields.
*/
@SuppressWarnings("unused")
int p00, p01, p02, p03,
p04, p05, p06, p07,
p08, p09, p0A, p0B,
p0C, p0D, p0E, p0F;
EnhancedQueueExecutorBase2() {}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/EnhancedQueueExecutorBase3.java 0000664 0000000 0000000 00000004213 14303134447 0031271 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2020 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import static org.jboss.threads.JBossExecutors.unsafe;
import org.wildfly.common.annotation.NotNull;
/**
* EQE base class: head section.
*/
abstract class EnhancedQueueExecutorBase3 extends EnhancedQueueExecutorBase2 {
static final long headOffset;
static {
try {
headOffset = unsafe.objectFieldOffset(EnhancedQueueExecutorBase3.class.getDeclaredField("head"));
} catch (NoSuchFieldException e) {
throw new NoSuchFieldError(e.getMessage());
}
}
// =======================================================
// Current state fields
// =======================================================
/**
* The node preceding the head node; this field is not {@code null}. This is
* the removal point for tasks (and the insertion point for waiting threads).
*/
@NotNull
@SuppressWarnings("unused") // used by field updater
volatile EnhancedQueueExecutor.TaskNode head;
EnhancedQueueExecutorBase3() {
head = tail = new EnhancedQueueExecutor.TaskNode(null);
}
// =======================================================
// Compare-and-set operations
// =======================================================
boolean compareAndSetHead(final EnhancedQueueExecutor.TaskNode expect, final EnhancedQueueExecutor.TaskNode update) {
return unsafe.compareAndSwapObject(this, headOffset, expect, update);
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/EnhancedQueueExecutorBase4.java 0000664 0000000 0000000 00000002061 14303134447 0031271 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
/**
* EQE base class: padding.
*/
abstract class EnhancedQueueExecutorBase4 extends EnhancedQueueExecutorBase3 {
/**
* Padding fields.
*/
@SuppressWarnings("unused")
int p00, p01, p02, p03,
p04, p05, p06, p07,
p08, p09, p0A, p0B,
p0C, p0D, p0E, p0F;
EnhancedQueueExecutorBase4() {}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/EnhancedQueueExecutorBase5.java 0000664 0000000 0000000 00000005265 14303134447 0031303 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2018, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.threads;
import static org.jboss.threads.JBossExecutors.unsafe;
/**
* EQE base: thread status
*/
abstract class EnhancedQueueExecutorBase5 extends EnhancedQueueExecutorBase4 {
static final long threadStatusOffset;
static {
try {
threadStatusOffset = unsafe.objectFieldOffset(EnhancedQueueExecutorBase5.class.getDeclaredField("threadStatus"));
} catch (NoSuchFieldException e) {
throw new NoSuchFieldError(e.getMessage());
}
}
// =======================================================
// Current state fields
// =======================================================
/**
* Active consumers:
*
* Bit 00..19: current number of running threads
* Bit 20..39: core pool size
* Bit 40..59: maximum pool size
* Bit 60: 1 = allow core thread timeout; 0 = disallow core thread timeout
* Bit 61: 1 = shutdown requested; 0 = shutdown not requested
* Bit 62: 1 = shutdown task interrupt requested; 0 = interrupt not requested
* Bit 63: 1 = shutdown complete; 0 = shutdown not complete
*
*/
@SuppressWarnings("unused") // used by field updater
volatile long threadStatus;
EnhancedQueueExecutorBase5() {
super();
}
// =======================================================
// Compare-and-set operations
// =======================================================
boolean compareAndSetThreadStatus(final long expect, final long update) {
return unsafe.compareAndSwapLong(this, threadStatusOffset, expect, update);
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/EnhancedQueueExecutorBase6.java 0000664 0000000 0000000 00000002555 14303134447 0031303 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2018, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.threads;
/**
* EQE base class: padding.
*/
abstract class EnhancedQueueExecutorBase6 extends EnhancedQueueExecutorBase5 {
/**
* Padding fields.
*/
@SuppressWarnings("unused")
int p00, p01, p02, p03,
p04, p05, p06, p07,
p08, p09, p0A, p0B,
p0C, p0D, p0E, p0F;
EnhancedQueueExecutorBase6() {}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/EnhancedViewExecutor.java 0000664 0000000 0000000 00000055174 14303134447 0030255 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2020 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import org.jboss.logging.Logger;
import org.wildfly.common.Assert;
import org.wildfly.common.annotation.Nullable;
import org.wildfly.common.cpu.ProcessorInfo;
import org.wildfly.common.lock.Locks;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import static org.jboss.threads.JBossExecutors.unsafe;
/**
* A View Executor implementation which avoids lock contention in the common path. This allows us to
* provide references to the same underlying pool of threads to different consumers and utilize distinct
* instrumentation without duplicating resources. This implementation is optimized to avoid locking.
*
* @author Carter Kozak
*/
final class EnhancedViewExecutor extends ViewExecutor {
private static final Logger log = Logger.getLogger("org.jboss.threads.view-executor");
private static final long stateOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset(EnhancedViewExecutor.class.getDeclaredField("state"));
} catch (NoSuchFieldException e) {
throw new NoSuchFieldError(e.getMessage());
}
}
private static final int QUEUE_FAILURE_LOG_INTERVAL =
readIntPropertyPrefixed("queue.failure.log.interval", 1_000_000);
private static final int MAX_QUEUE_SPINS =
readIntPropertyPrefixed("queue.poll.spins", ProcessorInfo.availableProcessors() == 1 ? 0 : 128);
private static final long SHUTDOWN_MASK = 1L << 63;
private static final long ACTIVE_COUNT_MASK = (1L << 31) - 1;
private static final int QUEUED_SIZE_OFFSET = 31;
private static final long QUEUED_SIZE_MASK = ((1L << 31) - 1) << QUEUED_SIZE_OFFSET;
private final Executor delegate;
private final int maxCount;
private final int queueLimit;
/**
* The execute lock is only needed necessary to guard from submitting the first queued task before the last
* non-queued (active == maxCount) task can be submitted to the executor. Locks are not used when the queue
* is disabled.
* This lock is required to prevent tasks from beginning to queue while tasks that have acquired permits
* have not been successfully submitted to the delegate executor. In this case the queue may be left in an
* unrecoverable state if the delegate executor rejects input.
*/
@Nullable
private final Lock executeLock;
private final Object shutdownLock = new Object();
private final Set activeRunnables = ConcurrentHashMap.newKeySet();
/**
* Queue handling:
*
* The queue must only be modified after a successful CAS {@link #state} update.
* After the queue length component of {@link #state} has been decremented a receiver
* may need to wait for the producer to successfully add an item to the queue.
*/
private final Queue queue = new ConcurrentLinkedQueue<>();
/**
* State structure.
*
*
* Bit 00..30: Number of active tasks (unsigned)
* Bit 31..61: Number of queued tasks (unsigned)
* Bit 62: unused; always zero
* Bit 63: executor shutdown state; 0 = shutdown has not been requested
*
*/
@SuppressWarnings("unused")
private volatile long state;
private volatile boolean interrupted = false;
EnhancedViewExecutor(
final Executor delegate,
final int maxCount,
final int queueLimit,
final Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {
this.delegate = Assert.checkNotNullParam("delegate", delegate);
this.maxCount = maxCount;
this.queueLimit = queueLimit;
// The lock is only necessary when queueing is possible
this.executeLock = queueLimit == 0
? null
// Lock must be reentrant to handle same-thread executors or CallerRunsPolicy
: Locks.reentrantLock();
this.setExceptionHandler(uncaughtExceptionHandler);
}
@Override
public void shutdown(boolean interrupt) {
for (;;) {
long stateSnapshot = state;
// Avoid unnecessary work if shutdown is already set.
if (isShutdown(stateSnapshot)) {
break; // nothing to do
}
long newState = stateSnapshot | SHUTDOWN_MASK;
if (compareAndSwapState(stateSnapshot, newState)) {
// state change sh1:
// state(snapshot) ← state(snapshot) | shutdown
// succeeds: -
// preconditions: -
// post-actions (succeed):
// If the resulting state is terminal, notify waiters and run the termination task
// post-actions (fail):
// repeat state change until success or break
notifyWaitersIfTerminated(newState);
break;
}
}
if (interrupt) {
interrupted = true;
activeRunnables.forEach(EnhancedViewExecutorRunnable::interrupt);
}
}
@Override
public List shutdownNow() {
int queuedElementsToRemove;
for (;;) {
long stateSnapshot = state;
// If shutdown is already set, the queue is still expected to be drained when shutdownNow is invoked.
queuedElementsToRemove = getQueueSize(stateSnapshot);
if (isShutdown(stateSnapshot) && queuedElementsToRemove == 0) {
break; // nothing to do
}
// state change sh2:
// state(snapshot).shutdown ← true
// state(snapshot).queueSize ← zero
// succeeds: -
// preconditions: -
// post-actions (succeed):
// If the resulting state is terminal, notify waiters and run the termination task
// Interrupt active threads
// Drain the queue by the value of state(snapshot).queueSize
// post-actions (fail):
// repeat state change until success or break
long newState = (stateSnapshot | SHUTDOWN_MASK) & ~QUEUED_SIZE_MASK;
if (compareAndSwapState(stateSnapshot, newState)) {
notifyWaitersIfTerminated(newState);
break;
}
}
interrupted = true;
activeRunnables.forEach(EnhancedViewExecutorRunnable::interrupt);
if (queuedElementsToRemove > 0) {
ArrayList neverCommencedExecution = new ArrayList<>(queuedElementsToRemove);
for (int i = 0; i < queuedElementsToRemove; i++) {
neverCommencedExecution.add(blockingTake().delegate);
}
return neverCommencedExecution;
}
return Collections.emptyList();
}
@Override
public boolean isShutdown() {
return isShutdown(state);
}
private static boolean isShutdown(long state) {
return (state & SHUTDOWN_MASK) != 0;
}
@Override
public boolean isTerminated() {
return isTerminated(state);
}
private static boolean isTerminated(long state) {
// SHUTDOWN_MASK is set with neither queued nor active tasks.
return state == SHUTDOWN_MASK;
}
private void notifyWaitersIfTerminated(long stateSnapshot) {
if (isTerminated(stateSnapshot)) {
synchronized (shutdownLock) {
shutdownLock.notifyAll();
}
// The queue must be empty when the executor is terminated.
// If this fails, something has not set state properly.
assert queue.isEmpty();
runTermination();
}
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
long remainingNanos = unit.toNanos(timeout);
// Use the system precise clock to avoid issues resulting from time changes.
long now = System.nanoTime();
synchronized (shutdownLock) {
while (!isTerminated()) {
remainingNanos -= Math.max(-now + (now = System.nanoTime()), 0L);
long remainingMillis = TimeUnit.MILLISECONDS.convert(remainingNanos, TimeUnit.NANOSECONDS);
if (remainingMillis <= 0) {
return false;
}
shutdownLock.wait(remainingMillis);
}
return true;
}
}
@Override
public void execute(Runnable task) {
Assert.checkNotNullParam("task", task);
final EnhancedViewExecutorRunnable decoratedTask = new EnhancedViewExecutorRunnable(
task, JBossExecutors.getContextClassLoader(Thread.currentThread()));
final int maxCount = this.maxCount;
final int queueLimit = this.queueLimit;
Lock executeLock = null;
try {
for (;;) {
long stateSnapshot = state;
if (isShutdown(stateSnapshot)) {
throw new RejectedExecutionException("Executor has been shut down");
}
int activeCount = getActiveCount(stateSnapshot);
int currentQueueSize = getQueueSize(stateSnapshot);
if (queueLimit != 0 && executeLock == null && currentQueueSize == 0 && activeCount >= (maxCount - 1)) {
executeLock = this.executeLock;
executeLock.lock();
// Try again after the lock has been acquired
continue;
}
if (activeCount < maxCount) {
assert getQueueSize(stateSnapshot) == 0;
long updatedActiveCount = activeCount + 1;
// state change ex1:
// state(snapshot).activeCount ← state(snapshot).activeCount + 1
// succeeds: -
// preconditions:
// ! state.shutdown
// state(snapshot).activeCount < maxCount
// state(snapshot).queueSize == 0
// post-actions (succeed):
// task is executed on the delegate executor
// post-actions (fail):
// retry with new state(snapshot)
if (compareAndSwapState(stateSnapshot, updatedActiveCount | (stateSnapshot & ~ACTIVE_COUNT_MASK))) {
try {
delegate.execute(decoratedTask);
return;
} catch (Throwable t) {
// The active count must be reduced when execute fails
taskComplete(false);
throw t;
}
}
continue;
}
if (currentQueueSize < queueLimit) {
assert activeCount == maxCount;
long updatedQueueSize = currentQueueSize + 1;
// state change ex2:
// state(snapshot).queueSize ← state(snapshot).queueSize + 1
// succeeds: -
// preconditions:
// ! state.shutdown
// state(snapshot).queueSize < queueLimit
// state(snapshot).activeCount == maxCount
// post-actions (succeed):
// task is enqueued
// post-actions (fail):
// retry with new state(snapshot)
if (compareAndSwapState(stateSnapshot, (updatedQueueSize << QUEUED_SIZE_OFFSET) | (stateSnapshot & ~QUEUED_SIZE_MASK))) {
enqueue(decoratedTask);
// Work is complete.
return;
} else {
continue;
}
}
throw new RejectedExecutionException("No executor queue space remaining");
}
} finally {
if (executeLock != null) executeLock.unlock();
}
// Assert.unreachableCode();
}
/**
* Only called after the queue size component of {@link #state} has been updated.
* At this point there may already be a thread waiting for the queued task after decrementing
* the queued count, so it's vital that this operation succeeds.
*/
private void enqueue(EnhancedViewExecutorRunnable task) {
int failures = 0;
for (;;) {
try {
if (queue.offer(task)) return;
throw new RejectedExecutionException("Task was rejected by the queue. This should never happen.");
} catch (Throwable t) {
// Enqueue can safely fail without leaving an executor thread spinning waiting for the queued element
// if the queue size can still be decremented.
if (decrementQueueSize()) throw t;
if (failures == 0) log.error("Failed to submit a task to the queue. This should never happen.", t);
}
if (++failures >= QUEUE_FAILURE_LOG_INTERVAL) {
failures = 0;
}
// try again, hope for the best
Thread.yield();
}
}
private boolean decrementQueueSize() {
for (;;) {
long snapshot = state;
int queueSize = getQueueSize(snapshot);
if (queueSize == 0) {
return false;
}
long newQueueSize = queueSize - 1;
long newState = (snapshot & ~QUEUED_SIZE_MASK) | (newQueueSize << QUEUED_SIZE_OFFSET);
if (compareAndSwapState(snapshot, newState)) {
notifyWaitersIfTerminated(newState);
return true;
}
}
}
/** Tasks are enqueued after a successful state update, they may not be immediately available at this point. */
private EnhancedViewExecutorRunnable blockingTake() {
int spins = 0;
int attempts = 0;
for (;;) {
try {
EnhancedViewExecutorRunnable result = queue.poll();
if (result != null) {
return result;
}
// try again
if (spins < MAX_QUEUE_SPINS) {
spins++;
JDKSpecific.onSpinWait();
} else {
Thread.yield();
}
} catch (Throwable t) {
if (attempts == 0) {
log.error("Failed to read from the queue. This should never happen.", t);
}
if (++attempts >= QUEUE_FAILURE_LOG_INTERVAL) {
attempts = 0;
}
// try again, hope for the best
Thread.yield();
}
}
}
// Returns a EnhancedViewExecutorRunnable which must be executed if it couldn't be submitted to the executor
// if allowQueuePolling is false, only null can be returned.
private EnhancedViewExecutorRunnable taskComplete(boolean allowQueuePolling) {
for (;;) {
long stateSnapshot = state;
int queueSize = getQueueSize(stateSnapshot);
if (queueSize > 0 && allowQueuePolling) {
// state change tc1:
// state(snapshot).queueSize ← state(snapshot).queueSize - 1
// succeeds: ex2
// preconditions:
// allowQueuePolling is true
// state(snapshot).queueSize > 0
// post-actions (succeed):
// the queued task is executed
// post-actions (fail):
// retry with new state(snapshot)
long updatedQueueSize = queueSize - 1;
if (compareAndSwapState(stateSnapshot, (updatedQueueSize << QUEUED_SIZE_OFFSET) | (stateSnapshot & ~QUEUED_SIZE_MASK))) {
// no need to check if waiters must be notified, the enqueued task is considered active.
EnhancedViewExecutorRunnable task = blockingTake();
try {
delegate.execute(task);
return null;
} catch (Throwable t) {
return task;
}
}
} else {
// state change tc2:
// state(snapshot).activeCount ← state(snapshot).activeCount - 1
// succeeds: ex1
// preconditions:
// state(snapshot).activeCount > 0
// post-actions (succeed):
// Waiters must be notified if the resulting state is terminal
// post-actions (fail):
// retry with new state(snapshot)
long newState = (getActiveCount(stateSnapshot) - 1) | (stateSnapshot & ~ACTIVE_COUNT_MASK);
if (compareAndSwapState(stateSnapshot, newState)) {
notifyWaitersIfTerminated(newState);
return null;
}
}
}
}
private static int getActiveCount(long state) {
return (int) (state & ACTIVE_COUNT_MASK);
}
private static int getQueueSize(long state) {
return (int) ((state & QUEUED_SIZE_MASK) >> QUEUED_SIZE_OFFSET);
}
private boolean compareAndSwapState(long expected, long update) {
return unsafe.compareAndSwapLong(this, stateOffset, expected, update);
}
private Thread.UncaughtExceptionHandler uncaughtExceptionHandler() {
Thread.UncaughtExceptionHandler handler = getExceptionHandler();
if (handler != null) {
return handler;
}
// If not uncaught exception handler is set, use the current threads existing handler if present.
// Otherwise use the default JBoss logging exception handler.
Thread.UncaughtExceptionHandler threadHandler =
Thread.currentThread().getUncaughtExceptionHandler();
return threadHandler != null ? threadHandler : JBossExecutors.loggingExceptionHandler();
}
@Override
public String toString() {
long snapshot = state;
int activeTasks = getActiveCount(snapshot);
int queueSize = getQueueSize(snapshot);
boolean shutdown = isShutdown(snapshot);
boolean terminated = isTerminated(snapshot);
return "EnhancedViewExecutor{delegate=" + delegate
+ ", active=" + activeTasks
+ ", queued=" + queueSize
+ ", shutdown=" + shutdown
+ ", terminated=" + terminated
+ '}';
}
private final class EnhancedViewExecutorRunnable implements Runnable {
private Runnable delegate;
private ClassLoader contextClassLoader;
@Nullable
private volatile Thread thread;
EnhancedViewExecutorRunnable(Runnable delegate, ClassLoader contextClassLoader) {
this.delegate = delegate;
this.contextClassLoader = contextClassLoader;
}
@Override
public void run() {
// Task is almost always 'this'. If the delegate executor has rejects attempts to submit work,
// existing active threads handle it, potentially violating FIFO order on the delegate queue.
EnhancedViewExecutorRunnable task = this;
// Loop is only used when the executor rejects tasks
while (task != null) {
Thread currentThread = Thread.currentThread();
Set runnables = activeRunnables;
task.thread = currentThread;
try {
runnables.add(task);
if (interrupted) {
// shutdownNow may have been invoked after this task was submitted
// but prior to activeRunnables.add(this).
currentThread.interrupt();
}
Runnable runnable = task.delegate;
ClassLoader loader = task.contextClassLoader;
// Avoid gc pressure from large runnables
task.delegate = null;
task.contextClassLoader = null;
// Matches ContextClassLoaderSavingRunnable without the allocation overhead or
// additional stack frames.
ClassLoader old = JBossExecutors.getAndSetContextClassLoader(currentThread, loader);
try {
runnable.run();
} finally {
JBossExecutors.setContextClassLoader(currentThread, old);
}
} catch (Throwable t) {
// The uncaught exception handler should be called on the current thread in order to log
// using the updated thread name based on nameFunction.
uncaughtExceptionHandler().uncaughtException(task.thread, t);
} finally {
runnables.remove(task);
// Synchronization is important to avoid racily reading the current thread and interrupting
// it after this task completes and a task from another view has begun execution.
synchronized (task) {
task.thread = null;
}
task = taskComplete(true);
}
}
}
synchronized void interrupt() {
Thread taskThread = this.thread;
if (taskThread != null) {
taskThread.interrupt();
}
}
@Override
public String toString() {
return "EnhancedViewExecutorRunnable{" + delegate + '}';
}
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/HandoffRejectedExecutionHandler.java 0000664 0000000 0000000 00000002446 14303134447 0032365 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
class HandoffRejectedExecutionHandler implements RejectedExecutionHandler {
private final Executor target;
HandoffRejectedExecutionHandler(final Executor target) {
this.target = target;
}
public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) {
target.execute(r);
}
public String toString() {
return String.format("%s -> %s", super.toString(), target);
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/InterruptHandler.java 0000664 0000000 0000000 00000002261 14303134447 0027455 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
/**
* A thread interrupt handler. Called when a thread's {@code interrupt()} method is invoked. The handler should
* not throw an exception; otherwise user code might end up in an unexpected state.
*/
public interface InterruptHandler {
/**
* Handle an interrupt condition on the given thread. This method should not throw an exception.
*
* @param thread the thread which was interrupted
*/
void handleInterrupt(Thread thread);
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/JBossExecutors.java 0000664 0000000 0000000 00000035620 14303134447 0027112 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.lang.reflect.Field;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ScheduledExecutorService;
import java.security.PrivilegedAction;
import java.security.AccessController;
import org.jboss.logging.Logger;
import org.wildfly.common.Assert;
import sun.misc.Unsafe;
/**
* JBoss thread- and executor-related utility and factory methods.
*/
public final class JBossExecutors {
private static final Logger THREAD_ERROR_LOGGER = Logger.getLogger("org.jboss.threads.errors");
private JBossExecutors() {}
private static final RuntimePermission COPY_CONTEXT_CLASSLOADER_PERMISSION = new RuntimePermission("copyClassLoader");
private static final ExecutorService REJECTING_EXECUTOR_SERVICE = new DelegatingExecutorService(RejectingExecutor.INSTANCE);
private static final ExecutorService DISCARDING_EXECUTOR_SERVICE = new DelegatingExecutorService(DiscardingExecutor.INSTANCE);
// ==================================================
// DIRECT EXECUTORS
// ==================================================
/**
* Get the direct executor. This executor will immediately run any task it is given, and propagate back any
* run-time exceptions thrown.
*
* @return the direct executor instance
*/
public static Executor directExecutor() {
return SimpleDirectExecutor.INSTANCE;
}
/**
* Get the rejecting executor. This executor will reject any task submitted to it.
*
* @return the rejecting executor instance
*/
public static Executor rejectingExecutor() {
return RejectingExecutor.INSTANCE;
}
/**
* Get a rejecting executor. This executor will reject any task submitted to it with the given message.
*
* @param message the reject message
* @return the rejecting executor instance
*/
public static Executor rejectingExecutor(final String message) {
return new RejectingExecutor(message);
}
/**
* Get the rejecting executor service. This executor will reject any task submitted to it. It cannot be shut down.
*
* @return the rejecting executor service instance
*/
public static ExecutorService rejectingExecutorService() {
return REJECTING_EXECUTOR_SERVICE;
}
/**
* Get the rejecting executor service. This executor will reject any task submitted to it with the given message.
* It cannot be shut down.
*
* @param message the reject message
* @return the rejecting executor service instance
*/
public static ExecutorService rejectingExecutorService(final String message) {
return protectedExecutorService(rejectingExecutor(message));
}
/**
* Get the discarding executor. This executor will silently discard any task submitted to it.
*
* @return the discarding executor instance
*/
public static Executor discardingExecutor() {
return DiscardingExecutor.INSTANCE;
}
/**
* Get the discarding executor service. This executor will silently discard any task submitted to it. It cannot
* be shut down.
*
* @return the discarding executor service instance
*/
public static ExecutorService discardingExecutorService() {
return DISCARDING_EXECUTOR_SERVICE;
}
/**
* Create an executor which runs tasks with the given context class loader.
*
* @param delegate the executor to delegate to
* @param taskClassLoader the context class loader to use
* @return the new direct executor
*/
public static Executor contextClassLoaderExecutor(final Executor delegate, final ClassLoader taskClassLoader) {
return new DelegatingExecutor(delegate) {
public void execute(final Runnable command) {
super.execute(new ContextClassLoaderSavingRunnable(taskClassLoader, command));
}
};
}
// ==================================================
// REJECTED EXECUTION HANDLERS
// ==================================================
private static final RejectedExecutionHandler ABORT_POLICY = new ThreadPoolExecutor.AbortPolicy();
private static final RejectedExecutionHandler CALLER_RUNS_POLICY = new ThreadPoolExecutor.CallerRunsPolicy();
private static final RejectedExecutionHandler DISCARD_OLDEST_POLICY = new ThreadPoolExecutor.DiscardOldestPolicy();
private static final RejectedExecutionHandler DISCARD_POLICY = new ThreadPoolExecutor.DiscardPolicy();
/**
* Get the abort policy for a {@link java.util.concurrent.ThreadPoolExecutor}.
*
* @return the abort policy
* @see java.util.concurrent.ThreadPoolExecutor.AbortPolicy
*/
public static RejectedExecutionHandler abortPolicy() {
return ABORT_POLICY;
}
/**
* Get the caller-runs policy for a {@link java.util.concurrent.ThreadPoolExecutor}.
*
* @return the caller-runs policy
* @see java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy
*/
public static RejectedExecutionHandler callerRunsPolicy() {
return CALLER_RUNS_POLICY;
}
/**
* Get the discard-oldest policy for a {@link java.util.concurrent.ThreadPoolExecutor}.
*
* @return the discard-oldest policy
* @see java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy
*/
public static RejectedExecutionHandler discardOldestPolicy() {
return DISCARD_OLDEST_POLICY;
}
/**
* Get the discard policy for a {@link java.util.concurrent.ThreadPoolExecutor}.
*
* @return the discard policy
* @see java.util.concurrent.ThreadPoolExecutor.DiscardPolicy
*/
public static RejectedExecutionHandler discardPolicy() {
return DISCARD_POLICY;
}
/**
* Get a handoff policy for a {@link java.util.concurrent.ThreadPoolExecutor}. The returned instance will
* delegate to another executor in the event that the task is rejected.
*
* @param target the target executor
* @return the new handoff policy implementation
*/
public static RejectedExecutionHandler handoffPolicy(final Executor target) {
return new HandoffRejectedExecutionHandler(target);
}
// ==================================================
// PROTECTED EXECUTOR SERVICE WRAPPERS
// ==================================================
/**
* Wrap an executor with an {@code ExecutorService} instance which supports all the features of {@code ExecutorService}
* except for shutting down the executor.
*
* @param target the target executor
* @return the executor service
*/
public static ExecutorService protectedExecutorService(final Executor target) {
return new DelegatingExecutorService(target);
}
/**
* Wrap a scheduled executor with a {@code ScheduledExecutorService} instance which supports all the features of
* {@code ScheduledExecutorService} except for shutting down the executor.
*
* @param target the target executor
* @return the executor service
*/
public static ScheduledExecutorService protectedScheduledExecutorService(final ScheduledExecutorService target) {
return new DelegatingScheduledExecutorService(target);
}
// ==================================================
// THREAD FACTORIES
// ==================================================
/**
* Create a thread factory which resets all thread-local storage and delegates to the given thread factory.
* You must have the {@link RuntimePermission}{@code ("modifyThread")} permission to use this method.
*
* @param delegate the delegate thread factory
* @return the resetting thread factory
* @throws SecurityException if the caller does not have the {@link RuntimePermission}{@code ("modifyThread")}
* permission
*/
public static ThreadFactory resettingThreadFactory(final ThreadFactory delegate) throws SecurityException {
return new ThreadFactory() {
public Thread newThread(final Runnable r) {
return delegate.newThread(new ThreadLocalResettingRunnable(r));
}
};
}
private static final Runnable TCCL_RESETTER = new Runnable() {
public void run() {
Thread.currentThread().setContextClassLoader(null);
}
public String toString() {
return "ContextClassLoader-resetting Runnable";
}
};
// ==================================================
// RUNNABLES
// ==================================================
private static final Runnable NULL_RUNNABLE = NullRunnable.getInstance();
/**
* Get the null runnable which does nothing.
*
* @return the null runnable
*/
public static Runnable nullRunnable() {
return NULL_RUNNABLE;
}
/**
* Get a {@code Runnable} which, when executed, clears the thread context class loader (if the caller has sufficient
* privileges).
*
* @return the runnable
*/
public static Runnable contextClassLoaderResetter() {
return TCCL_RESETTER;
}
/**
* Create a task that delegates to the given task, preserving the context classloader which was in effect when
* this method was invoked.
*
* @param delegate the delegate runnable
* @return the wrapping runnable
* @throws SecurityException if a security manager exists and the caller does not have the {@code "copyClassLoader"}
* {@link RuntimePermission}.
*/
public static Runnable classLoaderPreservingTask(final Runnable delegate) throws SecurityException {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(COPY_CONTEXT_CLASSLOADER_PERMISSION);
}
return classLoaderPreservingTaskUnchecked(delegate);
}
static final ClassLoader SAFE_CL;
static {
ClassLoader safeClassLoader = JBossExecutors.class.getClassLoader();
if (safeClassLoader == null) {
safeClassLoader = ClassLoader.getSystemClassLoader();
}
if (safeClassLoader == null) {
safeClassLoader = new ClassLoader() {
};
}
SAFE_CL = safeClassLoader;
}
static Runnable classLoaderPreservingTaskUnchecked(final Runnable delegate) {
Assert.checkNotNullParam("delegate", delegate);
return new ContextClassLoaderSavingRunnable(getContextClassLoader(Thread.currentThread()), delegate);
}
static final Unsafe unsafe;
static final long contextClassLoaderOffs;
static {
unsafe = AccessController.doPrivileged(new PrivilegedAction() {
public Unsafe run() {
try {
final Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (Unsafe) field.get(null);
} catch (IllegalAccessException e) {
throw new IllegalAccessError(e.getMessage());
} catch (NoSuchFieldException e) {
throw new NoSuchFieldError(e.getMessage());
}
}
});
try {
contextClassLoaderOffs = unsafe.objectFieldOffset(Thread.class.getDeclaredField("contextClassLoader"));
} catch (NoSuchFieldException e) {
throw new NoSuchFieldError(e.getMessage());
}
}
/**
* Privileged method to get the context class loader of the given thread.
*
* @param thread the thread to introspect
* @return the context class loader
*/
static ClassLoader getContextClassLoader(final Thread thread) {
return (ClassLoader) unsafe.getObject(thread, contextClassLoaderOffs);
}
/**
* Privileged method to get and set the context class loader of the given thread.
*
* @param thread the thread to introspect
* @param newClassLoader the new context class loader
* @return the old context class loader
*/
static ClassLoader getAndSetContextClassLoader(final Thread thread, final ClassLoader newClassLoader) {
try {
return getContextClassLoader(thread);
} finally {
setContextClassLoader(thread, newClassLoader);
}
}
/**
* Privileged method to set the context class loader of the given thread.
*
* @param thread the thread to introspect
* @param classLoader the new context class loader
*/
static void setContextClassLoader(final Thread thread, final ClassLoader classLoader) {
unsafe.putObject(thread, contextClassLoaderOffs, classLoader);
}
/**
* Privileged method to clear the context class loader of the given thread to a safe non-{@code null} value.
*
* @param thread the thread to introspect
*/
static void clearContextClassLoader(final Thread thread) {
unsafe.putObject(thread, contextClassLoaderOffs, SAFE_CL);
}
// ==================================================
// UNCAUGHT EXCEPTION HANDLERS
// ==================================================
/**
* Get an uncaught exception handler which logs to the given logger.
*
* @param log the logger
* @return the handler
*/
public static Thread.UncaughtExceptionHandler loggingExceptionHandler(final Logger log) {
return new LoggingUncaughtExceptionHandler(log);
}
/**
* Get an uncaught exception handler which logs to the given logger.
*
* @param categoryName the name of the logger category to log to
* @return the handler
*/
public static Thread.UncaughtExceptionHandler loggingExceptionHandler(final String categoryName) {
return new LoggingUncaughtExceptionHandler(Logger.getLogger(categoryName));
}
private static final Thread.UncaughtExceptionHandler LOGGING_HANDLER = loggingExceptionHandler(THREAD_ERROR_LOGGER);
/**
* Get an uncaught exception handler which logs to the default error logger.
*
* @return the handler
*/
public static Thread.UncaughtExceptionHandler loggingExceptionHandler() {
return LOGGING_HANDLER;
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/JBossScheduledThreadPoolExecutor.java 0000664 0000000 0000000 00000010522 14303134447 0032524 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public final class JBossScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
private final AtomicInteger rejectCount = new AtomicInteger();
private final Runnable terminationTask;
public JBossScheduledThreadPoolExecutor(int corePoolSize, final Runnable terminationTask) {
super(corePoolSize);
this.terminationTask = terminationTask;
setRejectedExecutionHandler(super.getRejectedExecutionHandler());
}
public JBossScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, final Runnable terminationTask) {
super(corePoolSize, threadFactory);
this.terminationTask = terminationTask;
setRejectedExecutionHandler(super.getRejectedExecutionHandler());
}
public JBossScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler, final Runnable terminationTask) {
super(corePoolSize);
this.terminationTask = terminationTask;
setRejectedExecutionHandler(handler);
}
public JBossScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler, final Runnable terminationTask) {
super(corePoolSize, threadFactory);
this.terminationTask = terminationTask;
setRejectedExecutionHandler(handler);
}
public long getKeepAliveTime() {
return getKeepAliveTime(TimeUnit.MILLISECONDS);
}
public void setKeepAliveTime(final long milliseconds) {
super.setKeepAliveTime(milliseconds, TimeUnit.MILLISECONDS);
super.allowCoreThreadTimeOut(milliseconds < Long.MAX_VALUE);
}
public void setKeepAliveTime(final long time, final TimeUnit unit) {
super.setKeepAliveTime(time, unit);
super.allowCoreThreadTimeOut(time < Long.MAX_VALUE);
}
public int getRejectedCount() {
return rejectCount.get();
}
public int getCurrentThreadCount() {
return getActiveCount();
}
public int getLargestThreadCount() {
return getLargestPoolSize();
}
public int getMaxThreads() {
return getCorePoolSize();
}
public void setMaxThreads(final int newSize) {
setCorePoolSize(newSize);
}
public RejectedExecutionHandler getRejectedExecutionHandler() {
return ((CountingRejectHandler)super.getRejectedExecutionHandler()).getDelegate();
}
public void setRejectedExecutionHandler(final RejectedExecutionHandler handler) {
super.setRejectedExecutionHandler(new CountingRejectHandler(handler));
}
/** {@inheritDoc} */
public int getQueueSize() {
return this.getQueue().size();
}
protected void terminated() {
terminationTask.run();
}
private final class CountingRejectHandler implements RejectedExecutionHandler {
private final RejectedExecutionHandler delegate;
public CountingRejectHandler(final RejectedExecutionHandler delegate) {
this.delegate = delegate;
}
public RejectedExecutionHandler getDelegate() {
return delegate;
}
public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) {
rejectCount.incrementAndGet();
if (isShutdown()) {
throw Messages.msg.shutDownInitiated();
}
delegate.rejectedExecution(r, executor);
}
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/JBossThread.java 0000664 0000000 0000000 00000067000 14303134447 0026335 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;
import org.wildfly.common.Assert;
import org.wildfly.common.cpu.ProcessorInfo;
import org.wildfly.common.function.ExceptionBiConsumer;
import org.wildfly.common.function.ExceptionBiFunction;
import org.wildfly.common.function.ExceptionConsumer;
import org.wildfly.common.function.ExceptionFunction;
import org.wildfly.common.function.ExceptionObjIntConsumer;
import org.wildfly.common.function.ExceptionObjLongConsumer;
import org.wildfly.common.function.ExceptionRunnable;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.common.function.Functions;
/**
* A JBoss thread. Supports logging and extra operations.
*/
public class JBossThread extends Thread {
private static final RuntimePermission MODIFY_THREAD_PERMISSION = new RuntimePermission("modifyThread");
private static final int MAX_INTERRUPT_SPINS = AccessController.doPrivileged(new PrivilegedAction() {
public Integer run() {
return Integer.valueOf(Integer.parseInt(System.getProperty("jboss.threads.interrupt.spins", ProcessorInfo.availableProcessors() == 1 ? "0" : "128")));
}
}).intValue();
static {
Version.getVersionString();
}
private volatile InterruptHandler interruptHandler;
private ThreadNameInfo threadNameInfo;
private List exitHandlers;
/**
* The thread is maybe interrupted. Possible transitions:
*
* {@link #STATE_INTERRUPT_DEFERRED}
* {@link #STATE_INTERRUPT_IN_PROGRESS}
*
*/
private static final int STATE_MAYBE_INTERRUPTED = 0;
/**
* The thread is not interrupted, and interrupts will be deferred. Possible transitions:
*
* {@link #STATE_MAYBE_INTERRUPTED}
* {@link #STATE_INTERRUPT_PENDING}
*
*/
private static final int STATE_INTERRUPT_DEFERRED = 1;
/**
* The thread is not interrupted, but there is an interrupt pending for once deferral is ended. Possible transitions:
*
* {@link #STATE_INTERRUPT_IN_PROGRESS}
*
*/
private static final int STATE_INTERRUPT_PENDING = 2;
/**
* The thread is in the process of executing interruption logic. If the thread attempts to defer interrupts
* during this phase, it will block until the interruption logic is complete.
*/
private static final int STATE_INTERRUPT_IN_PROGRESS = 3;
private final AtomicInteger stateRef = new AtomicInteger();
/**
* Construct a new instance.
*
* @param target the runnable target
* @see Thread#Thread(Runnable)
*/
public JBossThread(final Runnable target) {
super(target);
}
/**
* Construct a new instance.
*
* @param target the runnable target
* @param name the initial thread name
* @see Thread#Thread(Runnable, String)
*/
public JBossThread(final Runnable target, final String name) {
super(target, name);
}
/**
* Construct a new instance.
*
* @param group the parent thread group
* @param target the runnable target
* @see Thread#Thread(ThreadGroup, Runnable)
* @throws SecurityException if the current thread cannot create a thread in the specified thread group
*/
public JBossThread(final ThreadGroup group, final Runnable target) throws SecurityException {
super(group, target);
}
/**
* Construct a new instance.
*
* @param group the parent thread group
* @param target the runnable target
* @param name the initial thread name
* @see Thread#Thread(ThreadGroup,Runnable,String)
* @throws SecurityException if the current thread cannot create a thread in the specified thread group
*/
public JBossThread(final ThreadGroup group, final Runnable target, final String name) throws SecurityException {
super(group, target, name);
}
/**
* Construct a new instance.
*
* @param group the parent thread group
* @param target the runnable target
* @param name the initial thread name
* @see Thread#Thread(ThreadGroup,Runnable,String,long)
* @throws SecurityException if the current thread cannot create a thread in the specified thread group
*/
public JBossThread(final ThreadGroup group, final Runnable target, final String name, final long stackSize) throws SecurityException {
super(group, target, name, stackSize);
}
/**
* Interrupt this thread. Logs a trace message and calls the current interrupt handler, if any. The interrupt
* handler is called from the calling thread, not the thread being interrupted.
*/
public void interrupt() {
final boolean differentThread = Thread.currentThread() != this;
if (differentThread) checkAccess();
// fast check
if (isInterrupted()) return;
final AtomicInteger stateRef = this.stateRef;
int oldVal, newVal;
int spins = 0;
for (;;) {
oldVal = stateRef.get();
if (oldVal == STATE_INTERRUPT_PENDING) {
// already set
Messages.msg.tracef("Interrupting thread \"%s\" (already interrupted)", this);
return;
} else if (oldVal == STATE_INTERRUPT_IN_PROGRESS) {
// wait for interruption on other thread to be completed
if (spins < MAX_INTERRUPT_SPINS) {
JDKSpecific.onSpinWait();
spins++;
} else {
Thread.yield();
}
continue;
} else {
if (oldVal == STATE_INTERRUPT_DEFERRED) {
newVal = STATE_INTERRUPT_PENDING;
} else {
newVal = STATE_INTERRUPT_IN_PROGRESS;
}
}
if (stateRef.compareAndSet(oldVal, newVal)) {
break;
}
}
if (newVal == STATE_INTERRUPT_IN_PROGRESS) try {
doInterrupt();
} finally {
// after we return, the thread could be un-interrupted at any time without our knowledge
stateRef.set(STATE_MAYBE_INTERRUPTED);
if (differentThread) {
// unpark the thread if it was waiting to defer interrupts
// interrupting the thread will unpark it; it might park after the interrupt though, or wake up before the state is restored
LockSupport.unpark(this);
}
} else {
Messages.intMsg.tracef("Interrupting thread \"%s\" (deferred)", this);
}
}
private void doInterrupt() {
if (isInterrupted()) return;
Messages.msg.tracef("Interrupting thread \"%s\"", this);
try {
super.interrupt();
} finally {
final InterruptHandler interruptHandler = this.interruptHandler;
if (interruptHandler != null) {
try {
interruptHandler.handleInterrupt(this);
} catch (Throwable t) {
Messages.msg.interruptHandlerThrew(t, interruptHandler);
}
}
}
}
public boolean isInterrupted() {
return this == Thread.currentThread() ? super.isInterrupted() : super.isInterrupted() || (stateRef.get() == STATE_INTERRUPT_PENDING);
}
/**
* Defer interrupts for the duration of some task. Once the task is complete, any deferred interrupt will be
* delivered to the thread, thus the thread interrupt status should be checked upon return. If the current thread
* is not a {@code JBossThread}, the task is simply run as-is.
*
* @param task the task to run
*/
public static void executeWithInterruptDeferred(final Runnable task) {
final JBossThread thread = currentThread();
if (registerDeferral(thread)) try {
task.run();
} finally {
unregisterDeferral(thread);
} else {
// already deferred
task.run();
}
}
/**
* Defer interrupts for the duration of some task. Once the task is complete, any deferred interrupt will be
* delivered to the thread, thus the thread interrupt status should be checked upon return. If the current thread
* is not a {@code JBossThread}, the task is simply run as-is.
*
* @param action the task to run
* @param the callable's return type
* @return the value returned from the callable
* @throws Exception if the action throws an exception
*/
public static T executeWithInterruptDeferred(final Callable action) throws Exception {
final JBossThread thread = currentThread();
if (registerDeferral(thread)) try {
return action.call();
} finally {
unregisterDeferral(thread);
} else {
// already deferred
return action.call();
}
}
/**
* Defer interrupts for the duration of some task. Once the task is complete, any deferred interrupt will be
* delivered to the thread, thus the thread interrupt status should be checked upon return. If the current thread
* is not a {@code JBossThread}, the task is simply run as-is.
*
* @param action the task to run
* @param the action's return type
* @return the value returned from the callable
*/
public static T executeWithInterruptDeferred(final PrivilegedAction action) {
final JBossThread thread = currentThread();
if (registerDeferral(thread)) try {
return action.run();
} finally {
unregisterDeferral(thread);
} else {
// already deferred
return action.run();
}
}
/**
* Defer interrupts for the duration of some task. Once the task is complete, any deferred interrupt will be
* delivered to the thread, thus the thread interrupt status should be checked upon return. If the current thread
* is not a {@code JBossThread}, the task is simply run as-is.
*
* @param action the task to run
* @param the action's return type
* @return the value returned from the callable
* @throws Exception if the action throws an exception
*/
public static T executeWithInterruptDeferred(final PrivilegedExceptionAction action) throws Exception {
final JBossThread thread = currentThread();
if (registerDeferral(thread)) try {
return action.run();
} finally {
unregisterDeferral(thread);
} else {
// already deferred
return action.run();
}
}
public static R applyInterruptDeferredEx(final ExceptionBiFunction function, T param1, U param2) throws E {
final JBossThread thread = currentThread();
if (registerDeferral(thread)) try {
return function.apply(param1, param2);
} finally {
unregisterDeferral(thread);
} else {
// already deferred
return function.apply(param1, param2);
}
}
public static R applyInterruptDeferredEx(final ExceptionFunction function, T param) throws E {
return applyInterruptDeferredEx(Functions.exceptionFunctionBiFunction(), function, param);
}
public static T getInterruptDeferredEx(final ExceptionSupplier supplier) throws E {
return applyInterruptDeferredEx(Functions.exceptionFunctionBiFunction(), Functions.exceptionSupplierFunction(), supplier);
}
public static void acceptInterruptDeferredEx(final ExceptionObjLongConsumer consumer, T param1, long param2) throws E {
final JBossThread thread = currentThread();
if (registerDeferral(thread)) try {
consumer.accept(param1, param2);
} finally {
unregisterDeferral(thread);
} else {
// already deferred
consumer.accept(param1, param2);
}
}
public static void acceptInterruptDeferredEx(final ExceptionObjIntConsumer consumer, T param1, int param2) throws E {
final JBossThread thread = currentThread();
if (registerDeferral(thread)) try {
consumer.accept(param1, param2);
} finally {
unregisterDeferral(thread);
} else {
// already deferred
consumer.accept(param1, param2);
}
}
public static void acceptInterruptDeferredEx(final ExceptionBiConsumer consumer, T param1, U param2) throws E {
final JBossThread thread = currentThread();
if (registerDeferral(thread)) try {
consumer.accept(param1, param2);
} finally {
unregisterDeferral(thread);
} else {
// already deferred
consumer.accept(param1, param2);
}
}
public static void acceptInterruptDeferredEx(final ExceptionConsumer consumer, T param) throws E {
acceptInterruptDeferredEx(Functions.exceptionConsumerBiConsumer(), consumer, param);
}
public static void runInterruptDeferredEx(final ExceptionRunnable runnable) throws E {
acceptInterruptDeferredEx(Functions.exceptionConsumerBiConsumer(), Functions.exceptionRunnableConsumer(), runnable);
}
public static R applyInterruptResumedEx(final ExceptionBiFunction function, T param1, U param2) throws E {
final JBossThread thread = currentThread();
if (unregisterDeferral(thread)) try {
return function.apply(param1, param2);
} finally {
registerDeferral(thread);
} else {
// already resumed
return function.apply(param1, param2);
}
}
public static R applyInterruptResumedEx(final ExceptionFunction function, T param) throws E {
return applyInterruptResumedEx(Functions.exceptionFunctionBiFunction(), function, param);
}
public static T getInterruptResumedEx(final ExceptionSupplier supplier) throws E {
return applyInterruptResumedEx(Functions.exceptionFunctionBiFunction(), Functions.exceptionSupplierFunction(), supplier);
}
public static void acceptInterruptResumedEx(final ExceptionObjLongConsumer consumer, T param1, long param2) throws E {
final JBossThread thread = currentThread();
if (unregisterDeferral(thread)) try {
consumer.accept(param1, param2);
} finally {
registerDeferral(thread);
} else {
// already resumed
consumer.accept(param1, param2);
}
}
public static void acceptInterruptResumedEx(final ExceptionObjIntConsumer consumer, T param1, int param2) throws E {
final JBossThread thread = currentThread();
if (unregisterDeferral(thread)) try {
consumer.accept(param1, param2);
} finally {
registerDeferral(thread);
} else {
// already resumed
consumer.accept(param1, param2);
}
}
public static void acceptInterruptResumedEx(final ExceptionBiConsumer consumer, T param1, U param2) throws E {
final JBossThread thread = currentThread();
if (unregisterDeferral(thread)) try {
consumer.accept(param1, param2);
} finally {
registerDeferral(thread);
} else {
// already resumed
consumer.accept(param1, param2);
}
}
public static void acceptInterruptResumedEx(final ExceptionConsumer consumer, T param) throws E {
acceptInterruptResumedEx(Functions.exceptionConsumerBiConsumer(), consumer, param);
}
public static void runInterruptResumedEx(final ExceptionRunnable runnable) throws E {
acceptInterruptResumedEx(Functions.exceptionConsumerBiConsumer(), Functions.exceptionRunnableConsumer(), runnable);
}
private static boolean unregisterDeferral(final JBossThread thread) {
if (thread == null) {
return false;
}
int oldVal, newVal;
final AtomicInteger stateRef = thread.stateRef;
do {
oldVal = stateRef.get();
if (oldVal == STATE_MAYBE_INTERRUPTED || oldVal == STATE_INTERRUPT_IN_PROGRESS) {
// already not deferred
return false;
} else if (oldVal == STATE_INTERRUPT_DEFERRED) {
newVal = STATE_MAYBE_INTERRUPTED;
} else if (oldVal == STATE_INTERRUPT_PENDING) {
newVal = STATE_INTERRUPT_IN_PROGRESS;
} else {
throw Assert.unreachableCode();
}
} while (! stateRef.compareAndSet(oldVal, newVal));
if (newVal == STATE_INTERRUPT_IN_PROGRESS) try {
thread.doInterrupt();
} finally {
stateRef.set(STATE_MAYBE_INTERRUPTED);
}
return true;
}
private static boolean registerDeferral(final JBossThread thread) {
if (thread == null) {
return false;
}
final AtomicInteger stateRef = thread.stateRef;
int oldVal, newVal;
do {
oldVal = stateRef.get();
while (oldVal == STATE_INTERRUPT_IN_PROGRESS) {
LockSupport.park();
oldVal = stateRef.get();
}
if (oldVal == STATE_MAYBE_INTERRUPTED) {
newVal = Thread.interrupted() ? STATE_INTERRUPT_DEFERRED : STATE_INTERRUPT_PENDING;
} else if (oldVal == STATE_INTERRUPT_DEFERRED || oldVal == STATE_INTERRUPT_PENDING) {
// already deferred
return false;
} else {
throw Assert.unreachableCode();
}
} while (! stateRef.compareAndSet(oldVal, newVal));
if (newVal == STATE_INTERRUPT_DEFERRED && Thread.interrupted()) {
// in case we got interrupted right after we checked interrupt state but before we CAS'd the value.
stateRef.set(STATE_INTERRUPT_PENDING);
}
return true;
}
/**
* Execute the thread's {@code Runnable}. Logs a trace message at the start and end of execution and runs exit
* handlers when the thread exits.
*/
public void run() {
Messages.msg.tracef("Thread \"%s\" starting execution", this);
try {
super.run();
} finally {
Messages.msg.tracef("Thread \"%s\" exiting", this);
final List exitHandlers = this.exitHandlers;
if (exitHandlers != null) for (Runnable exitHandler : exitHandlers) {
try {
exitHandler.run();
} catch (Throwable t) {
try {
getUncaughtExceptionHandler().uncaughtException(this, t);
} catch (Throwable ignored) {}
}
}
}
}
/**
* Register a runnable task to be executed when the current thread exits.
*
* @param hook the task to run
* @return {@code true} if the task was registered; {@code false} if the task is {@code null} or if the current
* thread is not an instance of {@code JBossThread}
* @throws SecurityException if a security manager is installed and the caller's security context lacks the
* {@code modifyThread} {@link RuntimePermission}
*/
public static boolean onExit(Runnable hook) throws SecurityException {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(MODIFY_THREAD_PERMISSION);
}
final JBossThread thread = currentThread();
if (thread == null || hook == null) return false;
List exitHandlers = thread.exitHandlers;
if (exitHandlers == null) {
exitHandlers = new ArrayList<>();
thread.exitHandlers = exitHandlers;
}
exitHandlers.add(new ContextClassLoaderSavingRunnable(JBossExecutors.getContextClassLoader(thread), hook));
return true;
}
/**
* Get the current {@code JBossThread}, or {@code null} if the current thread is not a {@code JBossThread}.
*
* @return the current thread, or {@code null}
*/
public static JBossThread currentThread() {
final Thread thread = Thread.currentThread();
return thread instanceof JBossThread ? (JBossThread) thread : null;
}
/**
* Start the thread.
*
* @throws IllegalThreadStateException if the thread was already started.
*/
public void start() {
super.start();
Messages.msg.tracef("Started thread \"%s\"", this);
}
/**
* Change the uncaught exception handler for this thread.
*
* @param eh the new handler
*/
public void setUncaughtExceptionHandler(final UncaughtExceptionHandler eh) {
super.setUncaughtExceptionHandler(eh);
Messages.msg.tracef("Changed uncaught exception handler for \"%s\" to %s", this, eh);
}
/**
* Swap the current thread's active interrupt handler. Most callers should restore the old handler in a {@code finally}
* block like this:
*
* InterruptHandler oldHandler = JBossThread.getAndSetInterruptHandler(newHandler);
* try {
* ...execute interrupt-sensitive operation...
* } finally {
* JBossThread.getAndSetInterruptHandler(oldHandler);
* }
*
*
* @param newInterruptHandler the new interrupt handler
* @return the old interrupt handler
*/
public static InterruptHandler getAndSetInterruptHandler(final InterruptHandler newInterruptHandler) {
final JBossThread thread = currentThread();
if (thread == null) {
throw Messages.msg.noInterruptHandlers();
}
try {
return thread.interruptHandler;
} finally {
thread.interruptHandler = newInterruptHandler;
}
}
public static R applyWithInterruptHandler(InterruptHandler interruptHandler, ExceptionBiFunction function, T param1, U param2) throws E {
final JBossThread thread = currentThread();
if (thread == null) {
return function.apply(param1, param2);
} else {
final InterruptHandler old = thread.interruptHandler;
thread.interruptHandler = interruptHandler;
try {
return function.apply(param1, param2);
} finally {
thread.interruptHandler = old;
}
}
}
public static R applyWithInterruptHandler(InterruptHandler interruptHandler, ExceptionFunction function, T param1) throws E {
return applyWithInterruptHandler(interruptHandler, Functions.exceptionFunctionBiFunction(), function, param1);
}
public static R getWithInterruptHandler(InterruptHandler interruptHandler, ExceptionSupplier function) throws E {
return applyWithInterruptHandler(interruptHandler, Functions.exceptionFunctionBiFunction(), Functions.exceptionSupplierFunction(), function);
}
public static void acceptWithInterruptHandler(InterruptHandler interruptHandler, ExceptionObjLongConsumer function, T param1, long param2) throws E {
final JBossThread thread = currentThread();
if (thread == null) {
function.accept(param1, param2);
return;
} else {
final InterruptHandler old = thread.interruptHandler;
thread.interruptHandler = interruptHandler;
try {
function.accept(param1, param2);
return;
} finally {
thread.interruptHandler = old;
}
}
}
public static void acceptWithInterruptHandler(InterruptHandler interruptHandler, ExceptionObjIntConsumer function, T param1, int param2) throws E {
final JBossThread thread = currentThread();
if (thread == null) {
function.accept(param1, param2);
return;
} else {
final InterruptHandler old = thread.interruptHandler;
thread.interruptHandler = interruptHandler;
try {
function.accept(param1, param2);
return;
} finally {
thread.interruptHandler = old;
}
}
}
public static void acceptWithInterruptHandler(InterruptHandler interruptHandler, ExceptionBiConsumer function, T param1, U param2) throws E {
final JBossThread thread = currentThread();
if (thread == null) {
function.accept(param1, param2);
return;
} else {
final InterruptHandler old = thread.interruptHandler;
thread.interruptHandler = interruptHandler;
try {
function.accept(param1, param2);
return;
} finally {
thread.interruptHandler = old;
}
}
}
public static void acceptWithInterruptHandler(InterruptHandler interruptHandler, ExceptionConsumer function, T param1) throws E {
acceptWithInterruptHandler(interruptHandler, Functions.exceptionConsumerBiConsumer(), function, param1);
}
public static void runWithInterruptHandler(InterruptHandler interruptHandler, ExceptionRunnable function) throws E {
acceptWithInterruptHandler(interruptHandler, Functions.exceptionConsumerBiConsumer(), Functions.exceptionRunnableConsumer(), function);
}
/**
* Get the thread name information. This includes information about the thread's sequence number and so forth.
*
* @return the thread name info
*/
ThreadNameInfo getThreadNameInfo() {
return threadNameInfo;
}
/**
* Set the thread name information. This includes information about the thread's sequence number and so forth.
*
* @param threadNameInfo the new thread name info
* @throws SecurityException if the calling thread is not allowed to modify this thread
*/
void setThreadNameInfo(final ThreadNameInfo threadNameInfo) throws SecurityException {
checkAccess();
this.threadNameInfo = threadNameInfo;
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/JBossThreadFactory.java 0000664 0000000 0000000 00000012427 14303134447 0027670 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.AccessControlContext;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;
/**
* A factory for {@link JBossThread} instances.
*/
public final class JBossThreadFactory implements ThreadFactory {
private final ThreadGroup threadGroup;
private final Boolean daemon;
private final Integer initialPriority;
private final Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
private final Long stackSize;
private final String namePattern;
private final AtomicLong factoryThreadIndexSequence = new AtomicLong(1L);
private final long factoryIndex;
private final AccessControlContext creatingContext;
private static final AtomicLong globalThreadIndexSequence = new AtomicLong(1L);
private static final AtomicLong factoryIndexSequence = new AtomicLong(1L);
/**
* Construct a new instance. The access control context of the calling thread will be the one used to create
* new threads if a security manager is installed.
*
* @param threadGroup the thread group to assign threads to by default (may be {@code null})
* @param daemon whether the created threads should be daemon threads, or {@code null} to use the thread group's setting
* @param initialPriority the initial thread priority, or {@code null} to use the thread group's setting
* @param namePattern the name pattern string
* @param uncaughtExceptionHandler the uncaught exception handler, if any
* @param stackSize the JVM-specific stack size, or {@code null} to leave it unspecified
*/
public JBossThreadFactory(ThreadGroup threadGroup, final Boolean daemon, final Integer initialPriority, String namePattern, final Thread.UncaughtExceptionHandler uncaughtExceptionHandler, final Long stackSize) {
if (threadGroup == null) {
final SecurityManager sm = System.getSecurityManager();
threadGroup = sm != null ? sm.getThreadGroup() : Thread.currentThread().getThreadGroup();
}
this.threadGroup = threadGroup;
this.daemon = daemon;
this.initialPriority = initialPriority;
this.uncaughtExceptionHandler = uncaughtExceptionHandler;
this.stackSize = stackSize;
factoryIndex = factoryIndexSequence.getAndIncrement();
if (namePattern == null) {
namePattern = "pool-%f-thread-%t";
}
this.namePattern = namePattern;
this.creatingContext = AccessController.getContext();
}
/**
* @deprecated Use {@link #JBossThreadFactory(ThreadGroup, Boolean, Integer, String, Thread.UncaughtExceptionHandler, Long)} instead.
*/
public JBossThreadFactory(ThreadGroup threadGroup, final Boolean daemon, final Integer initialPriority, String namePattern, final Thread.UncaughtExceptionHandler uncaughtExceptionHandler, final Long stackSize, final AccessControlContext ignored) {
this(threadGroup, daemon, initialPriority, namePattern, uncaughtExceptionHandler, stackSize);
}
public Thread newThread(final Runnable target) {
final AccessControlContext context;
if ((context = creatingContext) != null) {
return AccessController.doPrivileged(new ThreadCreateAction(target), context);
} else {
return createThread(target);
}
}
private final class ThreadCreateAction implements PrivilegedAction {
private final Runnable target;
private ThreadCreateAction(final Runnable target) {
this.target = target;
}
public Thread run() {
return createThread(target);
}
}
private Thread createThread(final Runnable target) {
final ThreadNameInfo nameInfo = new ThreadNameInfo(globalThreadIndexSequence.getAndIncrement(), factoryThreadIndexSequence.getAndIncrement(), factoryIndex);
final JBossThread thread;
if (stackSize != null) {
thread = new JBossThread(threadGroup, target, "", stackSize.longValue());
} else {
thread = new JBossThread(threadGroup, target);
}
thread.setThreadNameInfo(nameInfo);
thread.setName(nameInfo.format(thread, namePattern));
if (initialPriority != null) thread.setPriority(initialPriority.intValue());
if (daemon != null) thread.setDaemon(daemon.booleanValue());
if (uncaughtExceptionHandler != null) thread.setUncaughtExceptionHandler(uncaughtExceptionHandler);
JBossExecutors.clearContextClassLoader(thread);
return thread;
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/JDKSpecific.java 0000664 0000000 0000000 00000002776 14303134447 0026254 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.concurrent.TimeUnit;
import org.wildfly.common.Assert;
/**
*/
final class JDKSpecific {
static TemporalUnit timeToTemporal(final TimeUnit timeUnit) {
switch (timeUnit) {
case NANOSECONDS: return ChronoUnit.NANOS;
case MICROSECONDS: return ChronoUnit.MICROS;
case MILLISECONDS: return ChronoUnit.MILLIS;
case SECONDS: return ChronoUnit.SECONDS;
case MINUTES: return ChronoUnit.MINUTES;
case HOURS: return ChronoUnit.HOURS;
case DAYS: return ChronoUnit.DAYS;
default: throw Assert.impossibleSwitchCase(timeUnit);
}
}
static void onSpinWait() {
// no operation
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/LoggingUncaughtExceptionHandler.java 0000664 0000000 0000000 00000002355 14303134447 0032431 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import org.jboss.logging.Logger;
class LoggingUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
private final Logger log;
LoggingUncaughtExceptionHandler(final Logger log) {
this.log = log;
}
public void uncaughtException(final Thread thread, final Throwable throwable) {
log.errorf(throwable, "Thread %s threw an uncaught exception", thread);
}
public String toString() {
return String.format("%s to \"%s\"", super.toString(), log.getName());
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/ManagedThreadPoolExecutor.java 0000664 0000000 0000000 00000016716 14303134447 0031232 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.jboss.threads.management.ManageableThreadPoolExecutorService;
import org.jboss.threads.management.StandardThreadPoolMXBean;
import org.wildfly.common.Assert;
/**
* A version of {@link ThreadPoolExecutor} which implements {@link ManageableThreadPoolExecutorService} in order to allow
* opting out of using {@link EnhancedQueueExecutor}.
*/
public final class ManagedThreadPoolExecutor extends ThreadPoolExecutor implements ManageableThreadPoolExecutorService {
private final Runnable terminationTask;
private final StandardThreadPoolMXBean mxBean = new MXBeanImpl();
private volatile Executor handoffExecutor = JBossExecutors.rejectingExecutor();
private static final RejectedExecutionHandler HANDLER = new RejectedExecutionHandler() {
public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) {
((ManagedThreadPoolExecutor) executor).reject(r);
}
};
public ManagedThreadPoolExecutor(final int corePoolSize, final int maximumPoolSize, final long keepAliveTime, final TimeUnit unit, final BlockingQueue workQueue, final Runnable terminationTask) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, HANDLER);
this.terminationTask = terminationTask;
}
public ManagedThreadPoolExecutor(final int corePoolSize, final int maximumPoolSize, final long keepAliveTime, final TimeUnit unit, final BlockingQueue workQueue, final ThreadFactory threadFactory, final Runnable terminationTask) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, HANDLER);
this.terminationTask = terminationTask;
}
public ManagedThreadPoolExecutor(final int corePoolSize, final int maximumPoolSize, final long keepAliveTime, final TimeUnit unit, final BlockingQueue workQueue, final Executor handoffExecutor, final Runnable terminationTask) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, HANDLER);
this.terminationTask = terminationTask;
this.handoffExecutor = handoffExecutor;
}
public ManagedThreadPoolExecutor(final int corePoolSize, final int maximumPoolSize, final long keepAliveTime, final TimeUnit unit, final BlockingQueue workQueue, final ThreadFactory threadFactory, final Executor handoffExecutor, final Runnable terminationTask) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, HANDLER);
this.terminationTask = terminationTask;
this.handoffExecutor = handoffExecutor;
}
public StandardThreadPoolMXBean getThreadPoolMXBean() {
return mxBean;
}
public Executor getHandoffExecutor() {
return handoffExecutor;
}
public void setHandoffExecutor(final Executor handoffExecutor) {
Assert.checkNotNullParam("handoffExecutor", handoffExecutor);
this.handoffExecutor = handoffExecutor;
super.setRejectedExecutionHandler(HANDLER);
}
void reject(Runnable r) {
handoffExecutor.execute(r);
}
protected void terminated() {
terminationTask.run();
}
class MXBeanImpl implements StandardThreadPoolMXBean {
public float getGrowthResistance() {
return 1.0f;
}
public void setGrowthResistance(final float value) {
// ignored
}
public boolean isGrowthResistanceSupported() {
return false;
}
public int getCorePoolSize() {
return ManagedThreadPoolExecutor.this.getCorePoolSize();
}
public void setCorePoolSize(final int corePoolSize) {
ManagedThreadPoolExecutor.this.setCorePoolSize(corePoolSize);
}
public boolean isCorePoolSizeSupported() {
return true;
}
public boolean prestartCoreThread() {
return ManagedThreadPoolExecutor.this.prestartCoreThread();
}
public int prestartAllCoreThreads() {
return ManagedThreadPoolExecutor.this.prestartAllCoreThreads();
}
public boolean isCoreThreadPrestartSupported() {
return true;
}
public int getMaximumPoolSize() {
return ManagedThreadPoolExecutor.this.getMaximumPoolSize();
}
public void setMaximumPoolSize(final int maxPoolSize) {
ManagedThreadPoolExecutor.this.setMaximumPoolSize(maxPoolSize);
}
public int getPoolSize() {
return ManagedThreadPoolExecutor.this.getPoolSize();
}
public int getLargestPoolSize() {
return ManagedThreadPoolExecutor.this.getLargestPoolSize();
}
public int getActiveCount() {
return ManagedThreadPoolExecutor.this.getActiveCount();
}
public boolean isAllowCoreThreadTimeOut() {
return ManagedThreadPoolExecutor.this.allowsCoreThreadTimeOut();
}
public void setAllowCoreThreadTimeOut(final boolean value) {
ManagedThreadPoolExecutor.this.allowCoreThreadTimeOut(value);
}
public long getKeepAliveTimeSeconds() {
return ManagedThreadPoolExecutor.this.getKeepAliveTime(TimeUnit.SECONDS);
}
public void setKeepAliveTimeSeconds(final long seconds) {
ManagedThreadPoolExecutor.this.setKeepAliveTime(seconds, TimeUnit.SECONDS);
}
public int getMaximumQueueSize() {
return 0;
}
public void setMaximumQueueSize(final int size) {
}
public int getQueueSize() {
return ManagedThreadPoolExecutor.this.getQueue().size();
}
public int getLargestQueueSize() {
return 0;
}
public boolean isQueueBounded() {
return false;
}
public boolean isQueueSizeModifiable() {
return false;
}
public boolean isShutdown() {
return ManagedThreadPoolExecutor.this.isShutdown();
}
public boolean isTerminating() {
return ManagedThreadPoolExecutor.this.isTerminating();
}
public boolean isTerminated() {
return ManagedThreadPoolExecutor.this.isTerminated();
}
public long getSubmittedTaskCount() {
return ManagedThreadPoolExecutor.this.getTaskCount();
}
public long getRejectedTaskCount() {
return 0;
}
public long getCompletedTaskCount() {
return ManagedThreadPoolExecutor.this.getCompletedTaskCount();
}
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/Messages.java 0000664 0000000 0000000 00000007647 14303134447 0025747 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.threads;
import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger;
import org.jboss.logging.annotations.Cause;
import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageLogger;
/**
* @author David M. Lloyd
*/
@MessageLogger(projectCode = "JBTHR", length = 5)
interface Messages extends BasicLogger {
Messages msg = Logger.getMessageLogger(Messages.class, "org.jboss.threads");
Messages intMsg = Logger.getMessageLogger(Messages.class, "org.jboss.threads.interrupt-handler");
// version
@Message(value = "JBoss Threads version %s")
@LogMessage(level = Logger.Level.INFO)
void version(String version);
// execution
// @Message(id = 1, value = "Thread factory did not produce a thread")
// @Message(id = 2, value = "Task limit reached")
// @Message(id = 3, value = "Operation timed out")
// @Message(id = 4, value = "Operation was cancelled")
// @Message(id = 5, value = "Operation failed")
// @Message(id = 6, value = "Unable to add new thread to the running set")
// @Message(id = 7, value = "Task execution interrupted")
// @Message(id = 8, value = "Task rejected")
@Message(id = 9, value = "Executor has been shut down")
StoppedExecutorException shutDownInitiated();
// @Message(id = 10, value = "Task execution timed out")
// @Message(id = 11, value = "Task execution failed for task %s")
@Message(id = 12, value = "Cannot await termination of a thread pool from one of its own threads")
IllegalStateException cannotAwaitWithin();
// @Message(id = 13, value = "No executors available to run task")
// @Message(id = 14, value = "Error submitting task %s to executor")
// validation
// @Message(id = 100, value = "Keep-alive may only be set to 0 for this executor type")
// @Message(id = 101, value = "Cannot reduce maximum threads below current thread number of running threads")
// @Message(id = 102, value = "Empty array parameter is not empty")
@Message(id = 103, value = "The current thread does not support interrupt handlers")
IllegalStateException noInterruptHandlers();
@Message(id = 104, value = "Executor is not shut down")
@Deprecated
IllegalStateException notShutDown();
// @Message(id = 105, value = "Concurrent modification of collection detected")
// @Message(id = 106, value = "No such element (iteration past end)")
// @Message(id = 107, value = "Unknown throwable received")
@Message(id = 108, value = "Interrupt handler %s threw an exception")
@LogMessage(level = Logger.Level.ERROR)
void interruptHandlerThrew(@Cause Throwable cause, InterruptHandler interruptHandler);
// security
@Message(id = 200, value = "%s() not allowed on container-managed executor")
SecurityException notAllowedContainerManaged(String methodName);
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/NullRunnable.java 0000664 0000000 0000000 00000001760 14303134447 0026567 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
final class NullRunnable implements Runnable {
private static final NullRunnable INSTANCE = new NullRunnable();
static NullRunnable getInstance() {
return INSTANCE;
}
NullRunnable() {
}
public void run() {
// do nothing
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/RejectingExecutor.java 0000664 0000000 0000000 00000002452 14303134447 0027616 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
class RejectingExecutor implements Executor {
static final RejectingExecutor INSTANCE = new RejectingExecutor();
private final String message;
private RejectingExecutor() {
message = null;
}
RejectingExecutor(final String message) {
this.message = message;
}
public void execute(final Runnable command) {
throw new RejectedExecutionException(message);
}
public String toString() {
return "Rejecting executor";
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/SimpleDirectExecutor.java 0000664 0000000 0000000 00000002114 14303134447 0030263 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.util.concurrent.Executor;
class SimpleDirectExecutor implements Executor {
static final SimpleDirectExecutor INSTANCE = new SimpleDirectExecutor();
private SimpleDirectExecutor() {
}
public void execute(final Runnable command) {
command.run();
}
public String toString() {
return "Direct executor";
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/StoppedExecutorException.java 0000664 0000000 0000000 00000004730 14303134447 0031202 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.util.concurrent.RejectedExecutionException;
/**
* Thrown when a task is submitted to an executor which is in the process of, or has completed shutting down.
*/
public class StoppedExecutorException extends RejectedExecutionException {
private static final long serialVersionUID = 4815103522815471074L;
/**
* Constructs a {@code StoppedExecutorException} with no detail message. The cause is not initialized, and may
* subsequently be initialized by a call to {@link #initCause(Throwable) initCause}.
*/
public StoppedExecutorException() {
}
/**
* Constructs a {@code StoppedExecutorException} with the specified detail message. The cause is not initialized, and
* may subsequently be initialized by a call to {@link #initCause(Throwable) initCause}.
*
* @param msg the detail message
*/
public StoppedExecutorException(final String msg) {
super(msg);
}
/**
* Constructs a {@code StoppedExecutorException} with the specified cause. The detail message is set to:
* (cause == null ? null : cause.toString())
* (which typically contains the class and detail message of {@code cause}).
*
* @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method)
*/
public StoppedExecutorException(final Throwable cause) {
super(cause);
}
/**
* Constructs a {@code StoppedExecutorException} with the specified detail message and cause.
*
* @param msg the detail message
* @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method)
*/
public StoppedExecutorException(final String msg, final Throwable cause) {
super(msg, cause);
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/Substitutions.java 0000664 0000000 0000000 00000002171 14303134447 0027062 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import javax.management.ObjectInstance;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
final class Substitutions {
@TargetClass(EnhancedQueueExecutor.MBeanRegisterAction.class)
static final class Target_EnhancedQueueExecutor_MBeanRegisterAction {
@Substitute
public ObjectInstance run() {
return null;
}
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/ThreadLocalResettingRunnable.java 0000664 0000000 0000000 00000003675 14303134447 0031733 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
final class ThreadLocalResettingRunnable extends DelegatingRunnable {
ThreadLocalResettingRunnable(final Runnable delegate) {
super(delegate);
}
public void run() {
try {
super.run();
} finally {
Resetter.run();
}
}
public String toString() {
return "Thread-local resetting Runnable";
}
static final class Resetter {
private static final long threadLocalMapOffs;
private static final long inheritableThreadLocalMapOffs;
static {
try {
threadLocalMapOffs = JBossExecutors.unsafe.objectFieldOffset(Thread.class.getDeclaredField("threadLocals"));
inheritableThreadLocalMapOffs = JBossExecutors.unsafe.objectFieldOffset(Thread.class.getDeclaredField("inheritableThreadLocals"));
} catch (NoSuchFieldException e) {
throw new NoSuchFieldError(e.getMessage());
}
}
static void run() {
final Thread thread = Thread.currentThread();
JBossExecutors.unsafe.putObject(thread, threadLocalMapOffs, null);
JBossExecutors.unsafe.putObject(thread, inheritableThreadLocalMapOffs, null);
}
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/ThreadNameInfo.java 0000664 0000000 0000000 00000007226 14303134447 0027015 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
* Thread name information.
*/
final class ThreadNameInfo {
private final long globalThreadSequenceNum;
private final long perFactoryThreadSequenceNum;
private final long factorySequenceNum;
ThreadNameInfo(final long globalThreadSequenceNum, final long perFactoryThreadSequenceNum, final long factorySequenceNum) {
this.globalThreadSequenceNum = globalThreadSequenceNum;
this.perFactoryThreadSequenceNum = perFactoryThreadSequenceNum;
this.factorySequenceNum = factorySequenceNum;
}
public long getGlobalThreadSequenceNum() {
return globalThreadSequenceNum;
}
public long getPerFactoryThreadSequenceNum() {
return perFactoryThreadSequenceNum;
}
public long getFactorySequenceNum() {
return factorySequenceNum;
}
private static final Pattern searchPattern = Pattern.compile("([^%]+)|%.");
/**
* Format the thread name string.
*
* {@code %%} - emit a percent sign
* {@code %t} - emit the per-factory thread sequence number
* {@code %g} - emit the global thread sequence number
* {@code %f} - emit the factory sequence number
* {@code %p} - emit the {@code ":"}-separated thread group path
* {@code %i} - emit the thread ID
* {@code %G} - emit the thread group name
*
*
* @param thread the thread
* @param formatString the format string
* @return the thread name string
*/
public String format(Thread thread, String formatString) {
final StringBuilder builder = new StringBuilder(formatString.length() * 5);
final ThreadGroup group = thread.getThreadGroup();
final Matcher matcher = searchPattern.matcher(formatString);
while (matcher.find()) {
if (matcher.group(1) != null) {
builder.append(matcher.group());
} else {
switch (matcher.group().charAt(1)) {
case '%': builder.append('%'); break;
case 't': builder.append(perFactoryThreadSequenceNum); break;
case 'g': builder.append(globalThreadSequenceNum); break;
case 'f': builder.append(factorySequenceNum); break;
case 'p': if (group != null) appendGroupPath(group, builder); break;
case 'i': builder.append(thread.getId()); break;
case 'G': if (group != null) builder.append(group.getName()); break;
}
}
}
return builder.toString();
}
private static void appendGroupPath(ThreadGroup group, StringBuilder builder) {
final ThreadGroup parent = group.getParent();
if (parent != null) {
appendGroupPath(parent, builder);
builder.append(':');
}
builder.append(group.getName());
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/TimeUtil.java 0000664 0000000 0000000 00000002263 14303134447 0025721 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import static java.lang.Math.max;
import java.time.Duration;
/**
*/
final class TimeUtil {
private TimeUtil() {}
private static final long LARGEST_SECONDS = 9_223_372_035L; // Long.MAX_VALUE / 1_000_000_000L - 1
static long clampedPositiveNanos(Duration duration) {
final long seconds = max(0L, duration.getSeconds());
return seconds > LARGEST_SECONDS ? Long.MAX_VALUE : max(1, seconds * 1_000_000_000L + duration.getNano());
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/Version.java 0000664 0000000 0000000 00000005537 14303134447 0025621 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Properties;
/**
*
*/
public final class Version {
private Version() {
}
private static final String JAR_NAME;
private static final String VERSION_STRING;
static {
Properties versionProps = new Properties();
String jarName = "(unknown)";
String versionString = "(unknown)";
try (InputStream stream = Version.class.getResourceAsStream("Version.properties")) {
if (stream != null) try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
versionProps.load(reader);
jarName = versionProps.getProperty("jarName", jarName);
versionString = versionProps.getProperty("version", versionString);
}
} catch (IOException ignored) {
}
JAR_NAME = jarName;
VERSION_STRING = versionString;
boolean logVersion = AccessController.doPrivileged((PrivilegedAction) Version::shouldLogVersion).booleanValue();
if (logVersion) try {
Messages.msg.version(versionString);
} catch (Throwable ignored) {}
}
private static Boolean shouldLogVersion() {
try {
return Boolean.valueOf(System.getProperty("jboss.log-version", "true"));
} catch (Throwable ignored) {
return Boolean.FALSE;
}
}
/**
* Get the name of the JBoss Modules JAR.
*
* @return the name
*/
public static String getJarName() {
return JAR_NAME;
}
/**
* Get the version string of JBoss Modules.
*
* @return the version string
*/
public static String getVersionString() {
return VERSION_STRING;
}
/**
* Print out the current version on {@code System.out}.
*
* @param args ignored
*/
public static void main(String[] args) {
System.out.printf("JBoss Threads version %s\n", VERSION_STRING);
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/Version.properties 0000664 0000000 0000000 00000001353 14303134447 0027064 0 ustar 00root root 0000000 0000000 #
# JBoss, Home of Professional Open Source.
# Copyright 2017 Red Hat, Inc., and individual contributors
# as indicated by the @author tags.
#
# 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.
#
version=${project.version}
jarName=${project.artifactId}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/ViewExecutor.java 0000664 0000000 0000000 00000011270 14303134447 0026614 0 ustar 00root root 0000000 0000000 package org.jboss.threads;
import org.wildfly.common.Assert;
import java.security.PrivilegedAction;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.Executor;
import static java.security.AccessController.doPrivileged;
/**
* An executor service that is actually a "view" over another executor service.
*/
public abstract class ViewExecutor extends AbstractExecutorService {
private volatile Thread.UncaughtExceptionHandler handler;
private volatile Runnable terminationTask;
// Intentionally package private to effectively seal the type.
ViewExecutor() {}
@Override
public final void shutdown() {
shutdown(false);
}
public abstract void shutdown(boolean interrupt);
public final Thread.UncaughtExceptionHandler getExceptionHandler() {
return handler;
}
public final void setExceptionHandler(final Thread.UncaughtExceptionHandler handler) {
Assert.checkNotNullParam("handler", handler);
this.handler = handler;
}
public final Runnable getTerminationTask() {
return terminationTask;
}
public final void setTerminationTask(final Runnable terminationTask) {
this.terminationTask = terminationTask;
}
public static Builder builder(Executor delegate) {
Assert.checkNotNullParam("delegate", delegate);
return new Builder(delegate);
}
public static final class Builder {
private final Executor delegate;
private int maxSize = 1;
private int queueLimit = Integer.MAX_VALUE;
private Thread.UncaughtExceptionHandler handler = JBossExecutors.loggingExceptionHandler();
Builder(final Executor delegate) {
this.delegate = delegate;
}
public int getMaxSize() {
return maxSize;
}
public Builder setMaxSize(final int maxSize) {
Assert.checkMinimumParameter("maxSize", 1, maxSize);
this.maxSize = maxSize;
return this;
}
public int getQueueLimit() {
return queueLimit;
}
public Builder setQueueLimit(final int queueLimit) {
Assert.checkMinimumParameter("queueLimit", 0, queueLimit);
this.queueLimit = queueLimit;
return this;
}
public Executor getDelegate() {
return delegate;
}
public Thread.UncaughtExceptionHandler getUncaughtHandler() {
return handler;
}
public Builder setUncaughtHandler(final Thread.UncaughtExceptionHandler handler) {
this.handler = handler;
return this;
}
/**
* @deprecated This value no longer has any impact.
*/
@Deprecated
public int getQueueInitialSize() {
return 0;
}
/**
* @deprecated This option no longer has any impact.
*/
@Deprecated
public Builder setQueueInitialSize(@SuppressWarnings("unused") final int queueInitialSize) {
return this;
}
public ViewExecutor build() {
return new EnhancedViewExecutor(
Assert.checkNotNullParam("delegate", delegate),
maxSize,
queueLimit,
handler);
}
}
protected void runTermination() {
final Runnable task = ViewExecutor.this.terminationTask;
ViewExecutor.this.terminationTask = null;
if (task != null) try {
task.run();
} catch (Throwable t) {
Thread.UncaughtExceptionHandler configuredHandler = handler;
if (configuredHandler != null) {
try {
handler.uncaughtException(Thread.currentThread(), t);
} catch (Throwable ignored) {
}
}
}
}
static int readIntPropertyPrefixed(String name, int defVal) {
try {
return Integer.parseInt(readPropertyPrefixed(name, Integer.toString(defVal)));
} catch (NumberFormatException ignored) {
return defVal;
}
}
static String readPropertyPrefixed(String name, String defVal) {
return readProperty("org.jboss.threads.view-executor." + name, defVal);
}
static String readProperty(String name, String defVal) {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
return doPrivileged((PrivilegedAction) () -> readPropertyRaw(name, defVal));
} else {
return readPropertyRaw(name, defVal);
}
}
static String readPropertyRaw(final String name, final String defVal) {
return System.getProperty(name, defVal);
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/Waiter.java 0000664 0000000 0000000 00000002253 14303134447 0025417 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
/**
*/
final class Waiter {
private volatile Thread thread;
private Waiter next;
Waiter(final Waiter next) {
this.next = next;
}
Thread getThread() {
return thread;
}
Waiter setThread(final Thread thread) {
this.thread = thread;
return this;
}
Waiter getNext() {
return next;
}
Waiter setNext(final Waiter next) {
this.next = next;
return this;
}
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/management/ 0000775 0000000 0000000 00000000000 14303134447 0025433 5 ustar 00root root 0000000 0000000 ManageableThreadPoolExecutorService.java 0000664 0000000 0000000 00000002540 14303134447 0035256 0 ustar 00root root 0000000 0000000 jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/management /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads.management;
import java.util.concurrent.ExecutorService;
import org.wildfly.common.annotation.NotNull;
/**
* A thread pool for which an MBean can be obtained.
*/
public interface ManageableThreadPoolExecutorService extends ExecutorService {
/**
* Create or acquire an MXBean instance for this thread pool. Note that the thread pool itself will not
* do anything in particular to register (or unregister) the MXBean with a JMX server; that is the caller's
* responsibility.
*
* @return the MXBean instance (must not be {@code null})
*/
@NotNull
StandardThreadPoolMXBean getThreadPoolMXBean();
}
jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/management/StandardThreadPoolMXBean.java 0000664 0000000 0000000 00000024164 14303134447 0033062 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads.management;
/**
* An MXBean which contains the attributes and operations found on all standard thread pools.
*/
public interface StandardThreadPoolMXBean {
/**
* Get the pool size growth resistance factor. If the thread pool does not support growth resistance,
* then {@code 0.0} (no resistance) is returned.
*
* @return the growth resistance factor ({@code 0.0 ≤ n ≤ 1.0})
*/
float getGrowthResistance();
/**
* Set the pool size growth resistance factor, if supported.
*
* @param value the growth resistance factor ({@code 0.0 ≤ n ≤ 1.0})
*/
void setGrowthResistance(float value);
/**
* Determine whether the thread pool supports a growth resistance factor.
*
* @return {@code true} if the growth resistance factor is supported, {@code false} otherwise
*/
boolean isGrowthResistanceSupported();
/**
* Get the core pool size. This is the size below which new threads will always be created if no idle threads
* are available. If the thread pool does not support a separate core pool size, this size will match the
* maximum size.
*
* @return the core pool size
*/
int getCorePoolSize();
/**
* Set the core pool size. If the configured maximum pool size is less than the configured core size, the
* core size will be reduced to match the maximum size when the thread pool is constructed. If the thread pool
* does not support a separate core pool size, this setting will be ignored.
*
* @param corePoolSize the core pool size (must be greater than or equal to 0)
*/
void setCorePoolSize(int corePoolSize);
/**
* Determine whether this implementation supports a separate core pool size.
*
* @return {@code true} if a separate core size is supported; {@code false} otherwise
*/
boolean isCorePoolSizeSupported();
/**
* Attempt to start a core thread without submitting work to it.
*
* @return {@code true} if the thread was started, or {@code false} if the pool is filled to the core size, the
* thread could not be created, or prestart of core threads is not supported.
*/
boolean prestartCoreThread();
/**
* Attempt to start all core threads. This is usually equivalent to calling {@link #prestartCoreThread()} in a loop
* until it returns {@code false} and counting the {@code true} results.
*
* @return the number of started core threads (may be 0)
*/
int prestartAllCoreThreads();
/**
* Determine whether this thread pool allows manual pre-start of core threads.
*
* @return {@code true} if pre-starting core threads is supported, {@code false} otherwise
*/
boolean isCoreThreadPrestartSupported();
/**
* Get the maximum pool size. This is the absolute upper limit to the size of the thread pool.
*
* @return the maximum pool size
*/
int getMaximumPoolSize();
/**
* Set the maximum pool size. If the configured maximum pool size is less than the configured core size, the
* core size will be reduced to match the maximum size when the thread pool is constructed.
*
* @param maxPoolSize the maximum pool size (must be greater than or equal to 0)
*/
void setMaximumPoolSize(final int maxPoolSize);
/**
* Get an estimate of the current number of active threads in the pool.
*
* @return an estimate of the current number of active threads in the pool
*/
int getPoolSize();
/**
* Get an estimate of the peak number of threads that the pool has ever held.
*
* @return an estimate of the peak number of threads that the pool has ever held
*/
int getLargestPoolSize();
/**
* Get an estimate of the current number of active (busy) threads.
*
* @return an estimate of the active count
*/
int getActiveCount();
/**
* Determine whether core threads are allowed to time out. A "core thread" is defined as any thread in the pool
* when the pool size is below the pool's {@linkplain #getCorePoolSize() core pool size}. If the thread pool
* does not support a separate core pool size, this method should return {@code false}.
*
* This method is named differently from the typical {@code allowsCoreThreadTimeOut()} in order to accommodate
* the requirements of MXBean attribute methods.
*
* @return {@code true} if core threads are allowed to time out, {@code false} otherwise
*/
boolean isAllowCoreThreadTimeOut();
/**
* Establish whether core threads are allowed to time out. A "core thread" is defined as any thread in the pool
* when the pool size is below the pool's {@linkplain #getCorePoolSize() core pool size}. If the thread pool
* does not support a separate core pool size, the value is ignored.
*
* This method is named differently from the typical {@code allowCoreThreadTimeOut(boolean)} in order to accommodate
* the requirements of MXBean attribute methods.
*
* @param value {@code true} if core threads are allowed to time out, {@code false} otherwise
*/
void setAllowCoreThreadTimeOut(boolean value);
/**
* Get the thread keep-alive time, in seconds.
*
* This method differs from the typical {@code getKeepAliveTime(TimeUnit)} due to the inability to send in a
* time units parameter on an MXBean attribute. As such, the unit is hard-coded to seconds.
*
* @return the thread keep-alive time, in seconds
*/
long getKeepAliveTimeSeconds();
/**
* Set the thread keep-alive time, in seconds.
*
* This method differs from the typical {@code getKeepAliveTime(TimeUnit)} due to the inability to send in a
* time units parameter on an MXBean attribute. As such, the unit is hard-coded to seconds.
*
* @param seconds the thread keep-alive time, in seconds (must be greater than or equal to 0)
*/
void setKeepAliveTimeSeconds(long seconds);
/**
* Get the maximum queue size for this thread pool. If there is no queue or it is not bounded, {@link Integer#MAX_VALUE} is
* returned.
*
* @return the maximum queue size
*/
int getMaximumQueueSize();
/**
* Set the maximum queue size for this thread pool. If the new maximum queue size is smaller than the current queue
* size, there is no effect other than preventing tasks from being enqueued until the size decreases below the
* maximum again. If changing the maximum queue size is not supported, or there is no bounded backing queue,
* then the value is ignored.
*
* @param size the maximum queue size for this thread pool
*/
void setMaximumQueueSize(int size);
/**
* Get an estimate of the current queue size, if any. If no backing queue exists, or its size cannot be determined,
* this method will return 0.
*
* @return an estimate of the current queue size
*/
int getQueueSize();
/**
* Get an estimate of the peak size of the queue, if any. If no backing queue exists, or its size cannot be determined,
* this method will return 0.
*
* @return an estimate of the peak size of the queue
*/
int getLargestQueueSize();
/**
* Determine whether there is a bounded queue backing this thread pool.
*
* @return {@code true} if there is a bounded backing queue, {@code false} otherwise
*/
boolean isQueueBounded();
/**
* Determine whether the maximum queue size is modifiable.
*
* @return {@code true} if the queue size is modifiable, false otherwise
*/
boolean isQueueSizeModifiable();
/**
* Determine whether shutdown was requested.
*
* @return {@code true} if shutdown was requested, {@code false} otherwise
*/
boolean isShutdown();
/**
* Determine whether shutdown is in progress.
*
* @return {@code true} if shutdown is in progress, {@code false} otherwise
*/
boolean isTerminating();
/**
* Determine whether shutdown is complete.
*
* @return {@code true} if shutdown is complete, {@code false} otherwise
*/
boolean isTerminated();
/**
* Get an estimate of the total number of tasks ever submitted to this thread pool. This number may be zero
* if the underlying thread pool does not support this metric.
*
* @return an estimate of the total number of tasks ever submitted to this thread pool
*/
long getSubmittedTaskCount();
/**
* Get an estimate of the total number of tasks ever rejected by this thread pool for any reason. This number may be zero
* if the underlying thread pool does not support this metric.
*
* @return an estimate of the total number of tasks ever rejected by this thread pool
*/
long getRejectedTaskCount();
/**
* Get an estimate of the number of tasks completed by this thread pool. This number may be zero
* if the underlying thread pool does not support this metric.
*
* @return an estimate of the number of tasks completed by this thread pool
*/
long getCompletedTaskCount();
/**
* Get the number of spin misses that have occurred. Spin misses indicate that contention is not being properly
* handled by the thread pool.
*
* @return an estimate of the number of spin misses
*/
default long getSpinMissCount() {
return 0;
}
}
jboss-threads-3.5.0.Final/src/main/java9/ 0000775 0000000 0000000 00000000000 14303134447 0020047 5 ustar 00root root 0000000 0000000 jboss-threads-3.5.0.Final/src/main/java9/org/ 0000775 0000000 0000000 00000000000 14303134447 0020636 5 ustar 00root root 0000000 0000000 jboss-threads-3.5.0.Final/src/main/java9/org/jboss/ 0000775 0000000 0000000 00000000000 14303134447 0021756 5 ustar 00root root 0000000 0000000 jboss-threads-3.5.0.Final/src/main/java9/org/jboss/threads/ 0000775 0000000 0000000 00000000000 14303134447 0023410 5 ustar 00root root 0000000 0000000 jboss-threads-3.5.0.Final/src/main/java9/org/jboss/threads/JDKSpecific.java 0000664 0000000 0000000 00000002007 14303134447 0026330 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.time.temporal.TemporalUnit;
import java.util.concurrent.TimeUnit;
/**
*/
final class JDKSpecific {
static TemporalUnit timeToTemporal(final TimeUnit timeUnit) {
return timeUnit.toChronoUnit();
}
static void onSpinWait() {
Thread.onSpinWait();
}
}
jboss-threads-3.5.0.Final/src/test/ 0000775 0000000 0000000 00000000000 14303134447 0017070 5 ustar 00root root 0000000 0000000 jboss-threads-3.5.0.Final/src/test/java/ 0000775 0000000 0000000 00000000000 14303134447 0020011 5 ustar 00root root 0000000 0000000 jboss-threads-3.5.0.Final/src/test/java/org/ 0000775 0000000 0000000 00000000000 14303134447 0020600 5 ustar 00root root 0000000 0000000 jboss-threads-3.5.0.Final/src/test/java/org/jboss/ 0000775 0000000 0000000 00000000000 14303134447 0021720 5 ustar 00root root 0000000 0000000 jboss-threads-3.5.0.Final/src/test/java/org/jboss/threads/ 0000775 0000000 0000000 00000000000 14303134447 0023352 5 ustar 00root root 0000000 0000000 jboss-threads-3.5.0.Final/src/test/java/org/jboss/threads/ArrayQueueTests.java 0000664 0000000 0000000 00000022024 14303134447 0027323 0 ustar 00root root 0000000 0000000 package org.jboss.threads;
import static org.junit.Assert.*;
import java.util.concurrent.TimeUnit;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class ArrayQueueTests {
static EnhancedQueueExecutor eqe;
static EnhancedQueueExecutor.AbstractScheduledFuture>[] ITEMS;
@BeforeClass
public static void beforeAll() {
eqe = new EnhancedQueueExecutor.Builder().build();
ITEMS = new EnhancedQueueExecutor.AbstractScheduledFuture[32];
for (int i = 0; i < 32; i ++) {
final String toString = "[" + i + "]";
ITEMS[i] = eqe.new RunnableScheduledFuture(new Runnable() {
public void run() {
// nothing
}
public String toString() {
return toString;
}
}, 0, TimeUnit.DAYS);
}
}
@Test
public void testMoveForward() {
EnhancedQueueExecutor.ArrayQueue aq = new EnhancedQueueExecutor.ArrayQueue(16);
int head = 5;
aq.testPoint_setHead(head);
aq.testPoint_setArrayItem(head + 0, ITEMS[0]);
aq.testPoint_setArrayItem(head + 1, ITEMS[1]);
aq.testPoint_setArrayItem(head + 2, ITEMS[2]);
aq.testPoint_setArrayItem(head + 3, ITEMS[3]);
aq.testPoint_setSize(4);
aq.moveForward(2, ITEMS[4]);
assertEquals(head, aq.testPoint_head());
assertSame(ITEMS[0], aq.testPoint_getArrayItem(head + 0));
assertSame(ITEMS[1], aq.testPoint_getArrayItem(head + 1));
assertSame(ITEMS[4], aq.testPoint_getArrayItem(head + 2));
assertSame(ITEMS[2], aq.testPoint_getArrayItem(head + 3));
assertSame(ITEMS[3], aq.testPoint_getArrayItem(head + 4));
}
@Test
public void testMoveForwardWrap() {
EnhancedQueueExecutor.ArrayQueue aq = new EnhancedQueueExecutor.ArrayQueue(16);
int head = 14;
aq.testPoint_setHead(head);
aq.testPoint_setArrayItem(head + 0, ITEMS[0]);
aq.testPoint_setArrayItem(head + 1, ITEMS[1]);
aq.testPoint_setArrayItem(head + 2, ITEMS[2]);
aq.testPoint_setArrayItem(head + 3, ITEMS[3]);
aq.testPoint_setSize(4);
aq.moveForward(2, ITEMS[4]);
assertEquals(head, aq.testPoint_head());
assertSame(ITEMS[0], aq.testPoint_getArrayItem(head + 0));
assertSame(ITEMS[1], aq.testPoint_getArrayItem(head + 1));
assertSame(ITEMS[4], aq.testPoint_getArrayItem(head + 2));
assertSame(ITEMS[2], aq.testPoint_getArrayItem(head + 3));
assertSame(ITEMS[3], aq.testPoint_getArrayItem(head + 4));
}
@Test
public void testMoveBackward() {
EnhancedQueueExecutor.ArrayQueue aq = new EnhancedQueueExecutor.ArrayQueue(16);
int head = 5;
aq.testPoint_setHead(head);
aq.testPoint_setArrayItem(head + 0, ITEMS[0]);
aq.testPoint_setArrayItem(head + 1, ITEMS[1]);
aq.testPoint_setArrayItem(head + 2, ITEMS[2]);
aq.testPoint_setArrayItem(head + 3, ITEMS[3]);
aq.testPoint_setSize(4);
aq.moveBackward(2, ITEMS[4]);
assertEquals(head - 1, aq.testPoint_head());
head--;
assertSame(ITEMS[0], aq.testPoint_getArrayItem(head + 0));
assertSame(ITEMS[1], aq.testPoint_getArrayItem(head + 1));
assertSame(ITEMS[4], aq.testPoint_getArrayItem(head + 2));
assertSame(ITEMS[2], aq.testPoint_getArrayItem(head + 3));
assertSame(ITEMS[3], aq.testPoint_getArrayItem(head + 4));
}
@Test
public void testMoveBackwardWrap() {
EnhancedQueueExecutor.ArrayQueue aq = new EnhancedQueueExecutor.ArrayQueue(16);
int head = 14;
aq.testPoint_setHead(head);
aq.testPoint_setArrayItem(head + 0, ITEMS[0]);
aq.testPoint_setArrayItem(head + 1, ITEMS[1]);
aq.testPoint_setArrayItem(head + 2, ITEMS[2]);
aq.testPoint_setArrayItem(head + 3, ITEMS[3]);
aq.testPoint_setSize(4);
aq.moveBackward(2, ITEMS[4]);
assertEquals(head - 1, aq.testPoint_head());
head--;
assertSame(ITEMS[0], aq.testPoint_getArrayItem(head + 0));
assertSame(ITEMS[1], aq.testPoint_getArrayItem(head + 1));
assertSame(ITEMS[4], aq.testPoint_getArrayItem(head + 2));
assertSame(ITEMS[2], aq.testPoint_getArrayItem(head + 3));
assertSame(ITEMS[3], aq.testPoint_getArrayItem(head + 4));
}
@Test
public void testQueueBehavior() {
EnhancedQueueExecutor.ArrayQueue aq = new EnhancedQueueExecutor.ArrayQueue(16);
// tc 0 (n/a)
assertTrue(aq.isEmpty());
assertEquals(0, aq.size());
assertEquals(0, aq.testPoint_head());
assertEquals(16, aq.testPoint_arrayLength());
// tc 1
aq.insertAt(0, ITEMS[0]);
assertFalse(aq.isEmpty());
assertEquals(1, aq.size());
assertEquals(0, aq.testPoint_head());
assertSame(ITEMS[0], aq.testPoint_getArrayItem(0));
assertNull(aq.testPoint_getArrayItem(1));
assertNull(aq.testPoint_getArrayItem(15));
// removal
assertSame(ITEMS[0], aq.pollFirst());
assertTrue(aq.isEmpty());
assertEquals(0, aq.size());
assertEquals(1, aq.testPoint_head());
assertNull(aq.testPoint_getArrayItem(0));
assertNull(aq.testPoint_getArrayItem(1));
// tc 1 (but this time with head == 1)
aq.insertAt(0, ITEMS[1]);
assertFalse(aq.isEmpty());
assertEquals(1, aq.size());
assertEquals(1, aq.testPoint_head());
assertNull(aq.testPoint_getArrayItem(0));
assertSame(ITEMS[1], aq.testPoint_getArrayItem(1));
assertNull(aq.testPoint_getArrayItem(2));
// tc 1 (but with head == 1 and size == 1)
aq.insertAt(1, ITEMS[2]);
assertFalse(aq.isEmpty());
assertEquals(2, aq.size());
assertEquals(1, aq.testPoint_head());
assertNull(aq.testPoint_getArrayItem(0));
assertSame(ITEMS[1], aq.testPoint_getArrayItem(1));
assertSame(ITEMS[2], aq.testPoint_getArrayItem(2));
assertNull(aq.testPoint_getArrayItem(3));
// tc 2 (but with head == 1 and size == 2)
aq.insertAt(0, ITEMS[3]);
assertFalse(aq.isEmpty());
assertEquals(3, aq.size()); // halfSize == 2
// head moves back to 0
assertEquals(0, aq.testPoint_head());
assertNull(aq.testPoint_getArrayItem(15));
assertSame(ITEMS[3], aq.testPoint_getArrayItem(0));
assertSame(ITEMS[1], aq.testPoint_getArrayItem(1));
assertSame(ITEMS[2], aq.testPoint_getArrayItem(2));
assertNull(aq.testPoint_getArrayItem(3));
// tc 2 (but with head == 0 and size == 3)
aq.insertAt(0, ITEMS[4]);
assertFalse(aq.isEmpty());
assertEquals(4, aq.size());
// head wraps around to 15
assertEquals(15, aq.testPoint_head());
assertNull(aq.testPoint_getArrayItem(14));
assertSame(ITEMS[4], aq.testPoint_getArrayItem(15));
assertSame(ITEMS[3], aq.testPoint_getArrayItem(0));
assertSame(ITEMS[1], aq.testPoint_getArrayItem(1));
assertSame(ITEMS[2], aq.testPoint_getArrayItem(2));
assertNull(aq.testPoint_getArrayItem(3));
// tc 2 (but with head == 15 and size == 4)
aq.insertAt(0, ITEMS[5]);
assertFalse(aq.isEmpty());
assertEquals(5, aq.size());
assertEquals(14, aq.testPoint_head());
assertNull(aq.testPoint_getArrayItem(13));
assertSame(ITEMS[5], aq.testPoint_getArrayItem(14));
assertSame(ITEMS[4], aq.testPoint_getArrayItem(15));
assertSame(ITEMS[3], aq.testPoint_getArrayItem(0));
assertSame(ITEMS[1], aq.testPoint_getArrayItem(1));
assertSame(ITEMS[2], aq.testPoint_getArrayItem(2));
assertNull(aq.testPoint_getArrayItem(3));
// tc
aq.insertAt(1, ITEMS[6]);
assertNull(aq.testPoint_getArrayItem(12));
assertSame(ITEMS[5], aq.testPoint_getArrayItem(13));
assertSame(ITEMS[6], aq.testPoint_getArrayItem(14));
assertSame(ITEMS[4], aq.testPoint_getArrayItem(15));
assertSame(ITEMS[3], aq.testPoint_getArrayItem(0));
assertSame(ITEMS[1], aq.testPoint_getArrayItem(1));
assertSame(ITEMS[2], aq.testPoint_getArrayItem(2));
assertNull(aq.testPoint_getArrayItem(3));
aq.insertAt(0, ITEMS[7]);
assertNull(aq.testPoint_getArrayItem(11));
assertSame(ITEMS[7], aq.testPoint_getArrayItem(12));
assertSame(ITEMS[5], aq.testPoint_getArrayItem(13));
assertSame(ITEMS[6], aq.testPoint_getArrayItem(14));
assertSame(ITEMS[4], aq.testPoint_getArrayItem(15));
assertSame(ITEMS[3], aq.testPoint_getArrayItem(0));
assertSame(ITEMS[1], aq.testPoint_getArrayItem(1));
assertSame(ITEMS[2], aq.testPoint_getArrayItem(2));
assertNull(aq.testPoint_getArrayItem(3));
}
@AfterClass
public static void afterAll() throws InterruptedException {
try {
eqe.shutdown();
eqe.awaitTermination(30, TimeUnit.SECONDS);
} finally {
eqe = null;
}
}
}
jboss-threads-3.5.0.Final/src/test/java/org/jboss/threads/DeferredInterruptTestCase.java 0000664 0000000 0000000 00000004462 14303134447 0031314 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
import junit.framework.TestCase;
/**
* @author David M. Lloyd
*/
public class DeferredInterruptTestCase extends TestCase {
public void testDeferral() throws Exception {
final AtomicBoolean delivered0 = new AtomicBoolean();
final AtomicBoolean deferred = new AtomicBoolean();
final AtomicBoolean delivered = new AtomicBoolean();
final CountDownLatch latch1 = new CountDownLatch(1);
final CountDownLatch latch2 = new CountDownLatch(1);
final JBossThread thread = new JBossThread(new Runnable() {
public void run() {
Thread.interrupted();
latch1.countDown();
LockSupport.parkNanos(3000000000L);
delivered0.set(Thread.interrupted());
JBossThread.executeWithInterruptDeferred(new Runnable() {
public void run() {
latch2.countDown();
LockSupport.parkNanos(500000000L);
deferred.set(! Thread.interrupted());
}
});
delivered.set(Thread.interrupted());
}
});
thread.start();
latch1.await();
thread.interrupt();
latch2.await();
thread.interrupt();
thread.join();
assertTrue(delivered0.get());
assertTrue(deferred.get());
assertTrue(delivered.get());
}
}
jboss-threads-3.5.0.Final/src/test/java/org/jboss/threads/EnhancedQueueExecutorTest.java 0000664 0000000 0000000 00000024100 14303134447 0031303 0 ustar 00root root 0000000 0000000 package org.jboss.threads;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import junit.framework.TestCase;
import org.junit.Ignore;
import org.junit.Test;
public class EnhancedQueueExecutorTest {
private int coreSize = 3;
private int maxSize = coreSize * 2;
private long keepaliveTimeMillis = 1000;
class TestTask implements Runnable {
private long sleepTime = 0;
public TestTask withSleepTime(long sleepTime) {
if (sleepTime > 0) {
this.sleepTime = sleepTime;
}
return this;
}
@Override
public void run() {
try {
if (sleepTime > 0) {
Thread.sleep(sleepTime);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* Test that unused threads are being reused. Scenario:
*
* max threads = 2x, core threads = x
* schedule x tasks, wait for tasks to finish
* schedule x tasks, expect pool size = x immediately after
*
*/
@Test
@Ignore("https://issues.jboss.org/browse/JBTHR-67")
public void testThreadReuse() throws TimeoutException, InterruptedException {
EnhancedQueueExecutor executor = (new EnhancedQueueExecutor.Builder())
.setKeepAliveTime(keepaliveTimeMillis, TimeUnit.MILLISECONDS)
.setCorePoolSize(coreSize)
.setMaximumPoolSize(maxSize)
.build();
for (int i = 0; i < coreSize; i++) {
executor.execute(new TestTask().withSleepTime(100));
}
assertEquals("expected: == " + coreSize + ", actual: " + executor.getPoolSize(), executor.getPoolSize(), coreSize);
waitForActiveCount(executor, 0, 1000);
assertEquals("expected: == " + coreSize + ", actual: " + executor.getPoolSize(), executor.getPoolSize(), coreSize);
for (int i = 0; i < coreSize; i++) {
executor.execute(new TestTask().withSleepTime(1000));
}
assertEquals("expected: == " + coreSize + ", actual: " + executor.getPoolSize(), executor.getPoolSize(), coreSize);
executor.shutdown();
}
/**
* Test that keepalive time is honored and threads above the core count are being removed when no tasks are
* available.
*
* @throws InterruptedException
* @throws TimeoutException
*/
@Test
@Ignore("https://issues.jboss.org/browse/JBTHR-67")
public void testKeepaliveTime() throws TimeoutException, InterruptedException {
EnhancedQueueExecutor executor = (new EnhancedQueueExecutor.Builder())
.setKeepAliveTime(keepaliveTimeMillis, TimeUnit.MILLISECONDS)
.setCorePoolSize(coreSize)
.setMaximumPoolSize(maxSize)
.build();
assertTrue("expected: <=" + coreSize + ", actual: " + executor.getPoolSize(), executor.getPoolSize() <= coreSize);
for (int i = 0; i < maxSize; i++) {
executor.execute(new TestTask().withSleepTime(1000));
}
assertEquals("expected: ==" + maxSize + ", actual: " + executor.getPoolSize(), executor.getPoolSize(), maxSize);
waitForActiveCount(executor, 0, 5000);
waitForPoolSize(executor, coreSize, keepaliveTimeMillis * 2);
executor.shutdown();
}
/**
* Test that max size setting is honored. Test that keepalive time is ignored when core threads are the same as max
* threads and core thread time out is disabled.
*/
@Test
@Ignore("https://issues.jboss.org/browse/JBTHR-67")
public void testKeepaliveTime2() throws TimeoutException, InterruptedException {
EnhancedQueueExecutor executor = (new EnhancedQueueExecutor.Builder())
.setKeepAliveTime(keepaliveTimeMillis, TimeUnit.MILLISECONDS)
.setCorePoolSize(coreSize)
.setMaximumPoolSize(coreSize)
.build();
for (int i = 0; i < 2*coreSize; i++) {
executor.execute(new TestTask().withSleepTime(100));
}
int currentThreads = executor.getPoolSize();
assertEquals("expected: == " + coreSize + ", actual: " + currentThreads, currentThreads, coreSize);
waitForActiveCount(executor, 0, 5000);
assertEquals("expected: == " + currentThreads + ", actual: " + executor.getPoolSize(), executor.getPoolSize(), currentThreads);
executor.shutdown();
}
/**
* Test the keepalive setting with core thread time out enabled.
*/
@Test
@Ignore("https://issues.jboss.org/browse/JBTHR-67")
public void testKeepaliveTime3() throws TimeoutException, InterruptedException {
EnhancedQueueExecutor executor = (new EnhancedQueueExecutor.Builder())
.setKeepAliveTime(keepaliveTimeMillis, TimeUnit.MILLISECONDS)
.allowCoreThreadTimeOut(true)
.setCorePoolSize(coreSize)
.setMaximumPoolSize(maxSize)
.build();
for (int i = 0; i < maxSize; i++) {
executor.execute(new TestTask().withSleepTime(0));
}
waitForActiveCount(executor, 0, 5000);
waitForPoolSize(executor, 0, keepaliveTimeMillis * 2);
executor.shutdown();
}
/**
* Tests that prestarting core threads starts exactly the core threads amount specified.
*/
@Test
public void testPrestartCoreThreads() {
EnhancedQueueExecutor executor = (new EnhancedQueueExecutor.Builder())
.setKeepAliveTime(keepaliveTimeMillis, TimeUnit.MILLISECONDS)
.setCorePoolSize(coreSize)
.setMaximumPoolSize(maxSize)
.build();
int prestarted = executor.prestartAllCoreThreads();
assertEquals("expected: == " + coreSize + ", actual: " + prestarted, prestarted, coreSize);
assertEquals("expected: == " + coreSize + ", actual: " + executor.getPoolSize(), executor.getPoolSize(), coreSize);
executor.shutdown();
}
@Test
public void testStackDepth() throws InterruptedException {
// Test exists to acknowledge changes which result in greater stack depth making stack traces
// created within the executor more difficult to follow. This isn't something that we should
// necessarily optimize for, rather something we should keep in mind when other options exist.
final int expectedStackFrames = 6;
assertStackDepth(new EnhancedQueueExecutor.Builder()
.setCorePoolSize(1)
.setMaximumPoolSize(1)
.build(), expectedStackFrames + 1);
// Use a standard ThreadPoolExecutor as a baseline for comparison.
assertStackDepth(Executors.newSingleThreadExecutor(), expectedStackFrames);
}
public void assertStackDepth(ExecutorService executor, int expectedStackFrames) throws InterruptedException {
CountDownLatch initialTaskCompletionBlockingLatch = new CountDownLatch(1);
AtomicInteger initialTaskStackFrames = new AtomicInteger();
Runnable initialTask = new Runnable() {
@Override
public void run() {
initialTaskStackFrames.set(new RuntimeException().getStackTrace().length);
try {
initialTaskCompletionBlockingLatch.await();
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
};
CountDownLatch queuedTaskCompletionLatch = new CountDownLatch(1);
AtomicInteger queuedTaskStackFrames = new AtomicInteger();
Runnable queuedTask = new Runnable() {
@Override
public void run() {
queuedTaskStackFrames.set(new RuntimeException().getStackTrace().length);
queuedTaskCompletionLatch.countDown();
}
};
try {
executor.submit(initialTask);
executor.submit(queuedTask);
initialTaskCompletionBlockingLatch.countDown();
queuedTaskCompletionLatch.await();
assertEquals(expectedStackFrames, initialTaskStackFrames.get());
assertEquals(expectedStackFrames, queuedTaskStackFrames.get());
} finally {
executor.shutdown();
assertTrue("Executor failed to terminate", executor.awaitTermination(5, TimeUnit.SECONDS));
}
}
private void waitForPoolSize(EnhancedQueueExecutor executor, int expectedPoolSize, long waitMillis) throws TimeoutException, InterruptedException {
long deadline = System.currentTimeMillis() + waitMillis;
long delayMillis = 100;
do {
if (executor.getPoolSize() == expectedPoolSize) {
break;
}
Thread.sleep(delayMillis);
} while (System.currentTimeMillis() + delayMillis < deadline);
if (executor.getPoolSize() != expectedPoolSize) {
throw new TimeoutException("Timed out waiting for pool size to become " + expectedPoolSize
+ ", current pool size is " + executor.getPoolSize());
}
}
private void waitForActiveCount(EnhancedQueueExecutor executor, int expectedActiveCount, long waitMillis) throws TimeoutException, InterruptedException {
long deadline = System.currentTimeMillis() + waitMillis;
long delayMillis = 100;
do {
if (executor.getActiveCount() == expectedActiveCount) {
break;
}
Thread.sleep(delayMillis);
} while (System.currentTimeMillis() + delayMillis < deadline);
if (executor.getActiveCount() != expectedActiveCount) {
throw new TimeoutException("Timed out waiting for active count to become " + expectedActiveCount
+ ", current active count is " + executor.getActiveCount());
}
}
}
jboss-threads-3.5.0.Final/src/test/java/org/jboss/threads/EnhancedThreadQueueExecutorTestCase.java 0000664 0000000 0000000 00000040705 14303134447 0033240 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
/**
* Tests for checking EnhancedTreadPoolExecutor
*
*/
public class EnhancedThreadQueueExecutorTestCase {
private int coreSize = 4;
private int maxSize = 7;
private long keepaliveTimeMillis = 1000;
private long defaultWaitTimeout = 5000;
class TestTask implements Runnable {
CountDownLatch exitLatch;
CountDownLatch allThreadsRunningLatch;
private TestTask(CountDownLatch exitLatch, CountDownLatch allThreadsRunningLatch) {
this.exitLatch = exitLatch;
this.allThreadsRunningLatch = allThreadsRunningLatch;
}
@Override
public void run() {
try {
allThreadsRunningLatch.countDown();
exitLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* Test invalid values:
* * Negative keepAlive, coreSize, maxSize
* * maxSize > coreSize
*/
@Test(expected = IllegalArgumentException.class)
public void testInvalidValuesKeepAliveZero() {
new EnhancedQueueExecutor.Builder()
.setKeepAliveTime(0, TimeUnit.MILLISECONDS)
.setCorePoolSize(coreSize)
.setMaximumPoolSize(maxSize)
.build();
}
@Test(expected = IllegalArgumentException.class)
public void testInvalidValuesKeepAliveNegative() {
new EnhancedQueueExecutor.Builder()
.setKeepAliveTime(-3456, TimeUnit.MILLISECONDS)
.setCorePoolSize(coreSize)
.setMaximumPoolSize(maxSize)
.build();
}
@Test(expected = IllegalArgumentException.class)
public void testInvalidValuesCoreSizeNegative() {
new EnhancedQueueExecutor.Builder()
.setKeepAliveTime(keepaliveTimeMillis, TimeUnit.MILLISECONDS)
.setCorePoolSize(-5)
.setMaximumPoolSize(maxSize)
.build();
}
@Test(expected = IllegalArgumentException.class)
public void testInvalidValuesMaxSizeNegative() {
new EnhancedQueueExecutor.Builder()
.setKeepAliveTime(keepaliveTimeMillis, TimeUnit.MILLISECONDS)
.setCorePoolSize(coreSize)
.setMaximumPoolSize(-3)
.build();
}
@Test
public void testCoreSizeBiggerThanMaxSize() {
int expectedCorePoolSize = 5;
EnhancedQueueExecutor executor = (new EnhancedQueueExecutor.Builder())
.setKeepAliveTime(keepaliveTimeMillis, TimeUnit.MILLISECONDS)
.setCorePoolSize(2 * expectedCorePoolSize)
.setMaximumPoolSize(expectedCorePoolSize)
.build();
Assert.assertEquals("Core size should be automatically adjusted to be equal to max size in case it's bigger.", expectedCorePoolSize, executor.getCorePoolSize());
}
/**
* Test that unused threads are being reused. Scenario:
*
* max threads = 2x, core threads = x
* schedule x tasks, wait for tasks to finish
* schedule x tasks, expect pool size = x immediately after
*
*/
@Test
public void testThreadReuse() throws TimeoutException, InterruptedException {
EnhancedQueueExecutor executor = (new EnhancedQueueExecutor.Builder())
.setKeepAliveTime(keepaliveTimeMillis, TimeUnit.MILLISECONDS)
.setCorePoolSize(coreSize)
.setMaximumPoolSize(maxSize)
.build();
CountDownLatch exitLatch = new CountDownLatch(1);
CountDownLatch allThreadsRunningLatch = new CountDownLatch(coreSize);
for (int i = 0; i < coreSize; i++) {
executor.execute(new TestTask(exitLatch, allThreadsRunningLatch));
}
Assert.assertTrue("Not all threads were running. They were most likely not scheduled for execution.",
allThreadsRunningLatch.await(defaultWaitTimeout, TimeUnit.MILLISECONDS));
waitForPoolSize(executor, coreSize, defaultWaitTimeout);
exitLatch.countDown();
waitForActiveCount(executor, 0, defaultWaitTimeout);
waitForPoolSize(executor, coreSize, defaultWaitTimeout);
exitLatch = new CountDownLatch(1);
allThreadsRunningLatch = new CountDownLatch(coreSize);
for (int i = 0; i < coreSize; i++) {
executor.execute(new TestTask(exitLatch, allThreadsRunningLatch));
}
Assert.assertTrue("Not all threads were running. They were most likely not scheduled for execution.",
allThreadsRunningLatch.await(defaultWaitTimeout, TimeUnit.MILLISECONDS));
exitLatch.countDown();
waitForPoolSize(executor, coreSize, defaultWaitTimeout);
executor.shutdown();
}
/**
* Test thread reuse above core size
* Scenario:
*
* setKeepAlive=60 sec
* max threads = 2x, core threads = x
* schedule x tasks and wait to occupy all core threads
* schedule one more task and let it finish
* schedule one task and check that pool size is still x+1
*
*/
@Test
public void testThreadReuseAboveCoreSize() throws TimeoutException, InterruptedException {
EnhancedQueueExecutor executor = (new EnhancedQueueExecutor.Builder())
.setKeepAliveTime(60000, TimeUnit.MILLISECONDS)
.setCorePoolSize(coreSize)
.setMaximumPoolSize(maxSize)
.build();
// submit 3 tasks to fill core size
CountDownLatch exitLatch = new CountDownLatch(1);
CountDownLatch allThreadsRunningLatch = new CountDownLatch(coreSize);
for (int i = 0; i < coreSize; i++) {
executor.execute(new TestTask(exitLatch, allThreadsRunningLatch));
}
Assert.assertTrue("Not all threads were running. They were most likely not scheduled for execution.",
allThreadsRunningLatch.await(defaultWaitTimeout, TimeUnit.MILLISECONDS));
waitForPoolSize(executor, coreSize, defaultWaitTimeout);
// submit one more task and allow it to finish
CountDownLatch singleExitLatch = new CountDownLatch(1);
CountDownLatch threadRunningLatch = new CountDownLatch(1);
executor.execute(new TestTask(singleExitLatch, threadRunningLatch));
threadRunningLatch.await(defaultWaitTimeout, TimeUnit.MILLISECONDS);
waitForPoolSize(executor, coreSize + 1, defaultWaitTimeout);
singleExitLatch.countDown();
waitForActiveCount(executor, coreSize, defaultWaitTimeout);
// now there are just core threads and one free thread, submit another task and check it's reused
singleExitLatch = new CountDownLatch(1);
threadRunningLatch = new CountDownLatch(1);
executor.execute(new TestTask(singleExitLatch, threadRunningLatch));
threadRunningLatch.await(defaultWaitTimeout, TimeUnit.MILLISECONDS);
waitForPoolSize(executor, coreSize + 1, defaultWaitTimeout);
singleExitLatch.countDown();
// finish all
exitLatch.countDown();
executor.shutdown();
}
/**
* Test that keepalive time is honored and threads above the core count are being removed when no tasks are
* available.
*
* @throws InterruptedException
* @throws TimeoutException
*/
@Test
@Ignore("This test consistently fails, see JBTHR-67")
public void testKeepaliveTime() throws TimeoutException, InterruptedException {
EnhancedQueueExecutor executor = (new EnhancedQueueExecutor.Builder())
.setKeepAliveTime(keepaliveTimeMillis, TimeUnit.MILLISECONDS)
.setCorePoolSize(coreSize)
.setMaximumPoolSize(maxSize)
.allowCoreThreadTimeOut(false)
.build();
CountDownLatch exitLatch = new CountDownLatch(1);
CountDownLatch allThreadsRunningLatch = new CountDownLatch(coreSize);
for (int i = 0; i < coreSize; i++) {
executor.execute(new TestTask(exitLatch, allThreadsRunningLatch));
}
Assert.assertTrue("Not all core threads are running. They were most likely not scheduled for execution.",
allThreadsRunningLatch.await(defaultWaitTimeout, TimeUnit.MILLISECONDS));
CountDownLatch exitLatch2 = new CountDownLatch(1);
CountDownLatch allThreadsRunningLatch2 = new CountDownLatch(maxSize - coreSize);
for (int i = 0; i < (maxSize - coreSize); i++) {
executor.execute(new TestTask(exitLatch2, allThreadsRunningLatch2));
}
Assert.assertTrue("Not all above core threads were running. They were most likely not scheduled for execution.",
allThreadsRunningLatch2.await(defaultWaitTimeout, TimeUnit.MILLISECONDS));
waitForPoolSize(executor, maxSize, defaultWaitTimeout);
// finish core tasks and let timeout "core" threads
exitLatch.countDown();
waitForActiveCount(executor, maxSize - coreSize, defaultWaitTimeout);
waitForPoolSize(executor, Math.max(coreSize, (maxSize - coreSize)), defaultWaitTimeout);
exitLatch2.countDown();
waitForActiveCount(executor, 0, defaultWaitTimeout);
waitForPoolSize(executor, coreSize, defaultWaitTimeout);
executor.shutdown();
}
/**
* Test that keepalive time is ignored when core threads are the same as max
* threads and core thread time out is disabled.
*/
@Test
public void testKeepaliveTime2() throws TimeoutException, InterruptedException {
EnhancedQueueExecutor executor = (new EnhancedQueueExecutor.Builder())
.setKeepAliveTime(keepaliveTimeMillis, TimeUnit.MILLISECONDS)
.setCorePoolSize(coreSize)
.setMaximumPoolSize(coreSize)
.build();
CountDownLatch exitLatch = new CountDownLatch(1);
CountDownLatch allThreadsRunningLatch = new CountDownLatch(coreSize);
for (int i = 0; i < coreSize; i++) {
executor.execute(new TestTask(exitLatch, allThreadsRunningLatch));
}
Assert.assertTrue("Not all threads were running. They were most likely not scheduled for execution.",
allThreadsRunningLatch.await(defaultWaitTimeout, TimeUnit.MILLISECONDS));
waitForPoolSize(executor, coreSize, defaultWaitTimeout);
exitLatch.countDown();
waitForActiveCount(executor, 0, defaultWaitTimeout);
waitForPoolSize(executor, coreSize, defaultWaitTimeout);
executor.shutdown();
}
/**
* Test the keepalive setting with core thread time out enabled.
*/
@Test
public void testKeepaliveTimeWithCoreThreadTimeoutEnabled() throws TimeoutException, InterruptedException {
EnhancedQueueExecutor executor = (new EnhancedQueueExecutor.Builder())
.setKeepAliveTime(keepaliveTimeMillis, TimeUnit.MILLISECONDS)
.allowCoreThreadTimeOut(true)
.setCorePoolSize(coreSize)
.setMaximumPoolSize(maxSize)
.build();
CountDownLatch exitLatch = new CountDownLatch(1);
CountDownLatch allThreadsRunningLatch = new CountDownLatch(maxSize);
for (int i = 0; i < maxSize; i++) {
executor.execute(new TestTask(exitLatch, allThreadsRunningLatch));
}
// this will make sure that all thread are running at the same time
Assert.assertTrue("Not all threads were running. They were most likely not scheduled for execution.",
allThreadsRunningLatch.await(defaultWaitTimeout, TimeUnit.MILLISECONDS));
exitLatch.countDown();
waitForActiveCount(executor, 0, defaultWaitTimeout);
waitForPoolSize(executor, 0, defaultWaitTimeout);
executor.shutdown();
}
/**
* Tests that prestarting core threads starts exactly the core threads amount specified.
*/
@Test
public void testPrestartCoreThreads() {
EnhancedQueueExecutor executor = (new EnhancedQueueExecutor.Builder())
.setKeepAliveTime(keepaliveTimeMillis, TimeUnit.MILLISECONDS)
.setCorePoolSize(coreSize)
.setMaximumPoolSize(maxSize)
.build();
int prestarted = executor.prestartAllCoreThreads();
Assert.assertEquals("expected: == " + coreSize + ", actual: " + prestarted, coreSize, prestarted);
Assert.assertEquals("expected: == " + coreSize + ", actual: " + executor.getPoolSize(), coreSize, executor.getPoolSize());
executor.shutdown();
}
private void waitForPoolSize(EnhancedQueueExecutor executor, int expectedPoolSize, long waitMillis) throws TimeoutException, InterruptedException {
long deadline = System.currentTimeMillis() + waitMillis;
long delayMillis = 100;
do {
if (executor.getPoolSize() == expectedPoolSize) {
break;
}
Thread.sleep(delayMillis);
} while (System.currentTimeMillis() + delayMillis < deadline);
if (executor.getPoolSize() != expectedPoolSize) {
throw new TimeoutException("Timed out waiting for pool size to become " + expectedPoolSize
+ ", current pool size is " + executor.getPoolSize());
}
}
private void waitForActiveCount(EnhancedQueueExecutor executor, int expectedActiveCount, long waitMillis) throws TimeoutException, InterruptedException {
long deadline = System.currentTimeMillis() + waitMillis;
long delayMillis = 100;
do {
if (executor.getActiveCount() == expectedActiveCount) {
break;
}
Thread.sleep(delayMillis);
} while (System.currentTimeMillis() + delayMillis < deadline);
if (executor.getActiveCount() != expectedActiveCount) {
throw new TimeoutException("Timed out waiting for active count to become " + expectedActiveCount
+ ", current active count is " + executor.getActiveCount());
}
}
@Test
public void testEnhancedExecutorShutdownNoTasks() throws Exception {
final CountDownLatch terminateLatch = new CountDownLatch(1);
EnhancedQueueExecutor executor = new EnhancedQueueExecutor.Builder()
.setCorePoolSize(10)
.setKeepAliveTime(1, TimeUnit.NANOSECONDS)
.setTerminationTask(new Runnable() {
@Override
public void run() {
terminateLatch.countDown();
}
})
.build();
executor.shutdown();
Assert.assertTrue(terminateLatch.await(10, TimeUnit.SECONDS));
}
@Test //JBTHR-50
public void testEnhancedExecutorShutdown() throws Exception {
final CountDownLatch terminateLatch = new CountDownLatch(1);
EnhancedQueueExecutor executor = new EnhancedQueueExecutor.Builder()
.setCorePoolSize(10)
.setKeepAliveTime(1, TimeUnit.NANOSECONDS)
.setTerminationTask(new Runnable() {
@Override
public void run() {
terminateLatch.countDown();
}
})
.build();
for (int i = 0; i < 10000; ++i) {
executor.submit(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
//ignore
}
}
});
}
executor.shutdown();
Assert.assertTrue(terminateLatch.await(10, TimeUnit.SECONDS));
}
}
jboss-threads-3.5.0.Final/src/test/java/org/jboss/threads/QueuelessViewExecutorTest.java 0000664 0000000 0000000 00000022677 14303134447 0031420 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2020 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import org.awaitility.Awaitility;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@RunWith(Parameterized.class)
public class QueuelessViewExecutorTest {
private static final String THREAD_BASE_NAME = "CachedExecutorViewTest-";
/*
* Both implementations have enough permits that queues shouldn't
* be used so the implementations should be equivalent.
*/
public enum ExecutorType {
QUEUELESS_VIEW() {
@Override
ExecutorService wrap(Executor delegate) {
return ViewExecutor.builder(delegate)
.setQueueLimit(0)
.setMaxSize(Short.MAX_VALUE)
.build();
}
},
QUEUED() {
@Override
ExecutorService wrap(Executor delegate) {
return ViewExecutor.builder(delegate)
.setQueueLimit(Integer.MAX_VALUE)
.setMaxSize(Short.MAX_VALUE)
.build();
}
};
abstract ExecutorService wrap(Executor delegate);
}
@Parameterized.Parameters(name = "{index}: {0}")
public static Iterable data() {
return Arrays.asList(new Object[][]{{ExecutorType.QUEUELESS_VIEW}, {ExecutorType.QUEUED}});
}
private final ExecutorType executorType;
public QueuelessViewExecutorTest(ExecutorType executorType) {
this.executorType = executorType;
}
@Test
public void testShutdownNow() throws InterruptedException {
AtomicBoolean interrupted = new AtomicBoolean();
ExecutorService cached = cachedExecutor();
ExecutorService view = executorType.wrap(cached);
assertThat(view.isShutdown()).isEqualTo(cached.isShutdown()).isFalse();
assertThat(view.isTerminated()).isEqualTo(cached.isTerminated()).isFalse();
CountDownLatch executionStartedLatch = new CountDownLatch(1);
CountDownLatch interruptedLatch = new CountDownLatch(1);
view.execute(() -> {
try {
executionStartedLatch.countDown();
Thread.sleep(10_000);
} catch (InterruptedException e) {
interrupted.set(true);
// Wait so we can validate the time between shutdown and terminated
try {
interruptedLatch.await();
} catch (InterruptedException ee) {
throw new AssertionError(ee);
}
}
});
executionStartedLatch.await();
assertThat(view.shutdownNow()).as("Cached executors have no queue").isEmpty();
assertThat(view.isShutdown()).isTrue();
assertThatThrownBy(() -> view.execute(NullRunnable.getInstance()))
.as("Submitting work after invoking shutdown or shutdownNow should fail")
.isInstanceOf(RejectedExecutionException.class);
Awaitility.waitAtMost(500, TimeUnit.MILLISECONDS).untilAsserted(() -> {
assertThat(interrupted).isTrue();
assertThat(view.isTerminated()).isFalse();
});
interruptedLatch.countDown();
Awaitility.waitAtMost(500, TimeUnit.MILLISECONDS)
.untilAsserted(() -> assertThat(view.isTerminated()).as("%s", view).isTrue());
assertCleanShutdown(cached);
}
@Test
public void testShutdownNow_immediatelyAfterTaskIsSubmitted() throws InterruptedException {
AtomicBoolean interrupted = new AtomicBoolean();
ExecutorService cached = cachedExecutor();
ExecutorService view = executorType.wrap(runnable -> {
cached.execute(() -> {
// Emphasize the jitter between when a task is submitted, and when it begins to execute
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new AssertionError(e);
}
runnable.run();
});
});
assertThat(view.isShutdown()).isEqualTo(cached.isShutdown()).isFalse();
assertThat(view.isTerminated()).isEqualTo(cached.isTerminated()).isFalse();
view.execute(() -> {
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
interrupted.set(true);
}
});
assertThat(view.shutdownNow()).as("Cached executors have no queue").isEmpty();
assertThat(view.awaitTermination(3, TimeUnit.SECONDS))
.as("View failed to terminate within 3 seconds: %s", view)
.isTrue();
assertThat(interrupted).as("Task should have been interrupted").isTrue();
assertCleanShutdown(cached);
}
@Test(timeout = 5_000) // Failing awaitTermination should return quickly
public void testAwaitTermination() throws InterruptedException {
AtomicBoolean interrupted = new AtomicBoolean();
ExecutorService cached = cachedExecutor();
ExecutorService view = executorType.wrap(cached);
assertThat(view.isShutdown()).isEqualTo(cached.isShutdown()).isFalse();
assertThat(view.isTerminated()).isEqualTo(cached.isTerminated()).isFalse();
view.execute(() -> {
try {
Thread.sleep(30_000);
} catch (InterruptedException e) {
interrupted.set(true);
}
});
view.shutdown();
assertThat(view.awaitTermination(10, TimeUnit.MILLISECONDS))
.as("Task should not have been interrupted, and is still sleeping")
.isFalse();
assertThat(interrupted).as("Task should not be interrupted by 'shutdown'").isFalse();
assertThat(view.shutdownNow()).as("Cached executors have no queue").isEmpty();
assertThat(view.awaitTermination(3, TimeUnit.SECONDS))
.as("Task should have been interrupted: %s", view)
.isTrue();
assertThat(interrupted).as("Task should be interrupted by 'shutdownNow'").isTrue();
assertCleanShutdown(cached);
}
@Test
public void testShutdown() throws InterruptedException {
AtomicBoolean interrupted = new AtomicBoolean();
ExecutorService cached = cachedExecutor();
ExecutorService view = executorType.wrap(cached);
assertThat(view.isShutdown()).isEqualTo(cached.isShutdown()).isFalse();
assertThat(view.isTerminated()).isEqualTo(cached.isTerminated()).isFalse();
CountDownLatch executionStartedLatch = new CountDownLatch(1);
view.execute(() -> {
try {
executionStartedLatch.countDown();
Thread.sleep(500);
} catch (InterruptedException e) {
interrupted.set(true);
}
});
executionStartedLatch.await();
view.shutdown();
assertThat(view.isShutdown()).isTrue();
assertThatThrownBy(() -> view.execute(NullRunnable.getInstance()))
.as("Submitting work after invoking shutdown or shutdown should fail")
.isInstanceOf(RejectedExecutionException.class);
assertThat(view.isTerminated()).isFalse();
Awaitility.waitAtMost(600, TimeUnit.MILLISECONDS)
.untilAsserted(() -> assertThat(view.isTerminated()).as("%s", view).isTrue());
assertThat(interrupted).isFalse();
assertCleanShutdown(cached);
}
private static ExecutorService cachedExecutor() {
AtomicInteger index = new AtomicInteger();
return Executors.newCachedThreadPool(
task -> {
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.setName(THREAD_BASE_NAME + index.getAndIncrement());
return thread;
});
}
private static void assertCleanShutdown(ExecutorService executor) {
assertThat(executor.isShutdown()).isFalse();
assertThat(executor.isTerminated()).isFalse();
executor.shutdown();
try {
assertThat(executor.awaitTermination(1, TimeUnit.SECONDS))
.as("Failed to clean up the executor")
.isTrue();
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
}
jboss-threads-3.5.0.Final/src/test/java/org/jboss/threads/ScheduledEnhancedQueueExecutorTest.java 0000664 0000000 0000000 00000015751 14303134447 0033140 0 ustar 00root root 0000000 0000000 package org.jboss.threads;
import static org.junit.Assert.*;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
public class ScheduledEnhancedQueueExecutorTest {
@Test
public void testCancel() throws Exception {
EnhancedQueueExecutor eqe = new EnhancedQueueExecutor.Builder().build();
try {
ScheduledFuture> future = eqe.schedule(() -> fail("Should never run"), 1000, TimeUnit.DAYS);
Thread.sleep(400); // a few ms to let things percolate
assertFalse(future.isCancelled());
// this should succeed since the task isn't submitted yet
assertTrue(future.cancel(false));
assertTrue(future.isCancelled());
eqe.shutdown();
assertTrue("Timely shutdown", eqe.awaitTermination(5, TimeUnit.SECONDS));
} finally {
eqe.shutdownNow();
}
}
@Test
public void testCancelWhileRunning() throws Exception {
EnhancedQueueExecutor eqe = new EnhancedQueueExecutor.Builder().build();
try {
CountDownLatch latch = new CountDownLatch(1);
ScheduledFuture future = eqe.schedule(() -> { latch.countDown(); Thread.sleep(1_000_000_000L); return Boolean.TRUE; }, 1, TimeUnit.NANOSECONDS);
assertTrue("Timely task execution", latch.await(5, TimeUnit.SECONDS));
assertFalse(future.isCancelled());
// task is running; cancel will fail
assertFalse(future.cancel(false));
assertFalse(future.isCancelled());
assertFalse(future.isDone());
// now try to interrupt it (cancel still fails but the interrupt should be delivered)
assertFalse(future.cancel(true));
assertFalse(future.isCancelled());
// now get it
try {
future.get(100L, TimeUnit.MILLISECONDS);
fail("Expected exception");
} catch (ExecutionException ee) {
Throwable cause = ee.getCause();
assertTrue("Expected " + cause + " to be an InterruptedException", cause instanceof InterruptedException);
}
assertTrue(future.isDone());
eqe.shutdown();
assertTrue("Timely shutdown", eqe.awaitTermination(5, TimeUnit.SECONDS));
} finally {
eqe.shutdownNow();
}
}
@Test
public void testReasonableExecutionDelay() throws Exception {
EnhancedQueueExecutor eqe = new EnhancedQueueExecutor.Builder().build();
try {
Callable task = () -> Boolean.TRUE;
long start = System.nanoTime();
ScheduledFuture future = eqe.schedule(task, 1, TimeUnit.MILLISECONDS);
Boolean result = future.get();
long execTime = System.nanoTime() - start;
long expected = 1_000_000L;
assertTrue("Execution too short (expected at least " + expected + ", got " + execTime + ")", execTime >= expected);
assertNotNull(result);
assertTrue(result.booleanValue());
start = System.nanoTime();
future = eqe.schedule(task, 500, TimeUnit.MILLISECONDS);
result = future.get();
execTime = System.nanoTime() - start;
expected = 500_000_000L;
assertTrue("Execution too short (expected at least " + expected + ", got " + execTime + ")", execTime >= expected);
assertNotNull(result);
assertTrue(result.booleanValue());
eqe.shutdown();
assertTrue("Timely shutdown", eqe.awaitTermination(5, TimeUnit.SECONDS));
} finally {
eqe.shutdownNow();
}
}
@Test
public void testFixedRateExecution() throws Exception {
EnhancedQueueExecutor eqe = new EnhancedQueueExecutor.Builder().build();
try {
AtomicInteger ai = new AtomicInteger();
CountDownLatch completeLatch = new CountDownLatch(1);
ScheduledFuture> future = eqe.scheduleAtFixedRate(() -> {
if (ai.incrementAndGet() == 5) {
completeLatch.countDown();
}
}, 20, 50, TimeUnit.MILLISECONDS);
assertTrue("Completion of enough iterations", completeLatch.await(1, TimeUnit.SECONDS));
assertFalse(future.isDone()); // they're never done
// don't assert, because there's a small chance it would happen to be running
future.cancel(false);
try {
future.get(1, TimeUnit.SECONDS);
fail("Expected cancellation exception");
} catch (CancellationException e) {
// expected
}
eqe.shutdown();
assertTrue("Timely shutdown", eqe.awaitTermination(5, TimeUnit.SECONDS));
} finally {
eqe.shutdownNow();
}
}
@Test
public void testFixedDelayExecution() throws Exception {
EnhancedQueueExecutor eqe = new EnhancedQueueExecutor.Builder().build();
try {
AtomicInteger ai = new AtomicInteger();
CountDownLatch completeLatch = new CountDownLatch(1);
ScheduledFuture> future = eqe.scheduleWithFixedDelay(() -> {
if (ai.incrementAndGet() == 5) {
completeLatch.countDown();
}
}, 20, 50, TimeUnit.MILLISECONDS);
assertTrue("Completion of enough iterations", completeLatch.await(1, TimeUnit.SECONDS));
assertFalse(future.isDone()); // they're never done
// don't assert, because there's a small chance it would happen to be running
future.cancel(false);
try {
future.get(1, TimeUnit.SECONDS);
fail("Expected cancellation exception");
} catch (CancellationException e) {
// expected
}
eqe.shutdown();
assertTrue("Timely shutdown", eqe.awaitTermination(5, TimeUnit.SECONDS));
} finally {
eqe.shutdownNow();
}
}
@Test
public void testCancelOnShutdown() throws Exception {
EnhancedQueueExecutor eqe = new EnhancedQueueExecutor.Builder().build();
try {
ScheduledFuture> future = eqe.schedule(() -> fail("Should never run"), 1, TimeUnit.DAYS);
eqe.shutdown();
assertTrue("Timely shutdown", eqe.awaitTermination(5, TimeUnit.SECONDS));
try {
future.get(1, TimeUnit.SECONDS);
fail("Expected cancellation exception");
} catch (CancellationException e) {
// expected
}
assertTrue("Was cancelled on shutdown", future.isCancelled());
} finally {
eqe.shutdownNow();
}
}
}
jboss-threads-3.5.0.Final/src/test/java/org/jboss/threads/ThreadFactoryTestCase.java 0000664 0000000 0000000 00000013457 14303134447 0030422 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.
*/
package org.jboss.threads;
import junit.framework.TestCase;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.CountDownLatch;
/**
*
*/
public final class ThreadFactoryTestCase extends TestCase {
private static final NullRunnable NULL_RUNNABLE = new NullRunnable();
private static class NullRunnable implements Runnable {
public void run() {
}
}
private static void doTestNamePattern(JBossThreadFactory threadFactory, int expectedPerFactoryId, int expectedGlobalId, int expectedFactoryId) {
final String name = threadFactory.newThread(NULL_RUNNABLE).getName();
assertTrue("Wrong thread name (" + name + ") ", name.matches("-([a-z]+:)*one:two:three-%-" + expectedPerFactoryId + "-" + expectedGlobalId + "-" + expectedFactoryId + "-"));
}
/**
* This MUST be the first test, otherwise the sequence numbers will be wrong.
*/
public void testNamePattern() {
// TODO - skip test for now since it depends on order.
if (true) return;
final JBossThreadFactory threadFactory1 = new JBossThreadFactory(new ThreadGroup(new ThreadGroup(new ThreadGroup("one"), "two"), "three"), null,
null, "-%p-%%-%t-%g-%f-", null, null);
doTestNamePattern(threadFactory1, 1, 1, 1);
doTestNamePattern(threadFactory1, 2, 2, 1);
doTestNamePattern(threadFactory1, 3, 3, 1);
final JBossThreadFactory threadFactory2 = new JBossThreadFactory(new ThreadGroup(new ThreadGroup(new ThreadGroup("one"), "two"), "three"), null,
null, "-%p-%%-%t-%g-%f-", null, null);
doTestNamePattern(threadFactory2, 1, 4, 2);
doTestNamePattern(threadFactory2, 2, 5, 2);
doTestNamePattern(threadFactory2, 3, 6, 2);
doTestNamePattern(threadFactory2, 4, 7, 2);
// back to the first factory...
doTestNamePattern(threadFactory1, 4, 8, 1);
}
public void testDaemon() {
final JBossThreadFactory threadFactory1 = new JBossThreadFactory(null, Boolean.TRUE, null, "%t", null, null);
assertTrue("Thread is not a daemon thread", threadFactory1.newThread(NULL_RUNNABLE).isDaemon());
final JBossThreadFactory threadFactory2 = new JBossThreadFactory(null, Boolean.FALSE, null, "%t", null, null);
assertFalse("Thread should not be a daemon thread", threadFactory2.newThread(NULL_RUNNABLE).isDaemon());
}
public void testInterruptHandler() throws InterruptedException {
final AtomicBoolean wasInterrupted = new AtomicBoolean();
final AtomicBoolean called = new AtomicBoolean();
final CountDownLatch latch = new CountDownLatch(1);
final JBossThreadFactory threadFactory = new JBossThreadFactory(null, null, null, null, null, null);
final Thread t = threadFactory.newThread(new Runnable() {
public void run() {
synchronized (this) {
final InterruptHandler old = JBossThread.getAndSetInterruptHandler(new InterruptHandler() {
public void handleInterrupt(final Thread thread) {
called.set(true);
}
});
Thread.interrupted();
latch.countDown();
try {
for (;;) wait();
} catch (InterruptedException e) {
wasInterrupted.set(true);
}
}
}
});
t.start();
latch.await();
t.interrupt();
t.join();
assertTrue("Was not interrupted", wasInterrupted.get());
assertTrue("Handler was not called", called.get());
}
public void testUncaughtHandler() throws InterruptedException {
final AtomicBoolean called = new AtomicBoolean();
final JBossThreadFactory factory = new JBossThreadFactory(null, null, null, null, new Thread.UncaughtExceptionHandler() {
public void uncaughtException(final Thread t, final Throwable e) {
called.set(true);
}
}, null);
final Thread t = factory.newThread(new Runnable() {
public void run() {
throw new RuntimeException("...");
}
});
t.start();
t.join();
assertTrue("Handler was not called", called.get());
}
public void testInitialPriority() {
assertEquals("Wrong initial thread priority", 1, new JBossThreadFactory(null, null, Integer.valueOf(1), null, null, null).newThread(NULL_RUNNABLE).getPriority());
assertEquals("Wrong initial thread priority", 2, new JBossThreadFactory(null, null, Integer.valueOf(2), null, null, null).newThread(NULL_RUNNABLE).getPriority());
final ThreadGroup grp = new ThreadGroup("blah");
grp.setMaxPriority(5);
assertEquals("Wrong initial thread priority", 5, new JBossThreadFactory(grp, null, Integer.valueOf(10), null, null, null).newThread(NULL_RUNNABLE).getPriority());
assertEquals("Wrong initial thread priority", 1, new JBossThreadFactory(grp, null, Integer.valueOf(1), null, null, null).newThread(NULL_RUNNABLE).getPriority());
}
}
jboss-threads-3.5.0.Final/src/test/java/org/jboss/threads/ViewExecutorTest.java 0000664 0000000 0000000 00000030340 14303134447 0027506 0 ustar 00root root 0000000 0000000 /*
* JBoss, Home of Professional Open Source.
* Copyright 2019, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.threads;
import static org.junit.Assert.*;
import java.time.Duration;
import java.util.ArrayDeque;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.awaitility.Awaitility;
import org.junit.Test;
public final class ViewExecutorTest {
@Test
public void testExecution() throws InterruptedException {
final ViewExecutor ve = ViewExecutor.builder(JBossExecutors.directExecutor()).build();
assertFalse(ve.isShutdown());
assertFalse(ve.isTerminated());
AtomicBoolean ran = new AtomicBoolean();
ve.execute(new Runnable() {
public void run() {
ran.set(true);
}
});
assertTrue(ran.get());
ve.shutdown();
assertTrue(ve.isShutdown());
assertTrue(ve.awaitTermination(10L, TimeUnit.SECONDS));
assertTrue(ve.isTerminated());
ve.shutdown();
assertTrue(ve.isTerminated());
}
@Test
public void testQueuedExecution() {
final ArrayDeque executedTasks = new ArrayDeque<>();
Executor testExecutor = new QueuedExecutor(executedTasks);
final ViewExecutor ve = ViewExecutor.builder(testExecutor).setMaxSize(1).build();
AtomicBoolean ran1 = new AtomicBoolean();
ve.execute(new Runnable() {
public void run() {
ran1.set(true);
}
});
assertEquals(1, executedTasks.size());
AtomicBoolean ran2 = new AtomicBoolean();
ve.execute(new Runnable() {
public void run() {
ran2.set(true);
}
});
assertEquals(1, executedTasks.size());
executedTasks.poll().run();
assertEquals(1, executedTasks.size());
assertTrue(ran1.get());
executedTasks.poll().run();
assertTrue(ran2.get());
assertEquals(0, executedTasks.size());
}
@Test
public void testInterruptedShutdown() throws InterruptedException {
ExecutorService testExecutor = Executors.newSingleThreadExecutor();
final ViewExecutor ve = ViewExecutor.builder(testExecutor).build();
AtomicBoolean intr = new AtomicBoolean();
CountDownLatch runGate = new CountDownLatch(1);
CountDownLatch finishGate = new CountDownLatch(1);
ve.execute(new Runnable() {
public void run() {
runGate.countDown();
try {
Thread.sleep(60_000L);
} catch (InterruptedException e) {
intr.set(true);
} finally {
finishGate.countDown();
}
}
});
runGate.await();
assertFalse(intr.get());
ve.shutdown(true);
finishGate.await();
assertTrue(intr.get());
testExecutor.shutdown();
assertTrue(testExecutor.awaitTermination(5L, TimeUnit.SECONDS));
}
// TaskWrapper instances are relatively small and take a long time to knock over a JVM,
// however when they aren't collected properly they will cause a GC spiral for much longer
// than ten seconds. Unfortunately this test is impacted by hardware changes and may flake
// (or erroneously pass on fast enough hardware).
@Test(timeout = 10_000)
public void testViewExecutorMemoryOverhead() {
Executor directExecutor = new Executor() {
@Override
public void execute(Runnable command) {
try {
command.run();
} catch (Throwable t) {
Thread currentThread = Thread.currentThread();
currentThread.getUncaughtExceptionHandler().uncaughtException(currentThread, t);
}
}
};
ExecutorService executorService = ViewExecutor.builder(directExecutor).build();
for (long i = 0; i < 20_000_000L; i++) {
executorService.execute(JBossExecutors.nullRunnable());
}
executorService.shutdown();
assertTrue(executorService.isTerminated());
}
@Test
public void testSameThreadDelegateDoesNotDeadlock() throws InterruptedException {
Executor direct = Runnable::run;
AtomicInteger completed = new AtomicInteger();
ExecutorService view = ViewExecutor.builder(direct).setQueueLimit(1).setMaxSize(1).build();
ExecutorService submittingExecutor = Executors.newCachedThreadPool();
try {
submittingExecutor.execute(() -> view.execute(() -> view.execute(completed::incrementAndGet)));
Awaitility.waitAtMost(Duration.ofSeconds(1)).untilAsserted(() -> assertEquals(1, completed.get()));
view.shutdown();
assertTrue(view.awaitTermination(100, TimeUnit.SECONDS));
} finally {
submittingExecutor.shutdown();
assertTrue(submittingExecutor.awaitTermination(1, TimeUnit.SECONDS));
}
}
@Test
public void testDelegateQueueProcessingRejection() throws InterruptedException {
// When the active task pulls from the queue, submitting the task to the delegate will fail because it's
// already consuming the only available thread. The task should be handled on the same thread.
CountDownLatch taskLatch = new CountDownLatch(1);
// One permit, throws RejectedExecutionException if a second task is provided
ExecutorService delegate = new ThreadPoolExecutor(0, 1,
5, TimeUnit.SECONDS, new SynchronousQueue<>());
try {
ExecutorService view = ViewExecutor.builder(delegate).setQueueLimit(1).setMaxSize(1).build();
List throwables = new CopyOnWriteArrayList<>();
for (int i = 0; i < 2; i++) {
view.execute(() -> {
try {
// latch used to ensure the second task is queued, otherwise the first task may complete
// before the second is submitted.
taskLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
throwables.add(new RuntimeException("task trace"));
});
}
taskLatch.countDown();
Awaitility.waitAtMost(Duration.ofSeconds(1))
.untilAsserted(() -> {
assertEquals(2, throwables.size());
// The stack size mustn't grow with each queued task, otherwise processing will eventually
// fail running out of stack space.
assertEquals(throwables.get(0).getStackTrace().length, throwables.get(1).getStackTrace().length);
});
} finally {
delegate.shutdown();
assertTrue(delegate.awaitTermination(1, TimeUnit.SECONDS));
}
}
@Test(timeout = 5_000)
public void testDelegateQueueProcessingRejectionTaskIsInterrupted() throws InterruptedException {
// Subsequent queued tasks run by the same wrapper should support interruption
CountDownLatch firstTaskLatch = new CountDownLatch(1);
// One permit, throws RejectedExecutionException if a second task is provided
ExecutorService delegate = new ThreadPoolExecutor(0, 1,
5, TimeUnit.SECONDS, new SynchronousQueue<>());
try {
ExecutorService view = ViewExecutor.builder(delegate).setQueueLimit(1).setMaxSize(1).build();
view.submit(() -> {
// latch used to ensure the second task is queued, otherwise the first task may complete
// before the second is submitted.
firstTaskLatch.await();
return null;
});
AtomicBoolean interrupted = new AtomicBoolean();
CountDownLatch secondTaskStartedLatch = new CountDownLatch(1);
view.execute(() -> {
secondTaskStartedLatch.countDown();
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
interrupted.set(true);
}
});
firstTaskLatch.countDown();
secondTaskStartedLatch.await();
view.shutdownNow();
assertTrue(view.awaitTermination(200, TimeUnit.MILLISECONDS));
assertTrue(interrupted.get());
} finally {
delegate.shutdown();
assertTrue(delegate.awaitTermination(1, TimeUnit.SECONDS));
}
}
@Test
public void testTestSlowExecuteInParallelWithEnqueue() throws InterruptedException {
// When a thread (threadA) submits a task to a delegate, a parallel task should not be able to
// successfully enqueue work. If an enqueue succeeded but delegate.execute did not, the queue
// would become detached from the executor, and never flush.
CountDownLatch taskLatch = new CountDownLatch(1);
// One permit, throws RejectedExecutionException if a second task is provided
Executor delegate = new Executor() {
private final AtomicBoolean firstCall = new AtomicBoolean(true);
@Override
public void execute(Runnable command) {
if (firstCall.getAndSet(false)) {
try {
taskLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
throw new RejectedExecutionException();
}
};
ExecutorService testRunner = Executors.newCachedThreadPool();
ExecutorService view = ViewExecutor.builder(delegate).setQueueLimit(1).setMaxSize(1).build();
List throwables = new CopyOnWriteArrayList<>();
for (int i = 0; i < 2; i++) {
testRunner.execute(() -> {
try {
view.execute(NullRunnable.getInstance());
throw new AssertionError("should not be reached");
} catch (Throwable t) {
throwables.add(t);
}
});
}
assertEquals(0, throwables.size());
taskLatch.countDown();
testRunner.shutdown();
assertTrue(testRunner.awaitTermination(1, TimeUnit.SECONDS));
assertEquals(2, throwables.size());
for (Throwable throwable : throwables) {
assertTrue(throwable instanceof RejectedExecutionException);
}
}
private static class QueuedExecutor implements Executor {
private final ArrayDeque executedTasks;
public QueuedExecutor(final ArrayDeque executedTasks) {
this.executedTasks = executedTasks;
}
public void execute(final Runnable command) {
executedTasks.add(command);
}
}
}
jboss-threads-3.5.0.Final/src/test/resources/ 0000775 0000000 0000000 00000000000 14303134447 0021102 5 ustar 00root root 0000000 0000000 jboss-threads-3.5.0.Final/src/test/resources/logging.properties 0000664 0000000 0000000 00000003073 14303134447 0024651 0 ustar 00root root 0000000 0000000 #
# JBoss, Home of Professional Open Source.
# Copyright 2017 Red Hat, Inc., and individual contributors
# as indicated by the @author tags.
#
# 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.
#
# Additional logger names to configure (root logger is always configured)
loggers=org.jboss.threads
# Root logger configuration
logger.level=INFO
logger.handlers=CONSOLE, FILE
logger.org.jboss.threads.level=${test.level:INFO}
# Console handler configuration
handler.CONSOLE=org.jboss.logmanager.handlers.ConsoleHandler
handler.CONSOLE.properties=autoFlush
handler.CONSOLE.level=ALL
handler.CONSOLE.autoFlush=true
handler.CONSOLE.formatter=PATTERN
# File handler configuration
handler.FILE=org.jboss.logmanager.handlers.FileHandler
handler.FILE.level=ALL
handler.FILE.properties=autoFlush,fileName
handler.FILE.autoFlush=true
handler.FILE.fileName=./target/test.log
handler.FILE.formatter=PATTERN
# Formatter pattern configuration
formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter
formatter.PATTERN.properties=pattern
formatter.PATTERN.pattern=%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n