Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Xenos2015-10-13 18:23:54 +0000
committerStefan Xenos2015-10-13 18:23:54 +0000
commitf67b2c8cc955aa41e949591c9325f76b2e37885c (patch)
tree02e92ea5661086e60f128d1ac81cf7ccb824170b
parent6d8a79c25c4cea891cc1cc35b82aaafe948f4161 (diff)
downloadrt.equinox.bundles-I20151014-1100.tar.gz
rt.equinox.bundles-I20151014-1100.tar.xz
rt.equinox.bundles-I20151014-1100.zip
Bug 475747 - Implement automatic cancellation checks in SubMonitorI20151014-1100
Add "split", a replacement for newChild which automatically performs a cancellation check if either the newly-created child will consume one or more ticks or a certain number of trivially-small children have been created since the last cancellation check. A cancellation check will throw OperationCanceledException if the progress monitor has been cancelled. Signed-off-by: Stefan Xenos <sxenos@gmail.com> Change-Id: I8e7c2d88b238f7b1a17f80f80f0f9fc3b9d30d8b
-rw-r--r--bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/SubMonitor.java293
1 files changed, 224 insertions, 69 deletions
diff --git a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/SubMonitor.java b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/SubMonitor.java
index cf930f170..36655b206 100644
--- a/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/SubMonitor.java
+++ b/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/SubMonitor.java
@@ -10,6 +10,7 @@
* Stefan Xenos - bug 174539 - add a 1-argument convert(...) method
* Stefan Xenos - bug 174040 - SubMonitor#convert doesn't always set task name
* Stefan Xenos - bug 206942 - updated javadoc to recommend better constants for infinite progress
+ * Stefan Xenos (Google) - bug 475747 - Support efficient, convenient cancellation checks in SubMonitor
* IBM Corporation - ongoing maintenance
*******************************************************************************/
package org.eclipse.core.runtime;
@@ -35,7 +36,7 @@ package org.eclipse.core.runtime;
* <ul>
* <li>At the start of your method, use <code>SubMonitor.convert(...).</code> to convert the IProgressMonitor
* into a SubMonitor. </li>
- * <li>Use <code>SubMonitor.newChild(...)</code> whenever you need to call another method that
+ * <li>Use <code>SubMonitor.split(...)</code> whenever you need to call another method that
* accepts an IProgressMonitor.</li>
* </ul>
* <p></p>
@@ -69,7 +70,7 @@ package org.eclipse.core.runtime;
* <p>This example demonstrates how the recommended usage of <code>SubMonitor</code> makes it unnecessary to call
* IProgressMonitor.done() in most situations.</p>
*
- * <p>It is never necessary to call done() on a monitor obtained from <code>convert</code> or <code>progress.newChild()</code>.
+ * <p>It is never necessary to call done() on a monitor obtained from <code>convert</code> or <code>progress.split()</code>.
* In this example, there is no guarantee that <code>monitor</code> is an instance of <code>SubMonitor</code>, making it
* necessary to call <code>monitor.done()</code>. The JavaDoc contract makes this the responsibility of the caller.</p>
*
@@ -83,13 +84,13 @@ package org.eclipse.core.runtime;
* SubMonitor progress = SubMonitor.convert(monitor, 100);
*
* // Use 30% of the progress to do some work
- * doSomeWork(progress.newChild(30));
+ * doSomeWork(progress.split(30));
*
* // Advance the monitor by another 30%
* progress.worked(30);
*
* // Use the remaining 40% of the progress to do some more work
- * doSomeWork(progress.newChild(40));
+ * doSomeWork(progress.split(40));
* }
* </pre>
*
@@ -109,13 +110,13 @@ package org.eclipse.core.runtime;
* SubMonitor progress = SubMonitor.convert(monitor, 100);
* try {
* // Use 30% of the progress to do some work
- * doSomeWork(progress.newChild(30));
+ * doSomeWork(progress.split(30));
*
* // Advance the monitor by another 30%
* progress.worked(30);
*
* // Use the remaining 40% of the progress to do some more work
- * doSomeWork(progress.newChild(40));
+ * doSomeWork(progress.split(40));
*
* } finally {
* if (monitor != null) {
@@ -136,7 +137,7 @@ package org.eclipse.core.runtime;
*
* if (condition) {
* // Use 50% of the progress to do some work
- * doSomeWork(progress.newChild(50));
+ * doSomeWork(progress.split(50));
* }
*
* // Don't report any work, but ensure that we have 50 ticks remaining on the progress monitor.
@@ -146,7 +147,7 @@ package org.eclipse.core.runtime;
* progress.setWorkRemaining(50);
*
* // Use the remainder of the progress monitor to do the rest of the work
- * doSomeWork(progress.newChild(50));
+ * doSomeWork(progress.split(50));
* }
* </pre>
*
@@ -155,7 +156,7 @@ package org.eclipse.core.runtime;
* <pre>
* if (condition) {
* // Use 50% of the progress to do some work
- * doSomeWork(progress.newChild(50));
+ * doSomeWork(progress.split(50));
* } else {
* // Bad: Causes the progress monitor to appear to start at 50%, wasting half of the
* // space in the monitor.
@@ -175,16 +176,16 @@ package org.eclipse.core.runtime;
*
* // Create a new progress monitor that uses 70% of the total progress and will allocate one tick
* // for each element of the given collection.
- * SubMonitor loopProgress = progress.newChild(70).setWorkRemaining(someCollection.size());
+ * SubMonitor loopProgress = progress.split(70).setWorkRemaining(someCollection.size());
*
* for (Iterator iter = someCollection.iterator(); iter.hasNext();) {
* Object next = iter.next();
*
- * doWorkOnElement(next, loopProgress.newChild(1));
+ * doWorkOnElement(next, loopProgress.split(1));
* }
*
* // Use the remaining 30% of the progress monitor to do some work outside the loop
- * doSomeWork(progress.newChild(30));
+ * doSomeWork(progress.split(30));
* }
* </pre>
*
@@ -204,7 +205,7 @@ package org.eclipse.core.runtime;
* // use 0.01% of the space remaining in the monitor to process the next node.
* progress.setWorkRemaining(10000);
*
- * doWorkOnElement(node, progress.newChild(1));
+ * doWorkOnElement(node, progress.split(1));
*
* node = node.next;
* }
@@ -220,6 +221,16 @@ package org.eclipse.core.runtime;
public final class SubMonitor implements IProgressMonitorWithBlocking {
/**
+ * Number of trivial operations (operations which do not report any progress) which can be
+ * performed before the monitor performs a cancellation check. This ensures that cancellation
+ * checks do not create a performance problem in tight loops that create a lot of SubMonitors,
+ * while still ensuring that cancellation is checked occasionally in such loops. This only
+ * affects operations which are too small to report any progress. Operations which are large
+ * enough to consume at least one tick will always be checked for cancellation.
+ */
+ private static final int TRIVIAL_OPERATIONS_BEFORE_CANCELLATION_CHECK = 1000;
+
+ /**
* Minimum number of ticks to allocate when calling beginTask on an unknown IProgressMonitor.
* Pick a number that is big enough such that, no matter where progress is being displayed,
* the user would be unlikely to notice if progress were to be reported with higher accuracy.
@@ -231,19 +242,25 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
* its active descendents share the same RootInfo.
*/
private static final class RootInfo {
- private final IProgressMonitor root;
+ final IProgressMonitor root;
/**
* Remembers the last task name. Prevents us from setting the same task name multiple
* times in a row.
*/
- private String taskName = null;
+ String taskName = null;
/**
* Remembers the last subtask name. Prevents the SubMonitor from setting the same
* subtask string more than once in a row.
*/
- private String subTask = null;
+ String subTask = null;
+
+ /**
+ * Counter that indicates when we should perform an cancellation check for a trivial
+ * operation.
+ */
+ int cancellationCheckCounter;
/**
* Creates a RootInfo struct that delegates to the given progress
@@ -294,6 +311,11 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
((IProgressMonitorWithBlocking) root).setBlocked(reason);
}
+ public void checkForCancellation() {
+ if (root.isCanceled()) {
+ throw new OperationCanceledException();
+ }
+ }
}
/**
@@ -314,14 +336,14 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
/**
* Number of ticks allocated for this instance's children. This is the total number
- * of ticks that may be passed into worked(int) or newChild(int).
+ * of ticks that may be passed into worked(int) or split(int).
*/
private int totalForChildren;
/**
- * Children created by newChild will be completed automatically the next time
+ * Children created by split will be completed automatically the next time
* the parent progress monitor is touched. This points to the last incomplete child
- * created with newChild.
+ * created with split.
*/
private IProgressMonitor lastSubMonitor = null;
@@ -336,7 +358,7 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
private final int flags;
/**
- * May be passed as a flag to newChild. Indicates that the calls
+ * May be passed as a flag to split. Indicates that the calls
* to subTask on the child should be ignored. Without this flag,
* calling subTask on the child will result in a call to subTask
* on its parent.
@@ -344,7 +366,7 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
public static final int SUPPRESS_SUBTASK = 0x0001;
/**
- * May be passed as a flag to newChild. Indicates that strings
+ * May be passed as a flag to split. Indicates that strings
* passed into beginTask should be ignored. If this flag is
* specified, then the progress monitor instance will accept null
* as the first argument to beginTask. Without this flag, any
@@ -354,7 +376,7 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
public static final int SUPPRESS_BEGINTASK = 0x0002;
/**
- * May be passed as a flag to newChild. Indicates that strings
+ * May be passed as a flag to split. Indicates that strings
* passed into setTaskName should be ignored. If this string
* is omitted, then a call to setTaskName on the child will
* result in a call to setTaskName on the parent.
@@ -362,19 +384,30 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
public static final int SUPPRESS_SETTASKNAME = 0x0004;
/**
- * May be passed as a flag to newChild. Indicates that strings
+ * May be passed as a flag to split. Indicates that strings
* passed to setTaskName, subTask, and beginTask should all be ignored.
*/
public static final int SUPPRESS_ALL_LABELS = SUPPRESS_SETTASKNAME | SUPPRESS_BEGINTASK | SUPPRESS_SUBTASK;
/**
- * May be passed as a flag to newChild. Indicates that strings
+ * May be passed as a flag to split. Indicates that strings
* passed to setTaskName, subTask, and beginTask should all be propagated
* to the parent.
*/
public static final int SUPPRESS_NONE = 0;
/**
+ * Bitwise combination of all flags which may be passed in to the public interface on {@link #split}
+ */
+ private static final int ALL_PUBLIC_FLAGS = SUPPRESS_ALL_LABELS;
+
+ /**
+ * Bitwise combination of all flags which are inherited directly from a parent SubMonitor to its immediate
+ * children. All other flags are either not inherited or are inherited from more complicated logic in {@link #split}
+ */
+ private static final int ALL_INHERITED_FLAGS = SUPPRESS_SUBTASK;
+
+ /**
* Creates a new SubMonitor that will report its progress via
* the given RootInfo.
* @param rootInfo the root of this progress monitor tree
@@ -397,6 +430,9 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
* <p>This method should generally be called at the beginning of a method that accepts
* an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
*
+ * <p>Since it is illegal to call beginTask on the same IProgressMonitor more than once,
+ * the same instance of IProgressMonitor must not be passed to convert more than once.</p>
+ *
* @param monitor monitor to convert to a SubMonitor instance or null. Treats null
* as a new instance of <code>NullProgressMonitor</code>.
* @return a SubMonitor instance that adapts the argument
@@ -413,6 +449,9 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
*
* <p>This method should generally be called at the beginning of a method that accepts
* an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
+ *
+ * <p>Since it is illegal to call beginTask on the same IProgressMonitor more than once,
+ * the same instance of IProgressMonitor must not be passed to convert more than once.</p>
*
* @param monitor monitor to convert to a SubMonitor instance or null. Treats null
* as a new instance of <code>NullProgressMonitor</code>.
@@ -431,7 +470,10 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
*
* <p>This method should generally be called at the beginning of a method that accepts
* an IProgressMonitor in order to convert the IProgressMonitor into a SubMonitor.</p>
- *
+ *
+ * <p>Since it is illegal to call beginTask on the same IProgressMonitor more than once,
+ * the same instance of IProgressMonitor must not be passed to convert more than once.</p>
+ *
* @param monitor to convert into a SubMonitor instance or null. If given a null argument,
* the resulting SubMonitor will not report its progress anywhere.
* @param taskName user readable name to pass to monitor.beginTask. Never null.
@@ -454,7 +496,7 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
/**
* <p>Sets the work remaining for this SubMonitor instance. This is the total number
- * of ticks that may be reported by all subsequent calls to worked(int), newChild(int), etc.
+ * of ticks that may be reported by all subsequent calls to worked(int), split(int), etc.
* This may be called many times for the same SubMonitor instance. When this method
* is called, the remaining space on the progress monitor is redistributed into the given
* number of ticks.</p>
@@ -580,6 +622,25 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
}
/**
+ * <p>Creates a new SubMonitor that will consume the given number of ticks from its parent.
+ * Shorthand for calling {@link #newChild(int, int)} with (totalWork, SUPPRESS_BEGINTASK).
+ *
+ * <p>This is much like {@link #split(int)} but it does not check for cancellation and will not
+ * throw {@link OperationCanceledException}. New code should consider using {@link #split(int)}
+ * to benefit from automatic cancellation checks.
+ *
+ * @param totalWork number of ticks to consume from the receiver
+ * @return new sub progress monitor that may be used in place of a new SubMonitor
+ */
+ public SubMonitor newChild(int totalWork) {
+ return newChild(totalWork, SUPPRESS_BEGINTASK);
+ }
+
+ /**
+ * <p>This is much like {@link #split}, but it does not check for cancellation and will not
+ * throw {@link OperationCanceledException}. New code should consider using {@link #split}
+ * to benefit from automatic cancellation checks.
+ *
* <p>Creates a sub progress monitor that will consume the given number of ticks from the
* receiver. It is not necessary to call <code>beginTask</code> or <code>done</code> on the
* result. However, the resulting progress monitor will not report any work after the first
@@ -639,31 +700,63 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
* @param totalWork number of ticks to consume from the receiver
* @return new sub progress monitor that may be used in place of a new SubMonitor
*/
- public SubMonitor newChild(int totalWork) {
- return newChild(totalWork, SUPPRESS_BEGINTASK);
+ public SubMonitor newChild(int totalWork, int suppressFlags) {
+ double totalWorkDouble = (totalWork > 0) ? totalWork : 0.0d;
+ totalWorkDouble = Math.min(totalWorkDouble, totalForChildren - usedForChildren);
+ cleanupActiveChild();
+
+ // Compute the flags for the child. We want the net effect to be as though the child is
+ // delegating to its parent, even though it is actually talking directly to the root.
+ // This means that we need to compute the flags such that - even if a label isn't
+ // suppressed by the child - if that same label would have been suppressed when the
+ // child delegated to its parent, the child must explicitly suppress the label.
+ int childFlags = flags & ALL_INHERITED_FLAGS;
+
+ if ((flags & SUPPRESS_SETTASKNAME) != 0) {
+ // If the parent was ignoring labels passed to setTaskName, then the child will ignore
+ // labels passed to either beginTask or setTaskName - since both delegate to setTaskName
+ // on the parent
+ childFlags |= SUPPRESS_SETTASKNAME | SUPPRESS_BEGINTASK;
+ }
+
+ // Note: the SUPPRESS_BEGINTASK flag does not affect the child since there
+ // is no method on the child that would delegate to beginTask on the parent.
+ childFlags |= (suppressFlags & ALL_PUBLIC_FLAGS);
+
+ SubMonitor result = new SubMonitor(root, consume(totalWorkDouble), (int) totalWorkDouble, childFlags);
+ lastSubMonitor = result;
+ return result;
}
/**
+ * This is shorthand for calling <code>split(totalWork, SUPPRESS_BEGINTASK)</code>. See
+ * {@link #split(int, int)} for more details.
+ *
* <p>Creates a sub progress monitor that will consume the given number of ticks from the
* receiver. It is not necessary to call <code>beginTask</code> or <code>done</code> on the
* result. However, the resulting progress monitor will not report any work after the first
- * call to done() or before ticks are allocated. Ticks may be allocated by calling beginTask
- * or setWorkRemaining.</p>
+ * call to done() or before ticks are allocated. Ticks may be allocated by calling {@link #beginTask}
+ * or {@link #setWorkRemaining}.</p>
*
- * <p>Each SubMonitor only has one active child at a time. Each time newChild() is called, the
- * result becomes the new active child and any unused progress from the previously-active child is
- * consumed.</p>
+ * <p>This method is much like {@link #newChild}, but it will additionally check for cancellation and
+ * will throw an OperationCanceledException if the monitor has been cancelled. Not every call to
+ * this method will trigger a cancellation check. The checks will be performed as often as possible
+ * without degrading the performance of the caller.
+ *
+ * <p>Each SubMonitor only has one active child at a time. Each time {@link #newChild} or
+ * {@link #split} is called, the result becomes the new active child and any unused progress
+ * from the previously-active child is consumed.</p>
*
* <p>This is property makes it unnecessary to call done() on a SubMonitor instance, since child
* monitors are automatically cleaned up the next time the parent is touched.</p>
*
* <code><pre>
* ////////////////////////////////////////////////////////////////////////////
- * // Example 1: Typical usage of newChild
+ * // Example 1: Typical usage of split
* void myMethod(IProgressMonitor parent) {
* SubMonitor progress = SubMonitor.convert(parent, 100);
- * doSomething(progress.newChild(50));
- * doSomethingElse(progress.newChild(50));
+ * doSomething(progress.split(50));
+ * doSomethingElse(progress.split(50));
* }
*
* ////////////////////////////////////////////////////////////////////////////
@@ -677,7 +770,7 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
* // Creating the next child monitor will clean up the previous one,
* // causing progress to be reported smoothly even if we don't do anything
* // with the monitors we create
- * progress.newChild(1);
+ * progress.split(1);
* }
* }
*
@@ -688,51 +781,113 @@ public final class SubMonitor implements IProgressMonitorWithBlocking {
*
* // WRONG WAY: Won't have the intended effect, as only one of these progress
* // monitors may be active at a time and the other will report no progress.
- * callMethod(progress.newChild(50), computeValue(progress.newChild(50)));
+ * callMethod(progress.split(50), computeValue(progress.split(50)));
* }
*
* void rightMethod(IProgressMonitor parent) {
* SubMonitor progress = SubMonitor.convert(parent, 100);
*
* // RIGHT WAY: Break up method calls so that only one SubMonitor is in use at a time.
- * Object someValue = computeValue(progress.newChild(50));
- * callMethod(progress.newChild(50), someValue);
+ * Object someValue = computeValue(progress.split(50));
+ * callMethod(progress.split(50), someValue);
* }
* </pre></code>
*
* @param totalWork number of ticks to consume from the receiver
- * @return new sub progress monitor that may be used in place of a new SubMonitor
+ * @return a new SubMonitor instance
+ * @since 3.8
*/
- public SubMonitor newChild(int totalWork, int suppressFlags) {
- double totalWorkDouble = (totalWork > 0) ? totalWork : 0.0d;
- totalWorkDouble = Math.min(totalWorkDouble, totalForChildren - usedForChildren);
- cleanupActiveChild();
-
- // Compute the flags for the child. We want the net effect to be as though the child is
- // delegating to its parent, even though it is actually talking directly to the root.
- // This means that we need to compute the flags such that - even if a label isn't
- // suppressed by the child - if that same label would have been suppressed when the
- // child delegated to its parent, the child must explicitly suppress the label.
- int childFlags = SUPPRESS_NONE;
-
- if ((flags & SUPPRESS_SETTASKNAME) != 0) {
- // If the parent was ignoring labels passed to setTaskName, then the child will ignore
- // labels passed to either beginTask or setTaskName - since both delegate to setTaskName
- // on the parent
- childFlags |= SUPPRESS_SETTASKNAME | SUPPRESS_BEGINTASK;
- }
+ public SubMonitor split(int totalWork) {
+ return split(totalWork, SUPPRESS_BEGINTASK);
+ }
- if ((flags & SUPPRESS_SUBTASK) != 0) {
- // If the parent was suppressing labels passed to subTask, so will the child.
- childFlags |= SUPPRESS_SUBTASK;
+ /**
+ * <p>Creates a sub progress monitor that will consume the given number of ticks from the
+ * receiver. It is not necessary to call <code>beginTask</code> or <code>done</code> on the
+ * result. However, the resulting progress monitor will not report any work after the first
+ * call to done() or before ticks are allocated. Ticks may be allocated by calling {@link #beginTask}
+ * or {@link #setWorkRemaining}</p>
+ *
+ * <p>This method is much like {@link #newChild}, but will additionally check for cancellation and
+ * will throw an OperationCanceledException if the monitor has been cancelled. Not every call to
+ * this method will trigger a cancellation check. The checks will be performed as often as possible
+ * without degrading the performance of the caller.
+ *
+ * <p>Each SubMonitor only has one active child at a time. Each time {@link #newChild} or
+ * {@link #split} is called, the result becomes the new active child and any unused progress
+ * from the previously-active child is consumed.</p>
+ *
+ * <p>This is property makes it unnecessary to call done() on a SubMonitor instance, since child
+ * monitors are automatically cleaned up the next time the parent is touched.</p>
+ *
+ * <code><pre>
+ * ////////////////////////////////////////////////////////////////////////////
+ * // Example 1: Typical usage of split
+ * void myMethod(IProgressMonitor parent) {
+ * SubMonitor progress = SubMonitor.convert(parent, 100);
+ * doSomething(progress.split(50));
+ * doSomethingElse(progress.split(50));
+ * }
+ *
+ * ////////////////////////////////////////////////////////////////////////////
+ * // Example 2: Demonstrates the function of active children. Creating children
+ * // is sufficient to smoothly report progress, even if worked(...) and done()
+ * // are never called.
+ * void myMethod(IProgressMonitor parent) {
+ * SubMonitor progress = SubMonitor.convert(parent, 100);
+ *
+ * for (int i = 0; i < 100; i++) {
+ * // Creating the next child monitor will clean up the previous one,
+ * // causing progress to be reported smoothly even if we don't do anything
+ * // with the monitors we create
+ * progress.split(1);
+ * }
+ * }
+ *
+ * ////////////////////////////////////////////////////////////////////////////
+ * // Example 3: Demonstrates a common anti-pattern
+ * void wrongMethod(IProgressMonitor parent) {
+ * SubMonitor progress = SubMonitor.convert(parent, 100);
+ *
+ * // WRONG WAY: Won't have the intended effect, as only one of these progress
+ * // monitors may be active at a time and the other will report no progress.
+ * callMethod(progress.split(50), computeValue(progress.split(50)));
+ * }
+ *
+ * void rightMethod(IProgressMonitor parent) {
+ * SubMonitor progress = SubMonitor.convert(parent, 100);
+ *
+ * // RIGHT WAY: Break up method calls so that only one SubMonitor is in use at a time.
+ * Object someValue = computeValue(progress.split(50));
+ * callMethod(progress.split(50), someValue);
+ * }
+ * </pre></code>
+ *
+ * @param totalWork number of ticks to consume from the receiver
+ * @return new sub progress monitor that may be used in place of a new SubMonitor
+ * @since 3.8
+ */
+ public SubMonitor split(int totalWork, int suppressFlags) {
+ int oldUsedForParent = this.usedForParent;
+ SubMonitor result = newChild(totalWork, suppressFlags);
+
+ int ticksTheChildWillReportToParent = result.totalParent;
+
+ // If the new child reports a nonzero amount of progress.
+ if (ticksTheChildWillReportToParent > 0) {
+ // Don't check for cancellation if the child is consuming 100% of its parent since whatever code created
+ // the parent already performed this check.
+ if (oldUsedForParent > 0 || usedForParent < totalParent) {
+ // Treat this as a nontrivial operation and check for cancellation unconditionally.
+ root.checkForCancellation();
+ }
+ } else {
+ // This is a trivial operation. Only perform a cancellation check after the counter expires.
+ if (++root.cancellationCheckCounter >= TRIVIAL_OPERATIONS_BEFORE_CANCELLATION_CHECK) {
+ root.cancellationCheckCounter = 0;
+ root.checkForCancellation();
+ }
}
-
- // Note: the SUPPRESS_BEGINTASK flag does not affect the child since there
- // is no method on the child that would delegate to beginTask on the parent.
- childFlags |= suppressFlags;
-
- SubMonitor result = new SubMonitor(root, consume(totalWorkDouble), (int) totalWorkDouble, childFlags);
- lastSubMonitor = result;
return result;
}

Back to the top