Index: src/org/openide/util/RequestProcessor.java =================================================================== RCS file: /cvs/openide/src/org/openide/util/RequestProcessor.java,v --- 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 --- 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 --- 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 --- 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(); + } + } + } + }