blob: 8bfcda96d77ad1d4339720dcaf65d55af3cd56a2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 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 Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.internal;
import java.util.Objects;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public final class PendingSyncExec {
private final Semaphore semaphore = new Semaphore(0);
private Thread operation;
private final Runnable runnable;
// Accessed by multiple threads. Synchronize on "this" before accessing.
private boolean hasFinishedRunning;
public PendingSyncExec(Runnable runnable) {
this.runnable = runnable;
}
/**
* Attempts to acquire this semaphore. Returns true if it was successfully
* acquired, and false otherwise.
*/
private boolean acquire(long delay) throws InterruptedException {
return semaphore.tryAcquire(delay, TimeUnit.MILLISECONDS);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof PendingSyncExec)) {
return false;
}
return (runnable == ((PendingSyncExec) obj).runnable);
}
public Thread getOperationThread() {
return operation;
}
public void run() {
// Clear the interrupted flag. The blocked thread may have been
// periodically interrupting the UI thread in
// order to interrupt other tasks in the queue and cause this job to
// start execution. (Note that it will
// continue to try to interrupt this runnable if it takes too long to
// run, until we set the hasFinishedRunning
// flag).
Thread.interrupted();
try {
if (runnable != null) {
runnable.run();
}
} finally {
// Record the fact that this pending syncExec has finished
// execution, to prevent the calling thread from
// interrupting this thread.
synchronized (this) {
hasFinishedRunning = true;
}
// The calling thread may have still interrupted this operation up
// until the point where we flipped the
// hasFinishedRunning flag.
Thread.interrupted();
semaphore.release();
}
}
public void waitUntilExecuted(UILockListener lockListener) throws InterruptedException {
// even if the UI was not blocked earlier, it might become blocked
// before it can serve the asyncExec to do the pending work
while (!acquire(1000)) {
if (lockListener.isUIWaiting()) {
synchronized (this) {
if (!hasFinishedRunning) {
lockListener.interruptUI(runnable);
}
}
}
}
}
@Override
public int hashCode() {
return Objects.hashCode(runnable);
}
public void setOperationThread(Thread operation) {
this.operation = operation;
}
// for debug only
@Override
public String toString() {
return "PendingSyncExec(" + runnable + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
}