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

hudson.util.TrackedExecutor Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2015 Hudson.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Hudson - initial API and implementation and/or initial documentation
 */

package hudson.util;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Logger;

/**
 * TrackedExecutor is an executor that can be created my clients knowing only
 * TrackedExecutor.class.getName(), e.g., by:
 * 
 * Class.forName(executorClassName).newInstance()
 * 
* But all instances of TrackedExecutor and for each instance, all threads * created by TrackedExecutor can be shut down when, e.g., Hudson is * undeployed by its container. *

Shutdown is aggressive. If a thread does not respond to interrupt * within 1 ms., the deprecated thread.stop() is called. This * can mean that sometimes thread will be stopped before it has a chance to * respond to interrupt. * @author Bob Foster */ public class TrackedExecutor implements Executor { private static final Logger LOGGER = Logger.getLogger(TrackedExecutor.class.getName()); /** Default implementation - subclasses may override */ protected ThreadFactory factory = Executors.defaultThreadFactory(); /** Executor list. Must not be weak as executors may be used and forgotten. */ private static final List executors = new ArrayList(); /** Thread list. Must be weak or could hold threads in memory. */ private final List> threads = new ArrayList>(); /** True if executor has been shut down */ private boolean shutdown; /** * Must have a null constructor if it is to be created by * hudson.util.TrackedExecutor.class.newInstance(), * as, e.g., guice does. */ public TrackedExecutor() { synchronized (executors) { executors.add(this); } } @Override public void execute(Runnable command) { String runnableClass = command.getClass().getName(); Thread thread = null; synchronized (threads) { if (shutdown) { LOGGER.severe("Attempt to run "+runnableClass+" after shutdown ignored"); throw new IllegalStateException(); } thread = factory.newThread(command); thread.setName(runnableClass); threads.add(new WeakReference(thread)); } thread.start(); } /** * Shutdown all threads started by any TrackedExecutor whose contextClassLoader * is the same as (==) the argument. TODO not certain * this precaution is necessary, as a TrackedExecutor should * always be created by a different classloader in each instance of * Hudson in a container; but it costs very little to test. * * @param contextClassLoader if not null, only threads with a matching * contextClassLoader are stopped; otherwise, all threads are stopped */ public static void shutdownAll(ClassLoader contextClassLoader) { synchronized (executors) { for (Iterator it = executors.iterator(); it.hasNext();) { TrackedExecutor executor = it.next(); executor.shutdown(contextClassLoader); if (executor.threads.isEmpty()) { it.remove(); } } } } /** * Shutdown all threads started by this executor whose contextClassLoader * is the same as (==) the argument. * @param contextClassLoader only threads whose contextClassLoader is == * are stopped */ public void shutdown(ClassLoader contextClassLoader) { synchronized (threads) { shutdown = true; for (Iterator> it = threads.iterator(); it.hasNext(); ) { Thread thread = it.next().get(); if (thread != null && (contextClassLoader == null || thread.getContextClassLoader() == contextClassLoader)) { stopThread(thread); it.remove(); } } } } /** * Subclass may override if there is a faster or better way to stop threads * for this executor. * @param thread to stop */ protected void stopThread(Thread thread) { thread.interrupt(); try { thread.join(1); } catch (InterruptedException ignore) { } if (thread.isAlive()) { thread.stop(); } } /** * Subclass for threads that are not interruptable. */ public static class NonInterruptableExecutor extends TrackedExecutor { @Override public void stopThread(Thread thread) { thread.stop(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy