Index: src/org/openide/util/RequestProcessor.java
===================================================================
RCS file: /cvs/openide/src/org/openide/util/RequestProcessor.java,v
retrieving revision 1.80
diff -u -r1.80 RequestProcessor.java
--- src/org/openide/util/RequestProcessor.java 4 Feb 2004 16:34:57 -0000 1.80
+++ src/org/openide/util/RequestProcessor.java 29 Jul 2004 16:57:54 -0000
@@ -447,6 +447,41 @@
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 JST-PENDING
+ */
+ public boolean waitFinished (long timeout) throws InterruptedException {
+ if (isRequestProcessorThread()) {
+ boolean toRun;
+
+ synchronized (processorLock) {
+ toRun = !isFinished () && (item == null || item.clear ());
+ }
+
+ 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
Index: src/org/openide/util/Task.java
===================================================================
RCS file: /cvs/openide/src/org/openide/util/Task.java,v
retrieving revision 1.25
diff -u -r1.25 Task.java
--- src/org/openide/util/Task.java 11 Jan 2004 21:10:24 -0000 1.25
+++ src/org/openide/util/Task.java 29 Jul 2004 16:58:03 -0000
@@ -41,6 +41,14 @@
static {
EMPTY.finished = true;
}
+ /** map of subclasses to booleans whether they override waitFinished() or not
+ *
+ */
+ private static java.util.WeakHashMap overrides;
+ /** request processor for workarounding compatibility problem with
+ * classes that do not override waitFinished (long)
+ */
+ private static RequestProcessor RP;
/** what to run */
private Runnable run;
@@ -89,6 +97,51 @@
}
}
+
+ /** Wait until the task is finished, but only a given time.
+ * @param milliseconds time in milliseconds to wait for the result
+ * @exception InterruptedException when the waiting has been interrupted
+ * @return true if the task is really finished, or false if the time out
+ * has been exceeded
+ * @since JST-PENDING
+ */
+ public boolean waitFinished (long milliseconds) throws InterruptedException {
+ synchronized (this) {
+ if (overridesTimeoutedWaitFinished ()) {
+ // the the task overrides waitFinished (timeout) or is
+ // one of the basic tasks, then we can just simply do our bese
+ // code. Otherwise we have to execute threading workaround
+ if (finished) return true;
+
+ long expectedEnd = System.currentTimeMillis () + milliseconds;
+ for (;;) {
+ wait (milliseconds);
+
+ if (finished) return true;
+ long now = System.currentTimeMillis ();
+
+ if (expectedEnd <= now) return false;
+
+ milliseconds = expectedEnd - now;
+ }
+ }
+ }
+
+
+ // as we know that RequestProcessor implements the waitFinished(long)
+ // correctly we just post a task for waitFinished() into some
+ // of its threads and wait just the given milliseconds time
+ // for the result, by that we can guarantee the semantics
+ // of the call
+ class Run implements Runnable {
+ public void run () {
+ Task.this.waitFinished ();
+ }
+ }
+ RequestProcessor.Task task = RP.post (new Run ());
+ return task.waitFinished (milliseconds);
+ }
+
/** Changes the state of the task to be running. Any call after this
* one and before notifyFinished to waitFinished blocks.
* @since 1.5
@@ -157,9 +210,41 @@
if (list == null) return;
list.remove (l);
}
-
+
public String toString () {
return "task " + run; // NOI18N
+ }
+
+ /** Checks whether the class overrides wait finished.
+ */
+ private boolean overridesTimeoutedWaitFinished () {
+ // yes we implement it corretly
+ if (getClass () == Task.class) return true;
+ // RequestProcessor.Task overrides correctly
+ if (getClass () == RequestProcessor.Task.class) return true;
+
+ java.util.WeakHashMap m;
+ Boolean does;
+ synchronized (Task.class) {
+ if (overrides == null) {
+ overrides = new java.util.WeakHashMap ();
+ RP = new RequestProcessor ("Timeout waitFinished compatibility processor", 255); // NOI18N
+ }
+ m = overrides;
+
+ does = (Boolean)m.get (getClass ());
+ if (does != null) return does.booleanValue ();
+
+ try {
+ java.lang.reflect.Method method = getClass ().getMethod ("waitFinished", new Class[] { Long.TYPE }); // NOI18N
+ does = Boolean.valueOf (method.getDeclaringClass () != Task.class);
+ m.put (getClass (), does);
+ return does.booleanValue ();
+ } catch (Exception ex) {
+ org.openide.ErrorManager.getDefault ().notify (ex);
+ return true;
+ }
+ }
}
/** Reveal the identity of the worker runnable.
Index: test/unit/src/org/openide/util/RequestProcessorTest.java
===================================================================
RCS file: /cvs/openide/test/unit/src/org/openide/util/RequestProcessorTest.java,v
retrieving revision 1.17
diff -u -r1.17 RequestProcessorTest.java
--- test/unit/src/org/openide/util/RequestProcessorTest.java 25 Feb 2004 11:11:37 -0000 1.17
+++ test/unit/src/org/openide/util/RequestProcessorTest.java 29 Jul 2004 16:58:04 -0000
@@ -564,6 +564,89 @@
assertTrue("Task was performed even if it is canceled", !x.performed);
}
+ public void testWaitWithTimeOutCanFinishEvenTheTaskHasNotRun () throws Exception {
+ class Run implements Runnable {
+ public boolean runned;
+ public synchronized void run () {
+ runned = true;
+ }
+ }
+
+ Run run = new Run ();
+
+ synchronized (run) {
+ RequestProcessor.Task task = RequestProcessor.getDefault ().post (run);
+ task.waitFinished (100);
+ assertFalse ("We are here and the task has not finished", run.runned);
+ assertFalse ("Not finished", task.isFinished ());
+ }
+ }
+
+ public void testWhenWaitingForALimitedTimeFromTheSameProcessorThenInterruptedExceptionIsThrownImmediatelly () throws Exception {
+ Counter x = new Counter ();
+ RequestProcessor rp = new RequestProcessor ("testWaitFinishedOnNotStartedTaskFromRPThread");
+ final RequestProcessor.Task task = rp.create (x);
+
+ class WaitTask implements Runnable {
+ public boolean finished;
+
+ public synchronized void run () {
+ long time = System.currentTimeMillis ();
+ try {
+ task.waitFinished (1000);
+ fail ("This should throw an exception. Btw time was: " + (System.currentTimeMillis () - time));
+ } catch (InterruptedException ex) {
+ // ok, this is expected
+ } finally {
+ time = System.currentTimeMillis () - time;
+ notifyAll ();
+ }
+ if (time > 100) {
+ fail ("Exception should be thrown quickly. Was: " + time);
+ }
+ finished = true;
+ }
+
+ }
+ WaitTask wt = new WaitTask ();
+ synchronized (wt) {
+ rp.post (wt);
+ wt.wait ();
+ }
+ assertTrue ("The task.waitFinished has to finish", wt.finished);
+ x.assertCnt ("The task has NOT been executed", 0);
+ }
+
+ public void testWhenWaitingForAlreadyFinishedTaskWithTimeOutTheResultIsGood () throws Exception {
+ Counter x = new Counter ();
+ RequestProcessor rp = new RequestProcessor ("testWaitFinishedOnStartedTaskFromRPThread");
+ final RequestProcessor.Task task = rp.post (x);
+ task.waitFinished ();
+ x.assertCnt ("The task has been executed before", 1);
+
+ class WaitTask implements Runnable {
+ public boolean finished;
+
+ public synchronized void run () {
+ notifyAll ();
+ try {
+ assertTrue ("The task has been already finished", task.waitFinished (1000));
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ fail ("Should not happen");
+ }
+ finished = true;
+ }
+
+ }
+ WaitTask wt = new WaitTask ();
+ synchronized (wt) {
+ rp.post (wt);
+ wt.wait ();
+ }
+ assertTrue ("The task.waitFinished has to finish", wt.finished);
+ }
+
private static void doGc (int count, Reference toClear) {
java.util.ArrayList l = new java.util.ArrayList (count);
while (count-- > 0) {
Index: test/unit/src/org/openide/util/TaskTest.java
===================================================================
RCS file: /cvs/openide/test/unit/src/org/openide/util/TaskTest.java,v
retrieving revision 1.2
diff -u -r1.2 TaskTest.java
--- test/unit/src/org/openide/util/TaskTest.java 29 Jul 2004 11:57:13 -0000 1.2
+++ test/unit/src/org/openide/util/TaskTest.java 29 Jul 2004 16:58:04 -0000
@@ -30,19 +30,12 @@
public static void main(java.lang.String[] args) {
TestRunner.run(new NbTestSuite(TaskTest.class));
}
-
+
+
+ //
+ // tests
+ //
public void testPlainTaskWaitsForBeingExecuted () throws Exception {
- class R implements Runnable {
- public synchronized void run () {
- notify ();
- try {
- wait ();
- } catch (InterruptedException ex) {
- ex.printStackTrace();
- }
- }
- }
-
R run = new R ();
Task t = new Task (run);
@@ -68,5 +61,74 @@
public void testWaitFinishedOnEMPTYTaskReturnsImmediatelly () throws Exception {
Task.EMPTY.waitFinished ();
}
+
+ public void testWaitWithTimeOutReturnsImmediatellyOnFinishedTasks () throws Exception {
+ assertTrue ("Was successfully finished", Task.EMPTY.waitFinished (0));
+ }
+ public void testWaitWithTimeOutReturnsAfterTimeOutWhenTheTaskIsNotComputedAtAll () throws Exception {
+ long time = System.currentTimeMillis ();
+ Task t = new Task (new R ());
+ t.waitFinished (1000);
+ time = System.currentTimeMillis () - time;
+
+ assertFalse ("Still not finished", t.isFinished ());
+
+ if (time < 900 || time > 1100) {
+ fail ("Something wrong happened the task should wait for 1000ms but it took: " + time);
+ }
+ }
+
+ public void testWaitOnStrangeTaskThatStartsItsExecutionInOverridenWaitFinishedMethodLikeFolderInstancesDo () throws Exception {
+ class MyTask extends Task {
+ private int values;
+
+ public MyTask () {
+ notifyFinished ();
+ }
+
+ public void waitFinished () {
+ notifyRunning ();
+ values++;
+ notifyFinished ();
+ }
+ }
+
+ MyTask my = new MyTask ();
+ assertTrue ("The task thinks that he is finished", my.isFinished ());
+ assertTrue ("Ok, even with timeout we got the result", my.waitFinished (1000));
+ assertEquals ("But the old waitFinished is called", 1, my.values);
+ }
+
+ public void testWaitOnStrangeTaskThatTakesReallyLongTime () throws Exception {
+ class MyTask extends Task {
+ public MyTask () {
+ notifyFinished ();
+ }
+
+ public void waitFinished () {
+ try {
+ Thread.sleep (5000);
+ } catch (InterruptedException ex) {
+ fail ("Should not happen");
+ }
+ }
+ }
+
+ MyTask my = new MyTask ();
+ assertTrue ("The task thinks that he is finished", my.isFinished ());
+ assertFalse ("but still it get's called, but timeouts", my.waitFinished (1000));
+ }
+
+ final class R implements Runnable {
+ public synchronized void run () {
+ notify ();
+ try {
+ wait ();
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
}