All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.threads.JBossThread Maven / Gradle / Ivy

There is a newer version: 62
Show newest version
/*
 * 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.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.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");

    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; do { oldVal = stateRef.get(); if (oldVal == STATE_INTERRUPT_PENDING || oldVal == STATE_INTERRUPT_IN_PROGRESS) { // already set Messages.msg.tracef("Interrupting thread \"%s\" (already interrupted)", this); return; } else if (oldVal == STATE_INTERRUPT_DEFERRED) { newVal = STATE_INTERRUPT_PENDING; } else { newVal = STATE_INTERRUPT_IN_PROGRESS; } } while (! stateRef.compareAndSet(oldVal, newVal)); 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy