Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Progress Reporting"

 
(5 intermediate revisions by one other user not shown)
Line 1: Line 1:
<p style="margin: 0.0px 0.0px 14.0px 0.0px; font: 18.0px Helvetica"><b>Are you working with Progress Monitors, and wish you did not have too?</b><br/>
+
{{Warning|This page has been migrated to https://github.com/eclipse-platform/eclipse.platform/blob/master/docs/Progress_Reporting.md.}}
<span style="font:10.0px Helvetica">''by Henrik Lindberg - originally posted on [http://henrik-eclipse.blogspot.com/2009/05/progress-monitor-patterns.html Eclipse by Planatery Transitions]''</span></p>
+
<p style="margin: 0.0px 0.0px 12.0px 0.0px; font: 12.0px Helvetica">I recently got to investigate issues in p2 why the progress bar did not move while it was very clear that things where happening as subtasks changed the text. Looking into what was going on made me find a new Progress Monitor anti pattern.</p>
+
<p style="margin: 0.0px 0.0px 12.0px 0.0px; font: 12.0px Helvetica">The problem is how to handle a case where you need to do something like this:</p>
+
<code>
+
<table border="0" cellpadding="3" cellspacing="0" bgcolor="#FFFFFF">
+
<tr>
+
<td  valign="top" align="left"><font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>for</b></font> <font color="#000000">(</font><font color="#7F0055"><b>int</b></font> <font color="#000000">i =</font> <font color="#990000">0</font><font color="#000000">; i &lt; candidates.length; i++</font><font color="#000000">)</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>if</b></font> <font color="#000000">(</font><font color="#000000">loadCandidate1</font><font color="#000000">(</font><font color="#000000">i</font><font color="#000000">))</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>break</b></font><font color="#000000">;</font></td>
+
</tr>
+
</table>
+
</code>
+
<!-- ======================================================== -->
+
<p style="margin: 0.0px 0.0px 12.0px 0.0px; font: 12.0px Helvetica"><br /></p>
+
<p style="margin: 0.0px 0.0px 12.0px 0.0px; font: 12.0px Helvetica">Where a call to <span style="font: 12.0px Monaco">loadCandidate</span> is potentially a long running task. The first loaded candidate means we are done.</p>
+
<p style="margin: 0.0px 0.0px 15.0px 0.0px; font: 14.0px Helvetica"><b>Antipattern<br /></b></p>
+
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 12px; margin-left: 0px; font: 12px Helvetica;"><span style="font-size: 12px; font-weight: normal;">Here is an implementation of the above example using the antipattern - i.e. "don't do this":</span></p>
+
<code>
+
<table border="0" cellpadding="3" cellspacing="0" bgcolor="#FFFFFF">
+
<tr>
+
<!-- start source code -->
+
<td nowrap="nowrap" valign="top" align="left"><font color="#FFFFFF">&nbsp;&nbsp;</font><font color="#7F0055"><b>public</b></font> <font color="#7F0055"><b>void</b></font> <font color="#000000">antiPattern</font><font color="#000000">(</font><font color="#000000">IProgressMonitor monitor</font><font color="#000000">) {</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">SubMonitor sub = SubMonitor.convert</font><font color="#000000">(</font><font color="#000000">monitor, candidates.length</font><font color="#000000">)</font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>for</b></font> <font color="#000000">(</font><font color="#7F0055"><b>int</b></font> <font color="#000000">i =</font> <font color="#990000">0</font><font color="#000000">; i &lt; candidates.length; i++</font><font color="#000000">) {</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>if</b></font> <font color="#000000">(</font><font color="#000000">loadCandidate</font><font color="#000000">(</font><font color="#000000">i, sub.newChild</font><font color="#000000">(</font><font color="#990000">1</font><font color="#000000">)))</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>break</b></font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">}</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;</font><font color="#000000">}</font><br />
+
<br />
+
<font color="#FFFFFF">&nbsp;&nbsp;</font><font color="#7F0055"><b>public</b></font> <font color="#7F0055"><b>boolean</b></font> <font color="#000000">loadCandidate</font><font color="#000000">(</font><font color="#7F0055"><b>int</b></font> <font color="#000000">index, IProgressMonitor monitor</font><font color="#000000">) {</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">SubMonitor sub = SubMonitor.convert</font><font color="#000000">(</font><font color="#000000">monitor,</font> <font color="#990000">1000</font><font color="#000000">)</font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>try</b></font> <font color="#000000">{</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>for</b></font> <font color="#000000">(</font><font color="#7F0055"><b>int</b></font> <font color="#000000">i =</font> <font color="#990000">0</font><font color="#000000">; i &lt;</font> <font color="#990000">1000</font><font color="#000000">; i++</font><font color="#000000">)</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">sub.worked</font><font color="#000000">(</font><font color="#990000">1</font><font color="#000000">)</font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#3F7F5F">// ...</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>return true</b></font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">}</font> <font color="#7F0055"><b>finally</b></font> <font color="#000000">{</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">monitor.done</font><font color="#000000">()</font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">}</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>return false</b></font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;</font><font color="#000000">}</font></td><!-- end source code -->
+
</tr>
+
</table>
+
</code>
+
<!-- =      END of automatically generated HTML code      = -->
+
<!-- ======================================================== --><br />
+
<p style="margin: 0.0px 0.0px 15.0px 0.0px; font: 14.0px Helvetica"><span style="font-size: 12px;">Well, what is wrong with this, you may ask… Well, the length of the progress bar will be divided into as many slots as there are candidates, and if the first candidate succeeds and uses its 1000 ticks, and the remaining candidates are never considered, we will end up reporting the 1000 ticks on a fraction of the overall progress bar. This means that for 10 candidates, you will see the progress-bar slowly go to about 10% of the overall length, to suddenly jump to 100%.</span></p>
+
<br/>
+
<p style="font: 14.0px Helvetica"><b>Good pattern</b></p>
+
<p style="margin: 0.0px 0.0px 12.0px 0.0px; font: 12.0px Helvetica">Here is the good pattern that makes use of the full progress bar:</p>
+
<code>
+
<table border="0" cellpadding="3" cellspacing="0" bgcolor="#FFFFFF">
+
<tr>
+
<td nowrap="nowrap" valign="top" align="left"><code><font color="#FFFFFF">&nbsp;&nbsp;</font><font color="#7F0055"><b>public</b></font> <font color="#7F0055"><b>void</b></font> <font color="#000000">goodPattern</font><font color="#000000">(</font><font color="#000000">IProgressMonitor monitor</font><font color="#000000">) {</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">SubMonitor sub = SubMonitor.convert</font><font color="#000000">(</font><font color="#000000">monitor,</font> <font color="#990000">1000</font><font color="#000000">)</font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>for</b></font> <font color="#000000">(</font><font color="#7F0055"><b>int</b></font> <font color="#000000">i =</font> <font color="#990000">0</font><font color="#000000">; i &lt; candidates.length; i++</font><font color="#000000">) {</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">sub.setWorkRemaining</font><font color="#000000">(</font><font color="#990000">1000</font><font color="#000000">)</font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>if</b></font> <font color="#000000">(</font><font color="#000000">goodLoadCandidate</font><font color="#000000">(</font><font color="#000000">i, sub.newChild</font><font color="#000000">(</font><font color="#990000">1000</font><font color="#000000">)))</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>break</b></font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">}</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;</font><font color="#000000">}</font><br />
+
<br />
+
<font color="#FFFFFF">&nbsp;&nbsp;</font><font color="#7F0055"><b>public</b></font> <font color="#7F0055"><b>boolean</b></font> <font color="#000000">goodLoadCandidate</font><font color="#000000">(</font><font color="#7F0055"><b>int</b></font> <font color="#000000">index, IProgressMonitor monitor</font><font color="#000000">) {</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">SubMonitor sub = SubMonitor.convert</font><font color="#000000">(</font><font color="#000000">monitor,</font> <font color="#990000">1000</font><font color="#000000">)</font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">sub.beginTask</font><font color="#000000">(</font><font color="#000000">null,</font> <font color="#990000">1000</font><font color="#000000">)</font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>try</b></font> <font color="#000000">{</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#3F7F5F">// … code that loads</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>for</b></font> <font color="#000000">(</font><font color="#7F0055"><b>int</b></font> <font color="#000000">i =</font> <font color="#990000">0</font><font color="#000000">; i &lt;</font> <font color="#990000">1000</font><font color="#000000">; i++</font><font color="#000000">)</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">sub.worked</font><font color="#000000">(</font><font color="#990000">1</font><font color="#000000">)</font><font color="#000000">;</font><br />
+
<br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>return true</b></font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">}</font> <font color="#7F0055"><b>finally</b></font> <font color="#000000">{</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#3F7F5F">// ignore errors</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">}</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>return false</b></font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;</font><font color="#000000">}</font></code></td><!-- end source code -->
+
</tr>
+
</table>
+
</code>
+
<!-- ======================================================== -->
+
<p style="font: 12.0px Helvetica">Notice how “setWorkRemaining” is called each time in the loop. This reallocates the remaining ticks, but there is no need to compute how many that actually remains, the child allocation of 1000 ticks will always give the child 1000 ticks to report, even if there were not enough ticks left in the parent. All the scaling is performed by the SubMonitor, so you don’t have to worry about it.</p>
+
<p style="font: 12.0px Helvetica; min-height: 14.0px">Now, let’s say that the routine you are calling do need to perform a bit of work even if it does not need all of its ticks. Don’t worry, that will work too as long as it is a small portion of the allocation. If you risk consuming a larger part, you may be better off doing a true partitioning of the progress-bar as shown in the next section.</p>
+
<br/>
+
<p style="font: 14.0px Helvetica"><b>Good pattern - regular loop</b></p>
+
<p style="margin: 0.0px 0.0px 12.0px 0.0px; font: 12.0px Helvetica">Here is the good pattern for a regular loop where each iteration does consume ticks</p>
+
<p style="font: 12.0px Monaco"></p>
+
<code>
+
<table border="0" cellpadding="3" cellspacing="0" bgcolor="#FFFFFF">
+
<tr>
+
<td nowrap="nowrap" valign="top" align="left"><code><font color="#FFFFFF">&nbsp;&nbsp;</font><font color="#7F0055"><b>public</b></font> <font color="#7F0055"><b>void</b></font> <font color="#000000">goodLoopPattern</font><font color="#000000">(</font><font color="#000000">IProgressMonitor monitor</font><font color="#000000">) {</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">SubMonitor sub = SubMonitor.convert</font><font color="#000000">(</font><font color="#000000">monitor, candidates.length*</font><font color="#990000">100</font><font color="#000000">)</font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>for</b></font> <font color="#000000">(</font><font color="#7F0055"><b>int</b></font> <font color="#000000">i =</font> <font color="#990000">0</font><font color="#000000">; i &lt; candidates.length; i++</font><font color="#000000">) {</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>if</b></font> <font color="#000000">(</font><font color="#000000">goodLoopCandidate</font><font color="#000000">(</font><font color="#000000">i, sub.newChild</font><font color="#000000">(</font><font color="#990000">100</font><font color="#000000">)))</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>break</b></font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">}</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;</font><font color="#000000">}</font><br />
+
<br />
+
<font color="#FFFFFF">&nbsp;&nbsp;</font><font color="#7F0055"><b>public</b></font> <font color="#7F0055"><b>boolean</b></font> <font color="#000000">goodLoopCandidate</font><font color="#000000">(</font><font color="#7F0055"><b>int</b></font> <font color="#000000">index, IProgressMonitor monitor</font><font color="#000000">) {</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">SubMonitor sub = SubMonitor.convert</font><font color="#000000">(</font><font color="#000000">monitor,</font> <font color="#990000">1000</font><font color="#000000">)</font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">sub.beginTask</font><font color="#000000">(</font><font color="#000000">null,</font> <font color="#990000">1000</font><font color="#000000">)</font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>try</b></font> <font color="#000000">{</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#3F7F5F">// … code that loads</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>for</b></font> <font color="#000000">(</font><font color="#7F0055"><b>int</b></font> <font color="#000000">i =</font> <font color="#990000">0</font><font color="#000000">; i &lt;</font> <font color="#990000">1000</font><font color="#000000">; i++</font><font color="#000000">)</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">sub.worked</font><font color="#000000">(</font><font color="#990000">1</font><font color="#000000">)</font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>return true</b></font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">}</font> <font color="#7F0055"><b>finally</b></font> <font color="#000000">{</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#3F7F5F">// ignore errors</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">}</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0055"><b>return false</b></font><font color="#000000">;</font><br />
+
<font color="#FFFFFF">&nbsp;&nbsp;</font><font color="#000000">}</font></code></td><!-- end source code -->
+
</tr>
+
</table>
+
</code>
+
<!-- ======================================================== -->
+
<p style="font: 12.0px Helvetica">Here I simply use SubMonitor’s <span style="font: 12.0px Monaco">newChild,</span> this works without calling “done” because the next call to newChild (or “done” for that matter) will consume the ticks allocated for the child.</p>
+
<br/>
+
<p style="font: 14.0px Helvetica"><b>Rules of Thumb</b></p>
+
<ol style="list-style-type: decimal">
+
  <li style="font: 12.0px Helvetica">Use SubMonitor</li>
+
  
  <li style="font: 12.0px Helvetica">Always begin by converting the monitor you get to a SubMonitor</li>
 
  
  <li style="font: 12.0px Helvetica">Use newChild(n) to create a sub monitor to pass to methods that take an IProgressMonitor as argument.</li>
+
'''Are you working with Progress Monitors, and wish you did not have too?'''
  
  <li style="font: 12.0px Helvetica">Never call “done” - but document that the caller must do so unless they used a SubMonitor</li>
+
''by Henrik Lindberg - originally posted on [http://henrik-eclipse.blogspot.com/2009/05/progress-monitor-patterns.html Eclipse by Planatery Transitions]''
</ol><br />
+
  
[[Category:Best_Practices]]
+
==Problem==
 +
I recently got to investigate issues in p2 why the progress bar did not move while it was very clear that things where happening as subtasks changed the text. Looking into what was going on made me find a new Progress Monitor anti pattern.
 +
 
 +
The problem is how to handle a case where you need to do something like this:
 +
 
 +
<source lang="java">
 +
for (int i = 0; i < candidates.length; i++) {
 +
  if (loadCandidate1(i)) {
 +
    break;
 +
  }
 +
}
 +
</source>
 +
 
 +
Where a call to <code>loadCandidate(int)</code> is potentially a long running task. The first loaded candidate means we are done.
 +
 
 +
==Antipattern==
 +
Here is an implementation of the above example using the antipattern - i.e. "don't do this":
 +
 
 +
<source lang="java">
 +
public void antiPattern(IProgressMonitor monitor) {
 +
  SubMonitor sub = SubMonitor.convert(monitor, candidates.length);
 +
  for (int i = 0; i < candidates.length; i++) {
 +
    if (loadCandidate(i, sub.newChild(1))) {
 +
      break;
 +
    }
 +
  }
 +
}
 +
 
 +
public boolean loadCandidate(int index, IProgressMonitor monitor) {
 +
  SubMonitor sub = SubMonitor.convert(monitor, 1000);
 +
  try {
 +
    for (int i = 0; i < 1000; i++) {
 +
      sub.worked(1);
 +
    }
 +
    // ...
 +
    return true;
 +
  } finally {
 +
    monitor.done();
 +
  }
 +
  return false;
 +
}
 +
</source>
 +
 
 +
Well, what is wrong with this, you may ask...Well, the length of the progress bar will be divided into as many slots as there are candidates, and if the first candidate succeeds and uses its 1000 ticks, and the remaining candidates are never considered, we will end up reporting the 1000 ticks on a fraction of the overall progress bar. This means that for 10 candidates, you will see the progress-bar slowly go to about 10% of the overall length, to suddenly jump to 100%.
 +
 
 +
==Good pattern==
 +
Here is the good pattern that makes use of the full progress bar:
 +
 
 +
<source lang="java">
 +
public void goodPattern(IProgressMonitor monitor) {
 +
  SubMonitor sub = SubMonitor.convert(monitor, 1000);
 +
  for (int i = 0; i < candidates.length; i++) {
 +
    sub.setWorkRemaining(1000);
 +
    if (goodLoadCandidate(i, sub.newChild(1000))) {
 +
      break;
 +
    }
 +
        break;
 +
  }
 +
}
 +
 
 +
public boolean goodLoadCandidate(int index, IProgressMonitor monitor) {
 +
  SubMonitor sub = SubMonitor.convert(monitor, 1000);
 +
  sub.beginTask(null, 1000);
 +
  try {
 +
    // code that loads...
 +
    for (int i = 0; i < 1000; i++) {
 +
      sub.worked(1);
 +
    }
 +
   
 +
    return true;
 +
  } finally {
 +
    // ignore errors
 +
  }
 +
  return false;
 +
}
 +
</source>
 +
 
 +
Notice how [http://help.eclipse.org/stable/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/SubMonitor.html#setWorkRemaining(int) <code>setWorkRemaining(int)</code>] is called each time in the loop. This reallocates the remaining ticks, but there is no need to compute how many that actually remains, the child allocation of 1000 ticks will always give the child 1000 ticks to report, even if there were not enough ticks left in the parent. All the scaling is performed by the SubMonitor, so you don’t have to worry about it.
 +
 
 +
Now, let’s say that the routine you are calling do need to perform a bit of work even if it does not need all of its ticks. Don’t worry, that will work too as long as it is a small portion of the allocation. If you risk consuming a larger part, you may be better off doing a true partitioning of the progress-bar as shown in the next section.
 +
 
 +
==Good pattern - regular loop==
 +
Here is the good pattern for a regular loop where each iteration does consume ticks.
 +
 
 +
<source lang="java">
 +
public void goodLoopPattern(IProgressMonitor monitor) {
 +
  SubMonitor sub = SubMonitor.convert(monitor, candidates.length*100);
 +
  for (int i = 0; i < candidates.length; i++) {
 +
    if (goodLoopCandidate(i, sub.newChild(100))) {
 +
      break;
 +
    }
 +
  }
 +
}
 +
 
 +
public boolean goodLoopCandidate(int index, IProgressMonitor monitor) {
 +
  SubMonitor sub = SubMonitor.convert(monitor, 1000);
 +
  sub.beginTask(null, 1000);
 +
  try {
 +
    // code that loads...
 +
    for (int i = 0; i < 1000; i++) {
 +
      sub.worked(1);
 +
    }
 +
 
 +
    return true;
 +
  } finally {
 +
    // ignore errors
 +
  }
 +
  return false;
 +
}
 +
</source>
 +
 
 +
Here I simply use SubMonitor’s [http://help.eclipse.org/stable/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/SubMonitor.html#newChild(int) <code>newChild(int)</code>], this works without calling “done” because the next call to newChild (or “done” for that matter) will consume the ticks allocated for the child.
 +
 
 +
==Rules of Thumb==
 +
#Use SubMonitor.
 +
#Always begin by converting the monitor you get to a SubMonitor.
 +
#Use newChild(n) to create a sub monitor to pass to methods that take an IProgressMonitor as argument.
 +
#Never call “done” - but document that the caller must do so unless they used a SubMonitor.
 +
 
 +
[[Category:Best Practices]]

Latest revision as of 08:32, 30 January 2024


Are you working with Progress Monitors, and wish you did not have too?

by Henrik Lindberg - originally posted on Eclipse by Planatery Transitions

Problem

I recently got to investigate issues in p2 why the progress bar did not move while it was very clear that things where happening as subtasks changed the text. Looking into what was going on made me find a new Progress Monitor anti pattern.

The problem is how to handle a case where you need to do something like this:

for (int i = 0; i < candidates.length; i++) {
  if (loadCandidate1(i)) {
    break;
  }
}

Where a call to loadCandidate(int) is potentially a long running task. The first loaded candidate means we are done.

Antipattern

Here is an implementation of the above example using the antipattern - i.e. "don't do this":

public void antiPattern(IProgressMonitor monitor) {
  SubMonitor sub = SubMonitor.convert(monitor, candidates.length);
  for (int i = 0; i < candidates.length; i++) {
    if (loadCandidate(i, sub.newChild(1))) {
      break;
    }
  }
}
 
public boolean loadCandidate(int index, IProgressMonitor monitor) {
  SubMonitor sub = SubMonitor.convert(monitor, 1000);
  try {
    for (int i = 0; i < 1000; i++) {
      sub.worked(1);
    }
    // ...
    return true;
  } finally {
    monitor.done();
  }
  return false;
}

Well, what is wrong with this, you may ask...Well, the length of the progress bar will be divided into as many slots as there are candidates, and if the first candidate succeeds and uses its 1000 ticks, and the remaining candidates are never considered, we will end up reporting the 1000 ticks on a fraction of the overall progress bar. This means that for 10 candidates, you will see the progress-bar slowly go to about 10% of the overall length, to suddenly jump to 100%.

Good pattern

Here is the good pattern that makes use of the full progress bar:

public void goodPattern(IProgressMonitor monitor) {
  SubMonitor sub = SubMonitor.convert(monitor, 1000);
  for (int i = 0; i < candidates.length; i++) {
    sub.setWorkRemaining(1000);
    if (goodLoadCandidate(i, sub.newChild(1000))) {
      break;
    }
        break;
  }
}
 
public boolean goodLoadCandidate(int index, IProgressMonitor monitor) {
  SubMonitor sub = SubMonitor.convert(monitor, 1000);
  sub.beginTask(null, 1000);
  try {
    // code that loads...
    for (int i = 0; i < 1000; i++) {
      sub.worked(1);
    }
 
    return true;
  } finally {
    // ignore errors
  }
  return false;
}

Notice how setWorkRemaining(int) is called each time in the loop. This reallocates the remaining ticks, but there is no need to compute how many that actually remains, the child allocation of 1000 ticks will always give the child 1000 ticks to report, even if there were not enough ticks left in the parent. All the scaling is performed by the SubMonitor, so you don’t have to worry about it.

Now, let’s say that the routine you are calling do need to perform a bit of work even if it does not need all of its ticks. Don’t worry, that will work too as long as it is a small portion of the allocation. If you risk consuming a larger part, you may be better off doing a true partitioning of the progress-bar as shown in the next section.

Good pattern - regular loop

Here is the good pattern for a regular loop where each iteration does consume ticks.

public void goodLoopPattern(IProgressMonitor monitor) {
  SubMonitor sub = SubMonitor.convert(monitor, candidates.length*100);
  for (int i = 0; i < candidates.length; i++) {
    if (goodLoopCandidate(i, sub.newChild(100))) {
      break;
    }
  }
}
 
public boolean goodLoopCandidate(int index, IProgressMonitor monitor) {
  SubMonitor sub = SubMonitor.convert(monitor, 1000);
  sub.beginTask(null, 1000);
  try {
    // code that loads...
    for (int i = 0; i < 1000; i++) {
      sub.worked(1);
    }
 
    return true;
  } finally {
    // ignore errors
  }
  return false;
}

Here I simply use SubMonitor’s newChild(int), this works without calling “done” because the next call to newChild (or “done” for that matter) will consume the ticks allocated for the child.

Rules of Thumb

  1. Use SubMonitor.
  2. Always begin by converting the monitor you get to a SubMonitor.
  3. Use newChild(n) to create a sub monitor to pass to methods that take an IProgressMonitor as argument.
  4. Never call “done” - but document that the caller must do so unless they used a SubMonitor.

Back to the top