/*******************************************************************************
 * Copyright (c) 2003, 2006 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 Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.core.runtime.jobs;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;

/**
 * The job manager provides facilities for scheduling, querying, and maintaining jobs
 * and locks.  In particular, the job manager provides the following services:
 * <ul>
 * <li>Maintains a queue of jobs that are waiting to be run.  Items can be added to
 * the queue using the <code>schedule</code> method.</li>
 * <li>Allows manipulation of groups of jobs called job families.  Job families can
 * be canceled, put to sleep, or woken up atomically.  There is also a mechanism
 * for querying the set of known jobs in a given family.</li>
 * <li>Allows listeners to find out about progress on running jobs, and to find out
 * when jobs have changed states.</li>
 * <li>Provides a factory for creating lock objects.  Lock objects are smart monitors
 * that have strategies for avoiding deadlock.</li>
 * <li>Provide feedback to a client that is waiting for a given job or family of jobs
 * to complete.</li>
 * </ul>
 * <p>
 * This interface is not intended to be implemented by clients.
 * </p>
 * 
 * @see Job
 * @see ILock
 * @since 3.0
 */
public interface IJobManager {
	/**
	 * Registers a job listener with the job manager.  
	 * Has no effect if an identical listener is already registered.
	 * 
	 * @param listener the listener to be added
	 * @see #removeJobChangeListener(IJobChangeListener)
	 * @see IJobChangeListener
	 */
	public void addJobChangeListener(IJobChangeListener listener);

	/**
	 * Begins applying this rule in the calling thread.  If the rule conflicts with another
	 * rule currently running in another thread, this method blocks until there are
	 * no conflicting rules.  Calls to <tt>beginRule</tt> must eventually be followed
	 * by a matching call to <tt>endRule</tt> in the same thread and with the identical
	 * rule instance. 
	 * <p>
	 * Rules can be nested only if the rule for the inner <tt>beginRule</tt>
	 * is contained within the rule for the outer <tt>beginRule</tt>.  Rule containment
	 * is tested with the API method <tt>ISchedulingRule.contains</tt>.  Also, begin/end
	 * pairs must be strictly nested.  Only the rule that has most recently begun
	 * can be ended at any given time.
	 * <p>
	 * A rule of <code>null</code> can be used, but will be ignored for scheduling 
	 * purposes.  The outermost non-null rule in the thread will be used for scheduling. A
	 * <code>null</code> rule that is begun must still be ended.
	 * <p>
	 * If this method is called from within a job that has a scheduling rule, the
	 * given rule must also be contained within the rule for the running job.
	 * <p>
	 * Note that <tt>endRule</tt> must be called even if <tt>beginRule</tt> fails.
	 * The recommended usage is:
	 * <pre>
	 * final ISchedulingRule rule = ...;
	 * try {
	 * 	manager.beginRule(rule, monitor);
	 * } finally {
	 * 	manager.endRule(rule);
	 * }
	 * </pre>
	 * 
	 * @param rule the rule to begin applying in this thread, or <code>null</code>
	 * @param monitor a progress monitor, or <code>null</code> if progress
	 *    reporting and cancellation are not desired
	 * @throws IllegalArgumentException if the rule is not strictly nested within
	 * 	all other rules currently active for this thread
	 * @throws OperationCanceledException if the supplied monitor reports cancelation
	 * 	before the rule becomes available
	 * @see ISchedulingRule#contains(ISchedulingRule)
	 */
	public void beginRule(ISchedulingRule rule, IProgressMonitor monitor);

	/**
	 * Cancels all jobs in the given job family.  Jobs in the family that are currently waiting
	 * will be removed from the queue.  Sleeping jobs will be discarded without having 
	 * a chance to wake up.  Currently executing jobs will be asked to cancel but there 
	 * is no guarantee that they will do so.
	 * 
	 * @param family the job family to cancel, or <code>null</code> to cancel all jobs
	 * @see Job#belongsTo(Object)
	 */
	public void cancel(Object family);

	/**
	 * Returns a progress monitor that can be used to provide
	 * aggregated progress feedback on a set of running jobs. A user
	 * interface will typically group all jobs in a progress group together,
	 * providing progress feedback for individual jobs as well as aggregated
	 * progress for the entire group.  Jobs in the group may be run sequentially,
	 * in parallel, or some combination of the two.
	 * <p>
	 * Recommended usage (this snippet runs two jobs in sequence in a 
	 * single progress group):
	 * <pre>
	 *    Job parseJob, compileJob;
	 *    IProgressMonitor pm = Platform.getJobManager().createProgressGroup();
	 *    try {
	 *       pm.beginTask("Building", 10);
	 *       parseJob.setProgressGroup(pm, 5);
	 *       parseJob.schedule();
	 *       compileJob.setProgressGroup(pm, 5);
	 *       compileJob.schedule();
	 *       parseJob.join();
	 *       compileJob.join();
	 *    } finally {
	 *       pm.done();
	 *    }
	 * </pre>
	 * 
	 * @see Job#setProgressGroup(IProgressMonitor, int)
	 * @see IProgressMonitor
	 * @return a progress monitor
	 */
	public IProgressMonitor createProgressGroup();

	/**
	 * Returns the job that is currently running in this thread, or <code>null</code> if there
	 * is no currently running job.
	 * 
	 * @return the job or <code>null</code>
	 */
	public Job currentJob();

	/**
	 * Ends the application of a rule to the calling thread.  Calls to <tt>endRule</tt> 
	 * must be preceded by a matching call to <tt>beginRule</tt> in the same thread
	 * with an identical rule instance.
	 * <p>
	 * Rules can be nested only if the rule for the inner <tt>beginRule</tt>
	 * is contained within the rule for the outer <tt>beginRule</tt>.  Also, begin/end
	 * pairs must be strictly nested.  Only the rule that has most recently begun
	 * can be ended at any given time.
	 * 
	 * @param rule the rule to end applying in this thread
	 * @throws IllegalArgumentException if this method is called on a rule for which
	 * there is no matching begin, or that does not match the most recent begin.
	 * @see ISchedulingRule#contains(ISchedulingRule)
	 */
	public void endRule(ISchedulingRule rule);

	/**
	 * Returns all waiting, executing and sleeping jobs belonging
	 * to the given family. If no jobs are found, an empty array is returned.
	 * 
	 * @param family the job family to find, or <code>null</code> to find all jobs
	 * @return the job array
	 * @see Job#belongsTo(Object)
	 */
	public Job[] find(Object family);

	/**
	 * Returns whether the job manager is currently idle.  The job manager is
	 * idle if no jobs are currently running or waiting to run.
	 * 
	 * @return <code>true</code> if the job manager is idle, and 
	 * <code>false</code> otherwise
	 * @since 3.1
	 */
	public boolean isIdle();

	/**
	 * Waits until all jobs of the given family are finished.  This method will block the 
	 * calling thread until all such jobs have finished executing, or until this thread is
	 * interrupted.   If there are no jobs in 
	 * the family that are currently waiting, running, or sleeping, this method returns 
	 * immediately.  Feedback on how the join is progressing is provided to a  progress 
	 * monitor.
	 * <p>
	 * If this method is called while the job manager is suspended, only jobs
	 * that are currently running will be joined; Once there are no jobs
	 * in the family in the {@link Job#RUNNING} state, this method returns.
	 * </p>
	 * <p>
	 * Note that there is a deadlock risk when using join.  If the calling thread owns
	 * a lock or object monitor that the joined thread is waiting for, deadlock 
	 * will occur. This method can also result in starvation of the current thread if
	 * another thread continues to add jobs of the given family, or if a
	 * job in the given family reschedules itself in an infinite loop.
	 * </p>
	 * 
	 * @param family the job family to join, or <code>null</code> to join all jobs.
	 * @param monitor Progress monitor for reporting progress on how the
	 * wait is progressing, or <code>null</code> if no progress monitoring is required.
	 * @exception InterruptedException if this thread is interrupted while waiting
	 * @exception OperationCanceledException if the progress monitor is canceled while waiting
	 * @see Job#belongsTo(Object)
	 * @see #suspend()
	 */
	public void join(Object family, IProgressMonitor monitor) throws InterruptedException, OperationCanceledException;

	/**
	 * Creates a new lock object.  All lock objects supplied by the job manager
	 * know about each other and will always avoid circular deadlock amongst
	 * themselves.
	 * 
	 * @return the new lock object
	 */
	public ILock newLock();

	/**
	 * Removes a job listener from the job manager.  
	 * Has no effect if an identical listener is not already registered.
	 * 
	 * @param listener the listener to be removed
	 * @see #addJobChangeListener(IJobChangeListener)
	 * @see IJobChangeListener
	 */
	public void removeJobChangeListener(IJobChangeListener listener);

	/**
	 * Resumes execution of jobs after a previous <code>suspend</code>.  All
	 * jobs that were sleeping or waiting prior to the suspension, or that were
	 * scheduled while the job manager was suspended, will now be eligible
	 * for execution.
	 * <p>
	 * Calling this method on a rule that is not suspended  has no effect.  If another 
	 * thread also owns the rule at the time this method is called, then the rule will 
	 * not be resumed until all threads have released the rule.
	 * 
	 * @deprecated This method is not safe and should not be used.
	 * Suspending a scheduling rule violates the thread safety
	 * of clients that use scheduling rules as a mutual exclusion mechanism,
	 * and can result in concurrency problems in all clients that use the suspended rule.
	 * @see #suspend(ISchedulingRule, IProgressMonitor)
	 */
	public void resume(ISchedulingRule rule);

	/**
	 * Resumes execution of jobs after a previous <code>suspend</code>.  All
	 * jobs that were sleeping or waiting prior to the suspension, or that were
	 * scheduled while the job manager was suspended, will now be eligible
	 * for execution.
	 * <p>
	 * Calling <code>resume</code> when the job manager is not suspended
	 * has no effect.
	 * 
	 * @see #suspend()
	 */
	public void resume();

	/**
	 * Provides a hook that is notified whenever a thread is about to wait on a lock,
	 * or when a thread is about to release a lock.  This hook must only be set once.
	 * <p>
	 * This method is for internal use by the platform-related plug-ins.  
	 * Clients should not call this method.
	 * </p>
	 * @see LockListener
	 */
	public void setLockListener(LockListener listener);

	/**
	 * Registers a progress provider with the job manager.  If there was a
	 * provider already registered, it is replaced.
	 * <p>
	 * This method is intended for use by the currently executing Eclipse application.
	 * Plug-ins outside the currently running application should not call this method.
	 * </p>
	 * 
	 * @param provider the new provider, or <code>null</code> if no progress
	 * is needed
	 */
	public void setProgressProvider(ProgressProvider provider);

	/**
	 * Suspends execution of all jobs.  Jobs that are already running
	 * when this method is invoked will complete as usual, but all sleeping and
	 * waiting jobs will not be executed until the job manager is resumed.
	 * <p>
	 * The job manager will remain suspended until a subsequent call to
	 * <code>resume</code>.  Further calls to <code>suspend</code>
	 * when the job manager is already suspended are ignored.
	 * <p>
	 * All attempts to join sleeping and waiting jobs while the job manager is
	 * suspended will return immediately.
	 * <p>
	 * Note that this very powerful function should be used with extreme caution.
	 * Suspending the job manager will prevent all jobs in the system from executing,
	 * which may have adverse affects on components that are relying on
	 * execution of jobs. The job manager should never be suspended without intent
	 * to resume execution soon afterwards.
	 * 
	 * @see #resume()
	 * @see #join(Object, IProgressMonitor)
	 */
	public void suspend();

	/**
	 * Defers execution of all jobs with scheduling rules that conflict with the
	 * given rule. The caller will be blocked until all currently executing jobs with
	 * conflicting rules are completed.  Conflicting jobs that are sleeping or waiting at
	 * the time this method is called will not be executed until the rule is resumed.
	 * <p>
	 * While a rule is suspended, all calls to <code>beginRule</code> and 
	 * <code>endRule</code> on a suspended rule will not block the caller.
	 * The rule remains suspended until a subsequent call to
	 * <code>resume(ISchedulingRule)</code> with the identical rule instance.  
	 * Further calls to <code>suspend</code> with an identical rule prior to calling
	 * <code>resume</code> are ignored.
	 * </p>
	 * <p>
	 * This method is long-running; progress and cancelation are provided by
	 * the given progress monitor. In the case of cancelation, the rule will
	 * not be suspended.
	 * </p>
	 * Note: this very powerful function should be used with extreme caution.
	 * Suspending rules will prevent jobs in the system from executing, which may 
	 * have adverse effects on components that are relying on execution of jobs. 
	 * The job manager should never be suspended without intent to resume 
	 * execution soon afterwards. Deadlock will result if the thread responsible
	 * for resuming the rule attempts to join a suspended job.
	 * 
	 * @deprecated This method is not safe and should not be used.
	 * Suspending a scheduling rule violates the thread safety
	 * of clients that use scheduling rules as a mutual exclusion mechanism,
	 * and can result in concurrency problems in all clients that use the suspended rule.
	 * @param rule The scheduling rule to suspend. Must not be <code>null</code>.
	 * @param monitor a progress monitor, or <code>null</code> if progress
	 * reporting is not desired
	 * @exception OperationCanceledException if the operation is canceled. 
	 * Cancelation can occur even if no progress monitor is provided.
	 * @see #resume(ISchedulingRule)
	 */
	public void suspend(ISchedulingRule rule, IProgressMonitor monitor);

	/**
	 * Requests that all jobs in the given job family be suspended.  Jobs currently 
	 * waiting to be run will be removed from the queue and moved into the 
	 * <code>SLEEPING</code> state.  Jobs that have been put to sleep
	 * will remain in that state until either resumed or canceled.  This method has
	 * no effect on jobs that are not currently waiting to be run.
	 * <p>
	 * Sleeping jobs can be resumed using <code>wakeUp</code>.
	 * 
	 * @param family the job family to sleep, or <code>null</code> to sleep all jobs.
	 * @see Job#belongsTo(Object)
	 */
	public void sleep(Object family);

	/**
	 * Transfers ownership of a scheduling rule to another thread.  The identical
	 * scheduling rule must currently be owned by the calling thread as a result of 
	 * a previous call to <code>beginRule</code>.  The destination thread must
	 * not already own a scheduling rule.
	 * <p>
	 * Calling this method is equivalent to atomically calling <code>endRule</code> 
	 * in the calling thread followed by an immediate <code>beginRule</code> in 
	 * the destination thread.  The destination thread is responsible for subsequently 
	 * calling <code>endRule</code> when it is finished using the rule.
	 * <p>
	 * This method has no effect when the destination thread is the same as the
	 * calling thread.
	 * 
	 * @param rule The scheduling rule to transfer
	 * @param destinationThread The new owner for the transferred rule.
	 * @since 3.1
	 */
	public void transferRule(ISchedulingRule rule, Thread destinationThread);

	/**
	 * Resumes scheduling of all sleeping jobs in the given family.  This method
	 * has no effect on jobs in the family that are not currently sleeping.
	 * 
	 * @param family the job family to wake up, or <code>null</code> to wake up all jobs
	 * @see Job#belongsTo(Object)
	 */
	public void wakeUp(Object family);
}
