Copyright (c) 2003, 2014 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 Stephan Wahlbrink - Fix for bug 200997. Thirumala Reddy Mutchukota (thirumala@google.com) - Bug 432049, JobGroup API and implementation Bug 105821, Support for Job#join with timeout and progress monitor
/******************************************************************************* * Copyright (c) 2003, 2014 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 * Stephan Wahlbrink - Fix for bug 200997. * Thirumala Reddy Mutchukota (thirumala@google.com) - * Bug 432049, JobGroup API and implementation * Bug 105821, Support for Job#join with timeout and progress monitor *******************************************************************************/
package org.eclipse.core.internal.jobs; import java.util.Map; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.*;
Internal implementation class for jobs. Clients must not implement this class directly. All jobs must be subclasses of the API org.eclipse.core.runtime.jobs.Job class.
/** * Internal implementation class for jobs. Clients must not implement this class * directly. All jobs must be subclasses of the API <code>org.eclipse.core.runtime.jobs.Job</code> class. */
public abstract class InternalJob extends PlatformObject implements Comparable {
Job state code (value 16) indicating that a job has been removed from the wait queue and is about to start running. From an API point of view, this is the same as RUNNING.
/** * Job state code (value 16) indicating that a job has been removed from * the wait queue and is about to start running. From an API point of view, * this is the same as RUNNING. */
static final int ABOUT_TO_RUN = 0x10;
Job state code (value 32) indicating that a job has passed scheduling precondition checks and is about to be added to the wait queue. From an API point of view, this is the same as WAITING.
/** * Job state code (value 32) indicating that a job has passed scheduling * precondition checks and is about to be added to the wait queue. From an API point of view, * this is the same as WAITING. */
static final int ABOUT_TO_SCHEDULE = 0x20;
Job state code (value 8) indicating that a job is blocked by another currently running job. From an API point of view, this is the same as WAITING.
/** * Job state code (value 8) indicating that a job is blocked by another currently * running job. From an API point of view, this is the same as WAITING. */
static final int BLOCKED = 0x08;
Job state code (value 64) indicating that a job is yielding. From an API point of view, this is the same as WAITING.
/** * Job state code (value 64) indicating that a job is yielding. * From an API point of view, this is the same as WAITING. */
static final int YIELDING = 0x40; //flag mask bits private static final int M_STATE = 0xFF; private static final int M_SYSTEM = 0x0100; private static final int M_USER = 0x0200; /* * flag on a job indicating that it was about to run, but has been canceled */ private static final int M_ABOUT_TO_RUN_CANCELED = 0x0400; /* * Flag on a job indicating that it was canceled when running. This flag * is used to ensure that #canceling is only ever called once on a job in * case of recursive cancelation attempts. */ private static final int M_RUN_CANCELED = 0x0800; private static int nextJobNumber = 0; protected static final JobManager manager = JobManager.getInstance();
Start time constant indicating a job should be started at a time in the infinite future, causing it to sleep forever.
/** * Start time constant indicating a job should be started at * a time in the infinite future, causing it to sleep forever. */
static final long T_INFINITE = Long.MAX_VALUE;
Start time constant indicating that the job has no start time.
/** * Start time constant indicating that the job has no start time. */
static final long T_NONE = -1; private volatile int flags = Job.NONE; private final int jobNumber = getNextJobNumber();
The list of job listeners. Never null. "itself")
/** * The list of job listeners. Never null. * @GuardedBy("itself") */
private final ListenerList<IJobChangeListener> listeners = new ListenerList<>(ListenerList.IDENTITY); private volatile IProgressMonitor monitor; private String name; private JobGroup jobGroup;
The job ahead of me in a queue or list. "manager.lock")
/** * The job ahead of me in a queue or list. * @GuardedBy("manager.lock") */
private InternalJob next;
The job behind me in a queue or list. "manager.lock")
/** * The job behind me in a queue or list. * @GuardedBy("manager.lock") */
private InternalJob previous; private int priority = Job.LONG;
Arbitrary properties (key,value) pairs, attached to a job instance by a third party.
/** * Arbitrary properties (key,value) pairs, attached * to a job instance by a third party. */
private ObjectMap properties;
Volatile because it is usually set via a Worker thread and is read via a client thread.
/** * Volatile because it is usually set via a Worker thread and is read via a * client thread. */
private volatile IStatus result;
"manager.lock")
/** * @GuardedBy("manager.lock") */
private ISchedulingRule schedulingRule;
If the job is waiting, this represents the time the job should start by. If this job is sleeping, this represents the time the job should wake up. If this job is running, this represents the delay automatic rescheduling, or -1 if the job should not be rescheduled. "manager.lock")
/** * If the job is waiting, this represents the time the job should start by. * If this job is sleeping, this represents the time the job should wake up. * If this job is running, this represents the delay automatic rescheduling, * or -1 if the job should not be rescheduled. * @GuardedBy("manager.lock") */
private long startTime;
Stamp added when a job is added to the wait queue. Used to ensure jobs in the wait queue maintain their insertion order even if they are removed from the wait queue temporarily while blocked "manager.lock")
/** * Stamp added when a job is added to the wait queue. Used to ensure * jobs in the wait queue maintain their insertion order even if they are * removed from the wait queue temporarily while blocked * @GuardedBy("manager.lock") */
private long waitQueueStamp = T_NONE; /* * The thread that is currently running this job */ private volatile Thread thread = null;
This lock will be held while performing state changes on this job. It is also used as a notifier used to wake up yielding jobs or waiting ThreadJobs when 1) a conflicting job completes and releases a scheduling rule, or 2) when a this job changes state. See also the lock ordering protocol explanation in JobManager's documentation. "itself")
/** * This lock will be held while performing state changes on this job. It is * also used as a notifier used to wake up yielding jobs or waiting ThreadJobs * when 1) a conflicting job completes and releases a scheduling rule, or 2) * when a this job changes state. * * See also the lock ordering protocol explanation in JobManager's * documentation. * * @GuardedBy("itself") */
final Object jobStateLock = new Object(); private static synchronized int getNextJobNumber() { return nextJobNumber++; } protected InternalJob(String name) { Assert.isNotNull(name); this.name = name; } protected void addJobChangeListener(IJobChangeListener listener) { listeners.add(listener); }
Adds an entry at the end of the list of which this item is the head. "manager.lock")
/** * Adds an entry at the end of the list of which this item is the head. * @GuardedBy("manager.lock") */
final void addLast(InternalJob entry) { InternalJob last = this; //find the end of the queue while (last.previous != null) last = last.previous; //add the new entry to the end of the queue last.previous = entry; entry.next = last; entry.previous = null; } protected boolean belongsTo(Object family) { return false; } protected boolean cancel() { return manager.cancel(this); } protected void canceling() { //default implementation does nothing } @Override public final int compareTo(Object otherJob) { return ((InternalJob) otherJob).startTime >= startTime ? 1 : -1; } protected void done(IStatus endResult) { manager.endJob(this, endResult, true); }
Returns the job listeners that are only listening to this job. Never returns null.
/** * Returns the job listeners that are only listening to this job. Never returns * null. */
final ListenerList<IJobChangeListener> getListeners() { return listeners; } protected String getName() { return name; } protected int getPriority() { return priority; }
Returns the job's progress monitor, or null if it is not running.
/** * Returns the job's progress monitor, or null if it is not running. */
final IProgressMonitor getProgressMonitor() { return monitor; } protected Object getProperty(QualifiedName key) { // thread safety: (Concurrency001 - copy on write) Map temp = properties; if (temp == null) return null; return temp.get(key); } protected IStatus getResult() { return result; } protected ISchedulingRule getRule() { return schedulingRule; }
Returns the time that this job should be started, awakened, or rescheduled, depending on the current state.
Returns:time in milliseconds
/** * Returns the time that this job should be started, awakened, or * rescheduled, depending on the current state. * @return time in milliseconds */
final long getStartTime() { return startTime; } protected int getState() { int state = flags & M_STATE; switch (state) { //blocked and yielding state is equivalent to waiting state for clients case YIELDING : case BLOCKED : return Job.WAITING; case ABOUT_TO_RUN : return Job.RUNNING; case ABOUT_TO_SCHEDULE : return Job.WAITING; default : return state; } } protected Thread getThread() { return thread; } protected JobGroup getJobGroup() { return jobGroup; }
Returns the raw job state, including internal states no exposed as API.
/** * Returns the raw job state, including internal states no exposed as API. */
final int internalGetState() { return flags & M_STATE; }
Must be called from JobManager#setPriority
/** * Must be called from JobManager#setPriority */
final void internalSetPriority(int newPriority) { this.priority = newPriority; }
Must be called from JobManager#setRule
/** * Must be called from JobManager#setRule */
final void internalSetRule(ISchedulingRule rule) { this.schedulingRule = rule; }
Must be called from JobManager#changeState
/** * Must be called from JobManager#changeState */
final void internalSetState(int i) { flags = (flags & ~M_STATE) | i; }
Returns whether this job was canceled when it was about to run
/** * Returns whether this job was canceled when it was about to run */
final boolean isAboutToRunCanceled() { return (flags & M_ABOUT_TO_RUN_CANCELED) != 0; }
Returns whether this job was canceled when it was running.
/** * Returns whether this job was canceled when it was running. */
final boolean isRunCanceled() { return (flags & M_RUN_CANCELED) != 0; } protected boolean isBlocking() { return manager.isBlocking(this); }
Returns true if this job conflicts with the given job, and false otherwise.
/** * Returns true if this job conflicts with the given job, and false otherwise. */
final boolean isConflicting(InternalJob otherJob) { ISchedulingRule otherRule = otherJob.getRule(); if (schedulingRule == null || otherRule == null) return false; //if one of the rules is a compound rule, it must be asked the question. if (schedulingRule.getClass() == MultiRule.class) return schedulingRule.isConflicting(otherRule); return otherRule.isConflicting(schedulingRule); } protected boolean isSystem() { return (flags & M_SYSTEM) != 0; } protected boolean isUser() { return (flags & M_USER) != 0; } protected void join() throws InterruptedException { manager.join(this, 0, null); } protected boolean join(long timeout, IProgressMonitor joinMonitor) throws InterruptedException, OperationCanceledException { return manager.join(this, timeout, joinMonitor); }
Returns the next entry (ahead of this one) in the list, or null if there is no next entry
/** * Returns the next entry (ahead of this one) in the list, or null if there is no next entry */
final InternalJob next() { return next; }
Returns the previous entry (behind this one) in the list, or null if there is no previous entry
/** * Returns the previous entry (behind this one) in the list, or null if there is no previous entry */
final InternalJob previous() { return previous; }
Removes this entry from any list it belongs to. Returns the receiver.
/** * Removes this entry from any list it belongs to. Returns the receiver. */
final InternalJob remove() { if (next != null) next.setPrevious(previous); if (previous != null) previous.setNext(next); next = previous = null; return this; } protected void removeJobChangeListener(IJobChangeListener listener) { listeners.remove(listener); } protected abstract IStatus run(IProgressMonitor progressMonitor); protected void schedule(long delay) { if (shouldSchedule()) manager.schedule(this, delay, false); }
Sets whether this job was canceled when it was about to run
/** * Sets whether this job was canceled when it was about to run */
final void setAboutToRunCanceled(boolean value) { flags = value ? flags | M_ABOUT_TO_RUN_CANCELED : flags & ~M_ABOUT_TO_RUN_CANCELED; }
Sets whether this job was canceled when it was running
/** * Sets whether this job was canceled when it was running */
final void setRunCanceled(boolean value) { flags = value ? flags | M_RUN_CANCELED : flags & ~M_RUN_CANCELED; } protected void setName(String name) { Assert.isNotNull(name); this.name = name; }
Sets the next entry in this linked list of jobs.
Params:
  • entry –
/** * Sets the next entry in this linked list of jobs. * @param entry */
final void setNext(InternalJob entry) { this.next = entry; }
Sets the previous entry in this linked list of jobs.
Params:
  • entry –
/** * Sets the previous entry in this linked list of jobs. * @param entry */
final void setPrevious(InternalJob entry) { this.previous = entry; } protected void setPriority(int newPriority) { switch (newPriority) { case Job.INTERACTIVE : case Job.SHORT : case Job.LONG : case Job.BUILD : case Job.DECORATE : manager.setPriority(this, newPriority); break; default : throw new IllegalArgumentException(String.valueOf(newPriority)); } } protected void setProgressGroup(IProgressMonitor group, int ticks) { Assert.isNotNull(group); IProgressMonitor pm = manager.createMonitor(this, group, ticks); if (pm != null) setProgressMonitor(pm); }
Sets the progress monitor to use for the next execution of this job, or for clearing the monitor when a job completes.
Params:
  • monitor – a progress monitor
/** * Sets the progress monitor to use for the next execution of this job, * or for clearing the monitor when a job completes. * @param monitor a progress monitor */
final void setProgressMonitor(IProgressMonitor monitor) { this.monitor = monitor; } protected void setProperty(QualifiedName key, Object value) { // thread safety: (Concurrency001 - copy on write) if (value == null) { if (properties == null) return; ObjectMap temp = (ObjectMap) properties.clone(); temp.remove(key); if (temp.isEmpty()) properties = null; else properties = temp; } else { ObjectMap temp = properties; if (temp == null) temp = new ObjectMap(5); else temp = (ObjectMap) properties.clone(); temp.put(key, value); properties = temp; } }
Sets or clears the result of an execution of this job.
Params:
  • result – a result status, or null "manager.lock")
/** * Sets or clears the result of an execution of this job. * @param result a result status, or <code>null</code> * @GuardedBy("manager.lock") */
final void setResult(IStatus result) { this.result = result; } protected void setRule(ISchedulingRule rule) { manager.setRule(this, rule); }
Sets a time to start, wake up, or schedule this job, depending on the current state
Params:
  • time – a time in milliseconds "manager.lock")
/** * Sets a time to start, wake up, or schedule this job, * depending on the current state * @param time a time in milliseconds * @GuardedBy("manager.lock") */
final void setStartTime(long time) { startTime = time; } protected void setSystem(boolean value) { if (getState() != Job.NONE) throw new IllegalStateException(); flags = value ? flags | M_SYSTEM : flags & ~M_SYSTEM; } protected void setThread(Thread thread) { this.thread = thread; } protected void setUser(boolean value) { if (getState() != Job.NONE) throw new IllegalStateException(); flags = value ? flags | M_USER : flags & ~M_USER; } protected void setJobGroup(JobGroup jobGroup) { if (getState() != Job.NONE) throw new IllegalStateException("Setting job group of an already scheduled job is not allowed"); //$NON-NLS-1$ this.jobGroup = jobGroup; } protected boolean shouldSchedule() { return true; } protected boolean sleep() { return manager.sleep(this); } protected Job yieldRule(IProgressMonitor progressMonitor) { return manager.yieldRule(this, progressMonitor); } @Override public String toString() { return getName() + "(" + jobNumber + ")"; //$NON-NLS-1$//$NON-NLS-2$ } protected void wakeUp(long delay) { manager.wakeUp(this, delay); }
Params:
  • waitQueueStamp – The waitQueueStamp to set. "manager.lock")
/** * @param waitQueueStamp The waitQueueStamp to set. * @GuardedBy("manager.lock") */
void setWaitQueueStamp(long waitQueueStamp) { this.waitQueueStamp = waitQueueStamp; }
Returns:Returns the waitQueueStamp. "manager.lock")
/** * @return Returns the waitQueueStamp. * @GuardedBy("manager.lock") */
long getWaitQueueStamp() { return waitQueueStamp; } }