Copyright (c) 2003, 2015 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM - Initial API and implementation Warren Paul (Nokia) - Fix for build scheduling bug 209236
/******************************************************************************* * Copyright (c) 2003, 2015 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM - Initial API and implementation * Warren Paul (Nokia) - Fix for build scheduling bug 209236 *******************************************************************************/
package org.eclipse.core.internal.events; import org.eclipse.core.internal.resources.*; import org.eclipse.core.internal.utils.Messages; import org.eclipse.core.internal.utils.Policy; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.Preferences.PropertyChangeEvent; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.osgi.framework.Bundle;
The job for performing workspace auto-builds, and pre- and post- autobuild notification. This job is run whenever the workspace changes regardless of whether autobuild is on or off.
/** * The job for performing workspace auto-builds, and pre- and post- autobuild * notification. This job is run whenever the workspace changes regardless * of whether autobuild is on or off. */
class AutoBuildJob extends Job implements Preferences.IPropertyChangeListener { private boolean avoidBuild = false; private boolean buildNeeded = false; private boolean forceBuild = false;
Indicates that another thread tried to modify the workspace during the autobuild. The autobuild should be immediately rescheduled so that it will run as soon as the next workspace modification completes.
/** * Indicates that another thread tried to modify the workspace during * the autobuild. The autobuild should be immediately rescheduled * so that it will run as soon as the next workspace modification completes. */
private boolean interrupted = false; private boolean isAutoBuilding = false; private volatile long lastBuild = 0L; private Preferences preferences = ResourcesPlugin.getPlugin().getPluginPreferences(); private final Bundle systemBundle = Platform.getBundle("org.eclipse.osgi"); //$NON-NLS-1$ private Workspace workspace; AutoBuildJob(Workspace workspace) { super(Messages.events_building_0); setRule(workspace.getRoot()); setPriority(BUILD); isAutoBuilding = workspace.isAutoBuilding(); this.workspace = workspace; this.preferences.addPropertyChangeListener(this); }
Used to prevent auto-builds at the end of operations that contain explicit builds
/** * Used to prevent auto-builds at the end of operations that contain * explicit builds */
synchronized void avoidBuild() { avoidBuild = true; }
Prevent auto-builds if the auto-build job was not interrupted.
/** * Prevent auto-builds if the auto-build job was not interrupted. */
synchronized void avoidBuildIfNotInterrupted() { if (!interrupted) { avoidBuild(); } } @Override public boolean belongsTo(Object family) { return family == ResourcesPlugin.FAMILY_AUTO_BUILD; }
Instructs the build job that a build is required. Ensure the build job is scheduled to run.
Params:
  • needsBuild – Whether a build is required, either due to workspace change or other factor that invalidates the built state.
/** * Instructs the build job that a build is required. Ensure the build * job is scheduled to run. * @param needsBuild Whether a build is required, either due to * workspace change or other factor that invalidates the built state. */
synchronized void build(boolean needsBuild) { buildNeeded |= needsBuild; long delay = computeScheduleDelay(); int state = getState(); if (Policy.DEBUG_BUILD_NEEDED) Policy.debug("Auto-Build requested, needsBuild: " + needsBuild + " state: " + state + " delay: " + delay); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ if (needsBuild && Policy.DEBUG_BUILD_NEEDED_STACK && state != Job.RUNNING) Policy.debug(new RuntimeException("Build needed")); //$NON-NLS-1$ //don't mess with the interrupt flag if the job is still running if (state != Job.RUNNING) setInterrupted(false); switch (state) { case Job.SLEEPING : wakeUp(delay); break; case NONE : try { setSystem(!isAutoBuilding); } catch (IllegalStateException e) { //ignore - the job has been scheduled since we last checked its state } schedule(delay); break; } }
Computes the delay time that autobuild should be scheduled with. The value will be in the range (MIN_BUILD_DELAY, MAX_BUILD_DELAY).
/** * Computes the delay time that autobuild should be scheduled with. The * value will be in the range (MIN_BUILD_DELAY, MAX_BUILD_DELAY). */
private long computeScheduleDelay() { // don't assume that the last build time is always less than the current system time long maxDelay = Math.min(Policy.MAX_BUILD_DELAY, Policy.MAX_BUILD_DELAY + lastBuild - System.currentTimeMillis()); return Math.max(Policy.MIN_BUILD_DELAY, maxDelay); }
The autobuild job has been canceled. There are two flavours of cancel, explicit user cancelation, and implicit interruption due to another thread trying to modify the workspace. In the latter case, we must make sure the build is immediately rescheduled if it was interrupted by another thread, so that clients waiting to join autobuild will properly continue waiting
Returns:a status with severity CANCEL
/** * The autobuild job has been canceled. There are two flavours of * cancel, explicit user cancelation, and implicit interruption due to another * thread trying to modify the workspace. In the latter case, we must * make sure the build is immediately rescheduled if it was interrupted * by another thread, so that clients waiting to join autobuild will properly * continue waiting * @return a status with severity <code>CANCEL</code> */
private synchronized IStatus canceled() { //regardless of the form of cancelation, the build state is not happy buildNeeded = true; //schedule a rebuild immediately if build was implicitly canceled if (interrupted) { if (Policy.DEBUG_BUILD_INTERRUPT) Policy.debug("Scheduling rebuild due to interruption"); //$NON-NLS-1$ setInterrupted(false); schedule(computeScheduleDelay()); } return Status.CANCEL_STATUS; } private void doBuild(IProgressMonitor monitor) throws CoreException, OperationCanceledException { SubMonitor subMonitor = SubMonitor.convert(monitor, Policy.opWork + 1); final ISchedulingRule rule = workspace.getRuleFactory().buildRule(); try { workspace.prepareOperation(rule, subMonitor.split(1)); workspace.beginOperation(true); final int trigger = IncrementalProjectBuilder.AUTO_BUILD; workspace.broadcastBuildEvent(workspace, IResourceChangeEvent.PRE_BUILD, trigger); IStatus result = Status.OK_STATUS; try { if (shouldBuild()) result = workspace.getBuildManager().build(workspace.getBuildOrder(), ICoreConstants.EMPTY_BUILD_CONFIG_ARRAY, trigger, subMonitor.split(Policy.opWork)); } finally { //always send POST_BUILD if there has been a PRE_BUILD workspace.broadcastBuildEvent(workspace, IResourceChangeEvent.POST_BUILD, trigger); } if (!result.isOK()) { throw new ResourceException(result); } buildNeeded = false; } finally { //building may close the tree, but we are still inside an // operation so open it if (workspace.getElementTree().isImmutable()) { workspace.newWorkingTree(); } workspace.endOperation(rule, false); } }
Forces an autobuild to occur, even if nothing has changed since the last build. This is used to force a build after a clean.
/** * Forces an autobuild to occur, even if nothing has changed since the last * build. This is used to force a build after a clean. */
public void forceBuild() { forceBuild = true; }
Another thread is attempting to modify the workspace. Flag the auto-build as interrupted so that it will cancel and reschedule itself
/** * Another thread is attempting to modify the workspace. Flag the auto-build * as interrupted so that it will cancel and reschedule itself */
synchronized void interrupt() { //if already interrupted, do nothing if (interrupted) return; switch (getState()) { case NONE : return; case WAITING : //put the job to sleep if it is waiting to run setInterrupted(!sleep()); break; case RUNNING : //make sure autobuild doesn't interrupt itself if (Job.getJobManager().currentJob() == this) return; setInterrupted(true); break; } //clear the autobuild avoidance flag if we were interrupted if (interrupted) avoidBuild = false; } synchronized boolean isInterrupted() { if (interrupted) return true; //check if another job is blocked by the build job if (isBlocking()) setInterrupted(true); return interrupted; } @Deprecated @Override public void propertyChange(PropertyChangeEvent event) { if (!event.getProperty().equals(ResourcesPlugin.PREF_AUTO_BUILDING)) return; // get the new value of auto-build directly from the preferences boolean wasAutoBuilding = isAutoBuilding; isAutoBuilding = preferences.getBoolean(ResourcesPlugin.PREF_AUTO_BUILDING); //force a build if autobuild has been turned on if (!forceBuild && !wasAutoBuilding && isAutoBuilding) { forceBuild = true; build(false); } } @Override public IStatus run(IProgressMonitor monitor) { SubMonitor subMonitor = SubMonitor.convert(monitor, 1); synchronized (this) { if (subMonitor.isCanceled()) { return canceled(); } } //if the system is shutting down, don't build if (systemBundle.getState() == Bundle.STOPPING) return Status.OK_STATUS; try { doBuild(subMonitor.split(1)); lastBuild = System.currentTimeMillis(); //if the build was successful then it should not be recorded as interrupted setInterrupted(false); return Status.OK_STATUS; } catch (OperationCanceledException e) { return canceled(); } catch (CoreException sig) { return sig.getStatus(); } }
Sets or clears the interrupted flag.
/** * Sets or clears the interrupted flag. */
private synchronized void setInterrupted(boolean value) { interrupted = value; if (interrupted && Policy.DEBUG_BUILD_INTERRUPT) Policy.debug(new RuntimeException("Autobuild was interrupted")); //$NON-NLS-1$ }
Returns true if a build is actually needed, and false otherwise.
/** * Returns true if a build is actually needed, and false otherwise. */
private synchronized boolean shouldBuild() { try { //if auto-build is off then we never run if (!workspace.isAutoBuilding()) return false; //build if the workspace requires a build (description changes) if (forceBuild) return true; if (avoidBuild) return false; //return whether there have been any changes to the workspace tree. return buildNeeded; } finally { //regardless of the result, clear the build flags for next time forceBuild = avoidBuild = buildNeeded = false; } } }