/******************************************************************************* * 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.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.jboss.tools.foundation.core.internal.Trace; /** * This class is a Job with the purpose of specifically * waiting on a barrier array to have a non-null value in the * index 0 slot. It includes implementations that ensure * safety for join() and interrupt() calls. * * 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 BarrierWaitJob extends InterruptableJoinJob { /** * Waits until the first entry in the given array is non-null. * Launch this job synchronously. * * @param jobName * @param barrier a barrier to wait on * @param system whether the launched job is to be a system job or not * @throws InterruptedException if it is interrupted OR if the job is canceled */ public static void waitForSynchronous(String jobName, Object[] barrier, boolean system) throws InterruptedException { BarrierWaitJob wait = new BarrierWaitJob(jobName, barrier, system); try { // Do not simply join, because then there is *NO* way to interrupt this at ALL // [293312] Instead, do a cutom-join wait.interruptableJoin(true); } catch (InterruptedException e) { // Do NOT log the error. Let the caller log it as they wish. // Clean up the job, since I'm the only one who has a reference to it. wait.cancel(); Trace.trace(Trace.STRING_FINER, "Canceling the barrier wait job due to interrupted flag: " + wait.getName()); // re-throw the interrupted exception, so whoever was waiting // knows to clean up if they can. throw e; } if( wait.hasBeenCanceled()) { Trace.trace(Trace.STRING_FINER, "Job " + wait.getName() + " has been canceled"); throw new InterruptedException(); } } /** * The barrier to block on */ private final Object[] barrier; /** * Whether this job has been canceled */ private boolean canceled = false; /** * Creates a wait job. This job will wait specifically * for the first element of the array to be non-null. * * * @param jobName A name for the job * @param barrir The job will wait until the first entry in the barrier is non-null * @param system Is this a system-level job */ public BarrierWaitJob(String jobName, Object[] barrier, boolean system) { super(jobName); this.barrier = barrier; setSystem(system); } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) */ protected IStatus run(IProgressMonitor monitor) { synchronized (barrier) { while (!monitor.isCanceled() && barrier[0] == null) { try { barrier.wait(); } catch (InterruptedException e) { //ignore } } } return Status.OK_STATUS; } /** * Ensure that if the job is canceled, we interrupt the barrier */ protected void canceling() { synchronized( barrier ) { Trace.trace(Trace.STRING_FINER, "Job " + getName() + " has been canceled. Notifying the barrier"); canceled = true; barrier.notify(); } } /** * Allow our synchronous static method to verify we've been canceled * @return */ private boolean hasBeenCanceled() { synchronized(barrier) { return canceled; } } }