Revert "Bug 470175 - Log jobs that don't check for cancellation often enough"
The commit was premature since violates the feature freeze.
This reverts commit 5dfcc46504bb8569be5ee21ccb2ccf329d8ee7ad.
Change-Id: I5bc69dd64446027e5641aaa0443472b5da00b08e
diff --git a/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobCancelabilityMonitor.java b/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobCancelabilityMonitor.java
deleted file mode 100644
index 990592a..0000000
--- a/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobCancelabilityMonitor.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2016 Eclipse Foundation and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Mikaël Barbero (Eclipse Foundation) - initial API and implementation
- *******************************************************************************/
-package org.eclipse.core.internal.jobs;
-
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import org.eclipse.core.runtime.*;
-import org.eclipse.osgi.util.NLS;
-
-/**
- * A progress monitor wrapper that computes the number of calls to
- * {@link #isCanceled()} and the maximum time interval without calls to
- * {@link #isCanceled()}.
- * <p>
- * Clients are expected to call {@link #aboutToStart()} and
- * {@link #hasStopped()} shortly before and after the job execution.
- * <p>
- * After {@link #hasStopped()} has been called, client can call
- * {@link #createCancelabilityStatus()} and retrieve an {@link IStatus} stating
- * whether the Job follows best practices regarding cancelability. Best
- * practices threshold and report details can be configured through an
- * {@link Options} given at instantiation time.
- */
-public final class JobCancelabilityMonitor extends ProgressMonitorWrapper {
- /**
- * For conversion in {@link #nanosToString(long)}
- */
- private static final long _1_SECOND_IN_NANOS = TimeUnit.SECONDS.toNanos(1);
-
- /**
- * Specific error code to help the Automatic Error Reporting Initiative
- * (AERI) identifying cancelability issues.
- */
- private static final int CANCELABILITY_ERROR_CODE = 8;
-
- /**
- * Will be incremented every time {@link #isCanceled()} is called.
- */
- private int isCanceledHitCount = 0;
-
- /**
- * Will be set to {@link System#nanoTime()} when {@link #aboutToStart()}
- * will be called.
- */
- private long startNano = -1;
-
- /**
- * Will be set to "{@link System#nanoTime()} - {@link #startNano}" when
- * hasStopped(); will be called.
- */
- private long elapsedNano = -1;
-
- /**
- * Keep the {@link System#nanoTime()} value of previous hit to
- * {@link #isCanceled()}. Will be initialized in {@link #aboutToStart()}.
- */
- private long lastHit = -1;
-
- /**
- * At every call to {@link #isCanceled()}, will be set to
- * {@code Math.max(maxTimeBetweenTwoCancelationCheck, System.nanoTime() - lastHit)}
- * .
- */
- private long maxTimeBetweenTwoCancelationCheck = -1;
-
- /**
- * List of stack traces that will be computed during calls to some
- * {@link IProgressMonitor} methods
- */
- private List<StackTraceSample> stackTraces;
-
- /**
- * Temporary holder of the last captured stack trace that may be added to
- * {@link #stackTraces} if {@link Options#maxStackSamples()} is not reached
- * or if it longer than one of the already recorded sample.
- */
- private StackTraceElement[] lastCapturedStackTrace;
-
- /**
- * Configurable threshold and options for the result of
- * {@link #createCancelabilityStatus()}.
- */
- private final Options options;
-
- /**
- * The job that report progress to this progress monitor.
- */
- private final InternalJob job;
-
- JobCancelabilityMonitor(InternalJob job, Options options) {
- super(job.getProgressMonitor());
- this.job = job;
- this.options = options;
- this.stackTraces = new ArrayList<>(options.maxStackSamples() + 1);
- }
-
- /**
- * Must be called before the {@link #job} starts.
- *
- * @return this to simplify calling code.
- */
- IProgressMonitor aboutToStart() {
- lastCapturedStackTrace = captureStackTrace();
- startNano = System.nanoTime();
- lastHit = startNano;
- return this;
- }
-
- /**
- * Must be called after the {@link #job} ends.
- */
- void hasStopped() {
- elapsedNano = System.nanoTime() - startNano;
- }
-
- /**
- * Captures the current thread stack trace and removes the top 3 frames to
- * avoid displaying the monitoring related frames in the log.
- */
- private StackTraceElement[] captureStackTrace() {
- final StackTraceElement[] ret;
- StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
- if (stackTrace.length > 3) {
- ret = Arrays.copyOfRange(stackTrace, 3, stackTrace.length);
- } else {
- ret = stackTrace;
- }
- return ret;
- }
-
- private static class StackTraceSample {
- final long nanoBetweenStackTraces;
- final StackTraceElement[] firstSte;
- final StackTraceElement[] secondSte;
-
- public StackTraceSample(long nanoBetweenStackTrace, StackTraceElement[] firstSte,
- StackTraceElement[] secondSte) {
- nanoBetweenStackTraces = nanoBetweenStackTrace;
- this.firstSte = firstSte;
- this.secondSte = secondSte;
- }
- }
-
- @Override
- public boolean isCanceled() {
- long elapsedSinceLastHit = System.nanoTime() - lastHit;
- lastHit = System.nanoTime();
- maxTimeBetweenTwoCancelationCheck = Math.max(maxTimeBetweenTwoCancelationCheck, elapsedSinceLastHit);
- lastCapturedStackTrace = storeStackTraceSample(elapsedSinceLastHit, captureStackTrace());
- isCanceledHitCount++;
- return super.isCanceled();
- }
-
- private StackTraceElement[] storeStackTraceSample(long elapsedSinceLastHit, StackTraceElement[] currentStackTrace) {
- if (elapsedSinceLastHit >= options.warningThreshold()) {
- if (stackTraces.size() >= options.maxStackSamples()) {
- int shortestStackTraceIdx = findShortestStackTraceSample(elapsedSinceLastHit);
- if (shortestStackTraceIdx >= 0) {
- stackTraces.set(shortestStackTraceIdx,
- new StackTraceSample(elapsedSinceLastHit, lastCapturedStackTrace, currentStackTrace));
- }
- } else {
- stackTraces.add(new StackTraceSample(elapsedSinceLastHit, lastCapturedStackTrace, currentStackTrace));
- }
- }
- return currentStackTrace;
- }
-
- /**
- * Returns the index of the {@link StackTraceSample} with the shortest
- * {@link StackTraceSample#nanoBetweenStackTraces} that is shorter than the
- * given {@code shorterThan} value.
- *
- * @param shorterThanNanos
- * threshold above which
- * {@link StackTraceSample#nanoBetweenStackTraces} will not be
- * considered during the search.
- * @return the index of the {@link StackTraceSample} with the shortest
- * nanoBetweenStackTraces that is shorter than the given
- * {@code shorterThan} value, or -1 if there is no such value.
- */
- private int findShortestStackTraceSample(long shorterThanNanos) {
- long minValue = shorterThanNanos;
- int shortest = -1;
- for (int i = 0; i < stackTraces.size(); i++) {
- final StackTraceSample stackTraceSample = stackTraces.get(i);
- if (stackTraceSample.nanoBetweenStackTraces < minValue) {
- shortest = i;
- minValue = stackTraceSample.nanoBetweenStackTraces;
- }
- }
- return shortest;
- }
-
- IStatus createCancelabilityStatus() {
- IStatus ret;
-
- if (isCanceledHitCount > 0) {
- ret = createCancelabilityStatus(severityForElapsedTime(maxTimeBetweenTwoCancelationCheck),
- NLS.bind(
- JobMessages.cancelability_monitor_waitedTooLong,
- new Object[] { job.getName(), nanosToString(maxTimeBetweenTwoCancelationCheck),
- isCanceledHitCount,
- nanosToString(elapsedNano) }));
- } else {
- ret = createCancelabilityStatus(severityForElapsedTime(elapsedNano),
- NLS.bind(JobMessages.cancelability_monitor_noCancelationCheck,
- new Object[] { job.getName(), isCanceledHitCount, nanosToString(elapsedNano) }));
- }
-
- return ret;
- }
-
- private int severityForElapsedTime(long nanoTime) {
- final int severity;
- if (job.isUser() && isCanceledHitCount == 0 && options.alwaysReportNonCancelableUserJobAsError()) {
- // even a short user job should check for cancelation
- severity = IStatus.ERROR;
- } else if (nanoTime >= options.errorThreshold()) {
- severity = IStatus.ERROR;
- } else if (nanoTime >= options.warningThreshold()) {
- severity = IStatus.WARNING;
- } else {
- severity = IStatus.OK;
- }
- return severity;
- }
-
- private IStatus createCancelabilityStatus(int severity, String msg) {
- IStatus ret;
- if (severity > IStatus.OK) {
- final MultiStatus ms = new MultiStatus(JobManager.PI_JOBS, CANCELABILITY_ERROR_CODE, msg, null);
- // Sort stack traces samples by elapsed time
- Collections.sort(stackTraces, new Comparator<StackTraceSample>() {
- @Override
- public int compare(StackTraceSample s1, StackTraceSample s2) {
- return (int) (s1.nanoBetweenStackTraces - s2.nanoBetweenStackTraces);
- }
- });
- for (StackTraceSample sts : stackTraces) {
- ms.add(createStatusFromStackTraceSample(sts));
- }
- ret = ms;
- } else {
- ret = Status.OK_STATUS;
- }
- return ret;
- }
-
- private IStatus createStatusFromStackTraceSample(StackTraceSample stackTraceSample) {
- MultiStatus ms = new MultiStatus(JobManager.PI_JOBS, CANCELABILITY_ERROR_CODE,
- NLS.bind(JobMessages.cancelability_monitor_sampledStackTraces,
- nanosToString(stackTraceSample.nanoBetweenStackTraces)),
- null);
- int severity = severityForElapsedTime(stackTraceSample.nanoBetweenStackTraces);
- ms.add(createStatusFromStackTrace(severity, JobMessages.cancelability_monitor_secondStackTrace,
- stackTraceSample.secondSte));
- ms.add(createStatusFromStackTrace(severity, JobMessages.cancelability_monitor_initialStackTrace,
- stackTraceSample.firstSte));
- return ms;
- }
-
- private static IStatus createStatusFromStackTrace(int severity, String msg, StackTraceElement[] stackTrace) {
- return new Status(severity, JobManager.PI_JOBS, msg, createThrowableFromStackTrace(stackTrace, msg));
- }
-
- private static Throwable createThrowableFromStackTrace(StackTraceElement[] stackTrace, String msg) {
- Throwable throwable = new Throwable(msg);
- throwable.setStackTrace(stackTrace);
- return throwable;
- }
-
- private static String nanosToString(long nanos) {
- double value = (double) nanos / _1_SECOND_IN_NANOS;
- final String format = nanos >= TimeUnit.SECONDS.toNanos(100) ? "%.0f %s" //$NON-NLS-1$
- : nanos >= TimeUnit.MILLISECONDS.toNanos(10) ? "%.2g %s" : "%.1g %s"; //$NON-NLS-1$ //$NON-NLS-2$
- return String.format(format, value, JobMessages.cancelability_monitor_abbrevUnitSeconds);
- }
-
- public static interface Options {
- boolean enabled();
-
- long errorThreshold();
-
- long warningThreshold();
-
- int maxStackSamples();
-
- boolean alwaysReportNonCancelableUserJobAsError();
- }
-
- /**
- * Static inactive singleton that will be used when no service has been
- * registered.
- */
- static final Options DEFAULT_OPTIONS = new BasicOptionsImpl();
- static {
- ((BasicOptionsImpl) DEFAULT_OPTIONS).setEnabled(false);
- }
-
- public static class BasicOptionsImpl implements Options {
- private boolean enabled;
- private long errorThreshold;
- private long warningThreshold;
- private int maxStackSamples;
- private boolean alwaysReportNonCancelableUserJobAsError;
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public void setErrorThreshold(long errorThreshold) {
- this.errorThreshold = errorThreshold;
- }
-
- public void setWarningThreshold(long warningThreshold) {
- this.warningThreshold = warningThreshold;
- }
-
- public void setMaxStackSamples(int maxStackSamples) {
- this.maxStackSamples = maxStackSamples;
- }
-
- public void setAlwaysReportNonCancelableUserJobAsError(boolean alwaysReportNonCancelableUserJobAsError) {
- this.alwaysReportNonCancelableUserJobAsError = alwaysReportNonCancelableUserJobAsError;
- }
-
- @Override
- public boolean enabled() {
- return enabled;
- }
-
- @Override
- public long errorThreshold() {
- return errorThreshold;
- }
-
- @Override
- public long warningThreshold() {
- return warningThreshold;
- }
-
- @Override
- public int maxStackSamples() {
- return maxStackSamples;
- }
-
- @Override
- public boolean alwaysReportNonCancelableUserJobAsError() {
- return alwaysReportNonCancelableUserJobAsError;
- }
- }
-}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobMessages.java b/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobMessages.java
index a025658..48522c8 100644
--- a/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobMessages.java
+++ b/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobMessages.java
@@ -1,5 +1,5 @@
/**********************************************************************
- * Copyright (c) 2005, 2016 IBM Corporation and others. All rights reserved. This
+ * Copyright (c) 2005, 2012 IBM Corporation and others. All rights reserved. This
* program and the accompanying materials are made available under the terms of
* the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
@@ -26,13 +26,6 @@
public static String jobs_waitFamSubOne;
// metadata
public static String meta_pluginProblems;
- // Job cancellation monitor
- public static String cancelability_monitor_waitedTooLong;
- public static String cancelability_monitor_noCancelationCheck;
- public static String cancelability_monitor_sampledStackTraces;
- public static String cancelability_monitor_initialStackTrace;
- public static String cancelability_monitor_secondStackTrace;
- public static String cancelability_monitor_abbrevUnitSeconds;
static {
// load message values from bundle file
diff --git a/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobOSGiUtils.java b/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobOSGiUtils.java
index 816fa22..a4d588a 100644
--- a/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobOSGiUtils.java
+++ b/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobOSGiUtils.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2005, 2016 IBM Corporation and others.
+ * Copyright (c) 2005, 2014 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -7,12 +7,10 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
- * Mikaël Barbero - Bug 470175
*******************************************************************************/
package org.eclipse.core.internal.jobs;
import java.util.Hashtable;
-import org.eclipse.core.internal.jobs.JobCancelabilityMonitor.Options;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.osgi.service.debug.DebugOptions;
import org.eclipse.osgi.service.debug.DebugOptionsListener;
@@ -33,7 +31,6 @@
class JobOSGiUtils {
private ServiceRegistration<DebugOptionsListener> debugRegistration = null;
private ServiceTracker bundleTracker = null;
- private ServiceTracker jobCancelabilityMonitorOptionsTracker = null;
private static final JobOSGiUtils singleton = new JobOSGiUtils();
@@ -68,9 +65,6 @@
bundleTracker = new ServiceTracker(context, PackageAdmin.class.getName(), null);
bundleTracker.open();
-
- jobCancelabilityMonitorOptionsTracker = new ServiceTracker(context, JobCancelabilityMonitor.Options.class, null);
- jobCancelabilityMonitorOptionsTracker.open();
}
void closeServices() {
@@ -82,10 +76,6 @@
bundleTracker.close();
bundleTracker = null;
}
- if (jobCancelabilityMonitorOptionsTracker != null) {
- jobCancelabilityMonitorOptionsTracker.close();
- jobCancelabilityMonitorOptionsTracker = null;
- }
}
/**
@@ -134,25 +124,4 @@
return false;
return "true".equalsIgnoreCase(value); //$NON-NLS-1$
}
-
- /**
- * Returns the options for the job cancelability monitor. Options have to
- * registered as an OSGi service.
- *
- * @return the options for the job cancelability monitor.
- */
- JobCancelabilityMonitor.Options getJobCancelabilityMonitorOptions() {
- final Options ret;
- if (jobCancelabilityMonitorOptionsTracker == null) {
- ret = null;
- } else {
- Object service = jobCancelabilityMonitorOptionsTracker.getService();
- if (service == null) {
- ret = JobCancelabilityMonitor.DEFAULT_OPTIONS;
- } else {
- ret = (Options) service;
- }
- }
- return ret;
- }
}
diff --git a/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Worker.java b/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Worker.java
index 92672c9..0fa63c3 100644
--- a/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Worker.java
+++ b/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Worker.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2003, 2016 IBM Corporation and others.
+ * Copyright (c) 2003, 2014 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -7,11 +7,9 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
- * Mikaël Barbero - Bug 470175
*******************************************************************************/
package org.eclipse.core.internal.jobs;
-import java.lang.reflect.Method;
import org.eclipse.core.internal.runtime.RuntimeLog;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
@@ -54,13 +52,7 @@
while ((currentJob = pool.startJob(this)) != null) {
IStatus result = Status.OK_STATUS;
try {
- JobCancelabilityMonitor.Options monitoringOptions = JobOSGiUtils.getDefault()
- .getJobCancelabilityMonitorOptions();
- if (shouldRunWithMonitoring(monitoringOptions)) {
- result = runWithCancelabilityMonitoring(monitoringOptions);
- } else {
- result = currentJob.run(currentJob.getProgressMonitor());
- }
+ result = currentJob.run(currentJob.getProgressMonitor());
} catch (OperationCanceledException e) {
result = Status.CANCEL_STATUS;
} catch (Exception e) {
@@ -72,12 +64,8 @@
} catch (Error e) {
result = handleException(currentJob, e);
} finally {
- // clear interrupted state for this thread
+ //clear interrupted state for this thread
Thread.interrupted();
- // check cancelability of the executed job.
- if (currentJob.getProgressMonitor() instanceof JobCancelabilityMonitor) {
- endCancelabilityMonitoring((JobCancelabilityMonitor) currentJob.getProgressMonitor());
- }
//result must not be null
if (result == null)
result = handleException(currentJob, new NullPointerException());
@@ -94,43 +82,4 @@
pool.endWorker(this);
}
}
-
- private boolean shouldRunWithMonitoring(JobCancelabilityMonitor.Options monitoringOptions) {
- return monitoringOptions != null && monitoringOptions.enabled() && !isJobOverridingCancelingMethod();
- }
-
- private IStatus runWithCancelabilityMonitoring(JobCancelabilityMonitor.Options jobCancelabilityMonitorOptions) {
- JobCancelabilityMonitor pm = new JobCancelabilityMonitor(currentJob, jobCancelabilityMonitorOptions);
- currentJob.setProgressMonitor(pm);
- IStatus jobResult = currentJob.run(pm.aboutToStart());
- return jobResult;
- }
-
- private void endCancelabilityMonitoring(JobCancelabilityMonitor pm) {
- pm.hasStopped();
- IStatus cancelabilityStatus = pm.createCancelabilityStatus();
- if (!cancelabilityStatus.isOK()) {
- RuntimeLog.log(cancelabilityStatus);
- }
- }
-
- /**
- * A job may be made responsive to cancelation by overriding
- * {@code Job.canceling()}. This method checks that {@link #currentJob} is
- * not overriding it.
- *
- * @return true if {@link #currentJob} overrides Job#canceling(), false
- * otherwise.
- */
- private boolean isJobOverridingCancelingMethod() {
- Method cancelingMethod = null;
- try {
- cancelingMethod = currentJob.getClass().getDeclaredMethod("canceling"); //$NON-NLS-1$
- } catch (NoSuchMethodException | SecurityException e) {
- return false;
- }
- // Sufficient as InternalJob and Job classes are abstract,
- // currentJob.getClass cannot be one or the other.
- return cancelingMethod != null;
- }
}
diff --git a/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/messages.properties b/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/messages.properties
index b610864..8451b40 100644
--- a/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/messages.properties
+++ b/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/messages.properties
@@ -1,5 +1,5 @@
###############################################################################
-# Copyright (c) 2000, 2016 IBM Corporation and others.
+# Copyright (c) 2000, 2012 IBM Corporation and others.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
@@ -19,11 +19,3 @@
### metadata
meta_pluginProblems = Problems occurred when invoking code from plug-in: \"{0}\".
-
-### Job cancelation monitor
-cancelability_monitor_waitedTooLong=Job \"{0}\" has waited up to {1} between two cancelation checks. It should do it more frequently to respond quickly to user cancelation requests. IProgressMonitor.isCanceled() hit count: {2}. Job execution time: {3}.
-cancelability_monitor_noCancelationCheck=Job \"{0}\" did not do any cancelation checks. It cannot respond to user cancelation requests. IProgressMonitor.isCanceled() hit count: {1}. Job execution time: {2}.
-cancelability_monitor_sampledStackTraces=Sampled stack traces of calls to IProgressMonitor.isCanceled. Time between the two calls: {0}.
-cancelability_monitor_initialStackTrace=Last cancellation check before a long gap.
-cancelability_monitor_secondStackTrace=First cancellation check after a long gap.
-cancelability_monitor_abbrevUnitSeconds=s