Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FIXED JENKINS-35112] Interrupt the computation when deleting
  • Loading branch information
stephenc committed Mar 8, 2017
1 parent 382ecdf commit 65682eb
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 1 deletion.
Expand Up @@ -36,15 +36,18 @@
import hudson.model.Cause;
import hudson.model.CauseAction;
import hudson.model.Descriptor;
import hudson.model.Executor;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.Items;
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.Queue;
import hudson.model.ResourceList;
import hudson.model.Result;
import hudson.model.TaskListener;
import hudson.model.TopLevelItem;
import hudson.model.User;
import hudson.model.listeners.ItemListener;
import hudson.model.queue.CauseOfBlockage;
import hudson.model.queue.SubTask;
Expand All @@ -64,13 +67,15 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.servlet.ServletException;
import jenkins.model.CauseOfInterruption;
import jenkins.model.Jenkins;
import jenkins.model.ParameterizedJobMixIn;
import jenkins.util.TimeDuration;
Expand Down Expand Up @@ -220,9 +225,14 @@ public void onLoad(ItemGroup<? extends Item> parent, String name) throws IOExcep
}

/**
* Called to (re-)compute the set of children of this folder.
* Called to (re-)compute the set of children of this folder. It is recommended that the computation checks the
* {@link Thread#interrupted()} status and throws a {@link InterruptedException} if set at least once every 5
* seconds to allow the user to interrupt a computation..
*
* @param observer how to indicate which children should be seen
* @param listener a way to report progress
* @throws IOException if there was an {@link IOException} during the computation.
* @throws InterruptedException if the computation was interrupted.
*/
protected abstract void computeChildren(ChildObserver<I> observer, TaskListener listener) throws IOException, InterruptedException;

Expand Down Expand Up @@ -270,6 +280,31 @@ final void updateChildren(final TaskListener listener) throws IOException, Inter
}
}

/**
* {@inheritDoc}
*/
@Override
public void delete() throws IOException, InterruptedException {
checkPermission(DELETE);
FolderComputation<I> computation = getComputation();
Executor executor = Executor.of(computation);
if (executor != null) {
LOGGER.log(Level.INFO, "Interrupting {0} in order to delete it", this);
executor.interrupt(Result.ABORTED, new CauseOfInterruption.UserInterruption(User.current()));
// give it 15 seconds or so to respond to the interrupt
long expiration = System.nanoTime() + TimeUnit.SECONDS.toNanos(15);
while (executor.isAlive()
&& executor.getCurrentExecutable() == computation
&& expiration - System.nanoTime() > 0L) {
Thread.sleep(50L);
}
if (executor.isAlive() && executor.getCurrentExecutable() == computation) {
LOGGER.log(Level.WARNING, "Interrupted {0} in order to delete it, but it has not stopped yet", this);
}
}
super.delete();
}

/**
* Creates a {@link ChildObserver} that subclasses can use when handling events that might create new / update
* existing child items. The handling of orphaned items is a responsibility of the {@link OrphanedItemStrategy}
Expand Down
Expand Up @@ -390,6 +390,20 @@ public void concurrentEvents() throws Exception {
assertThat("None of the executors have died abnormally", deaths, containsInAnyOrder());
}

@Test
@Issue("JENKINS-35112")
public void deleteWhileComputing() throws Exception {
CoordinatedComputedFolder d = r.jenkins.createProject(CoordinatedComputedFolder.class, "d");
d.kids.addAll(Arrays.asList("A", "B"));
QueueTaskFuture<Queue.Executable> future = d.scheduleBuild2(0).getFuture();
FolderComputation<FreeStyleProject> computation;
while (Executor.of((computation = d.getComputation())) == null) {
Thread.sleep(50);
}
d.delete();
assertThat(computation.getResult(), is(Result.ABORTED));
}

/**
* Waits until Hudson finishes building everything, including those in the queue, or fail the test
* if the specified timeout milliseconds is
Expand Down

0 comments on commit 65682eb

Please sign in to comment.