This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

Bug 180386 - Support for Callable and Future in RequestProcessor
Summary: Support for Callable and Future in RequestProcessor
Status: RESOLVED FIXED
Alias: None
Product: platform
Classification: Unclassified
Component: -- Other -- (show other bugs)
Version: 6.x
Hardware: All Windows XP
: P3 normal (vote)
Assignee: _ tboudreau
URL:
Keywords: API, API_REVIEW_FAST
Depends on:
Blocks:
 
Reported: 2010-02-06 21:54 UTC by _ tboudreau
Modified: 2010-02-26 11:19 UTC (History)
3 users (show)

See Also:
Issue Type: ENHANCEMENT
Exception Reporter:


Attachments
Patch adding submit() methods to RequestProcessor. If agreed to, will update version, since, etc. (1.81 KB, patch)
2010-02-06 21:54 UTC, _ tboudreau
Details | Diff
Implementing ScheduledExecutionService over RequestProcessor (77.24 KB, patch)
2010-02-18 02:15 UTC, _ tboudreau
Details | Diff
Patch which also provides re-schedulable transactions (96.61 KB, patch)
2010-02-19 20:11 UTC, _ tboudreau
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description _ tboudreau 2010-02-06 21:54:46 UTC
Created attachment 93938 [details]
Patch adding submit() methods to RequestProcessor.  If agreed to, will update version, since, etc.

These are popular and useful classes which people used to standard Java threading code are used to.  Patch attached.
Comment 1 _ tboudreau 2010-02-16 11:58:22 UTC
Any objections to integrating this?
Comment 2 Jaroslav Tulach 2010-02-17 01:10:28 UTC
This is a bit raw patch, I expect you still want to work on other attributes of acceptable API patch. So just one general comment:

Y01 Align more with java.util.concurent. 

Rather then inventing new methods and method names, keep consistency with one of already available JDK interfaces. The best option seems to make RequestProcessor implement http://java.sun.com/javase/6/docs/api/java/util/concurrent/ScheduledExecutorService.html
It is more work, but it will make the API easier to absorb for regular Java developers.
Comment 3 Jesse Glick 2010-02-17 15:12:26 UTC
Agreed with Y01.


[JG01] Odd use of bitwise &, perhaps you meant &&.
Comment 4 _ tboudreau 2010-02-18 02:15:33 UTC
Created attachment 94268 [details]
Implementing ScheduledExecutionService over RequestProcessor

Agreed.  It was a fun way to spend a day :-)

Some tests may be somewhat timing dependent.  I have marked the obvious ones as @RandomlyFails - they are still useful during development.  If problems appear in others (they have only been run on one machine), the values may be able to be adjusted - in particular, testScheduleRepeatingIntervalsAreRoughlyCorrect and testScheduleFixedRateAreRoughlyCorrect test that if you specify a repeating task to run every 2 seconds and let it run for a while, that it never is run more frequently than 1.6 seconds.  If this proves unstable, it could be adjusted to, say, 20 seconds and 16 seconds, and it should be fairly rare for that to fail.  Such tests could also be made self-calibrating by measuring the standard deviation of timing for java.util.Timer in setUpClass().

At any rate, everything has tests, and seems to do what the javadoc for ExecutionService says.
Comment 5 _ tboudreau 2010-02-18 02:17:35 UTC
@JG01:  Bitwise & use is intentional - both java.util.concurrent.FutureTask.cancel() and RequestProcessor.Task.cancel() need to be called - if I used &&, the second would not be invoked if the first returned true.
Comment 6 Jesse Glick 2010-02-18 09:27:21 UTC
(In reply to comment #5)
> Bitwise & use is intentional - both
> java.util.concurrent.FutureTask.cancel() and RequestProcessor.Task.cancel()
> need to be called

OK, but this is such an unusual idiom it would be better to write it explicitly for the benefit of future generations:

boolean canceled1 = ...;
boolean canceled2 = ...; // need to run both!
return canceled1 && canceled2;


[JG02] "implements Executor, ScheduledExecutorService" is redundant.


[JG03] Learn to use @link and @linkplain, e.g.

* is called on the {@linkplain #getDefault default request processor},


[JG04] Class Javadoc should say that SES is implemented since 8.2.

(BTW we usually include the full module code name in @since tags, e.g. "@since org.openide.util 8.2", to avoid misleading comments after refactoring modules.)


[JG05] Use @throws idiomatically:

* @throws IllegalStateException if called on the {@linkplain #getDefault default request processor}


[JG06] Use {@inheritDoc} rather than "...in accordance with..." when implementing an interface if you wish to just add information to the specification (such as a special @throws clause, a note about Cancellable, or other deviations from normal behavior). In this case omit any text or tags which the super Javadoc already includes. And since the class Javadoc should note when SES has been implemented, it is not necessary to mark methods like isShutdown with any Javadoc comment at all: they merely implement the specification.


[JG07] Rather than

  RequestProcessor.logger().setLevel(Level.ALL);

you can use logLevel() to display log messages iff a test fails. That means passing tests are quiet, as they should be.


[JG08] Consider just appending to the existing RequestProcessorTest. It is natural when making an edit in RequestProcessor.java to just press Ctrl-F6 and expect that the right tests will be run. (I suppose apisupport could try to find all tests that look related to the testable code and run them all, but currently it does not.)
Comment 7 Jaroslav Tulach 2010-02-18 10:51:30 UTC
Re. JG04 btw. - I don't include module FQN and it never caused a problem. Just make sure when splitting a module to make both module versions higher than the original one.

Re. JG08 - I am fine to have the tests separated. They separate what was in the original RP and the new SES. If you need Ctrl-F6, then probably define pu. st. Test suite() { .... } that will run both tests (of course, rename the new file to not end with Test to prevent double run).
Comment 8 _ tboudreau 2010-02-18 21:00:25 UTC
Well, if nearly all the only complaints are about the Javadoc, I'm happy ;-)

> unusual idiom 

Huh, I suppose it is, if you think of it as a bit operation when dealing one-bit values...for that case I always simply thought of it as a way to force full evaluation.  But I suppose it would be clearer.

> [JG07] Rather than
>  RequestProcessor.logger().setLevel(Level.ALL);
> you can use logLevel() to display log messages if a test fails. That means
> passing tests are quiet, as they should be.

So *that's* what JUnit thinks it is doing which it horrifically mis-orders and mangles test output.  How I would love to be liberated from the still-useless JUnit window and just have normal, sequential output that shows up as it goes...

Anyway, I was never able to actually get any logging out of RequestProcessor.logger() even with setting it explicitly.  Didn't find out why, although it might have saved some time.


Re separating the tests:  It's fairly easy to create a request processor in a test and forget to shut it down, causing the JUnit process not to exit.  So it was easier to keep them all out of each others way during test development so new tests couldn't affect old tests.  I don't feel strongly about it either way.

Anyway, easy enough to clean up the javadoc.
Comment 9 Jesse Glick 2010-02-19 08:22:03 UTC
(In reply to comment #8)
> I was never able to actually get any logging out of
> RequestProcessor.logger() even with setting it explicitly.

OK, so remove the call to setLevel.

> It's fairly easy to create a request processor in a
> test and forget to shut it down, causing the JUnit process not to exit.  So it
> was easier to keep them all out of each others way during test development so
> new tests couldn't affect old tests.

In this case it is better to keep the new tests in a separate class, since our harness implicitly guarantees that each suite gets its own JVM.
Comment 10 _ tboudreau 2010-02-19 12:36:47 UTC
One trivial potential race condition I can imagine couldn't figure out how to test without polluting RequestProcessor with a lock provided by a test - any ideas?

Theoretically, one thread can call stop() (or shutdown() with the patch).  Concurrent with that, another thread can post a new task, before the other thread enters processorLock to stop the RP.  The first thread calls awaitTerminated() - this will block until all threads exit.  However, it cannot hold processorLock while it waits - it's calling Thread.join() on the still-alive threads, and that will block the remaining threads from being returned to the pool.  So it makes a copy of the current threads, and iterates that - which can be a copy without the newly active thread (possible fix would be to repeat that process until everything exits).  So it is possible for awaitTerminated() to return before all threads have really exited (the stopped flag is useless since it indicates pending shutdown, not completed shutdown).

May be too trivial to worry about, as the last thread will eventually exit.

But to write a test for it, I need to be able to block RP.stop() before it starts its dequeueing process, post the new runnable, and then release it, to reproduce this condition.  So far I have not changed any of the logic of the existing RP code, except to add a private Task.cancel() method that can interrupt the RP thread even if the RP says not to, to fulfil the contract of Future.cancel(true).  Any clever tricks?

One other thing to think about:  Two features of Task are not supported by ScheduledExecutor:
 - Ability to reschedule an already run Future - i.e. define 
interface ReschedulableFuture<T> extends Future<T> {
    void schedule (long timeout, TimeUnit unit);
}
and appropriate methods that return it.
 - Ability to create a Task which is initially finished (is this useful enough to be worthwhile?)

With those, or at least rescheduling, we could deprecate Task and friends.


One minor annoyance - worth dealing with? - is that the argument to Task.schedule() is an int;  however both ExecutorService and the underlying java.util.Timer accept long.  I'm guessing this is just an old accident.  All tests pass with an overloaded version of Task.schedule() that takes a long (not in the current patch).  If we do not decide to deprecate Task, should we deprecate schedule(int)? Or just not worry about it?  Or make the overload public?
Comment 11 _ tboudreau 2010-02-19 13:27:17 UTC
Re {@inheritDoc} - when I build javadoc for openide.util, I do not get any inherited javadoc embedded (presumably because it would have to be downloaded).

Will this actually work for production builds of the javadoc?
Comment 12 Jesse Glick 2010-02-19 13:30:27 UTC
(In reply to comment #11)
> Re {@inheritDoc} - when I build javadoc for openide.util, I do not get any
> inherited javadoc embedded (presumably because it would have to be downloaded).

Might be limited when -linkoffline is used. Would not be relevant for editor code completion. Don't worry about it, use it anyway. People can always click on the super method anyway. It is more important to avoid redundancy.
Comment 13 _ tboudreau 2010-02-19 20:11:35 UTC
Created attachment 94350 [details]
Patch which also provides re-schedulable transactions

This patch adds the following, with tests:

public interface ReschedulableFuture<T> extends ScheduledFuture<T>{
    ScheduledFuture<T> schedule (long delay, TimeUnit unit);
    ScheduledFuture<T> schedule (long delayMillis);
}

and

    public ReschedulableFuture<?> createReschedulable(Runnable runnable, boolean initiallyFinished);
    public ReschedulableFuture<?> createReschedulable(Runnable runnable);

which fulfill the same role as creating and rescheduling an RP.Task within the conceptual space of java.util.concurrent.

One thing to think about:  ReschedulableFuture.schedule(*) could have a void return type;  however, there would not be any way to compatibly change it later if we decide to support reschedulable Callables, where there is a legitimate for monitoring and getting a result from an individual run.
Comment 14 Jaroslav Tulach 2010-02-22 01:50:47 UTC
Y02 I do not think we shall introduce our own ReschedulableFuture. The functionality is available with RP.Task and we don't need yet another way to do the same thing. If you feel strongly that it is needed, please donate that interface to java.util.concurent

Btw. if you need to reproduce race condition in test, use logging control
Comment 15 _ tboudreau 2010-02-23 13:33:52 UTC
Integrated, minus ReschedulableFuture, in main/ 7e4413ade2a2
Comment 16 Jesse Glick 2010-02-23 14:05:27 UTC
(In reply to comment #15)
> in main 7e4413ade2a2

In the future please only push changes to core modules to core-main, as this ensures that the changes are properly vetted by the long list of tests that NB-Core-Build runs. (If you do not have a clone of core-main handy to push to, send a bundle or patch to someone who does.)
Comment 17 Vladimir Voskresensky 2010-02-23 14:33:59 UTC
Tim, 7e4413ade2a2 is about "Updating Paint Application sample for 6.9" :-)
Comment 18 Quality Engineering 2010-02-26 11:19:40 UTC
Integrated into 'main-golden', will be available in build *201002261426* on http://bits.netbeans.org/dev/nightly/ (upload may still be in progress)
Changeset: http://hg.netbeans.org/main/rev/99c6bde5f215
User: Tim Boudreau <tboudreau@netbeans.org>
Log: #180386 - support for Callable and Future in RequestProcessor