blob: 614ac02be120e8e6b48a85d023e7e767cc7be0ed [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2013 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
*******************************************************************************/
package org.eclipse.compare.internal;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.ProgressMonitorWrapper;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
/**
* A worker performs a set of tasks in order and accumulates any errors
* that may have occurred. If the same task is queued multiple times,
* the last occurrence will be run. If a task is queued while it is
* running, the running task will be canceled and the task added
* to the end of the work queue.
*/
public class Worker implements IRunnableWithProgress {
private final WorkQueue work = new WorkQueue();
private boolean isWorking;
private final List<Throwable> errors = new ArrayList<>();
private WorkProgressMonitor currentMonitor;
private IRunnableWithProgress currentTask;
private final String taskName;
/**
* Progress monitor that supports local cancellation of a task.
*/
private static class WorkProgressMonitor extends ProgressMonitorWrapper {
private boolean localCancel;
protected WorkProgressMonitor(IProgressMonitor monitor) {
super(monitor);
}
public void cancelTask() {
localCancel = true;
}
@Override
public boolean isCanceled() {
return localCancel || super.isCanceled();
}
}
public Worker(String taskName) {
this.taskName = taskName;
}
@Override
public void run(IProgressMonitor monitor) {
errors.clear();
SubMonitor progress = SubMonitor.convert(monitor, getTaskName(), 100);
try {
isWorking = true;
while (!work.isEmpty()) {
try {
performNextTask(progress);
progress.checkCanceled();
} catch (OperationCanceledException e) {
// Only cancel all the work if the outer monitor is canceled.
progress.checkCanceled();
} catch (InterruptedException e) {
// Only cancel all the work if the outer monitor is canceled.
progress.checkCanceled();
} catch (InvocationTargetException e) {
handleError(e.getTargetException());
}
progress.setWorkRemaining(100);
}
progress.done();
} catch (OperationCanceledException e) {
// The user chose to cancel
work.clear();
} finally {
isWorking = false;
currentMonitor = null;
currentTask = null;
}
}
private WorkProgressMonitor subMonitorFor(SubMonitor pm, int ticks) {
return new WorkProgressMonitor(pm.newChild(ticks));
}
private void handleError(Throwable targetException) {
errors.add(targetException);
}
public Throwable[] getErrors() {
return errors.toArray(new Throwable[errors.size()]);
}
protected String getTaskName() {
return taskName;
}
private void performNextTask(SubMonitor pm) throws InvocationTargetException, InterruptedException {
synchronized (this) {
if (work.isEmpty())
return;
currentTask = work.remove();
currentMonitor= subMonitorFor(pm, 10);
}
currentTask.run(currentMonitor);
}
public synchronized void add(IRunnableWithProgress r) {
if (currentTask != null && currentTask.equals(r)) {
currentMonitor.cancelTask();
}
work.add(r);
}
public boolean isWorking() {
return isWorking;
}
public boolean hasWork() {
return isWorking() || !work.isEmpty();
}
}