
org.openide.util.RequestProcessor Maven / Gradle / Ivy
The newest version!
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s):
*
* The Original Software is NetBeans. The Initial Developer of the Original
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
* Microsystems, Inc. All Rights Reserved.
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*/
package org.openide.util;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Stack;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executor;
import java.util.logging.Level;
import java.util.logging.Logger;
/** Request processor that is capable to execute requests in dedicated threads.
* You can create your own instance or use the shared one.
*
* There are several use cases for RequestProcessor:
*
*
- Having something done asynchronously in some other thread,
* not insisting on any kind of serialization of the requests:
* Use
RequestProcessor.{@link RequestProcessor#getDefault
* }.{@link #post(java.lang.Runnable) post(runnable)}
* for this purpose.
* - Having something done later in some other thread:
* Use
RequestProcessor.{@link RequestProcessor#getDefault
* }.{@link #post(java.lang.Runnable,int) post(runnable, delay)}
* - Having something done periodically in any thread: Use the
* {@link RequestProcessor.Task}'s ability to
* {@link RequestProcessor.Task#schedule schedule()}, like
*
* static RequestProcessor.Task CLEANER = RequestProcessor.getDefault().post(runnable,DELAY);
* public void run() {
* doTheWork();
* CLEANER.schedule(DELAY);
* }
*
* Note: Please think twice before implementing some periodic
* background activity. It is generally considered evil if it will run
regardless of user actions and the application state, even while the application
is minimized / not currently used.
* - Having something done in some other thread but properly ordered:
* Create a private instance of the
* {@link RequestProcessor#RequestProcessor(java.lang.String) RequestProcessor(name)}
* and use it from all places you'd like to have serialized. It works
* like a simple Mutex.
*
DOWN()
s.
*
*
* Note: If you don't need to serialize your requests but
* you're generating them in bursts, you should use your private
* RequestProcessor
instance with limited throughput (probably
* set to 1), NetBeans would try to run all your requests in parallel otherwise.
*
* * Since version 6.3 there is a conditional support for interruption of long running tasks. * 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. 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 immediatelly as in this example: *
* public void run () { * while (veryLongTimeLook) { * doAPieceOfIt (); * * if (Thread.interrupted ()) return; * } * } ** Since version 7.16 it implements {@link Executor} * * @author Petr Nejedly, Jaroslav Tulach */ public final class RequestProcessor implements Executor{ /** the static instance for users that do not want to have own processor */ private static RequestProcessor DEFAULT = new RequestProcessor(); // 50: a conservative value, just for case of misuse /** the static instance for users that do not want to have own processor */ private static RequestProcessor UNLIMITED = new RequestProcessor("Default RequestProcessor", 50); // NOI18N /** A shared timer used to pass timeouted tasks to pending queue */ private static Timer starterThread = new Timer(true); /** logger */ private static Logger logger; /** The counter for automatic naming of unnamed RequestProcessors */ private static int counter = 0; static final boolean SLOW = Boolean.getBoolean("org.openide.util.RequestProcessor.Item.SLOW"); /** 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 */ boolean stopped = false; /** The lock covering following four fields. They should be accessed * only while having this lock held. */ private Object processorLock = new Object(); /** The set holding all the Processors assigned to this RequestProcessor */ private HashSet
* 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.throughput = throughput; this.name = (name != null) ? name : ("OpenIDE-request-processor-" + (counter++)); this.interruptThread = interruptThread; } /** The getter for the shared instance of the
RequestProcessor
.
* This instance is shared by anybody who
* needs a way of performing sporadic or repeated asynchronous work.
* 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
*/
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);
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();
// return c instanceof Processor && ((Processor)c).source == this;
synchronized (processorLock) {
return processors.contains(c);
}
}
/** 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 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 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 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 immediatelly 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 use cases. */
@Deprecated
public static Task createRequest(Runnable run) {
return DEFAULT.create(run);
}
/** Logger for the error manager.
*/
static Logger logger() {
synchronized (starterThread) {
if (logger == null) {
logger = Logger.getLogger("org.openide.util.RequestProcessor"); // NOI18N
}
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 immediatelly processed
* in the Processor.
*/
void enqueue(Item item) {
Logger em = logger();
boolean loggable = em.isLoggable(Level.FINE);
synchronized (processorLock) {
if (item.getTask() == null) {
if (loggable) {
em.fine("Null task for item " + item); // NOI18N
}
return;
}
prioritizedEnqueue(item);
if (running < throughput) {
running++;
Processor proc = Processor.get();
processors.add(proc);
proc.setName(name);
proc.attachTo(this);
}
}
if (loggable) {
em.fine("Item enqueued: " + item.action + " status: " + item.enqueued); // NOI18N
}
}
// call it under queue lock i.e. processorLock
private void prioritizedEnqueue(Item item) {
int iprio = item.getPriority();
if (queue.isEmpty()) {
queue.add(item);
item.enqueued = true;
return;
} else if (iprio <= queue.get(queue.size() - 1).getPriority()) {
queue.add(item);
item.enqueued = true;
} else {
for (ListIteratorInterruptedException
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
*/
public boolean waitFinished(long timeout) throws InterruptedException {
if (isRequestProcessorThread()) {
boolean toRun;
synchronized (processorLock) {
toRun = !isFinished() && ((item == null) || item.clear(null));
}
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);
}
}
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 {
private final RequestProcessor owner;
private Object action;
private boolean enqueued;
Item(Task task, RequestProcessor rp) {
super("Posted StackTrace"); // NOI18N
action = task;
owner = rp;
}
Task getTask() {
Object a = action;
return (a instanceof Task) ? (Task) a : 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) {
synchronized (owner.processorLock) {
action = processor;
return enqueued ? owner.queue.remove(this) : true;
}
}
Processor getProcessor() {
Object a = action;
return (a instanceof Processor) ? (Processor) a : null;
}
int getPriority() {
return getTask().getPriority();
}
public Throwable fillInStackTrace() {
return SLOW ? super.fillInStackTrace() : this;
}
}
//------------------------------------------------------------------------------
// 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 Stack© 2015 - 2025 Weber Informatics LLC | Privacy Policy