| /******************************************************************************* |
| * Copyright (c) 2003, 2016 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 |
| * Mikaƫl Barbero - Bug 470175 |
| *******************************************************************************/ |
| package org.eclipse.core.internal.jobs; |
| |
| import java.lang.reflect.Method; |
| import org.eclipse.core.internal.runtime.RuntimeLog; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.osgi.util.NLS; |
| |
| /** |
| * A worker thread processes jobs supplied to it by the worker pool. When |
| * the worker pool gives it a null job, the worker dies. |
| */ |
| public class Worker extends Thread { |
| //worker number used for debugging purposes only |
| private static int nextWorkerNumber = 0; |
| private volatile InternalJob currentJob; |
| private final WorkerPool pool; |
| |
| public Worker(WorkerPool pool) { |
| super("Worker-" + nextWorkerNumber++); //$NON-NLS-1$ |
| this.pool = pool; |
| //set the context loader to avoid leaking the current context loader |
| //for the thread that spawns this worker (bug 98376) |
| setContextClassLoader(pool.defaultContextLoader); |
| } |
| |
| /** |
| * Returns the currently running job, or null if none. |
| */ |
| public Job currentJob() { |
| return (Job) currentJob; |
| } |
| |
| private IStatus handleException(InternalJob job, Throwable t) { |
| String message = NLS.bind(JobMessages.jobs_internalError, job.getName()); |
| return new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, message, t); |
| } |
| |
| @Override |
| public void run() { |
| setPriority(Thread.NORM_PRIORITY); |
| try { |
| while ((currentJob = pool.startJob(this)) != null) { |
| IStatus result = Status.OK_STATUS; |
| try { |
| JobCancelabilityMonitor.Options monitoringOptions = JobOSGiUtils.getDefault() |
| .getJobCancelabilityMonitorOptions(); |
| if (shouldRunWithMonitoring(monitoringOptions)) { |
| result = runWithCancelabilityMonitoring(monitoringOptions); |
| } else { |
| result = currentJob.run(currentJob.getProgressMonitor()); |
| } |
| } catch (OperationCanceledException e) { |
| result = Status.CANCEL_STATUS; |
| } catch (Exception e) { |
| result = handleException(currentJob, e); |
| } catch (ThreadDeath e) { |
| //must not consume thread death |
| result = handleException(currentJob, e); |
| throw e; |
| } catch (Error e) { |
| result = handleException(currentJob, e); |
| } finally { |
| // clear interrupted state for this thread |
| Thread.interrupted(); |
| // check cancelability of the executed job. |
| if (currentJob.getProgressMonitor() instanceof JobCancelabilityMonitor) { |
| endCancelabilityMonitoring((JobCancelabilityMonitor) currentJob.getProgressMonitor()); |
| } |
| //result must not be null |
| if (result == null) |
| result = handleException(currentJob, new NullPointerException()); |
| pool.endJob(currentJob, result); |
| currentJob = null; |
| //reset thread priority in case job changed it |
| setPriority(Thread.NORM_PRIORITY); |
| } |
| } |
| } catch (Throwable t) { |
| RuntimeLog.log(new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, "Unhandled error", t)); //$NON-NLS-1$ |
| } finally { |
| currentJob = null; |
| pool.endWorker(this); |
| } |
| } |
| |
| private boolean shouldRunWithMonitoring(JobCancelabilityMonitor.Options monitoringOptions) { |
| return monitoringOptions != null && monitoringOptions.enabled() && !isJobOverridingCancelingMethod(); |
| } |
| |
| private IStatus runWithCancelabilityMonitoring(JobCancelabilityMonitor.Options jobCancelabilityMonitorOptions) { |
| JobCancelabilityMonitor pm = new JobCancelabilityMonitor(currentJob, jobCancelabilityMonitorOptions); |
| currentJob.setProgressMonitor(pm); |
| IStatus jobResult = currentJob.run(pm.aboutToStart()); |
| return jobResult; |
| } |
| |
| private void endCancelabilityMonitoring(JobCancelabilityMonitor pm) { |
| pm.hasStopped(); |
| IStatus cancelabilityStatus = pm.createCancelabilityStatus(); |
| if (!cancelabilityStatus.isOK()) { |
| RuntimeLog.log(cancelabilityStatus); |
| } |
| } |
| |
| /** |
| * A job may be made responsive to cancelation by overriding |
| * {@code Job.canceling()}. This method checks that {@link #currentJob} is |
| * not overriding it. |
| * |
| * @return true if {@link #currentJob} overrides Job#canceling(), false |
| * otherwise. |
| */ |
| private boolean isJobOverridingCancelingMethod() { |
| Method cancelingMethod = null; |
| try { |
| cancelingMethod = currentJob.getClass().getDeclaredMethod("canceling"); //$NON-NLS-1$ |
| } catch (NoSuchMethodException | SecurityException e) { |
| return false; |
| } |
| // Sufficient as InternalJob and Job classes are abstract, |
| // currentJob.getClass cannot be one or the other. |
| return cancelingMethod != null; |
| } |
| } |