diff options
Diffstat (limited to 'Article-Progress-Monitors/article.html')
-rw-r--r-- | Article-Progress-Monitors/article.html | 948 |
1 files changed, 571 insertions, 377 deletions
diff --git a/Article-Progress-Monitors/article.html b/Article-Progress-Monitors/article.html index 805e39a..4b31cdc 100644 --- a/Article-Progress-Monitors/article.html +++ b/Article-Progress-Monitors/article.html @@ -1,433 +1,627 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> -<head> - - - +<head> <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> - - - - - - <title>How to Correctly and Uniformly Use Progress Monitors</title> + <title>Using Progress Monitors</title> <link rel="stylesheet" href="../default_style.css"> </head> - <body link="#0000ff" vlink="#800080"> - - <div align="right"> <font face="Times New Roman, Times, serif" size="2">Copyright - © 2006 Kenneth Ölwing</font> + © 2016 Stefan Xenos and others</font> <table border="0" cellpadding="2" cellspacing="0" width="100%"> - - <tbody> - <tr> - - <td colspan="2" align="left" bgcolor="#0080c0" valign="top"><b><font face="Arial,Helvetica"><font color="#ffffff"> Eclipse Corner Article</font></font></b></td> - - </tr> - - - - </tbody> </table> - - </div> - - <div align="left"> - <h1><img src="images/Idea.jpg" align="middle" height="86" width="120"></h1> - - </div> - - <p> </p> - - - -<h1 align="center">How to Correctly and Uniformly Use Progress Monitors</h1> - - - +<h1 align="center">Using Progress Monitors</h1> <blockquote> <b>Summary</b> <br> -Handling a progress monitor instance is deceptively simple. It seems to -be straightforward but it is easy to make a mistake when using them. -And, depending on numerous factors such as the underlying -implementation, how it is displayed, if it’s set to use a fixed -number of work items or ‘unknown’, if used through a <span style="font-style: italic;">SubProgressMonitor</span> wrapper etc., the result can range from completely ok, mildly confusing or outright silliness. <br> +In this article I'll explain how to report progress in Eclipse. I'll +discuss the contract on IProgressMonitor, demonstrate some common patterns +using SubMonitor, and explain how to migrate legacy code to take advantage +of the API introduced in Eclipse 4.6. <br> -In this article I hope I can lay down a few ground rules that will help -anyone use progress monitors in a way that will work with the explicit -and implicit contract of <span style="font-style: italic;">IProgressMonitor</span>. Also, understanding the usage side makes it easier to understand how to implement a monitor.<br> - - - <p><b> By Kenneth Ölwing, BEA JRPG</b> <br> -January 18, 2006<font size="-1"></font> </p> - + <p><b> By Stefan Xenos, Google</b> <br> +April 19, 2016<font size="-1"></font> </p> </blockquote> - - <hr width="100%"> -<h2>Using a progress monitor - what's up with that?</h2> - -It all really comes down to a few, not too complex, rules. A common -theme is 'know what you know - but only that'. This means that you -shouldn't assume you know things you really don't know, and this -includes the common mistake of only considering progress monitors you -have seen, i.e. typically the graphical ones when using the IDE. -Another thing to watch out for is the fact that commonly you design a -number of tasks that may call each other using sub progress monitors, -and while doing that make assumptions based on your knowledge that they -will be called in this manner - never forget that sometime maybe your -separate subtasks may be called from not-yet-written routines. It's -then vitally important that your subtasks act exactly in a 'neutral' -manner, i.e. with no 'implicit assumptions' on what happened before or -what will happen after.<br> - -<br> - -One of the motivations for this article is when I tried my hand at -implementing a progress monitor intended for headless/console use - and -realised that code using it could make it look really wacky when the -monitor was wrongly used, and this was issues that were not as readily -apparent with a graphical monitor. Also, code (including my own) -frequently abuses the explicit and implicit (which admittedly are my -interpretation of reasonable behavior) contract that the -IProgressMonitor interface states, and this makes for dicey decisions -for a monitor implementor - should it complain (and how) when it gets -conflicting orders? If not, how should it then behave to make for a -reasonable and intuitive user experience?<br> - -<br> - -<!-- Anyway, since an article of this type would have helped me immensely -when I started working in the Eclipse environment, I figured I should -try my hand at summing up my view of how the interaction with <span style="font-style: italic;">IProgressMonitor</span> should work.<br>--> - -<h3>The protocol of <span style="font-style: italic;">IProgressMonitor</span></h3> - -Generally, all interaction with a progress monitor is through the interface <span style="font-style: italic;">IProgressMonitor</span> -and this interface defines the protocol behavior expected. It does -leave some things up in the air though; for example, the description -states some things that should be true, but the methods have no <span style="font-style: italic;">throws</span> -clause that helps enforce some invariants. I have chosen to interpret -the descriptions ‘hard’, even to the point of saying -it’s valid to throw an (unchecked) exception if a described rule -is violated (this is somewhat controversial of course - if you -implement a monitor doing this you should probably provide a way to -turn off 'strictness'). Hopefully we could eventually see a new -interface that deprecates the old methods and provides new ones that -better reflect the contract. The discussion below is based on the -assumption that the reader is familiar with the general API; review it -in the Eclipse help.<br> - -<br> - -The first important consideration is the realization that a monitor -(contract wise) can be in basically four states. Any given -implementation may or may not track those state changes and may or may -not do anything about them, which is part of the reason that -misbehaving users of a monitor sometimes gets away with it. Only one of -these states are readily testable using the interface however (if the -monitor is canceled); the other states are just a given from correct -use of the interface.<br> - -<br> - -Essentially, the state changes are governed by the methods <span style="font-style: italic;">beginTask()</span>, <span style="font-style: italic;">done()</span> and <span style="font-style: italic;">setCanceled()</span>, -plus the implicit initial state of a new instance. Note that for the -purposes discussed here the perceived ‘changes in state’ -occurring as a result from calling <span style="font-style: italic;">worked()</span> is not relevant. A separate discussion below details how to deal with <span style="font-style: italic;">worked()</span> calls.<br> - -<br> - -NB: The states described here are not any ‘officialese’ -that can be found as constants or anything like that; they’re -only here to serve so they can be used for discussion.<br> - -<br> - -<ul> - - <li>PRISTINE<br> - -This is the initial state of a newly created instance of an <span style="font-style: italic;">IProgressMonitor</span> implementation, i.e. before <span style="font-style: italic;">beginTask()</span> -has been called. In principle a given implementation may handle a -single instance such that it is reusable and reverted back to the -PRISTINE state after a done() call, but that is opaque from the point -of the contract. In this state it should be essentially correct and -possible to go to any of the other states, but the typical and expected -transition should be from PRISTINE to IN_USE as a result from a -successful beginTask() call. The transition to FINISHED should result -only in a very particular situation, see more below.</li> +<h2>1.0 Introduction</h2> + +So you're writing a long-running method. It takes an IProgressMonitor as a parameter. You want it to +report smooth progress, be cancellable from the UI thread and accomplish this with minimal boilerplate. +Unfortunately, most of the examples you've seen either omit progress reporting entirely or contain +more boilerplate than code. +<p> +Is it possible to get the smooth progress reporting without all that boilerplate? If so, what are the +responsibilities of your method and what are the responsibilities of its caller? This article should help. + +<h2>2.0 Using SubMonitor</h2> + +Use SubMonitor. If you remember nothing else from this article, remember that. +<p> +SubMonitor is an implementation of IProgressMonitor that simplifies everything related to progress +reporting. Whenever you write a method that accepts an IProgressMonitor, the first thing you should +do is convert it to a SubMonitor using <code>SubMonitor.convert(...)</code>. + +<h3>2.1 Allocating Ticks</h3> +<p> +There are several overloads for <code>convert</code> and most of them accept a number of ticks as an +argument. These are said to "allocate ticks". A tick is a unit of work to be performed. Allocating +ticks distributes those ticks across the remaining space of the progress monitor but doesn't +actually report any progress. It basically sets the denominator that will be used for subsequent +progress reporting. +<p> +How many ticks do you need? Take a look at all the slow things your method does and assign them +a number of ticks based on how long you think they will take. Any method you call that takes a +progress monitor should be considered a "slow thing". If you think one thing will take longer +than another, give it more ticks. If you think it will take twice as long, give it twice as many. +When you're done, add up all the ticks. That's how many you should allocate. Ticks only have +meaning relative to other ticks on the same monitor instance: their absolute value doesn't +mean anything. +<p> +There are several methods which allocate ticks. Normally you'll allocate them at construction-time +using <code>SubMonitor.convert(...)</code> but this only works if you're creating a new +SubMonitor instance. +<p> +Sometimes you'll want to allocate (or reallocate) the ticks on an existing +monitor in which case you'll want <code>SubMonitor.setWorkRemaining</code>. You can +call this as often as you like on a SubMonitor instance. When you do, any remaining unconsumed +space on the monitor is divided into the given number of ticks and any previously-allocated +ticks are forgotten. +<p> +The last method that allocates ticks is called <code>beginTask</code>. It's used as part of the +progress reporting framework and you rarely need to call it directly. You'll see this used +a lot in older code and we'll get more into it later. For now, it's best to avoid it in new +code unless you're implementing your own IProgressMonitor. + +<h3>2.2 Consuming Ticks</h3> + +Once you've allocated the ticks you can consume them. Consuming ticks is what reports progress. +Let's say you allocate 50 ticks and then consume 3 of them. That means your progress bar will +be 3/50 of the way across, or 6%. Note that you must allocate ticks before you can consume them +on any given progress monitor instance. Attempting to consume ticks without allocating them is an error. +<p> +There are several methods on SubMonitor which consume ticks. Namely, <code>split(...)</code>, +<code>newChild(...)</code>, and <code>worked(...)</code>. Practically speaking the only one you +need is <code>split(...)</code>. +<p> +I'll be using <code>split(...)</code> for most of the code examples in this article, but it is new +in Eclipse 4.6. If your code is meant to work on earlier versions of Eclipse you should use +<code>newChild(...)</code> instead of <code>split(...)</code>. The two do pretty much the same +thing except that <code>split(...)</code> also performs cancellation checks. +<p> + +<h3>2.3 Split</h3> + +<code>split(...)</code> doesn't immediately consume the ticks. It uses the ticks to create a new +child monitor but first it fully consumes any leftover ticks in any previous children +of the same parent. +<p> +I'll demonstrate with an example: +<pre> + void myMethod(IProgressMonitor monitor) { + // No ticks have been allocated yet, so we can't consume them. + SubMonitor subMonitor = SubMonitor.convert(monitor, 100); + + // monitor is now being managed by subMonitor. We shouldn't use it directly again. + // subMonitor has 100 ticks allocated for it and we can start consuming them. + + SubMonitor child1 = subMonitor.split(10); + + // subMonitor now has 90 ticks allocated. 10 of the original 100 were used to build child1. + // child1 has no ticks allocated for it yet so we can't consume ticks from it yet. + + child1.setWorkRemaining(100); + + // child1 now has 100 ticks allocated for it. Consuming 1 tick from child1 now would + // advance the root monitor by 0.1%. + + SubMonitor grandchild1 = child1.split(50); + + // child1 now has 50 ticks allocated. + + SubMonitor grandchild2 = child1.split(50); + + // Allocating a new grandchild from child1 has caused grandchild1 to be consumed. + // Our root progress monitor now shows 5% progress. + + SubMonitor child2 = subMonitor.split(40); + + // Allocating a new child from subMonitor has caused child1 and grandchild2 to be + // consumed. Our root progress monitor now shows 10% progress. + + SubMonitor child3 = subMonitor.split(10); + + // Child2 was consumed. The root progress monitor now shows 50% progress. + + SubMonitor child4 = subMonitor.split(40); + + // Child3 was consumed. The root progress monitor now shows 60% progress. + } +</pre> + +<h3>2.4 Cancellation</h3> + +Long-running methods should check the value of <code>IProgressMonitor.isCanceled()</code> +periodically. If it returns true, they should terminate cleanly and throw +<code>OperationCanceledException</code>. In Eclipse 4.5 and earlier, this was done with +explicit cancellation checks like this: + +<pre> + if (monitor.isCanceled()) { + throw new OperationCanceledException(); + } +</pre> + +Unfortunately, these sorts of cancellation checks are cumbersome and can become a +performance bottleneck if performed too frequently. +<p> +In Eclipse 4.6 and on, cancellation checks are be performed implicitly by +<code>SubMonitor.split(...)</code>. Code should be migrated to use <code>split</code> +wherever possible and explicit cancellation checks should be deleted. +<p> +So how does <code>split</code> work and how does it replace explicit cancellation checks? +It's basically just a helper that does the same thing as <code>newChild</code> but +additionally includes a cancellation check. Internally, split does something like this +(pseudocode): + +<pre> + SubMonitor split(int ticks) { + if (checking_cancelation_now_wouldnt_cause_a_performance_problem()) { + if (isCanceled()) { + throw new OperationCanceledException(); + } + } + return newChild(ticks); + } +</pre> + +<h2>3.0 Examples</h2> + +Lets look at some examples of common progress reporting patterns. + +<h3>3.1 Calling child methods</h3> + +Most long-running operation will need to call out to other long-running operations. + +<pre> + void doSomething(IProgressMonitor monitor) { + // Convert the given monitor into a SubMonitor instance. We shouldn't use the original + // monitor object again since subMonitor will consume the entire monitor. + SubMonitor subMonitor = SubMonitor.convert(monitor, 100); - <li>IN_USE<br> + // Use 30% of the progress to do some work + someChildTask(subMonitor.split(30)); -This is the state the monitor after the first and <span style="text-decoration: underline;">only</span> call to <span style="font-style: italic;">beginTask()</span>. This is one of those things that are very easy to get wrong; contract wise, <span style="font-style: italic;">beginTask()</span> -can and should only be called at most once for a given instance. A more -detailed discussion on the code pattern required to deal with this -obligation can be found below.</li> + // Use the remaining 70% of the progress to do some more work + someChildTask(subMonitor.split(70)); + } +</pre> - <li>FINISHED<br> +<h3>3.2 Branches</h3> -The transition to this state is achieved by calling <span style="font-style: italic;">done()</span>. As with <span style="font-style: italic;">beginTask()</span>, <span style="font-style: italic;">done()</span> should only be called <span style="text-decoration: underline;">once</span> and should <span style="text-decoration: underline;">always</span> be called on a monitor when <span style="font-style: italic;">beginTask()</span> has been called (i.e. it is ok to not call <span style="font-style: italic;">done()</span> -only if the monitor is still in the PRISTINE state). Again, the -discussion below is more detailed on how to ensure proper protocol.</li> +Sometimes some long-running piece of code is optional. If the optional piece is skipped, you still want the +total work to add up to 100%, but you also don't want to waste a portion of the progress monitor just to +make the ticks line up. One approach is to do the optional part first and then use setWorkRemaining to +redistribute the remaining ticks. - <li>CANCELED<br> - -Actually, this state is slightly murky; it’s possible that -canceled/not canceled should be tracked separately from the others. -But, contract wise it should be adequate if this state is either -achieved directly from PRISTINE and just left that way, or if <span style="font-style: italic;">done()</span> is called (likely as a result of detecting the canceled status), it is cleared and the monitor then transitions to FINISHED.</li> +<pre> + void doSomething(IProgressMonitor monitor) { + SubMonitor subMonitor = SubMonitor.convert(monitor, 100); + + if (condition1) { + doSomeWork(subMonitor.split(20)); + } + + // Don't report any work, but ensure that we have 80 ticks remaining on the progress monitor. + // If we already consumed ticks in the above branch, this is a no-op. Otherwise, the remaining + // space in the monitor is redistributed. + subMonitor.setWorkRemaining(80); + + if (condition2) { + doMoreWork(subMonitor.split(40)); + } + + subMonitor.setWorkRemaining(40) + + doSomeMoreWork(subMonitor.split(40)); + } +</pre> + +This approach works well enough in most cases and requires minimal boilerplate but the progress it reports +can sometimes be uneven if the method ends with a bunch of conditionals that are often skipped. Another +approach is to count the number of ticks in advance: + +<pre> + void doSomething(IProgressMonitor monitor) { + int totalTicks = 0; + if (condition1) { + totalTicks += OPERATION_ONE_TICKS; + } + if (condition2) { + totalTicks += OPERATION_TWO_TICKS; + } + if (condition3) { + totalTicks += OPERATION_THREE_TICKS; + } + // Allocate a different number of ticks based on which branches we expect to execute. + SubMonitor subMonitor = SubMonitor.convert(monitor, totalTicks); + + if (condition1) { + doSomeWork(subMonitor.split(OPERATION_ONE_TICKS)); + } + if (condition2) { + doSomeWork(subMonitor.split(OPERATION_TWO_TICKS)); + } + if (condition3) { + doSomeWork(subMonitor.split(OPERATION_THREE_TICKS)); + } + } +</pre> + +This will usually report smoother progress, but due to the extra complexity it's usually best to only +use this pattern if otherwise jerkiness of reported progress would be annoying to the user. + +<h3>3.3 Loops</h3> + +This example demonstrates how to report progress in a loop where every iteration takes roughly +the same amount of time.</p> + +<pre> + void doSomething(IProgressMonitor monitor, Collection someCollection) { + SubMonitor subMonitor = SubMonitor.convert(monitor, 100); + + // Create a new progress monitor for the loop. + SubMonitor loopMonitor = subMonitor.split(70).setWorkRemaining(someCollection.size()); + + for (Object next: someCollection) { + // Create a progress monitor for each loop iteration. + SubMonitor iterationMonitor = loopMonitor.split(1); + + doWorkOnElement(next, iterationMonitor); + } + + // The original progress monitor can be used for further work after the loop terminates. + doSomeWork(subMonitor.split(30)); + } +</pre> + +<h3>3.4 Skipping elements in a loop</h3> + +Sometimes some elements from a loop will be skipped. In that case, it's better to use the space +in the progress monitor to report the work done for the long-running elements rather than the +fast elements. + +<pre> + void doSomething(IProgressMonitor monitor, Collection someCollection) { + SubMonitor loopMonitor = SubMonitor.convert(monitor, someCollection.size()); + + int remaining = someCollection.size(); + for (Object next : someCollection) { + loopMonitor.setWorkRemaining(remaining--); + + if (shouldSkip(next)) { + continue; + } + + // Create a progress monitor for each loop iteration. + SubMonitor iterationMonitor = loopMonitor.split(1); + doWorkOnElement(next, iterationMonitor); + } + } +</pre> + +This works well enough if skipped elements are rare but if most of the elements are skipped, this pattern +will tend to make poor use of the end of the monitor. If many elements might be skipped, it's better to +pre-filter the list like this: + +<pre> + void doSomething(IProgressMonitor monitor, Collection someCollection) { + List filteredElements = someCollection + .stream() + .filter(next -> !shouldSkip(next)) + .collect(Collectors.toList()); + + SubMonitor loopMonitor = SubMonitor.convert(monitor, filteredElements.size()); + for (Object next : filteredElements) { + doWorkOnElement(next, loopMonitor.split(1)); + } + } +</pre> + +<h3>3.5 Queues</h3> + +What if you're performing a depth-first search or some other algorithm where more work is +continually discovered as you proceed? Try putting the work-discovered-so-far in a queue +and using the size of the queue to allocate ticks on your monitor. When using this pattern, +always make sure you never allocate less than some minimum number of ticks or you'll consume +the entire progress monitor on the first few iterations of your algorithm. + +<pre> + void depthFirstSearch(IProgressMonitor monitor, Object root) { + SubMonitor subMonitor = SubMonitor.convert(monitor); + ArrayList queue = new ArrayList(); + queue.add(root); + + while (!queue.isEmpty()) { + // Allocate a number of ticks equal to the size of the queue or some constant, + // whatever is larger. This constant prevents the entire monitor from being consumed + // at the start when the queue is very small. + subMonitor.setWorkRemaining(Math.max(queue.size(), 20)); + Object next = queue.remove(queue.size() - 1); + processElement(next, subMonitor.split(1)); + queue.addAll(getChildrenFor(next)); + } + } +</pre> + +<h3>3.6 Unknown progress</h3> + +What about those situations where you really have no idea how much work to report or how +long it will take? Try allocating a small percentage of the remaining space on each iteration. + +<pre> + void unknownProgress(IProgressMonitor monitor) { + SubMonitor subMonitor = SubMonitor.convert(monitor); + while (hasMore()) { + // Use 1% of the remaining space for each iteration + processNext(subMonitor.setWorkRemaining(100).split(1)); + } + } +</pre> + +Notice the idiom <code>setWorkRemaining(denominator).split(numerator)</code>. This can be used +at any point to consume numerator/denominator of the remaining space in a monitor. + +<h3>3.7 Naming conventions</h3> + +In these examples we've used the same naming convention that has been used within +the Eclipse platform. You may wish to use the same convention to help convey the purpose of your +progress monitors: +<ul> +<li><b>subMonitor</b> - a SubMonitor that tracks the entire progress of the method.</li> +<li><b>loopMonitor</b> - a SubMonitor that tracks the progress of a single loop. If a method contains + multiple loops, this is used as a suffix.</li> +<li><b>iterationMonitor</b> - a SubMonitor that tracks the progress of a single iteration of a loop. + If there are multiple nested loops, this may be used as a suffix.</li> +<li><b>monitor</b> - an IProgressMonitor that is passed as a method argument.</li> </ul> -<br> - -Now, one contract pattern described above is that if <span style="font-style: italic;">beginTask()</span> is ever called, <span style="font-style: italic;">done()</span> MUST be called. This is achieved by always following this code pattern (all code is simplified):<br> +</ul> -<pre style="margin-left: 40px;">monitor = … // somehow get a new progress monitor which is in a pristine state<br>// figure some things out such as number of items to process etc…<br>try<br> {<br> monitor.beginTask(…)<br> // do stuff and call worked() for each item worked on, and check for cancellation<br> }<br>finally<br> {<br> monitor.done()<br> } <br></pre> +<h2>4.0 Responsibilities of callers and callees</h2> -The important thing here then is to ensure that <span style="font-style: italic;">done()</span> is always called (by virtue of being in the <span style="font-style: italic;">finally</span> clause) but (normally) only if <span style="font-style: italic;">beginTask()</span> has been successfully called (by virtue of being the first thing called in the <span style="font-style: italic;">try</span> clause). There is a small loophole that could cause <span style="font-style: italic;">done()</span> -to be called without the monitor actually transitioning from PRISTINE -to IN_USE. This loophole can with this pattern only happen if a -particular <span style="font-style: italic;">beginTask()</span> implementation throws an unchecked exception (The interface itself declares no <span style="font-style: italic;">throws</span> -clause) before it internally makes a note of the state change (if the -specific implementation even tracks state in this manner and/or is too -loose in its treatment of the interface contract).<br> -<br> -<img style="width: 62px; height: 13px;" alt="tip" src="images/note.gif"> Arguably, you should <span style="font-weight: bold; text-decoration: underline;">always</span> strive for calling <span style="font-style: italic;">beginTask()</span>/<span style="font-style: italic;">done()</span>. -The reasons for this are buried in the fact that you in principle never -know when you are being called as a subtask. If you don't 'complete' -the monitor, the parent can end up with an incorrect count for its own -task. The full rationale is covered more below, in the section <a href="#Ensure_to_always_complete_your_monitor">"Ensure to always complete your monitor!"</a>.<br> +Imagine you need to invoke another method that accepts an IProgressMonitor. +What are the responsibilities of the caller and what are the responsibilities +of the callee? +<p> +The caller: +<ul> +<li><b>Will</b> pass an IProgressMonitor instance which has not had <code>beginTask</code> invoked on it yet + (or an implementation such as SubMonitor which permits beginTask to be invoked multiple times).</li> +<li><b>Will not</b> expect that the callee invokes <code>done()</code> on the monitor. The + caller must either use an SubMonitor (or a similar implementation which does not require <code>done()</code> + to be invoked), or it must take responsibility for calling <code>done()</code> on the monitor after the + callee has finished.</li> +<li><b>Will not</b> pass in a null monitor unless the JavaDoc for the callee says that it accepts null.</li> +<li><b>Will</b> pass in a monitor which ignores the String argument to <code>beginTask</code> unless the JavaDoc + for the callee says that it requires otherwise.</li> +</ul> +The callee: +<ul> +<li><b>Will</b> call <code>beginTask</code> 0 or 1 times on the monitor, at its option.</li> +<li><b>Will not</b> promise to call <code>done()</code> on the monitor, although it is allowed to do so.</li> +<li><b>Will not</b> call <code>setCanceled</code> on the monitor.</li> +<li><b>Will not</b> accept a null monitor unless its JavaDoc says otherwise.</li> +<li><b>Will not</b> expect the caller to do anything with the string passed to <code>beginTask</code> unless + its JavaDoc says otherwise.</li> +</ul> -<h3>Delegating use of a progress monitor to subtasks</h3> +In practice, callers will be using SubMonitor wherever possible and method +implementations will not be calling <code>done()</code>. This means that the only +calls to <code>done()</code> will occur in root-level methods (methods which obtain +their own IProgressMonitor via some mechanism other than having it passed it as a method parameter). -Above for the IN_USE state I mentioned that it’s very easy to get things wrong; <span style="font-style: italic;">beginTask()</span> -should never be called more than once. This frequently happens in code -that doesn’t correctly understand the implications of the -contract. Specifically, such code pass on the same instance it has been -given to subtasks, and those subtasks; not aware that the caller -already has begun following the contract, also tries following the -contract in the expected manner – i.e. they start by doing a <span style="font-style: italic;">beginTask()</span>.<br> +<h2>5.0 Different versions of Eclipse</h2> -<br> +In Eclipse 4.5 (Mars) and earlier, it was standard practice for method implementations +to invoke <code>done()</code> on any monitor passed in as an argument and for the caller +to rely upon this fact. +<p> +In Eclipse 4.6 (Neon), method implementations should still invoke <code>done()</code> if +they did so previously. Callers are also required to either invoke <code>done()</code> +or select a monitor implementation like SubMonitor which doesn't require the use of +<code>done()</code>. +<p> +In Eclipse 4.7 (Oxygen) and higher, method implementations are not required to invoke +<code>done()</code>. Callers must either invoke <code>done()</code> or select a monitor +implementation like SubMonitor which doesn't require the use of <code>done()</code>. -Thus, passing on a monitor instance is almost always wrong unless the -code knows exactly what the implications are. So the rule becomes: In -the general case, a piece of code that has received a progress monitor -from a caller should always assume that the instance they are given is -theirs and thus completely follow the <span style="font-style: italic;">beginTask()</span>/<span style="font-style: italic;">done()</span> -protocol, and if it has subtasks that also needs a progress monitor, -they should be given their own monitor instances through further use of -the <span style="font-style: italic;">SubProgressMonitor</span> implementation that wraps the ‘top-level’ monitor and correctly passes on <span style="font-style: italic;">worked()</span> calls etc (more on this below).<br> +<h3>5.1 Changes in Eclipse 4.6</h3> -<br> +The following changes were made in Eclipse 4.6: -Sample code to illustrate this:<br> +<ul> +<li>SubMonitor now has a static <code>done(IProgressMonitor)</code> method that can be used to + call <code>done()</code> on a possibly-null IProgressMonitor instance.</li> +<li>The recommended policy for checking cancellation has changed. Rather than + using explicit cancellation checks, clients should rely on the implicit + cancellation checks done by SubMonitor.split.</li> +<li>SubProgressMonitor is now deprecated.</li> +</ul> -<div style="margin-left: 40px;"> -<pre>monitor = … // somehow get a new progress monitor which is in a pristine state<br>// figure some things out such as number of items to process etc…<br>try<br> {<br> monitor.beginTask(…)<br> // do stuff and call worked() for each item processed, and check for cancellation<br> …<br> // farm out a piece of the work that is logically done by ‘me’ to something else<br> someThing.doWork(new SubProgressMonitor(monitor,…))<br> // farm out another piece of the work that is logically done by ‘me’ to something else<br> anotherThing.doWork(new SubProgressMonitor(monitor,…))<br> }<br>finally<br> {<br> monitor.done()<br> }<br></pre> +As of Eclipse 4.6 (Neon), any client code that obtains a root monitor (any monitor +that isn't passed in as a method argument) is responsible for invoking <code>done()</code> +on that monitor. It must not rely on the methods it calls to invoke +<code>done()</code>. Please see the +<a href="https://www.eclipse.org/eclipse/development/porting/eclipse_4_6_porting_guide.html">Migration guide</a> +for more information on how to locate such code. +<p> +Method implementations that previously invoked <code>done()</code> should +continue to do so, since the root monitors need to be updated first. + +<h3>5.2 Migrating from SubProgressMonitor to SubMonitor</h3> + +Eclipse 3.2 and earlier used SubProgressMonitor for nesting progress monitors. This class is +deprecated as of Eclipse 4.6 (Neon). It has been replaced by SubMonitor. +<p> +The process for converting code which used SubProgressMonitor into SubMonitor is: +<ul> +<li>Calls to <code>IProgressMonitor.beginTask</code> on the root monitor should be replaced by a call + to <code>SubMonitor.convert</code>. Keep the returned SubMonitor around as a local variable and refer + to it instead of the root monitor for the remainder of the method.</li> +<li>All calls to <code>new SubProgressMonitor(IProgressMonitor, int)</code> should be replaced by calls to + <code>SubMonitor.split(int)</code>.</li> +<li>If a SubProgressMonitor is constructed using the SUPPRESS_SUBTASK_LABEL flag, replace it with the + two-argument version of <code>SubMonitor.split(int, int)</code> using <code>SubMonitor.SUPPRESS_SUBTASK</code> + as the second argument.</li> +<li>It is not necessary to call done on an instance of <code>SubMonitor</code>.</li> +<li>There is no drop-in replacement for PREPEND_MAIN_LABEL_TO_SUBTASK. PREPEND_MAIN_LABEL_TO_SUBTASK + made use of string arithmetic that didn't handle translation well. Clients that were using this + previously should switch to using fully-formatted task labels instead. +</ul> -</div> +Note that <code>SubMonitor.convert(monitor, ticks)</code> is <b>not</b> a direct replacement for +<code>new SubProgressMonitor(monitor, ticks)</code>. The former fully consumes a +monitor which hasn't had ticks allocated on it yet and creates a new monitor with the given +number of ticks allocated. The latter consumes only the given number of ticks from an input +monitor which has already had ticks allocated and produces a monitor with no ticks allocated. +If you attempt to do a search-and-replace of one to the other, your progress reporting won't work. + +<h3>5.3 Changes in Eclipse 4.7</h3> + +In Eclipse 4.7 (Oxygen) and higher, method implementations that receive a progress monitor are +not required to invoke <code>done()</code> on it. Such calls may be removed if they were present +in earlier versions. +<p> +Methods in plugins that are also intended for use with earlier Eclipse versions should continue calling +<code>done()</code> as long as those earlier Eclipse versions are still being supported by the plugin. + +<h2>6.0 The protocol of <i>IProgressMonitor</i></h2> + +This section is for those of you doing something advanced. What if you want to implement your own +IProgressMonitor or can't use the SubMonitor helper class? In that case you'll need +to work with the IProgressMonitor contracts directly. +<p> +An IProgressMonitor instance can be in one of three states. Any given +implementation may or may not track state changes and may or may +not do anything about them. +<p> + +<b>UNALLOCATED</b> +<p> +This is the initial state of a newly created <code>IProgressMonitor</code> instance, +before <code>beginTask()</code> has been called to allocate ticks. Attempting to +call <code>worked()</code> or otherwise consume ticks from a monitor in this state is an error. +From here the monitor can enter the ALLOCATED state as a result of a <code>beginTask()</code> +call or the FINISHED <code>done()</code> state as a result of a call to <code>done()</code>. +<p> +<b>ALLOCATED</b> +<p> +The monitor enters this state after the first and only call to <code>beginTask()</code> +has allocated ticks for the monitor. Attempting to call <code>beginTask()</code> in this +state is an error. From here the monitor can enter the FINISHED state as a result of a +<code>done()</code> call. +<p> +<b>FINISHED</b> +<p> +The monitor enters this state after the first call to <code>done()</code>. +Unlike <code>beginTask()</code>, <code>done()</code> may be called any number of times. +However, only the first call to <code>done()</code> has any effect. +Reporting work or calling <code>beginTask()</code> on a FINISHED monitor is a programming +error and will have no effect. Unless the implementation says otherwise, <code>done()</code> must always +be called on a monitor once <code>beginTask()</code> has been called. +It is not necessary to call <code>done()</code> on a monitor in the UNALLOCATED +state. +</ul> -Note that each <span style="font-style: italic;">doWork()</span> call gets a new instance of a <span style="font-style: italic;">SubProgressMonitor</span>; such instances can and should not be reused for all the protocol reasons already discussed.<br> - -<br> - -The only time a single instance of a monitor passed to, or retrieved -by, a certain piece code can be reused in multiple places (e.g. -typically methods called by the original receiver), is when the code in -such methods is so intimately coupled so that they in effect -constitute a single <span style="font-style: italic;">try</span>/<span style="font-style: italic;">finally</span> block. Also, for this to work each method must know exactly who does <span style="font-style: italic;">beginTask()</span>/<span style="font-style: italic;">done()</span> calls, and also (don’t forget this) how many work items they represent of the total reported to <span style="font-style: italic;">beginTask()</span> -so that they can make the correct reports. Personally, I believe this -is generally more trouble than it’s worth – always follow -the regular pattern of one receiver, one unique monitor instead and the -code as a whole is more maintainable.<br> - -<h3>Managing the item count</h3> - -This section is about how to do the initial <span style="font-style: italic;">beginTask()</span> call and report the amount of total work expected, and then ideally report <span style="text-decoration: underline;">exactly</span> -that many items to the monitor. It is ok to end up not reporting all -items in one particular case: when the job is aborted (due to -cancellation by user, an exception thrown and so on) – this is -normal and expected behavior and we will wind up in the <span style="font-style: italic;">finally</span> clause where <span style="font-style: italic;">done()</span> is called.<br> - -<br> - -It is however sloppy technique to ‘just pick a number’ for the total and then call <span style="font-style: italic;">worked()</span>, -reporting a number and hope that the total is never exceeded. Either -way this can cause very erratic behavior of the absolute top level and -user visible progress bar (it is for a human we’re doing this -after all) – if the total is too big compared to the actual items -reported, a progress bar will move slowly, perhaps not at all due to -scaling and then suddenly (at the <span style="font-style: italic;">done()</span> -call) jump directly to completed. If the total is too small, the bar -will quickly reach ’100%’ or very close to it and then stay -there ‘forever’. <br> - -<br> - -So, first and foremost: do <span style="text-decoration: underline;">not</span> -guess on the number of work items. It’s a simple binary answer: -either you know exactly how many things that will be -processed…or you don’t know. It IS ok to not know! If you -don't know, just report <span style="font-style: italic;">IProgressMonitor.UNKNOWN</span> as the total number, call <span style="font-style: italic;">worked()</span> -to your hearts content and a clever progress monitor implementation -will still do something useful with it. Note that each (sub)task can -and should make its own decision on what it knows or not. If all are -following the protocol it will ensure proper behavior at the outer, -human visible end. A heads up though: never call the <span style="font-style: italic;">SubProgressMonitor(parentMonitor, subticks)</span> constructor using <span style="font-style: italic;">IProgressMonitor.UNKNOWN</span> for subticks - this is wrong! More on this later.<br> - -<h3>How to call <span style="font-style: italic;">beginTask()</span> and <span style="font-style: italic;">worked()</span></h3> - -There are typically two basic patterns where you know how many items -you want to process: either you are going to call several different -methods to achieve the full result, or you are going to call one method -for each instance in a collection of some sort. Either way you know the -total item count to process (the number of methods or the size of the -collection). Variations of this are obviously combinations of these -basic patterns so just multiply and sum it all up.<br> - -<br> - -There is sometimes a benefit of scaling your total a bit. So, instead of reporting ‘3’ as the total (and do <span style="font-style: italic;">worked(1)</span> for each item) you may consider scaling with, say 1000, and reporting ‘3000’ instead (and do <span style="font-style: italic;">worked(1000)</span> -for each item). The benefit comes up when you are farming out work to -subtasks through a SubProgressMonitor; since they may internally have a -very different total, especially one that is much bigger than your -total, you give them (and the monitor instance) some ‘room’ -to more smoothly consume and display the allotment you’ve given -them (more explanations below on how to mix <span style="font-style: italic;">worked()</span> and <span style="font-style: italic;">SubProgressMonitor</span> -work below). Consider that you say ‘my total is 3’ and you -then give a subtask ‘1’ of these to consume. If the subtask -now will report several thousand <span style="font-style: italic;">worked()</span> -calls, and assuming that the actual human visible progress bar also has -the room, the internal protocol between a SubProgressMonitor and -it’s wrapped monitor will scale better and give more smooth -movement if you instead would have given it ‘1000’ out of -‘3000’. Or not - the point is really that you don't know -what monitor implementation will be active, all you can do is give some -information. How it's then displayed in reality is a matter of how -nifty the progress monitor implementation is.<br> - -<br> - -A sample of simple calls:<br> - -<pre style="margin-left: 40px;">monitor = … // somehow get a new progress monitor which is in a pristine state<br>int total = 3 // hardcoded and known<br>try<br> {<br> monitor.beginTask(total)<br> <br> // item 1<br> this.doPart1()<br> monitor.worked(1)<br><br> // item 2<br> this.doPart2()<br> monitor.worked(1)<br><br> // item 3<br> this.doPart3()<br> monitor.worked(1)<br> }<br>finally<br> {<br> monitor.done()<br> }</pre> - -No reason to scale and no collection to dynamically compute.<br> - -<br> - -A more elaborate sample:<br> - -<pre style="margin-left: 40px;">monitor = … // somehow get a new progress monitor which is in a pristine state<br>int total = thingyList.size() * 3 + 2<br>try<br> {<br> monitor.beginTask(total)<br> <br> // item 1<br> this.doBeforeAllThingies()<br> monitor.worked(1)<br> <br> // items 2 to total-1<br> for (Thingy t : thingyList)<br> {<br> t.doThisFirst()<br> monitor.worked(1)<br> t.thenDoThat()<br> monitor.worked(1)<br> t.lastlyDoThis()<br> monitor.worked(1)<br> }<br><br> // final item <br> this.doAfterAllThingies()<br> monitor.worked(1)<br> }<br>finally<br> {<br> monitor.done()<br> }<br></pre> - -<h3>Mixing straightforward calls with subtasks</h3> - -I was initially confused by how to report progress when I farmed out -work to subtasks. I experienced ‘reporting too much work’ -since I didn’t understand when to call and when to not call <span style="font-style: italic;">worked()</span>. Once I caught on, the rule is very simple however: calling a subtask with a <span style="font-style: italic;">SubProgressMonitor</span> is basically an implicit call to <span style="font-style: italic;">worked()</span> with the amount allotted to the subtask. So instead of this:<br> - -<pre style="margin-left: 40px;">monitor = … // somehow get a new progress monitor which is in a pristine state<br>int scale = 1000<br>int total = 3 // hardcoded and known<br>try<br> {<br> monitor.beginTask(total * scale)<br> <br> // item 1<br> this.doPart1()<br> monitor.worked(1 * scale)<br><br> // item 2<br> this.doPart2(new SubProgressMonitor(monitor, 1 * scale)) // allot 1 item<br> monitor.worked(1 * scale) // WRONG! Not needed, already managed by the SubProgressMonitor<br><br> // item 3<br> this.doPart3()<br> monitor.worked(1 * scale)<br> }<br>finally<br> {<br> monitor.done()<br> }</pre> - -You should just leave out the second call to <span style="font-style: italic;">worked()</span>.<br> - -<h4><img style="width: 62px; height: 13px;" alt="Tip" src="images/note.gif"> Never pass <span style="font-style: italic;">IProgressMonitor.UNKNOWN</span> (or any other <span style="text-decoration: underline;">negative</span> value) when creating a <span style="font-style: italic;">SubProgressMonitor()</span> wrapper!</h4> - -A -situation I just the other day experienced was when doing an <span style="font-style: italic;">IProgressMonitor.UNKNOWN</span> -number of things - I needed to call a subtask, and hence I set up to -call it using a <span style="font-style: italic;">SubProgressMonitor(parent, subticks)</span> but -I realized that I hadn't ever considered how the sub monitor should be -created - how many subticks it should be given - in the unknown case. I -figured it should be ok to pass <span style="font-style: italic;">IProgressMonitor.UNKNOWN</span> there also. However, when -later trying my code I saw to my horror that my progress bar went -backwards! Not the effect I figured on...<br> - -<br> - -As it turns out, this is because the implementation (as of Eclipse -3.2M3) blindly uses the incoming ticks as a scaling factor. However, it -goes haywire when it receives a negative value (and <span style="font-style: italic;">IProgressMonitor.UNKNOWN</span> happens to have a value of -1). It does computations with it, and it ends up calling <span style="font-style: italic;">worked()</span> -with negative values which my monitor tried to process...that code is -now fixed to be more resilient in such cases. I've filed <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=119018">bug #119018</a> to -request that <span style="font-style: italic;">SubProgressMonitor</span> handles it better and/or document that negative values is a bad idea for the constructor call.<br> - -<br> - -Whatever, passing <span style="font-style: italic;">IProgressMonitor.UNKNOWN</span> is incorrect in any case. If you have called <span style="font-style: italic;">beginTask()</span> using <span style="font-style: italic;">IProgressMonitor.UNKNOWN</span> you can gladly pass in any reasonable tick value to a <span style="font-style: italic;">SubProgressMonitor</span>, it will give the correct result.<br> -<h3><a name="Ensure_to_always_complete_your_monitor"></a>Ensure to always complete your monitor!</h3> -Consider the concept described in the previous section: the important -thing here is that basically, you say that you have three distinct and -logical things to do, and then you tick them off - but one of the ticks -is actually farmed out to a subtask through a <span style="font-style: italic;">SubProgressMonitor</span>. You don't really know how many distinct and logical things the subtask has to do, nor should you care. The mechanics of using a <span style="font-style: italic;">SubProgressMonitor</span> makes the advancement of one of <span style="font-weight: bold;">your</span> -ticks happen in the correct way. So, the end expectation is that -once you reach the end of your three things, the monitor you have, have -actually fulfilled the count you intended - the internal state of it -should reflect this: "the user said three things should happen and my -work count is now indeed '3'".<br> -<br> -But, as I recently found out, this can fail. Specifically, I blindly invoked <span style="font-style: italic;">IProject.build()</span> on a project which had no builders configured. To this method I sent in a <span style="font-style: italic;">SubProgressMonitor</span> -and allotted it one 'tick' of mine. But, as it turned out, internally -it never used the monitor it got, presumably because there was no work -to perform - not very unreasonable in a sense. However, this did have -the effect that one of my ticks never got, well, 'tocked' :-). I could -solve this specific problem by simply checking if there was any -builders configured, and if there were none, I simply advanced the tick -by <span style="font-style: italic;">worked(1)</span> instead. But, it requires me, the caller, to make assumptions on the internal workings of the subtask, which is never good.<br> -<br> -This is not a huge problem of course. But, I think it would make sense to always act the same. The code resulting from <span style="font-style: italic;">IProject.build()</span> could just call <span style="font-style: italic;">beginTask("", countOfBuilders)</span> regardless of if countOfBuilders was 0, iterate over the empty array or whatever, and then call <span style="font-style: italic;">done()</span>. This would correctly advance <span style="font-weight: bold;">my</span> tick. -<h3>Cancellation</h3> - -The sample code above does not show cancellation checks. However, it -is obviously recommended that users of a progress monitor actively -check for cancellation to timely break out of the operation. The more -(potentially) long-running, the more important of course. And remember: -you don't know if the operation is running in a context that allows it -to be canceled or not - so you just have to code defensively. A sample -of how it should look could be this:<br> - -<pre style="margin-left: 40px;">monitor = … // somehow get a new progress monitor which is in a pristine state<br>try<br> {<br> monitor.beginTask(thingyList.size())<br> <br> for (Thingy t : thingyList)<br> {<br> if(monitor.isCanceled())<br> throw new OperationCanceledException();<br> t.doSomething()<br> monitor.worked(1) <br> }<br> }<br>finally<br> {<br> monitor.done()<br>}<br></pre> - -<h3>The <span style="font-style: italic;">NullProgressMonitor</span></h3> - -A common pattern is to allow callers to skip sending a monitor, i.e. sending ‘<span style="font-style: italic;">null</span>’. A simple and convenient way to deal with such calls is this:<br> - -<pre style="margin-left: 40px;">public void doIt(IProgressMonitor monitor)<br>{<br> // ensure there is a monitor of some sort<br> if(monitor == null)<br> monitor = new NullProgressMonitor();<br><br> try<br> {<br> monitor.beginTask(thingyList.size())<br> <br> for (Thingy t : thingyList)<br> {<br> if(monitor.isCanceled())<br> throw new OperationCanceledException();<br> t.doSomething()<br> monitor.worked(1) <br> }<br> }<br>finally<br> {<br> monitor.done()<br> } <br>}<br></pre> - -<h2>Conclusion</h2> - -I believe that by diligently following these rules and patterns, you -will never have a problem in using the progress monitor mechanism. -Obviously, it requires implementations to follow the contract as well. -But remember, if you mistreat the protocol you will sooner or later end -up talking to a progress monitor implementation that is stern and will -simply throw an exception or give strange visual effects if you call -it’s <span style="font-style: italic;">beginTask()</span> one time too many. It’s essentially valid if the <span style="font-style: italic;">IProgressMonitor</span> interface description is to be believed – and you will get blamed by your customer… +<h3>6.1 Cancellation</h3> + +A monitor can become cancelled at any time due to activity on another thread. +The monitor indicates that it has been cancelled by returning +true from <code>isCanceled()</code>. When a long-running operation detects that +it has been cancelled, it should abort its operation cleanly. +<p> +It is technically possible to cancel a monitor by invoking +<code>setCanceled()</code> but you should +never do this. A long-running method that wishes to cancel itself it should +throw OperationCanceledException rather than invoking any particular method +on its monitor. + +<h3>6.2 Threading</h3> + +All implementations of IProgressMonitor are single-threaded by default. Unless the +JavaDoc for the specific monitor implementation says otherwise, all methods +must be invoked on the same thread that instantiated the monitor. If the concrete +type of the IProgressMonitor is unknown, it must be assumed to be single-threaded. + +<h3>6.3 Allocating ticks</h3> + +When using <code>beginTask</code> to allocate ticks on an IProgressMonitor, the caller should +always allocate at least 1000 ticks. Many root monitors use this value to set +the resolution for all subsequent progress reporting, even if the ticks are +later subdivided using a SubMonitor. +<p> +SubMonitor users don't need to worry about this since <code>SubMonitor.convert</code> +does this internally when converting an unknown monitor. +<p> + +<h3>6.4 Passing strings to beginTask</h3> + +At the lowest level, any method that needs to allocate ticks ends up calling +<code>beginTask</code>. Usually this is done indirectly by a call to +<code>SubMonitor.convert(...)</code>. Unfortunately, <code>beginTask</code> +also takes a string argument which root monitors use to set the task name. +<p> +Every long-running operation needs to allocate ticks but most don't want to +modify the task name. For this reason, most callers of <code>beginTask</code> +call it with the empty string or a temporary string that isn't intended +to be seen by the end user. Similarly, most implementations of +<code>beginTask</code> are expected to ignore the string argument. +<p> +The exception is root monitors. Many root monitors display the string argument +somewhere. For this reason, any code that obtains a root monitor is expected +to convert it to a form that will filter out the string argument +before passing it to another method. +<p> +The default expectation is that progress monitors passed as parameters will +do this filtering. Any method that receives a progress monitor and does not +want the <code>beginTask(...)</code> argument filtered must say so clearly in +its JavaDoc. + +<h3>6.5 IProgressMonitor.UNKNOWN</h3> + +IProgressMonitor.UNKNOWN is only supported by root monitors and should never +be used by method implementations which receive a progress monitor as a parameter. + +<h3>6.6 Task names and performance concerns</h3> + +As of the time of this writing (Eclipse 4.6), several of the root monitors +in Eclipse have expensive implementations of <code>setTaskName(...)</code> +and <code>subTask(...)</code>. Plugin authors are advised not to call +these methods more than 3 times per second. Doing so may introduce +performance problems. See +<a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=445802">Eclipse bug 445802</a> +for more information. +<p> </body> </html> |