/*******************************************************************************
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* 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:
*
*
*
*
*******************************************************************************/
package hudson.model.queue;
import hudson.model.Action;
import hudson.model.Executor;
import hudson.model.Queue;
import hudson.model.Queue.BuildableItem;
import hudson.model.Queue.Task;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Holds the information shared between {@link WorkUnit}s created from the same
* {@link Task}.
*
* @author Kohsuke Kawaguchi
*/
public final class WorkUnitContext {
//TODO: review and check whether we can do it private
public final BuildableItem item;
//TODO: review and check whether we can do it private
public final Task task;
/**
* Once the execution is complete, update this future object with the
* outcome.
*/
//TODO: review and check whether we can do it private
public final FutureImpl future;
/**
* Associated parameters to the build.
*/
//TODO: review and check whether we can do it private
public final List<Action> actions;
private final Latch startLatch, endLatch;
private List<WorkUnit> workUnits = new ArrayList<WorkUnit>();
/**
* If the execution is aborted, set to non-null that indicates where it was
* aborted.
*/
private volatile Throwable aborted;
public WorkUnitContext(BuildableItem item) {
this.item = item;
this.task = item.task;
this.future = (FutureImpl) item.getFuture();
this.actions = item.getActions();
// +1 for the main task
int workUnitSize = Tasks.getSubTasksOf(task).size();
startLatch = new Latch(workUnitSize) {
@Override
protected void onCriteriaMet() {
// on behalf of the member Executors,
// the one that executes the main thing will send notifications
Executor e = Executor.currentExecutor();
if (e.getCurrentWorkUnit().isMainWork()) {
e.getOwner().taskAccepted(e, task);
}
}
};
endLatch = new Latch(workUnitSize);
}
public BuildableItem getItem() {
return item;
}
public Task getTask() {
return task;
}
public FutureImpl getFuture() {
return future;
}
public List<Action> getActions() {
return actions;
}
/**
* Called by the executor that executes a member {@link SubTask} that
* belongs to this task to create its {@link WorkUnit}.
*/
public WorkUnit createWorkUnit(SubTask execUnit) {
future.addExecutor(Executor.currentExecutor());
WorkUnit wu = new WorkUnit(this, execUnit);
workUnits.add(wu);
return wu;
}
public List<WorkUnit> getWorkUnits() {
return Collections.unmodifiableList(workUnits);
}
public WorkUnit getPrimaryWorkUnit() {
return workUnits.get(0);
}
/**
* All the {@link Executor}s that jointly execute a {@link Task} call this
* method to synchronize on the start.
*/
public void synchronizeStart() throws InterruptedException {
startLatch.synchronize();
}
/**
* All the {@link Executor}s that jointly execute a {@link Task} call this
* method to synchronize on the end of the task.
*
* @throws InterruptedException If any of the member thread is interrupted
* while waiting for other threads to join, all the member threads will
* report {@link InterruptedException}.
*/
public void synchronizeEnd(Queue.Executable executable, Throwable problems, long duration) throws InterruptedException {
endLatch.synchronize();
// the main thread will send a notification
Executor e = Executor.currentExecutor();
WorkUnit wu = e.getCurrentWorkUnit();
if (wu.isMainWork()) {
if (problems == null) {
future.set(executable);
e.getOwner().taskCompleted(e, task, duration);
} else {
future.set(problems);
e.getOwner().taskCompletedWithProblems(e, task, duration, problems);
}
}
}
/**
* When one of the work unit is aborted, call this method to abort all the
* other work units.
*/
public synchronized void abort(Throwable cause) {
if (cause == null) {
throw new IllegalArgumentException();
}
if (aborted != null) {
return; // already aborted
}
aborted = cause;
startLatch.abort(cause);
endLatch.abort(cause);
Thread c = Thread.currentThread();
for (WorkUnit wu : workUnits) {
Executor e = wu.getExecutor();
if (e != null && e != c) {
e.interrupt();
}
}
}
}