/******************************************************************************* * Copyright (c) 2013 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is 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: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.foundation.core.jobs; import org.eclipse.core.internal.jobs.Semaphore; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.IJobChangeListener; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.jboss.tools.foundation.core.internal.Trace; /** * This is a job which provides a interruptableJoin() method to make it interruptable. * Use of this class ensures that if you are joined to a job such as this, * and your thread is blocking or waiting on a lock, the job CAN be interrupted. * * Superclass implementation does not propagate {@link InterruptedException} * despite the fact that its javadoc says it does. * * This class should NOT be subclassed to do ANY ui work AT ALL, * as the implementation is NOT UI-safe. The safety for * join() and interrupt() may cause a deadlock if the job is * performing any UI functionality at all. Calls to * Display.syncExec() are specifically forbidden. * */ public class InterruptableJoinJob extends Job { public InterruptableJoinJob(String name) { super(name); } protected IStatus run(IProgressMonitor monitor) { // TODO Auto-generated method stub return null; } /** * A custom implementation of join because the official one * cannot be interrupted at all. [293312] * * @throws InterruptedException */ public void interruptableJoin() throws InterruptedException { interruptableJoin(false); } /** * A custom implementation of join because the official one * cannot be interrupted at all. [293312] * * If schedule is true, this implementation will be sure to add the listener BEFORE schedule, * to prevent any issues for fast-completion jobs. * If schedule is false, the user must schedule the job themselves. * * @throws InterruptedException */ public void interruptableJoin(boolean schedule) throws InterruptedException { Trace.trace(Trace.STRING_FINER, "Joining job " + getName() + " in interruptable fashion"); final IJobChangeListener listener; final Semaphore barrier2; barrier2 = new Semaphore(null); listener = new JobChangeAdapter() { public void done(IJobChangeEvent event) { Trace.trace(Trace.STRING_FINER, "Job " + event.getJob().getName() + " completed. Releasing barrier."); barrier2.release(); } }; addJobChangeListener(listener); if( schedule ) { Trace.trace(Trace.STRING_FINER, "Scheduling Job " + getName()); schedule(); } try { if (barrier2.acquire(Long.MAX_VALUE)) return; } catch (InterruptedException e) { Trace.trace(Trace.STRING_FINER, "Job " + getName() + " has been interrupted, so this join is terminating"); // Actual join implementation LOOPS here, and ignores the exception. throw new InterruptedException(); } } }