Mercurial > projects > dwt-addons
diff dwtx/core/internal/jobs/InternalJob.d @ 122:9d0585bcb7aa
Add core.jobs package
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Tue, 12 Aug 2008 02:34:21 +0200 |
parents | |
children | 862b05e0334a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/core/internal/jobs/InternalJob.d Tue Aug 12 02:34:21 2008 +0200 @@ -0,0 +1,628 @@ +/******************************************************************************* + * Copyright (c) 2003, 2007 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 + * + * Contributors: + * IBM - Initial API and implementation + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.core.internal.jobs.InternalJob; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.IProgressMonitor; +import dwtx.core.runtime.IStatus; +import dwtx.core.runtime.ListenerList; +import dwtx.core.runtime.PlatformObject; +import dwtx.core.runtime.QualifiedName; +import dwtx.core.runtime.jobs.IJobChangeListener; +import dwtx.core.runtime.jobs.ISchedulingRule; +import dwtx.core.runtime.jobs.Job; +import dwtx.core.runtime.jobs.MultiRule; + +import dwtx.core.internal.jobs.JobManager; +import dwtx.core.internal.jobs.ObjectMap; + +import tango.core.Thread; +import tango.text.convert.Format; + +/** + * Internal implementation class for jobs. Clients must not implement this class + * directly. All jobs must be subclasses of the API <code>dwtx.core.runtime.jobs.Job</code> class. + */ +public abstract class InternalJob : PlatformObject, 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. + */ + static const 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. + */ + static const 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. + */ + static const int BLOCKED = 0x08; + + //flag mask bits + private static const int M_STATE = 0xFF; + private static const int M_SYSTEM = 0x0100; + private static const int M_USER = 0x0200; + + /* + * flag on a job indicating that it was about to run, but has been canceled + */ + private static const int M_ABOUT_TO_RUN_CANCELED = 0x0400; + + private static JobManager manager_; + protected static JobManager manager(){ + if( manager_ is null ){ + synchronized( InternalJob.classinfo ){ + if( manager_ is null ){ + manager_ = JobManager.getInstance(); + } + } + } + return manager_; + } + private static int nextJobNumber = 0; + + /** + * Start time constant indicating a job should be started at + * a time in the infinite future, causing it to sleep forever. + */ + static const long T_INFINITE = Long.MAX_VALUE; + /** + * Start time constant indicating that the job has no start time. + */ + static const long T_NONE = -1; + + private /+volatile+/ int flags = Job.NONE; + private const int jobNumber; + private ListenerList listeners = null; + private IProgressMonitor monitor; + private String name; + /** + * The job ahead of me in a queue or list. + */ + private InternalJob next_; + /** + * The job behind me in a queue or list. + */ + private InternalJob previous_; + private int priority = Job.LONG; + /** + * Arbitrary properties (key,value) pairs, attached + * to a job instance by a third party. + */ + private ObjectMap properties; + private IStatus result; + 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. + */ + 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 + */ + private long waitQueueStamp = T_NONE; + + /* + * The thread that is currently running this job + */ + private /+volatile+/ Thread thread = null; + + protected this(String name) { + Assert.isNotNull(name); + jobNumber = nextJobNumber++; + this.name = name; + } + + /* (non-Javadoc) + * @see Job#addJobListener(IJobChangeListener) + */ + protected void addJobChangeListener(IJobChangeListener listener) { + if (listeners is null) + listeners = new ListenerList(ListenerList.IDENTITY); + listeners.add(cast(Object)listener); + } + package void addJobChangeListener_package(IJobChangeListener listener) { + addJobChangeListener(listener); + } + + /** + * Adds an entry at the end of the list of which this item is the head. + */ + final void addLast(InternalJob entry) { + InternalJob last = this; + //find the end of the queue + while (last.previous_ !is null) + last = last.previous_; + //add the new entry to the end of the queue + last.previous_ = entry; + entry.next_ = last; + entry.previous_ = null; + } + + /* (non-Javadoc) + * @see Job#belongsTo(Object) + */ + protected bool belongsTo(Object family) { + return false; + } + package bool belongsTo_package(Object family) { + return belongsTo(family); + } + + /* (non-Javadoc) + * @see Job#cancel() + */ + protected package bool cancel() { + return manager.cancel_package(this); + } + + /* (non-Javadoc) + * @see Job#canceling() + */ + protected package void canceling() { + //default implementation does nothing + } + + /* (on-Javadoc) + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + public final int compareTo(Object otherJob) { + return (cast(InternalJob) otherJob).startTime >= startTime ? 1 : -1; + } + + /* (non-Javadoc) + * @see Job#done(IStatus) + */ + protected void done(IStatus endResult) { + manager.endJob_package(this, endResult, true); + } + + /** + * Returns the job listeners that are only listening to this job. Returns + * <code>null</code> if this job has no listeners. + */ + final ListenerList getListeners() { + return listeners; + } + + /* (non-Javadoc) + * @see Job#getName() + */ + protected String getName() { + return name; + } + package String getName_package() { + return name; + } + + /* (non-Javadoc) + * @see Job#getPriority() + */ + protected package int getPriority() { + return priority; + } + + /** + * Returns the job's progress monitor, or null if it is not running. + */ + final IProgressMonitor getProgressMonitor() { + return monitor; + } + + /* (non-Javadoc) + * @see Job#getProperty + */ + protected Object getProperty(QualifiedName key) { + // thread safety: (Concurrency001 - copy on write) + Map temp = properties; + if (temp is null) + return null; + return temp.get(key); + } + + /* (non-Javadoc) + * @see Job#getResult + */ + protected IStatus getResult() { + return result; + } + + /* (non-Javadoc) + * @see Job#getRule + */ + protected package ISchedulingRule getRule() { + return schedulingRule; + } + package ISchedulingRule getRule_package() { + return getRule(); + } + + /** + * 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; + } + + /* (non-Javadoc) + * @see Job#getState() + */ + protected int getState() { + int state = flags & M_STATE; + switch (state) { + //blocked state is equivalent to waiting state for clients + case BLOCKED : + return Job.WAITING; + case ABOUT_TO_RUN : + return Job.RUNNING; + case ABOUT_TO_SCHEDULE : + return Job.WAITING; + default : + return state; + } + } + package int getState_package() { + return getState(); + } + + /* (non-javadoc) + * @see Job.getThread + */ + protected Thread getThread() { + return thread; + } + package Thread getThread_package() { + return getThread(); + } + + /** + * 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 + */ + final void internalSetPriority(int newPriority) { + this.priority = newPriority; + } + + /** + * Must be called from JobManager#setRule + */ + final void internalSetRule(ISchedulingRule rule) { + this.schedulingRule = rule; + } + + /** + * 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 + */ + final bool isAboutToRunCanceled() { + return (flags & M_ABOUT_TO_RUN_CANCELED) !is 0; + } + + /* (non-Javadoc) + * @see Job#isBlocking() + */ + protected bool isBlocking() { + return manager.isBlocking_package(this); + } + + /** + * Returns true if this job conflicts with the given job, and false otherwise. + */ + final bool isConflicting(InternalJob otherJob) { + ISchedulingRule otherRule = otherJob.getRule(); + if (schedulingRule is null || otherRule is null) + return false; + //if one of the rules is a compound rule, it must be asked the question. + if (schedulingRule.classinfo is MultiRule.classinfo) + return schedulingRule.isConflicting(otherRule); + return otherRule.isConflicting(schedulingRule); + } + + /* (non-javadoc) + * @see Job.isSystem() + */ + protected bool isSystem() { + return (flags & M_SYSTEM) !is 0; + } + package bool isSystem_package() { + return isSystem(); + } + + /* (non-javadoc) + * @see Job.isUser() + */ + protected bool isUser() { + return (flags & M_USER) !is 0; + } + + /* (non-Javadoc) + * @see Job#join() + */ + protected void join() { + manager.join_package(this); + } + + /** + * 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 + */ + final InternalJob previous() { + return previous_; + } + + /** + * Removes this entry from any list it belongs to. Returns the receiver. + */ + final InternalJob remove() { + if (next_ !is null) + next_.setPrevious(previous_); + if (previous_ !is null) + previous_.setNext(next_); + next_ = previous_ = null; + return this; + } + + /* (non-Javadoc) + * @see Job#removeJobListener(IJobChangeListener) + */ + protected void removeJobChangeListener(IJobChangeListener listener) { + if (listeners !is null) + listeners.remove(cast(Object)listener); + } + package void removeJobChangeListener_package(IJobChangeListener listener) { + removeJobChangeListener(listener); + } + + /* (non-Javadoc) + * @see Job#run(IProgressMonitor) + */ + protected abstract IStatus run(IProgressMonitor progressMonitor); + package IStatus run_package(IProgressMonitor progressMonitor){ + return run(progressMonitor); + } + + /* (non-Javadoc) + * @see Job#schedule(long) + */ + protected void schedule(long delay) { + if (shouldSchedule()) + manager.schedule_package(this, delay, false); + } + package void schedule_package(long delay) { + schedule(delay); + } + + /** + * Sets whether this job was canceled when it was about to run + */ + final void setAboutToRunCanceled(bool value) { + flags = value ? flags | M_ABOUT_TO_RUN_CANCELED : flags & ~M_ABOUT_TO_RUN_CANCELED; + + } + + /* (non-Javadoc) + * @see Job#setName(String) + */ + protected void setName(String name) { + Assert.isNotNull(name); + this.name = name; + } + + /** + * 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. + * @param entry + */ + final void setPrevious(InternalJob entry) { + this.previous_ = entry; + } + + /* (non-Javadoc) + * @see Job#setPriority(int) + */ + protected void setPriority(int newPriority) { + switch (newPriority) { + case Job.INTERACTIVE : + case Job.SHORT : + case Job.LONG : + case Job.BUILD : + case Job.DECORATE : + manager.setPriority_package(this, newPriority); + break; + default : + throw new IllegalArgumentException(Integer.toString(newPriority)); + } + } + + /* (non-Javadoc) + * @see Job#setProgressGroup(IProgressMonitor, int) + */ + protected void setProgressGroup(IProgressMonitor group, int ticks) { + Assert.isNotNull(cast(Object)group); + IProgressMonitor pm = manager.createMonitor_package(this, group, ticks); + if (pm !is 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. + * @param monitor a progress monitor + */ + final void setProgressMonitor(IProgressMonitor monitor) { + this.monitor = monitor; + } + + /* (non-Javadoc) + * @see Job#setProperty(QualifiedName,Object) + */ + protected void setProperty(QualifiedName key, Object value) { + // thread safety: (Concurrency001 - copy on write) + if (value is null) { + if (properties is null) + return; + ObjectMap temp = cast(ObjectMap) properties.clone(); + temp.remove(key); + if (temp.isEmpty()) + properties = null; + else + properties = temp; + } else { + ObjectMap temp = properties; + if (temp is null) + temp = new ObjectMap(5); + else + temp = cast(ObjectMap) properties.clone(); + temp.put(key, value); + properties = temp; + } + } + + /** + * Sets or clears the result of an execution of this job. + * @param result a result status, or <code>null</code> + */ + final void setResult(IStatus result) { + this.result = result; + } + + /* (non-Javadoc) + * @see Job#setRule(ISchedulingRule) + */ + 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 + * @param time a time in milliseconds + */ + final void setStartTime(long time) { + startTime = time; + } + + /* (non-javadoc) + * @see Job.setSystem + */ + protected void setSystem(bool value) { + if (getState() !is Job.NONE) + throw new IllegalStateException(); + flags = value ? flags | M_SYSTEM : flags & ~M_SYSTEM; + } + + /* (non-javadoc) + * @see Job.setThread + */ + protected void setThread(Thread thread) { + this.thread = thread; + } + package void setThread_package(Thread thread) { + setThread(thread); + } + + /* (non-javadoc) + * @see Job.setUser + */ + protected void setUser(bool value) { + if (getState() !is Job.NONE) + throw new IllegalStateException(); + flags = value ? flags | M_USER : flags & ~M_USER; + } + + /* (Non-javadoc) + * @see Job#shouldSchedule + */ + protected bool shouldSchedule() { + return true; + } + package bool shouldSchedule_package() { + return shouldSchedule(); + } + + /* (non-Javadoc) + * @see Job#sleep() + */ + protected bool sleep() { + return manager.sleep_package(this); + } + + /* (non-Javadoc) + * Prints a string-based representation of this job instance. + * For debugging purposes only. + */ + public String toString() { + return Format( "{}({})", getName(), jobNumber ); //$NON-NLS-1$//$NON-NLS-2$ + } + + /* (non-Javadoc) + * @see Job#wakeUp(long) + */ + protected void wakeUp(long delay) { + manager.wakeUp_package(this, delay); + } + + /** + * @param waitQueueStamp The waitQueueStamp to set. + */ + void setWaitQueueStamp(long waitQueueStamp) { + this.waitQueueStamp = waitQueueStamp; + } + + /** + * @return Returns the waitQueueStamp. + */ + long getWaitQueueStamp() { + return waitQueueStamp; + } +}