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

org.openide.util.RequestProcessor Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.openide.util;

import java.security.PrivilegedAction;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.WeakHashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openide.util.lookup.Lookups;

/** Request processor is {@link Executor} (since version 7.16) capable to
 * perform asynchronous requests in a dedicated thread pool.
 * There are several use cases for RequestProcessor,
 * most of them start with creating own RequestProcessor
 * instance (which by itself is quite lightweight).
 * 

* Do something later *

* In case you want something to be done later in some background thread, * create an instance of RequestProcessor and post tasks to it. *

 * private static final RequestProcessor RP = new {@link RequestProcessor#RequestProcessor(java.lang.Class) RequestProcessor(MyClass.class)};
 * // later
 * RP.{@link #post(java.lang.Runnable,int) post(runnable, delay)}
 * 
* * The above example guarantees that there is at most one runnable being * processed in parallel. All your requests are serialized and processed * one by one. RP works here like a simple mutex. *

* In case you want more tasks to run in parallel (not very often use case) * you can specify higher * throughput via {@link #RequestProcessor(java.lang.String, int)}. Then * the RP works like a queue of requests passing through a * semaphore with predefined number of DOWN()s. *

* You can wait for your tasks to be processed by keeping a reference to the * last one and using {@link RequestProcessor.Task#waitFinished waitFinished()}: *

 * private static final RequestProcessor RP = new RequestProcessor("My tasks");
 * private volatile {@link RequestProcessor.Task} last;
 *
 * // when posting update the task
 * last = RP.{@link #post(java.lang.Runnable,int) post(runnable, delay)}
 *
 * // later wait
 * last.{@link RequestProcessor.Task#waitFinished waitFinished()}
 * 
*

* Periodic task *

* It is also possible to do something periodically. Use the {@link RequestProcessor.Task#schedule schedule} method: *

 * class Periodic implements Runnable {
 *   private static final RequestProcessor RP = new {@link RequestProcessor#RequestProcessor(java.lang.Class) RequestProcessor(Periodic.class)};
 *   private final RequestProcessor.Task CLEANER = RP.{@link #create(java.lang.Runnable) create(this)};
 *   public void run() {
 *     doTheWork();
 *     CLEANER.schedule(DELAY);
 *   }
 * }
 * 
* Please think twice before using such periodic * background activity. It is generally considered evil if some code runs * without any user action. Your code shall respect the application's state, * and for example when the application is minimized, do nothing. *

* Sliding task *

* Often you want to perform an update of your object internals * based on changes in some model. However your update may be costly * and you want to do it just once, regardless of how many changes are * reported by the model. This can be achieved with a sliding task: *

 * class Updater implements PropertyChangeListener, Runnable {
 *   private static final RequestProcessor RP = new {@link RequestProcessor#RequestProcessor(java.lang.Class) RequestProcessor(Updater.class)};
 *   private final RequestProcessor.Task UPDATE = RP.{@link #create(java.lang.Runnable) create(this)};
 *
 *   public void propertyChange(PropertyChangeEvent ev) {
 *     UPDATE.{@link RequestProcessor.Task#schedule schedule(1000)};
 *   }
 *
 *   public void run() {
 *     doTheWork();
 *   }
 * }
 * 
* The above code coalesces all events that arrive in 1s and for all of them * does doTheWork just once. * *

* Interruption of tasks *

* Since version 6.3 there is a conditional support for interruption of long running tasks. * There always was a way to cancel not yet running task using {@link RequestProcessor.Task#cancel } * but if the task's run() method was already running, one was out of luck. * Since version 6.3 * the thread running the task is interrupted and the Runnable can check for that * and terminate its execution sooner. In the runnable one shall check for * thread interruption (done from {@link RequestProcessor.Task#cancel }) and * if true, return immediately as in this example: *

 * private static final RequestProcessor RP = new {@link #RequestProcessor(String,int,boolean) RequestProcessor("Interruptible", 1, true)};
 * public void run () {
 *     while (veryLongTimeLoop) {
 *       doAPieceOfIt ();
 *
 *       if (Thread.interrupted ()) return;
 *     }
 * }
 * 
*

* Since org.openide.util, implements * {@link java.util.concurrent.ScheduledExecutorService} * @author Petr Nejedly, Jaroslav Tulach, Tim Boudreau */ public final class RequestProcessor implements ScheduledExecutorService { static { Processor.class.hashCode(); // ensure loaded; cf. FELIX-2128 } /** the static instance for users that do not want to have own processor */ private static final RequestProcessor DEFAULT = new RequestProcessor(); /** logger */ private static final Logger logger = Logger.getLogger("org.openide.util.RequestProcessor"); // NOI18N /** the static instance for users that do not want to have own processor */ private static final RequestProcessor UNLIMITED; /** The counter for automatic naming of unnamed RequestProcessors */ private static int counter = 0; private static final boolean SLOW; static { boolean slow = false; assert slow = true; SLOW = slow; // 55: a conservative value, just for case of misuse UNLIMITED = new RequestProcessor("Default RequestProcessor", 55, false, SLOW, SLOW ? 3 : 0); // NOI18N } /** The name of the RequestProcessor instance */ String name; /** If the RP was stopped, this variable will be set, every new post() * will throw an exception and no task will be processed any further */ volatile boolean stopped = false; /** Flag indicating that awaiting tasks should be executed although * RP is in stopped state (rejecting new tasks) */ volatile boolean finishAwaitingTasks = false; /** The lock covering following five fields. They should be accessed * only while having this lock held. */ private final Object processorLock = new Object(); /** The set holding all the Processors assigned to this RequestProcessor */ private final HashSet processors = new HashSet(); /** Actualy the first item is pending to be processed. * Can be accessed/trusted only under the above processorLock lock. * If null, nothing is scheduled and the processor is not running. * @GuardedBy("processorLock") */ private final SortedSet queue = new TreeSet(); /** The maximal number of processors that can perform the requests sent * to this RequestProcessors. If 1, all the requests are serialized. */ private int throughput; /** mapping of classes executed in parallel */ private Map,AtomicInteger> inParallel; /** Warn if there is parallel execution */ private final int warnParallel; /** support for interrupts or not? */ private boolean interruptThread; /** fill stacktraces when task is posted? */ private boolean enableStackTraces; /** Creates new RequestProcessor with automatically assigned unique name. */ public RequestProcessor() { this(null, 1); } /** Creates a new named RequestProcessor with throughput 1. * @param name the name to use for the request processor thread */ public RequestProcessor(String name) { this(name, 1); } /** Convenience constructor for a new RequestProcessor with throughput 1. * Typical usage is: *

     * class MyClass {
     *   private static final RequestProcessor RP = new RequestProcessor(MyClass.class);
     * 
     * }
     * 
* Behaves as new RequestProcessor(MyClass.class.getName()). * * @param forClass name of this class gives name for the processor threads * @since 8.6 */ public RequestProcessor(Class forClass) { this(forClass.getName()); } /** Creates a new named RequestProcessor with defined throughput. * @param name the name to use for the request processor thread * @param throughput the maximal count of requests allowed to run in parallel * * @since OpenAPI version 2.12 */ public RequestProcessor(String name, int throughput) { this(name, throughput, false); } /** Creates a new named RequestProcessor with defined throughput which * can support interruption of the thread the processor runs in. * There always was a way how to cancel not yet running task using {@link RequestProcessor.Task#cancel } * but if the task was already running, one was out of luck. With this * constructor one can create a {@link RequestProcessor} which threads * thread running tasks are interrupted and the Runnable can check for that * and terminate its execution sooner. In the runnable one shall check for * thread interruption (done from {@link RequestProcessor.Task#cancel }) and * if true, return immediatelly as in this example: *
     * public void run () {
     *     while (veryLongTimeLook) {
     *       doAPieceOfIt ();
     *
     *       if (Thread.interrupted ()) return;
     *     }
     * }
     * 
* * @param name the name to use for the request processor thread * @param throughput the maximal count of requests allowed to run in parallel * @param interruptThread true if {@link RequestProcessor.Task#cancel} shall interrupt the thread * * @since 6.3 */ public RequestProcessor(String name, int throughput, boolean interruptThread) { this(name, throughput, interruptThread, SLOW); } /** Creates a new named RequestProcessor that allows to disable stack trace filling. * By default, when assertions are on, each task posted on RequestProcessor stores * the stack trace at the time of posting. When an exception is later thrown from the task, * it allows to print not only stack trace of the task but also stack trace of the code that posted it. * However this may be a performance bottleneck in cases when hundreds of short task are scheduled. * This constructor then allows to create RequestProcessor which never stores stack traces * at the time of posting. *

* See constructor {@link #RequestProcessor(String, int, boolean)} for details of interruptThread * parameter. *

* @param name the name to use for the request processor thread * @param throughput the maximal count of requests allowed to run in parallel * @param interruptThread true if {@link RequestProcessor.Task#cancel} shall interrupt the thread * @param enableStackTraces false when request processor should not fill stack traces when task is posted. * Default is true when assertions are enabled, false otherwise. * @since 7.24 */ public RequestProcessor(String name, int throughput, boolean interruptThread, boolean enableStackTraces) { this(name, throughput, interruptThread, enableStackTraces, 0); } private RequestProcessor(String name, int throughput, boolean interruptThread, boolean enableStackTraces, int warnParallel) { this.throughput = throughput; this.name = (name != null) ? name : ("OpenIDE-request-processor-" + (counter++)); this.interruptThread = interruptThread; this.enableStackTraces = enableStackTraces; this.warnParallel = warnParallel; } /** Warning: The instance of RequestProcessor returned * by this method has very bad performance side effects, don't use unless * you understand all implications! *

* This is the getter for the shared instance of the RequestProcessor. * This instance is shared by anybody who * needs a way of performing sporadic asynchronous work. *

* The problem of this method lays exactly in the definition of sporadic. * Often one needs to process something at some sporadic moment, * but, for example * due to storm of events, one needs to execute more than one tasks * at the same sporadic moment. In this situation * using {@link #getDefault()} is horribly inefficient. All such tasks * would be processed in parallel, allocating their own execution threads * (up to 50). As the price per one thread is estimated to 1MB on common * systems, you shall think twice whether you want to increase the memory * consumption of your application so much at these sporadic moments. *

* There is a runtime detection of the parallel misuse of this * method since version 8.3. It is activated only in development mode * (when executed with assertions on) and prints warning into log * whenever there are more than three same tasks running in parallel. * In case you see such warning, or in case you are in doubts consider * creation of your own, private, single throughput processor: *

     * class YourClass {
     *   private static final RequestProcessor RP = new {@link RequestProcessor#RequestProcessor(java.lang.Class) RequestProcessor(YourClass.class)};
     * }
     * 
* Such private field is lightweight and guarantees that all your tasks * will be processed sequentially, one by one. Just don't forget to make * the field static! *

* Tasks posted to this instance may be canceled until they start their * execution. If a there is a need to cancel a task while it is running * a seperate request processor needs to be created via * {@link #RequestProcessor(String, int, boolean)} constructor. * * @return an instance of RequestProcessor that is capable of performing * "unlimited" (currently limited to 50, just for case of misuse) number * of requests in parallel. * * @see #RequestProcessor(String, int, boolean) * @see RequestProcessor.Task#cancel * * @since version 2.12 */ public static RequestProcessor getDefault() { return UNLIMITED; } /** Implements contract of {@link Executor}. * Simply delegates to {@link #post(java.lang.Runnable)}. * @param command the runnable to execute * @since 7.16 */ @Override public void execute(Runnable command) { post(command); } /** This methods asks the request processor to start given * runnable immediately. The default priority is {@link Thread#MIN_PRIORITY}. * * @param run class to run * @return the task to control the request */ public Task post(Runnable run) { return post(run, 0, Thread.MIN_PRIORITY); } /** This methods asks the request processor to start given * runnable after timeToWait milliseconds. The default priority is {@link Thread#MIN_PRIORITY}. * * @param run class to run * @param timeToWait to wait before execution * @return the task to control the request */ public Task post(final Runnable run, int timeToWait) { return post(run, timeToWait, Thread.MIN_PRIORITY); } /** This methods asks the request processor to start given * runnable after timeToWait milliseconds. Given priority is assigned to the * request.

* For request relaying please consider: *

    *    post(run, timeToWait, Thread.currentThread().getPriority());
    * 
* * @param run class to run * @param timeToWait to wait before execution * @param priority the priority from {@link Thread#MIN_PRIORITY} to {@link Thread#MAX_PRIORITY} * @return the task to control the request */ public Task post(final Runnable run, int timeToWait, int priority) { RequestProcessor.Task task = new Task(run, priority); task.schedule(timeToWait); return task; } /** Creates request that can be later started by setting its delay. * The request is not immediatelly put into the queue. It is planned after * setting its delay by schedule method. By default the initial state of * the task is !isFinished() so doing waitFinished() will * block on and wait until the task is scheduled. * * @param run action to run in the process * @return the task to control execution of given action */ public Task create(Runnable run) { return create(run, false); } /** Creates request that can be later started by setting its delay. * The request is not immediatelly put into the queue. It is planned after * setting its delay by schedule method. * * @param run action to run in the process * @param initiallyFinished should the task be marked initially finished? If * so the {@link Task#waitFinished} on the task will succeeded immediatelly even * the task has not yet been {@link Task#schedule}d. * @return the task to control execution of given action * @since 6.8 */ public Task create(Runnable run, boolean initiallyFinished) { Task t = new Task(run); t.markCreated(); if (initiallyFinished) { t.notifyFinished(); } return t; } /** Tests if the current thread is request processor thread. * This method could be used to prevent the deadlocks using * waitFinished method. Any two tasks created * by request processor must not wait for themself. * * @return true if the current thread is request processor * thread, otherwise false */ public boolean isRequestProcessorThread() { Thread c = Thread.currentThread(); if (c instanceof Processor) { Processor p = (Processor)c; return p.procesing == this; } return false; } /** Stops processing of runnables processor. * The currently running runnable is finished and no new is started. */ public void stop() { if ((this == UNLIMITED) || (this == DEFAULT)) { throw new IllegalArgumentException("Can't stop shared RP's"); // NOI18N } synchronized (processorLock) { stopped = true; for (Processor p : processors) { p.interrupt(); } } } // // Static methods communicating with default request processor // /** This methods asks the request processor to start given * runnable after timeToWait milliseconds. The default priority is {@link Thread#MIN_PRIORITY}. * * @param run class to run * @return the task to control the request * * @deprecated Sharing of one singlethreaded RequestProcessor * among different users and posting even blocking requests is inherently * deadlock-prone. See {@link RequestProcessor use cases}. */ @Deprecated public static Task postRequest(Runnable run) { return DEFAULT.post(run); } /** This methods asks the request processor to start given * runnable after timeToWait milliseconds. * The default priority is {@link Thread#MIN_PRIORITY}. * * @param run class to run * @param timeToWait to wait before execution * @return the task to control the request * * @deprecated Sharing of one singlethreaded RequestProcessor * among different users and posting even blocking requests is inherently * deadlock-prone. See {@link RequestProcessor use cases}. */ @Deprecated public static Task postRequest(final Runnable run, int timeToWait) { return DEFAULT.post(run, timeToWait); } /** This methods asks the request processor to start given * runnable after timeToWait milliseconds. Given priority is assigned to the * request. * @param run class to run * @param timeToWait to wait before execution * @param priority the priority from {@link Thread#MIN_PRIORITY} to {@link Thread#MAX_PRIORITY} * @return the task to control the request * * @deprecated Sharing of one singlethreaded RequestProcessor * among different users and posting even blocking requests is inherently * deadlock-prone. See {@link RequestProcessor use cases}. */ @Deprecated public static Task postRequest(final Runnable run, int timeToWait, int priority) { return DEFAULT.post(run, timeToWait, priority); } /** Creates request that can be later started by setting its delay. * The request is not immediately put into the queue. It is planned after * setting its delay by setDelay method. * @param run action to run in the process * @return the task to control execution of given action * * @deprecated Sharing of one singlethreaded RequestProcessor * among different users and posting even blocking requests is inherently * deadlock-prone. See {@link RequestProcessor use cases}. */ @Deprecated public static Task createRequest(Runnable run) { return DEFAULT.create(run); } /** Logger for the error manager. */ static Logger logger() { return logger; } //------------------------------------------------------------------------------ // The pending queue management implementation //------------------------------------------------------------------------------ /** Place the Task to the queue of pending tasks for immediate processing. * If there is no other Task planned, this task is immediately processed * in the Processor. */ void enqueue(Item item) { Logger em = logger(); boolean loggable = em.isLoggable(Level.FINE); boolean wasNull; synchronized (processorLock) { wasNull = item.getTask() == null; if (!wasNull) { prioritizedEnqueue(item); if (processors.size() < throughput) { Processor proc = Processor.get(); processors.add(proc); if (proc.getContextClassLoader() != item.ctxLoader) { if (loggable) { // item classloader may be null, if the item was posted from the Finalizer thread ClassLoader itemLoader = item.ctxLoader; ClassLoader procLoader = proc.getContextClassLoader(); em.log(Level.FINE, "Setting ctxLoader for old:{0} loader:{1}#{2} new:{3} loader:{4}#{5}", new Object[] { proc.getName(), procLoader == null ? "" : procLoader.getClass().getName(), procLoader == null ? "-" : Integer.toHexString(System.identityHashCode(proc.getContextClassLoader())), name, itemLoader == null ? "" : item.ctxLoader.getClass().getName(), itemLoader == null ? "-" : Integer.toHexString(System.identityHashCode(item.ctxLoader)) } ); } proc.setContextClassLoader(item.ctxLoader); } proc.setName(name); proc.attachTo(this); } } } if (loggable) { if (wasNull) { em.log(Level.FINE, "Null task for item {0}", item); // NOI18N } else { em.log(Level.FINE, "Item enqueued: {0} status: {1}", new Object[]{item.action, item.enqueued}); // NOI18N } } } // call it under queue lock i.e. processorLock private void prioritizedEnqueue(Item item) { getQueue().add(item); item.enqueued = true; } Task askForWork(Processor worker, String debug, Lookup[] lkp) { if (getQueue().isEmpty() || (stopped && !finishAwaitingTasks)) { // no more work in this burst, return him processors.remove(worker); Processor.put(worker, debug); return null; } else { // we have some work for the worker, pass it Item i = getQueue().first(); getQueue().remove(i); Task t = i.getTask(); lkp[0] = i.current; i.clear(worker); return t; } } /** * {@inheritDoc} * @throws IllegalStateException if called on the * {@linkplain #getDefault default request processor} * @since org.openide.util 8.2 */ @Override public void shutdown() { if (this == UNLIMITED) { throw new IllegalStateException ("Cannot shut down the default " + //NOI18N "request processor"); //NOI18N } stopped = true; finishAwaitingTasks = true; } /** * {@inheritDoc} * @throws IllegalStateException if called on the * {@linkplain #getDefault default request processor} * @since org.openide.util 8.2 */ @Override public List shutdownNow() { if (this == UNLIMITED) { throw new IllegalStateException ("Cannot shut down the default " + //NOI18N "request processor"); //NOI18N } //XXX more aggressive shutdown? stop(); synchronized (processorLock) { List result = new ArrayList(getQueue().size()); for (Item item : getQueue()) { Task task = item.getTask(); if (task != null && task.run != null) { Runnable r = task.run; if (r instanceof RunnableWrapper) { Runnable other = ((RunnableWrapper) r).getRunnable(); r = other == null ? r : other; } result.add(r); } } return result; } } /** * {@inheritDoc} * @since org.openide.util 8.2 */ @Override public boolean isShutdown() { return stopped; } /** * {@inheritDoc} * @since org.openide.util 8.2 */ @Override public boolean isTerminated() { boolean result = true; Set set = collectProcessors(new HashSet()); for (Processor p : set) { if (p.isAlive() && p.belongsTo(this)) { result = false; break; } } return result; } /** * {@inheritDoc} * @since org.openide.util 8.2 */ @Override public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { Parameters.notNull("unit", unit); //NOI18N long timeoutMillis = TimeUnit.MILLISECONDS.convert(timeout, unit); boolean result = stopped; long doneTime = System.currentTimeMillis() + timeoutMillis; Set procs = new HashSet(); outer: do { procs = collectProcessors(procs); if (procs.isEmpty()) { return true; } for (Processor p : procs) { long remaining = doneTime - System.currentTimeMillis(); if (remaining <= 0) { result = collectProcessors(procs).isEmpty(); break outer; } if (p.belongsTo(this)) { p.join(remaining); } result = !p.isAlive() || !p.belongsTo(this); } procs.clear(); } while (!procs.isEmpty()); return result; } private Set collectProcessors (Set procs) { procs.clear(); synchronized (processorLock) { for (Processor p : processors) { if (p.belongsTo(this)) { procs.add(p); } } } return procs; } /** * {@inheritDoc} *

* Note: If the passed {@link java.util.concurrent.Callable} implements * {@link org.openide.util.Cancellable}, then that object's {@link org.openide.util.Cancellable#cancel()} * method will be called if {@link java.util.concurrent.Future#cancel(boolean)} is invoked. * If Cancellable.cancel() returns false, then the job will not be * cancelled. * @since org.openide.util 8.2 */ @Override public Future submit(Callable task) { Parameters.notNull("task", task); //NOI18N if (stopped) { throw new RejectedExecutionException("Request Processor already " + //NOI18N "stopped"); //NOI18N } RPFutureTask result = new RPFutureTask(task); Task t = create(result); result.setTask(t); t.schedule(0); return result; } /** * {@inheritDoc} * Note: If the passed {@link java.lang.Runnable} implements * {@link org.openide.util.Cancellable}, then that object's {@link org.openide.util.Cancellable#cancel()} * method will be called if {@link java.util.concurrent.Future#cancel(boolean)} is invoked. * If Cancellable.cancel() returns false, then the job will not be * cancelled. * @since org.openide.util 8.2 */ @Override public Future submit(Runnable task, T predefinedResult) { Parameters.notNull("task", task); //NOI18N if (stopped) { throw new RejectedExecutionException("Request Processor already " + //NOI18N "stopped"); //NOI18N } RPFutureTask result = new RPFutureTask(task, predefinedResult); Task t = create(result); result.setTask(t); t.schedule(0); return result; } /** * {@inheritDoc} *

* Note: If the passed {@link java.lang.Runnable} implements * {@link org.openide.util.Cancellable}, then that object's {@link org.openide.util.Cancellable#cancel()} * method will be called if {@link java.util.concurrent.Future#cancel(boolean)} is invoked. * If Cancellable.cancel() returns false, then the job will not be * cancelled. * @since org.openide.util 8.2 */ @Override public Future submit(Runnable task) { return this.submit (task, null); } /** * {@inheritDoc} * @since org.openide.util 8.2 */ @Override public List> invokeAll(Collection> tasks) throws InterruptedException { Parameters.notNull("tasks", tasks); //NOI18N List> result = new ArrayList>(tasks.size()); CountDownLatch wait = new CountDownLatch(tasks.size()); for (Callable c : tasks) { if (c == null) { throw new NullPointerException ("Contains null tasks: " + //NOI18N tasks); } Callable delegate = new WaitableCallable(c, wait); result.add (submit(delegate)); } wait.await(); return result; } /** * {@inheritDoc} *

* Executes the given tasks, returning a list of Futures holding their * status and results when all complete or the timeout expires, whichever * happens first. * @since org.openide.util 8.2 */ @Override public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { Parameters.notNull("unit", unit); //NOI18N Parameters.notNull("tasks", tasks); //NOI18N CountDownLatch wait = new CountDownLatch(tasks.size()); List> result = new ArrayList>(tasks.size()); for (Callable c : tasks) { if (c == null) { throw new NullPointerException ("Contains null tasks: " + tasks); //NOI18N } Callable delegate = new WaitableCallable(c, wait); result.add (submit(delegate)); } if (!wait.await(timeout, unit)) { for (Future f : result) { RPFutureTask ft = (RPFutureTask) f; ft.cancel(true); } } return result; } /** * {@inheritDoc} *

* Executes the given tasks, returning the result of one which has * completed and cancelling any incomplete tasks. * @since org.openide.util 8.2 */ @Override public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { Parameters.notNull("tasks", tasks); //NOI18N CountDownLatch wait = new CountDownLatch(1); List> result = new ArrayList>(tasks.size()); AtomicReference ref = new AtomicReference(); try { for (Callable c : tasks) { if (c == null) { throw new NullPointerException ("Contains null tasks: " + //NOI18N tasks); } Callable delegate = new WaitableCallable(c, ref, wait); result.add (submit(delegate)); } wait.await(); } finally { for (Future f : result) { RPFutureTask ft = (RPFutureTask) f; ft.cancel(true); } } return ref.get(); } /** * {@inheritDoc} *

* Executes the given tasks, returning a list of Futures holding their * status and results when all complete or the timeout expires, whichever * happens first. * @param The result type * @param tasks A collection of callables * @param timeout The maximum time to wait for completion, in the specified time units * @param unit The time unit * @return A list of futures * @throws InterruptedException if the timeout expires or execution is interrupted * @since org.openide.util 8.2 */ @Override public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { Parameters.notNull("unit", unit); //NOI18N Parameters.notNull("tasks", tasks); //NOI18N CountDownLatch wait = new CountDownLatch(1); List> result = new ArrayList>(tasks.size()); AtomicReference ref = new AtomicReference(); try { for (Callable c : tasks) { if (c == null) { throw new NullPointerException ("Contains null tasks: " + //NOI18N tasks); } Callable delegate = new WaitableCallable(c, ref, wait); result.add (submit(delegate)); } wait.await(timeout, unit); } finally { for (Future f : result) { RPFutureTask ft = (RPFutureTask) f; ft.cancel(true); } } return ref.get(); } /** * {@inheritDoc} * @since org.openide.util 8.2 */ @Override public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { Parameters.notNull("command", command); //NOI18N Parameters.notNull("unit", unit); //NOI18N if (delay < 0) { throw new IllegalArgumentException ("Negative delay: " + delay); } if (stopped) { throw new RejectedExecutionException("Request Processor already stopped"); //NOI18N } long delayMillis = TimeUnit.MILLISECONDS.convert(delay, unit); ScheduledRPFutureTask result = new ScheduledRPFutureTask(command, null, delayMillis); Task t = create(result); result.setTask(t); t.schedule(delayMillis); return result; } /** * {@inheritDoc} * @since org.openide.util 8.2 */ @Override public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { Parameters.notNull("unit", unit); //NOI18N Parameters.notNull("callable", callable); //NOI18N if (delay < 0) { throw new IllegalArgumentException ("Negative delay: " + delay); } if (stopped) { throw new RejectedExecutionException("Request Processor already " + //NOI18N "stopped"); //NOI18N } long delayMillis = TimeUnit.MILLISECONDS.convert(delay, unit); ScheduledRPFutureTask result = new ScheduledRPFutureTask(callable, delayMillis); Task t = create(result); result.setTask(t); t.schedule(delayMillis); return result; } /** * {@inheritDoc} *

* Schedules a runnable which will run with a given frequency, regardless * of how long execution takes, with the exception that if execution takes * longer than the specified delay, execution will be delayed but will * never be run on two threads concurrently. * @since org.openide.util 8.2 */ @Override public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { return scheduleFixed(command, initialDelay, period, unit, false); } /** * {@inheritDoc} *

* Schedules a runnable which will run repeatedly after the specified initial * delay, with the specified delay between the completion of one run and * the start of the next. * @since org.openide.util 8.2 */ @Override public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { return scheduleFixed(command, initialDelay, delay, unit, true); } private ScheduledFuture scheduleFixed (Runnable command, long initialDelay, long period, TimeUnit unit, boolean fixedDelay) { Parameters.notNull("unit", unit); //NOI18N Parameters.notNull("command", command); //NOI18N if (period < 0) { throw new IllegalArgumentException ("Negative delay: " + period); //NOI18N } if (initialDelay < 0) { throw new IllegalArgumentException ("Negative initialDelay: " //NOI18N + initialDelay); } if (stopped) { throw new RejectedExecutionException("Request Processor already " + //NOI18N "stopped"); //NOI18N } long initialDelayMillis = TimeUnit.MILLISECONDS.convert(initialDelay, unit); long periodMillis = TimeUnit.MILLISECONDS.convert(period, unit); TaskFutureWrapper wrap = fixedDelay ? new FixedDelayTask(command, initialDelayMillis, periodMillis) : new FixedRateTask(command, initialDelay, periodMillis); Task t = create(wrap); wrap.t = t; t.cancelled = wrap.cancelled; t.schedule (initialDelayMillis); return wrap; } private SortedSet getQueue() { assert Thread.holdsLock(processorLock); return queue; } /** * @return a top level ThreadGroup. The method ensures that even Processors * created by internal execution will survive the end of the task. */ private static final TopLevelThreadGroup TOP_GROUP = new TopLevelThreadGroup(); private static final class TopLevelThreadGroup implements PrivilegedAction { public ThreadGroup getTopLevelThreadGroup() { /* There used to be a workaround for https://bz.apache.org/netbeans/show_bug.cgi?id=184494 here, relating to Applet and JNLP environments. It was removed, since these environments are never used anymore. */ return java.security.AccessController.doPrivileged(this); } @Override public ThreadGroup run() { ThreadGroup current = Thread.currentThread().getThreadGroup(); while (current.getParent() != null) { current = current.getParent(); } return current; } } private abstract static class TaskFutureWrapper implements ScheduledFuture, Runnable, RunnableWrapper { volatile Task t; protected final Runnable toRun; protected final long initialDelay; protected final long period; final AtomicBoolean cancelled = new AtomicBoolean(); TaskFutureWrapper(Runnable run, long initialDelay, long period) { this.toRun = run; this.initialDelay = initialDelay; this.period = period; } @Override public final Runnable getRunnable() { return toRun; } @Override public int compareTo(Delayed o) { long other = o.getDelay(TimeUnit.MILLISECONDS); long ours = getDelay(TimeUnit.MILLISECONDS); //Might overflow on, say, ms compared to Long.MAX_VALUE, TimeUnit.DAYS return (int) (ours - other); } @Override public boolean cancel(boolean mayInterruptIfRunning) { boolean result = true; if (toRun instanceof Cancellable) { result = ((Cancellable) toRun).cancel(); } if (result) { //will invoke cancelled.set(true) result = t.cancel(mayInterruptIfRunning); } return result; } @Override public boolean isCancelled() { return cancelled.get(); } @Override public boolean isDone() { return cancelled.get() || t.isFinished(); } @Override public Void get() throws InterruptedException, ExecutionException { if (cancelled.get()) { throw new CancellationException(); } t.waitFinished(); if (cancelled.get()) { throw new CancellationException(); } return null; } @Override public Void get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (cancelled.get()) { throw new CancellationException(); } long millis = TimeUnit.MILLISECONDS.convert(timeout, unit); t.waitFinished(millis); if (cancelled.get()) { throw new CancellationException(); } return null; } } private static final class FixedRateTask extends TaskFutureWrapper { private final Object runLock = new Object(); private final Object timeLock = new Object(); //must be accessed holding timeLock private int runCount; private long nextRunTime; private long start = Long.MIN_VALUE; volatile boolean firstRun = true; FixedRateTask (Runnable run, long initialDelay, long period) { super (run, initialDelay, period); } @Override public void run() { if (firstRun) { synchronized (timeLock) { start = System.currentTimeMillis(); firstRun = false; } } try { synchronized(runLock) { toRun.run(); } } catch (RuntimeException e) { cancel(true); throw e; } reschedule(); } private void reschedule() { //All access to nextRunTime & runCount under lock. long interval; synchronized (timeLock) { nextRunTime = start + (initialDelay + period * runCount); runCount++; interval = Math.max(0, nextRunTime - System.currentTimeMillis()); } boolean canContinue = !cancelled.get() && !Thread.currentThread().isInterrupted(); if (canContinue) { t.schedule(interval); } } @Override public long getDelay(TimeUnit unit) { if (isCancelled()) { return Long.MAX_VALUE; } long delay; synchronized (timeLock) { delay = Math.min(0, nextRunTime - System.currentTimeMillis()); } return unit.convert(delay, TimeUnit.MILLISECONDS); } } private static final class FixedDelayTask extends TaskFutureWrapper { private final AtomicLong nextRunTime = new AtomicLong(); FixedDelayTask(Runnable run, long initialDelay, long period) { super (run, initialDelay, period); } @Override public long getDelay(TimeUnit unit) { long next = nextRunTime.get(); return unit.convert (next - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } @Override public void run() { if (!fini()) { toRun.run(); } if (!fini()) { reschedule(); } } private boolean fini() { boolean result = cancelled.get() || Thread.currentThread().isInterrupted(); return result; } private void reschedule() { nextRunTime.set(System.currentTimeMillis() + period); if (!fini()) { t.schedule((int) period); } } } private interface RunnableWrapper { Runnable getRunnable(); } private static final class WaitableCallable implements Callable, Cancellable { private final CountDownLatch countdown; private final Callable delegate; private final AtomicReference ref; private volatile boolean failed; WaitableCallable(Callable delegate, CountDownLatch countdown) { this (delegate, null, countdown); } WaitableCallable(Callable delegate, AtomicReference ref, CountDownLatch countdown) { this.delegate = delegate; this.countdown = countdown; this.ref = ref; } boolean failed() { return failed; } @Override public T call() throws Exception { try { T result = delegate.call(); if (ref != null) { ref.set(result); } return result; } catch (RuntimeException e) { failed = true; throw e; } catch (Error e) { failed = true; throw e; } finally { if (!failed || ref == null) { countdown.countDown(); } } } @Override public boolean cancel() { return delegate instanceof Cancellable ? ((Cancellable) delegate).cancel() : true; } } private static class RPFutureTask extends FutureTask implements RunnableWrapper { protected volatile Task task; private final Runnable runnable; private final Cancellable cancellable; RPFutureTask(Callable c) { super (c); this.runnable = null; this.cancellable = c instanceof Cancellable ? (Cancellable) c : null; } RPFutureTask(Runnable r, T result) { super (r, result); this.runnable = r; this.cancellable = r instanceof Cancellable ? (Cancellable) r : null; } void setTask(Task task) { this.task = task; } RPFutureTask(Callable c, T predefinedResult) { this (c); set(predefinedResult); } @Override public boolean cancel(boolean mayInterruptIfRunning) { boolean result = cancellable == null ? true : cancellable.cancel(); if (result) { boolean taskCancelled = task.cancel(); boolean superCancel = super.cancel(mayInterruptIfRunning); //must call both! result = taskCancelled && superCancel; } return result; } @Override public Runnable getRunnable() { return this.runnable; } } private static final class ScheduledRPFutureTask extends RPFutureTask implements ScheduledFuture { protected final long delayMillis; ScheduledRPFutureTask(Callable c, long delayMillis) { super (c); this.delayMillis = delayMillis; } ScheduledRPFutureTask(Runnable r, T result, long delayMillis) { super (r, result); this.delayMillis = delayMillis; } @Override public long getDelay(TimeUnit unit) { return unit.convert(delayMillis, TimeUnit.MILLISECONDS); } @Override public int compareTo(Delayed o) { //Can overflow, if one delay is, say, days, and the other, microseconds long otherDelayMillis = o.getDelay(TimeUnit.MILLISECONDS); return (int) (delayMillis - otherDelayMillis); } } /** * The task describing the request sent to the processor. * Cancellable since 4.1. */ public final class Task extends org.openide.util.Task implements Cancellable { private Item item; private int priority = Thread.MIN_PRIORITY; private long time = 0; private Thread lastThread = null; private AtomicBoolean cancelled; /** @param run runnable to start */ Task(Runnable run) { super(run); } /** @param run runnable to start * @param priority the priorty of the task */ Task(Runnable run, int priority) { super(run); if (priority < Thread.MIN_PRIORITY) { priority = Thread.MIN_PRIORITY; } if (priority > Thread.MAX_PRIORITY) { priority = Thread.MAX_PRIORITY; } this.priority = priority; } @Override public void run() { try { synchronized (Task.class) { while (lastThread != null) { try { Task.class.wait(); } catch (InterruptedException ex) { // OK wait again } } lastThread = Thread.currentThread(); } notifyRunning(); run.run(); } finally { Item scheduled = this.item; if (scheduled != null && !scheduled.isNew() && scheduled.getTask() == this) { // do not mark as finished, we are scheduled for future } else { notifyFinished(); } synchronized (Task.class) { lastThread = null; Task.class.notifyAll(); } } } /** Getter for amount of millis till this task * is started. * @return amount of millis */ public int getDelay() { long delay = time - System.currentTimeMillis(); if (delay < 0L) { return 0; } if (delay > (long) Integer.MAX_VALUE) { return Integer.MAX_VALUE; } return (int) delay; } /** (Re-)schedules a task to run in the future. * If the task has not been run yet, it is postponed to * the new time. If it has already run and finished, it is scheduled * to be started again. If it is currently running, it is nevertheless * left to finish, and also scheduled to run again. * @param delay time in milliseconds to wait (starting from now) */ public void schedule(int delay) { schedule((long) delay); } void schedule(long delay) { if (stopped) { return; } time = System.currentTimeMillis() + delay; final Item localItem; synchronized (processorLock) { if (cancelled != null) { cancelled.set(false); } notifyRunning(); if (item != null) { item.clear(null); } item = enableStackTraces ? new SlowItem(this, RequestProcessor.this) : new FastItem(this, RequestProcessor.this); localItem = item; } if (delay == 0) { // Place it to pending queue immediatelly enqueue(localItem); } else { // Post the starter TickTac.schedule(localItem, delay); } } private void markCreated() { assert item == null; item = new CreatedItem(this, null); } /** Removes the task from the queue. * * @return true if the task has been removed from the queue, * false it the task has already been processed */ @Override public boolean cancel() { return cancelOrNew(false); } private boolean cancelOrNew(boolean canBeNew) { synchronized (processorLock) { boolean success; if (item == null) { success = false; } else { Processor p = item.getProcessor(); success = item.clearOrNew(canBeNew); if (p != null) { p.interruptTask(this, RequestProcessor.this); item = null; } if (success) { item = null; } } if (success) { notifyFinished(); // mark it as finished } return success; } } /** * Implementation of cancel for use with Future objects, to guarantee * that the thread will be interrupted, no matter what the setting * on the owning RP. * @param interrupt If true, the thread should be interrupted * @return true if cancellation occurred */ boolean cancel (boolean interrupt) { synchronized (processorLock) { if (cancelled != null) { boolean wasCancelled = !cancelled.getAndSet(true); if (wasCancelled) { return false; } } boolean success; if (item == null) { success = false; } else { Processor p = item.getProcessor(); success = item.clear(null); if (p != null) { if (interrupt) { success = p.interrupt(this, RequestProcessor.this); } else { //despite its name, will not actually interrupt //unless the RP specifies that it should p.interruptTask(this, RequestProcessor.this); } if (success) { item = null; } } } if (success) { notifyFinished(); // mark it as finished } return success; } } /** Current priority of the task. * @return the priority level (see e.g. {@link Thread#NORM_PRIORITY} */ public int getPriority() { return priority; } /** Changes the priority the task will be performed with. * @param priority the priority level (see e.g. {@link Thread#NORM_PRIORITY} */ public void setPriority(int priority) { if (this.priority == priority) { return; } if (priority < Thread.MIN_PRIORITY) { priority = Thread.MIN_PRIORITY; } if (priority > Thread.MAX_PRIORITY) { priority = Thread.MAX_PRIORITY; } // update queue position accordingly synchronized (processorLock) { if (item != null && getQueue().remove(item)) { this.priority = priority; prioritizedEnqueue(item); } else { this.priority = priority; } } } /** This method is an implementation of the waitFinished method * in the RequestProcessor.Task. It check the current thread if it is * request processor thread and in such case runs the task immediatelly * to prevent deadlocks. */ @Override public void waitFinished() { if (isRequestProcessorThread()) { //System.err.println( boolean runAtAll; boolean toRun; Logger em = logger(); boolean loggable = em.isLoggable(Level.FINE); if (loggable) { em.log(Level.FINE, "Task.waitFinished on {0} from other task in RP: {1}", new Object[]{this, Thread.currentThread().getName()}); // NOI18N } synchronized (processorLock) { // correct line: toRun = (item == null) ? !isFinished (): (item.clear() && !isFinished ()); // the same: toRun = !isFinished () && (item == null ? true : item.clear ()); runAtAll = cancelOrNew(true); toRun = runAtAll && ((item == null) || item.clear(null)); if (loggable) { em.log(Level.FINE, " ## finished: {0}", isFinished()); // NOI18N em.log(Level.FINE, " ## item: {0}", item); // NOI18N } } if (toRun) { if (loggable) { em.fine(" ## running it synchronously"); // NOI18N } Processor processor = (Processor)Thread.currentThread(); processor.doEvaluate (this, processorLock, RequestProcessor.this); } else { // it is already running in other thread of this RP if (loggable) { em.fine(" ## not running it synchronously"); // NOI18N } if ((runAtAll || lastThread != null) && lastThread != Thread.currentThread()) { if (loggable) { em.log(Level.FINE, " ## waiting for it to be finished: {0} now: {1}", new Object[]{lastThread, Thread.currentThread()}); // NOI18N } super.waitFinished(); } // else { //System.err.println("Thread waiting for itself!!!!! - semantics broken!!!"); //Thread.dumpStack(); // } } if (loggable) { em.fine(" ## exiting waitFinished"); // NOI18N } } else { super.waitFinished(); } } /** Enhanced reimplementation of the {@link Task#waitFinished(long)} * method. The added semantic is that if one calls this method from * another task of the same processor, and the task has not yet been * executed, the method will immediatelly detect that and throw * InterruptedException to signal that state. * * @param timeout the amount of time to wait * @exception InterruptedException if waiting has been interrupted or if * the wait cannot succeed due to possible deadlock collision * @return true if the task was finished successfully during the * timeout period, false otherwise * @since 5.0 */ @Override public boolean waitFinished(long timeout) throws InterruptedException { if (isRequestProcessorThread()) { boolean toRun; synchronized (processorLock) { toRun = cancelOrNew(true); } if (toRun) { throw new InterruptedException( "Cannot wait with timeout " + timeout + " from the RequestProcessor thread for task: " + this ); // NOI18N } else { // it is already running in other thread of this RP if (lastThread != Thread.currentThread()) { return super.waitFinished(timeout); } else { return true; } } } else { return super.waitFinished(timeout); } } @Override public String toString() { return "RequestProcessor.Task [" + name + ", " + priority + "] for " + super.toString(); // NOI18N } } /* One item representing the task pending in the pending queue */ private static class Item extends Exception implements Comparable { private static int counter; private final RequestProcessor owner; private final int cnt; final Lookup current; final ClassLoader ctxLoader; Object action; boolean enqueued; String message; /** @GuardedBy(TICK) */ long when; Item(Task task, RequestProcessor rp) { action = task; owner = rp; cnt = counter++; current = Lookup.getDefault(); ctxLoader = Thread.currentThread().getContextClassLoader(); } final Task getTask() { Object a = action; return (a instanceof Task) ? (Task) a : null; } boolean clearOrNew(boolean canBeNew) { return clear(null); } /** Annulate this request iff still possible. * @returns true if it was possible to skip this item, false * if the item was/is already processed */ boolean clear(Processor processor) { boolean ret; synchronized (owner.processorLock) { ret = enqueued ? owner.getQueue().remove(this) : true; action = processor; } TickTac.cancel(this); return ret; } boolean isNew() { return false; } final Processor getProcessor() { Object a = action; return (a instanceof Processor) ? (Processor) a : null; } final int getPriority() { final Task t = getTask(); return t == null ? 0 : t.getPriority(); } public final @Override String getMessage() { return message; } @Override public int compareTo(Item o) { if (this == o) { return 0; } int myp = getPriority(); int yrp = o.getPriority(); if (myp == yrp) { return cnt - o.cnt; } else { return yrp - myp; } } } private static class CreatedItem extends Item { public CreatedItem(Task task, RequestProcessor rp) { super(task, rp); } @Override boolean clearOrNew(boolean canBeNew) { return canBeNew; } @Override boolean clear(Processor processor) { return false; } @Override boolean isNew() { return true; } } // end of CreatedItem private static class FastItem extends Item { FastItem(Task task, RequestProcessor rp) { super(task, rp); } @Override public Throwable fillInStackTrace() { return this; } } private static class SlowItem extends Item { SlowItem(Task task, RequestProcessor rp) { super(task, rp); } @Override public Throwable fillInStackTrace() { Throwable ret = super.fillInStackTrace(); StackTraceElement[] arr = ret.getStackTrace(); for (int i = 1; i < arr.length; i++) { if (arr[i].getClassName().startsWith("java.lang")) { continue; } if (arr[i].getClassName().startsWith(RequestProcessor.class.getName())) { continue; } ret.setStackTrace(Arrays.asList(arr).subList(i - 1, arr.length).toArray(new StackTraceElement[0])); break; } return ret; } } //------------------------------------------------------------------------------ // The Processor management implementation //------------------------------------------------------------------------------ /** /** A special thread that processes timouted Tasks from a RequestProcessor. * It uses the RequestProcessor as a synchronized queue (a Channel), * so it is possible to run more Processors in paralel for one RequestProcessor */ private static class Processor extends Thread { /** A stack containing all the inactive Processors */ private static final Deque POOL = new ArrayDeque<>(); /* One minute of inactivity and the Thread will die if not assigned */ private static final int INACTIVE_TIMEOUT = Integer.getInteger("org.openide.util.RequestProcessor.inactiveTime", 60000); // NOI18N /** Internal variable holding the Runnable to be run. * Used for passing Runnable through Thread boundaries. */ //private Item task; private RequestProcessor source; /** task we are working on */ private RequestProcessor.Task todo; private boolean idle = true; /** Waiting lock */ private final Object lock = new Object(); private RequestProcessor procesing; public Processor() { super(TOP_GROUP.getTopLevelThreadGroup(), "Inactive RequestProcessor thread"); // NOI18N setDaemon(true); assert !Thread.holdsLock(POOL); // new Thread may lead to huge classloading } /** Provide an inactive Processor instance. It will return either * existing inactive processor from the pool or will create a new instance * if no instance is in the pool. * * @return inactive Processor */ static Processor get() { Processor newP = null; for (;;) { synchronized (POOL) { if (POOL.isEmpty()) { if (newP != null) { Processor proc = newP; proc.idle = false; proc.start(); return proc; } } else { assert checkAccess(TOP_GROUP.getTopLevelThreadGroup()); Processor proc = POOL.pop(); proc.idle = false; return proc; } } newP = new Processor(); } } private static boolean checkAccess(ThreadGroup g) throws SecurityException { g.checkAccess(); return true; } /** A way of returning a Processor to the inactive pool. * * @param proc the Processor to return to the pool. It shall be inactive. * @param last the debugging string identifying the last client. */ static void put(Processor proc, String last) { synchronized (POOL) { proc.setName("Inactive RequestProcessor thread [Was:" + proc.getName() + "/" + last + "]"); // NOI18N proc.idle = true; POOL.push(proc); } } /** setPriority wrapper that skips setting the same priority * we'return already running at */ void setPrio(int priority) { if (priority != getPriority()) { setPriority(priority); } } /** * Sets an Item to be performed and notifies the performing Thread * to start the processing. * * @param r the Item to run. */ public void attachTo(RequestProcessor src) { synchronized (lock) { //assert(source == null); source = src; lock.notify(); } } boolean belongsTo(RequestProcessor r) { synchronized (lock) { return source == r; } } /** * The method that will repeatedly wait for a request and perform it. */ @Override public void run() { for (;;) { RequestProcessor current = null; synchronized (lock) { try { if (source == null) { lock.wait(INACTIVE_TIMEOUT); // wait for the job } } catch (InterruptedException e) { } // not interesting current = source; source = null; if (current == null) { // We've timeouted synchronized (POOL) { if (idle) { // and we're idle POOL.remove(this); break; // exit the thread } else { // this will happen if we've been just continue; // before timeout when we were assigned } } } } String debug = null; Logger em = logger(); boolean loggable = em.isLoggable(Level.FINE); if (loggable) { try { procesing = current; em.log(Level.FINE, "Begining work {0}", getName()); // NOI18N } finally { procesing = null; } } // while we have something to do for (;;) { Lookup[] lkp = new Lookup[1]; // need the same sync as interruptTask synchronized (current.processorLock) { todo = current.askForWork(this, debug, lkp); if (todo == null) { break; } } setPrio(todo.getPriority()); try { procesing = current; if (loggable) { em.log(Level.FINE, " Executing {0}", todo); // NOI18N } registerParallel(todo, current); Lookups.executeWith(lkp[0], todo); lkp[0] = null; if (loggable) { em.log(Level.FINE, " Execution finished in {0}", getName()); // NOI18N } debug = todo.debug(); } catch (OutOfMemoryError oome) { // direct notification, there may be no room for // annotations and we need OOME to be processed // for debugging hooks em.log(Level.SEVERE, null, oome); } catch (StackOverflowError e) { // recoverable too doNotify(todo, e); } catch (ThreadDeath t) { // #201098: ignore } catch (Throwable t) { doNotify(todo, t); } finally { procesing = null; unregisterParallel(todo, current); } // need the same sync as interruptTask synchronized (current.processorLock) { // to improve GC todo = null; // and to clear any possible interrupted state // set by calling Task.cancel () Thread.interrupted(); } } if (loggable) { try { procesing = current; em.log(Level.FINE, "Work finished {0}", getName()); // NOI18N } finally { procesing = null; } } } } /** Evaluates given task directly. */ final void doEvaluate (Task t, Object processorLock, RequestProcessor src) { Task previous = todo; boolean interrupted = Thread.interrupted(); try { todo = t; t.run (); } finally { synchronized (processorLock) { todo = previous; if (interrupted || todo.item == null) { if (src.interruptThread) { // reinterrupt the thread if it was interrupted and // we support interrupts Thread.currentThread().interrupt(); } } } } } /** Called under the processorLock */ public void interruptTask(Task t, RequestProcessor src) { if (t != todo) { // not running this task so return; } if (src.interruptThread) { // otherwise interrupt this thread interrupt(); } } boolean interrupt (Task t, RequestProcessor src) { if (t != todo) { return false; } interrupt(); return true; } /** See #20467. */ private static void doNotify(RequestProcessor.Task todo, Throwable ex) { if (SLOW) { Item item = todo.item; if (item != null && item.message == null) { item.message = ex.toString(); item.initCause(ex); ex = item; } } logger().log(Level.SEVERE, "Error in RequestProcessor " + todo.debug(), ex); } private static final Map,Object> warnedClasses = Collections.synchronizedMap( new WeakHashMap,Object>() ); private void registerParallel(Task todo, RequestProcessor rp) { if (rp.warnParallel == 0 || todo.run == null) { return; } final Class c = todo.run.getClass(); AtomicInteger number; synchronized (rp.processorLock) { if (rp.inParallel == null) { rp.inParallel = new WeakHashMap,AtomicInteger>(); } number = rp.inParallel.get(c); if (number == null) { rp.inParallel.put(c, number = new AtomicInteger(1)); } else { number.incrementAndGet(); } } if (number.get() >= rp.warnParallel && warnedClasses.put(c, "") == null) { final String msg = "Too many " + c.getName() + " (" + number + ") in shared RequestProcessor; create your own"; // NOI18N Exception ex = null; Item itm = todo.item; if (itm != null) { ex = new IllegalStateException(msg); ex.setStackTrace(itm.getStackTrace()); } logger().log(Level.WARNING, msg, ex); } } private void unregisterParallel(Task todo, RequestProcessor rp) { if (rp.warnParallel == 0 || todo.run == null) { return; } synchronized (rp.processorLock) { Class c = todo.run.getClass(); rp.inParallel.get(c).decrementAndGet(); } } } private static final class TickTac extends Thread implements Comparator { private static TickTac TICK; private final PriorityQueue queue; public TickTac() { super("RequestProcessor queue manager"); // NOI18N setDaemon(true); queue = new PriorityQueue(128, this); } @Override public int compare(Item o1, Item o2) { if (o1.when < o2.when) { return -1; } if (o1.when > o2.when) { return 1; } return 0; } static final synchronized void schedule(Item localItem, long delay) { if (TICK == null) { TICK = new TickTac(); TICK.scheduleImpl(localItem, delay); TICK.start(); } else { TICK.scheduleImpl(localItem, delay); } TickTac.class.notifyAll(); } private void scheduleImpl(Item localItem, long delay) { assert Thread.holdsLock(TickTac.class); localItem.when = System.currentTimeMillis() + delay; queue.add(localItem); } static final synchronized void cancel(Item localItem) { if (TICK != null) { TICK.cancelImpl(localItem); TickTac.class.notifyAll(); } } private void cancelImpl(Item localItem) { assert Thread.holdsLock(TickTac.class); queue.remove(localItem); } @Override public void run() { while (TICK == this) { try { Item first = obtainFirst(); if (first != null) { first.owner.enqueue(first); } } catch (InterruptedException ex) { continue; } } } private static synchronized Item obtainFirst() throws InterruptedException { if (TICK == null) { return null; } PriorityQueue q = TICK.queue; Item first = q.poll(); if (first == null) { TICK = null; return null; } long delay = first.when - System.currentTimeMillis(); if (delay > 0) { q.add(first); TickTac.class.wait(delay); return null; } return first; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy