pax_global_header00006660000000000000000000000064143031344470014515gustar00rootroot0000000000000052 comment=6fae54131cf0e88823e710c2255f60e5e57b8550 jboss-threads-3.5.0.Final/000077500000000000000000000000001430313444700153225ustar00rootroot00000000000000jboss-threads-3.5.0.Final/.github/000077500000000000000000000000001430313444700166625ustar00rootroot00000000000000jboss-threads-3.5.0.Final/.github/workflows/000077500000000000000000000000001430313444700207175ustar00rootroot00000000000000jboss-threads-3.5.0.Final/.github/workflows/build.yml000066400000000000000000000012361430313444700225430ustar00rootroot00000000000000# This workflow will build a Java project with Maven # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven name: Java CI with Maven on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up JDK 11 uses: actions/setup-java@v1 with: java-version: 11 - name: Set up JDK 8 uses: actions/setup-java@v1 with: java-version: 8 - name: Build with Maven run: JAVA_HOME=$JAVA_HOME_11_X64 mvn verify -Djava8.home=$JAVA_HOME_8_X64 jboss-threads-3.5.0.Final/.gitignore000066400000000000000000000001061430313444700173070ustar00rootroot00000000000000target .classpath .project .settings .idea *.iml *.ipr *.iws .DS_Storejboss-threads-3.5.0.Final/LICENSE.txt000066400000000000000000000236761430313444700171630ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS jboss-threads-3.5.0.Final/build-include-jdk-misc000066400000000000000000000000001430313444700214520ustar00rootroot00000000000000jboss-threads-3.5.0.Final/build-test-java11000066400000000000000000000000001430313444700203700ustar00rootroot00000000000000jboss-threads-3.5.0.Final/build-test-java8000066400000000000000000000000001430313444700203160ustar00rootroot00000000000000jboss-threads-3.5.0.Final/pom.xml000066400000000000000000000162561430313444700166510ustar00rootroot00000000000000 4.0.0 org.jboss.threads jboss-threads jar 3.5.0.Final JBoss Threads org.jboss jboss-parent 36 Jira https://issues.jboss.org/browse/JBTHR Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt https://github.com/jbossas/jboss-threads https://github.com/jbossas/jboss-threads.git scm:git:git@github.com:jbossas/jboss-threads.git HEAD INFO true false false 9 --release=8 org.graalvm.nativeimage svm 19.3.1 provided * * org.jboss.logging jboss-logging-annotations 2.2.0.Final provided org.jboss.logging jboss-logging-processor 2.2.0.Final provided org.jboss.logging jboss-logging 3.4.1.Final org.wildfly.common wildfly-common 1.5.0.Final junit junit 4.13.1 test org.assertj assertj-core 3.16.1 test org.awaitility awaitility 4.0.2 test org.jboss.logmanager jboss-logmanager 2.1.14.Final test ${project.basedir} LICENSE.txt META-INF false src/main/java true **/*.properties maven-javadoc-plugin org.jboss jdk-misc 2.Final maven-compiler-plugin maven-source-plugin maven-surefire-plugin test.level ${test.level} java.util.logging.manager org.jboss.logmanager.LogManager jboss.threads.eqe.statistics ${jboss.threads.eqe.statistics} jboss.threads.eqe.unlimited-queue ${jboss.threads.eqe.unlimited-queue} jboss.threads.eqe.register-mbean ${jboss.threads.eqe.register-mbean} always jboss-threads-3.5.0.Final/src/000077500000000000000000000000001430313444700161115ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/.DS_Store000066400000000000000000000140041430313444700175730ustar00rootroot00000000000000Bud1%mainfdscboolfdscbool  @ @ @ @ E%DSDB` @ @ @jboss-threads-3.5.0.Final/src/main/000077500000000000000000000000001430313444700170355ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/main/java/000077500000000000000000000000001430313444700177565ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/main/java/org/000077500000000000000000000000001430313444700205455ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/main/java/org/jboss/000077500000000000000000000000001430313444700216655ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/000077500000000000000000000000001430313444700233175ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/ContextClassLoaderSavingRunnable.java000066400000000000000000000027041430313444700325650ustar00rootroot00000000000000/* * 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; class ContextClassLoaderSavingRunnable implements Runnable { private final ClassLoader loader; private final Runnable delegate; ContextClassLoaderSavingRunnable(final ClassLoader loader, final Runnable delegate) { this.loader = loader; this.delegate = delegate; } public void run() { final Thread currentThread = Thread.currentThread(); final ClassLoader old = JBossExecutors.getAndSetContextClassLoader(currentThread, loader); try { delegate.run(); } finally { JBossExecutors.setContextClassLoader(currentThread, old); } } public String toString() { return "Context class loader saving " + delegate.toString(); } } jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/ContextHandler.java000066400000000000000000000017431430313444700271110ustar00rootroot00000000000000package org.jboss.threads; /** * A handler for propagating context from a task submitter to a task execution. * * @param the class of the context to capture */ public interface ContextHandler { /** * The context handler which captures no context. */ ContextHandler NONE = new ContextHandler() { public Object captureContext() { return null; } public void runWith(final Runnable task, final Object context) { task.run(); } }; /** * Capture the current context from the submitting thread. * * @return the captured context */ T captureContext(); /** * Run the given task with the given captured context. The context should be cleared * when this method returns. * * @param task the task to run (not {@code null}) * @param context the context returned from {@link #captureContext()} */ void runWith(Runnable task, T context); } jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/DeclaredFieldAction.java000066400000000000000000000023761430313444700277770ustar00rootroot00000000000000/* * 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.lang.reflect.Field; import java.security.PrivilegedAction; /** */ final class DeclaredFieldAction implements PrivilegedAction { private final Class clazz; private final String fieldName; DeclaredFieldAction(final Class clazz, final String fieldName) { this.clazz = clazz; this.fieldName = fieldName; } public Field run() { try { return clazz.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { return null; } } } jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/DelegatingExecutor.java000066400000000000000000000026011430313444700277430ustar00rootroot00000000000000/* * 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; /** * An executor that simply delegates to another executor. Use instances of this class to hide extra methods on * another executor. */ class DelegatingExecutor implements Executor { private final Executor delegate; DelegatingExecutor(final Executor delegate) { this.delegate = delegate; } /** * Execute a task by passing it to the delegate executor. * * @param command the task */ public void execute(final Runnable command) { delegate.execute(command); } public String toString() { return String.format("%s -> %s", super.toString(), delegate); } } jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/DelegatingExecutorService.java000066400000000000000000000041621430313444700312700ustar00rootroot00000000000000/* * 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.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.Executor; import java.util.List; /** * An implementation of {@code ExecutorService} that delegates to the real executor, while disallowing termination. */ class DelegatingExecutorService extends AbstractExecutorService implements ExecutorService { private final Executor delegate; DelegatingExecutorService(final Executor delegate) { this.delegate = delegate; } public void execute(final Runnable command) { delegate.execute(command); } public boolean isShutdown() { // container managed executors are never shut down from the application's perspective return false; } public boolean isTerminated() { // container managed executors are never shut down from the application's perspective return false; } public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException { return false; } public void shutdown() { throw Messages.msg.notAllowedContainerManaged("shutdown"); } public List shutdownNow() { throw Messages.msg.notAllowedContainerManaged("shutdownNow"); } public String toString() { return String.format("%s -> %s", super.toString(), delegate); } } jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/DelegatingRunnable.java000066400000000000000000000020641430313444700277160ustar00rootroot00000000000000/* * 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; class DelegatingRunnable implements Runnable { private final Runnable delegate; DelegatingRunnable(final Runnable delegate) { this.delegate = delegate; } public void run() { delegate.run(); } public String toString() { return String.format("%s -> %s", super.toString(), delegate); } } jboss-threads-3.5.0.Final/src/main/java/org/jboss/threads/DelegatingScheduledExecutorService.java000066400000000000000000000041671430313444700331160ustar00rootroot00000000000000/* * 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.TimeUnit; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.Callable; /** * An implementation of {@code ScheduledExecutorService} that delegates to the real executor, while disallowing termination. */ class DelegatingScheduledExecutorService extends DelegatingExecutorService implements ScheduledExecutorService { private final ScheduledExecutorService delegate; DelegatingScheduledExecutorService(final ScheduledExecutorService delegate) { super(delegate); this.delegate = delegate; } public ScheduledFuture schedule(final Runnable command, final long delay, final TimeUnit unit) { return delegate.schedule(command, delay, unit); } public ScheduledFuture schedule(final Callable 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.java000066400000000000000000000021031430313444700277440ustar00rootroot00000000000000/* * 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.java000066400000000000000000004073321430313444700304240ustar00rootroot00000000000000/* * 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.java000066400000000000000000000045351430313444700312750ustar00rootroot00000000000000/* * 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.java000066400000000000000000000036771430313444700313040ustar00rootroot00000000000000/* * 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.java000066400000000000000000000020611430313444700312670ustar00rootroot00000000000000/* * 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.java000066400000000000000000000042131430313444700312710ustar00rootroot00000000000000/* * 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.java000066400000000000000000000020611430313444700312710ustar00rootroot00000000000000/* * 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.java000066400000000000000000000052651430313444700313030ustar00rootroot00000000000000/* * 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.java000066400000000000000000000025551430313444700313030ustar00rootroot00000000000000/* * 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.java000066400000000000000000000551741430313444700302550ustar00rootroot00000000000000/* * 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.java000066400000000000000000000024461430313444700323650ustar00rootroot00000000000000/* * 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.java000066400000000000000000000022611430313444700274550ustar00rootroot00000000000000/* * 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.java000066400000000000000000000356201430313444700271120ustar00rootroot00000000000000/* * 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.java000066400000000000000000000105221430313444700325240ustar00rootroot00000000000000/* * 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.java000066400000000000000000000670001430313444700263350ustar00rootroot00000000000000/* * 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.java000066400000000000000000000124271430313444700276700ustar00rootroot00000000000000/* * 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.java000066400000000000000000000027761430313444700262540ustar00rootroot00000000000000/* * 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.java000066400000000000000000000023551430313444700324310ustar00rootroot00000000000000/* * 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.java000066400000000000000000000167161430313444700312320ustar00rootroot00000000000000/* * 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.java000066400000000000000000000076471430313444700257470ustar00rootroot00000000000000/* * 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.java000066400000000000000000000017601430313444700265670ustar00rootroot00000000000000/* * 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.java000066400000000000000000000024521430313444700276160ustar00rootroot00000000000000/* * 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.java000066400000000000000000000021141430313444700302630ustar00rootroot00000000000000/* * 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.java000066400000000000000000000047301430313444700312020ustar00rootroot00000000000000/* * 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.java000066400000000000000000000021711430313444700270620ustar00rootroot00000000000000/* * 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.java000066400000000000000000000036751430313444700317330ustar00rootroot00000000000000/* * 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.java000066400000000000000000000072261430313444700270150ustar00rootroot00000000000000/* * 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.java000066400000000000000000000022631430313444700257210ustar00rootroot00000000000000/* * 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.java000066400000000000000000000055371430313444700256210ustar00rootroot00000000000000/* * 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.properties000066400000000000000000000013531430313444700270640ustar00rootroot00000000000000# # 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.java000066400000000000000000000112701430313444700266140ustar00rootroot00000000000000package 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.java000066400000000000000000000022531430313444700254170ustar00rootroot00000000000000/* * 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/000077500000000000000000000000001430313444700254335ustar00rootroot00000000000000ManageableThreadPoolExecutorService.java000066400000000000000000000025401430313444700352560ustar00rootroot00000000000000jboss-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.java000066400000000000000000000241641430313444700330620ustar00rootroot00000000000000/* * 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/000077500000000000000000000000001430313444700200475ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/main/java9/org/000077500000000000000000000000001430313444700206365ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/main/java9/org/jboss/000077500000000000000000000000001430313444700217565ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/main/java9/org/jboss/threads/000077500000000000000000000000001430313444700234105ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/main/java9/org/jboss/threads/JDKSpecific.java000066400000000000000000000020071430313444700263300ustar00rootroot00000000000000/* * 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/000077500000000000000000000000001430313444700170705ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/test/java/000077500000000000000000000000001430313444700200115ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/test/java/org/000077500000000000000000000000001430313444700206005ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/test/java/org/jboss/000077500000000000000000000000001430313444700217205ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/test/java/org/jboss/threads/000077500000000000000000000000001430313444700233525ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/test/java/org/jboss/threads/ArrayQueueTests.java000066400000000000000000000220241430313444700273230ustar00rootroot00000000000000package 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.java000066400000000000000000000044621430313444700313140ustar00rootroot00000000000000/* * 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.java000066400000000000000000000241001430313444700313030ustar00rootroot00000000000000package 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.java000066400000000000000000000407051430313444700332400ustar00rootroot00000000000000/* * 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.java000066400000000000000000000226771430313444700314200ustar00rootroot00000000000000/* * 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.java000066400000000000000000000157511430313444700331400ustar00rootroot00000000000000package 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.java000066400000000000000000000134571430313444700304220ustar00rootroot00000000000000/* * 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.java000066400000000000000000000303401430313444700275060ustar00rootroot00000000000000/* * 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/000077500000000000000000000000001430313444700211025ustar00rootroot00000000000000jboss-threads-3.5.0.Final/src/test/resources/logging.properties000066400000000000000000000030731430313444700246510ustar00rootroot00000000000000# # 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