All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.core.internal.jobs.ImplicitJobs Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/*******************************************************************************
 *  Copyright (c) 2003, 2012 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
 *******************************************************************************/
package org.eclipse.core.internal.jobs;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.internal.runtime.RuntimeLog;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;

/**
 * Implicit jobs are jobs that are running by virtue of a JobManager.begin/end
 * pair. They act like normal jobs, except they are tied to an arbitrary thread
 * of the client's choosing, and they can be nested.
 */
class ImplicitJobs {

	/**
	 * Cached unused instance that can be reused
	 * @GuardedBy("this")
	 */
	private ThreadJob jobCache = null;
	protected JobManager manager;

	/**
	 * Set of suspended scheduling rules.
	 * @GuardedBy("this")
	 */
	private final Set suspendedRules = new HashSet<>(20);

	/**
	 * Maps (Thread->ThreadJob), threads to the currently running job for that
	 * thread.
	 * @GuardedBy("this")
	 */
	private final Map threadJobs = new HashMap<>(20);

	ImplicitJobs(JobManager manager) {
		this.manager = manager;
	}

	/*
	 * @see IJobManager#beginRule(ISchedulingRule, IProgressMonitor)
	 */
	void begin(ISchedulingRule rule, IProgressMonitor monitor, boolean suspend) {
		if (JobManager.DEBUG_BEGIN_END)
			JobManager.debug("Begin rule: " + rule); //$NON-NLS-1$
		final Thread currentThread = Thread.currentThread();
		ThreadJob threadJob;
		synchronized (this) {
			threadJob = threadJobs.get(currentThread);
			if (threadJob != null) {
				//nested rule, just push on stack and return
				threadJob.push(rule);
				return;
			}
			//no need to schedule a thread job for a null rule
			if (rule == null)
				return;
			//create a thread job for this thread, use the rule from the real job if it has one
			Job realJob = manager.currentJob();
			if (realJob != null && realJob.getRule() != null)
				threadJob = newThreadJob(realJob.getRule());
			else {
				threadJob = newThreadJob(rule);
				threadJob.acquireRule = true;
			}
			//don't acquire rule if it is a suspended rule
			if (isSuspended(rule))
				threadJob.acquireRule = false;
			//indicate if it is a system job to ensure isBlocking works correctly
			threadJob.setRealJob(realJob);
			threadJob.setThread(currentThread);
		}
		try {
			threadJob.push(rule);
			//join the thread job outside sync block
			if (threadJob.acquireRule) {
				//no need to re-acquire any locks because the thread did not wait to get this lock
				if (manager.runNow(threadJob, false) == null)
					manager.getLockManager().addLockThread(Thread.currentThread(), rule);
				else
					threadJob = ThreadJob.joinRun(threadJob, monitor);
			}
		} finally {
			//remember this thread job  - only do this
			//after the rule is acquired because it is ok for this thread to acquire
			//and release other rules while waiting.
			synchronized (this) {
				threadJobs.put(currentThread, threadJob);
				if (suspend)
					suspendedRules.add(rule);
			}
		}
	}

	/*
	 * @see IJobManager#endRule
	 */
	synchronized void end(ISchedulingRule rule, boolean resume) {
		if (JobManager.DEBUG_BEGIN_END)
			JobManager.debug("End rule: " + rule); //$NON-NLS-1$
		ThreadJob threadJob = threadJobs.get(Thread.currentThread());
		if (threadJob == null)
			Assert.isLegal(rule == null, "endRule without matching beginRule: " + rule); //$NON-NLS-1$
		else if (threadJob.pop(rule)) {
			endThreadJob(threadJob, resume, false);
		}
	}

	/**
	 * Called when a worker thread has finished running a job. At this
	 * point, the worker thread must not own any scheduling rules
	 * @param lastJob The last job to run in this thread
	 */
	void endJob(InternalJob lastJob) {
		final Thread currentThread = Thread.currentThread();
		IStatus error;
		synchronized (this) {
			ThreadJob threadJob = threadJobs.get(currentThread);
			if (threadJob == null) {
				if (lastJob.getRule() != null)
					notifyWaitingThreadJobs(lastJob);
				return;
			}
			String msg = "Worker thread ended job: " + lastJob + ", but still holds rule: " + threadJob; //$NON-NLS-1$ //$NON-NLS-2$
			error = new Status(IStatus.ERROR, JobManager.PI_JOBS, 1, msg, new IllegalStateException(msg));
			//end the thread job
			endThreadJob(threadJob, false, true);
		}
		try {
			RuntimeLog.log(error);
		} catch (RuntimeException e) {
			//failed to log, so print to console instead
			System.err.println(error.getMessage());
		}
	}

	/**
	 * @GuardedBy("this")
	 */
	private void endThreadJob(ThreadJob threadJob, boolean resume, boolean worker) {
		Thread currentThread = Thread.currentThread();
		//clean up when last rule scope exits
		threadJobs.remove(currentThread);
		ISchedulingRule rule = threadJob.getRule();
		if (resume && rule != null)
			suspendedRules.remove(rule);
		//if this job had a rule, then we are essentially releasing a lock
		//note it is safe to do this even if the acquire was aborted
		if (threadJob.acquireRule) {
			manager.getLockManager().removeLockThread(currentThread, rule);
			notifyWaitingThreadJobs(threadJob);
		}
		//if the job was started, we need to notify job manager to end it
		if (threadJob.isRunning())
			manager.endJob(threadJob, Status.OK_STATUS, false, worker);
		recycle(threadJob);
	}

	/**
	 * Returns true if this rule has been suspended, and false otherwise.
	 * @GuardedBy("this")
	 */
	private boolean isSuspended(ISchedulingRule rule) {
		if (suspendedRules.isEmpty())
			return false;
		for (ISchedulingRule iSchedulingRule : suspendedRules)
			if (iSchedulingRule.contains(rule))
				return true;
		return false;
	}

	/**
	 * Returns a new or reused ThreadJob instance.
	 * @GuardedBy("this")
	 */
	private ThreadJob newThreadJob(ISchedulingRule rule) {
		if (jobCache != null) {
			ThreadJob job = jobCache;
			// calling setRule will try to acquire JobManager.lock, breaking
			// lock acquisition protocol. Since we managing this special job
			// ourselves we can call internalSetRule
			((InternalJob) job).internalSetRule(rule);
			job.acquireRule = job.isRunning = false;
			job.realJob = null;
			jobCache = null;
			return job;
		}
		return new ThreadJob(rule);
	}

	/**
	 * A job has just finished that was holding a scheduling rule, and the
	 * scheduling rule is now free.  Wake any blocked thread jobs so they can
	 * compete for the newly freed lock
	 */
	void notifyWaitingThreadJobs(InternalJob job) {
		synchronized (job.jobStateLock) {
			job.jobStateLock.notifyAll();
		}
	}

	/**
	 * Indicates that a thread job is no longer in use and can be reused.
	 * @GuardedBy("this")
	 */
	private void recycle(ThreadJob job) {
		if (jobCache == null && job.recycle())
			jobCache = job;
	}

	/**
	 * Implements IJobManager#resume(ISchedulingRule)
	 * @param rule
	 */
	void resume(ISchedulingRule rule) {
		//resume happens as a consequence of freeing the last rule in the stack
		end(rule, true);
		if (JobManager.DEBUG_BEGIN_END)
			JobManager.debug("Resume rule: " + rule); //$NON-NLS-1$
	}

	/**
	 * Implements IJobManager#suspend(ISchedulingRule, IProgressMonitor)
	 * @param rule
	 * @param monitor
	 */
	void suspend(ISchedulingRule rule, IProgressMonitor monitor) {
		if (JobManager.DEBUG_BEGIN_END)
			JobManager.debug("Suspend rule: " + rule); //$NON-NLS-1$
		//the suspend job will be remembered once the rule is acquired
		begin(rule, monitor, true);
	}

	/**
	 * Implements IJobManager#transferRule(ISchedulingRule, Thread)
	 */
	synchronized void transfer(ISchedulingRule rule, Thread destinationThread) {
		//nothing to do for null
		if (rule == null)
			return;
		final Thread currentThread = Thread.currentThread();
		//nothing to do if transferring to the same thread
		if (currentThread == destinationThread)
			return;
		//ensure destination thread doesn't already have a rule
		ThreadJob target = threadJobs.get(destinationThread);
		Assert.isLegal(target == null, "Transfer rule to job that already owns a rule"); //$NON-NLS-1$
		//ensure calling thread owns the job being transferred
		ThreadJob source = threadJobs.get(currentThread);
		Assert.isNotNull(source, "transferRule without beginRule"); //$NON-NLS-1$
		Assert.isLegal(source.getRule() == rule, "transferred rule " + rule + " does not match beginRule: " + source.getRule()); //$NON-NLS-1$ //$NON-NLS-2$		// transfer the thread job without ending it
		source.setThread(destinationThread);
		threadJobs.remove(currentThread);
		threadJobs.put(destinationThread, source);
		// transfer lock
		if (source.acquireRule) {
			manager.getLockManager().removeLockThread(currentThread, rule);
			manager.getLockManager().addLockThread(destinationThread, rule);
		}
		// Wake up any blocked jobs (waiting within yield or joinRun) waiting on
		// this rule
		notifyWaitingThreadJobs(source);
	}

	synchronized void removeWaiting(ThreadJob threadJob) {
		synchronized (((InternalJob) threadJob).jobStateLock) {
			threadJob.isWaiting = false;
			notifyWaitingThreadJobs(threadJob);
			((InternalJob) threadJob).setWaitQueueStamp(InternalJob.T_NONE);
		}
		manager.dequeue(manager.waitingThreadJobs, threadJob);
	}

	synchronized void addWaiting(ThreadJob threadJob) {
		synchronized (((InternalJob) threadJob).jobStateLock) {
			threadJob.isWaiting = true;
			notifyWaitingThreadJobs(threadJob);
			((InternalJob) threadJob).setWaitQueueStamp(manager.getNextWaitQueueStamp());
		}
		manager.enqueue(manager.waitingThreadJobs, threadJob);
	}

	synchronized ThreadJob getThreadJob(Thread thread) {
		return threadJobs.get(thread);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy