Skip to content
Browse files

Merge pull request #1645 from stephenc/jenkins-27708

JENKINS-27708, JENKINS-27871  Ensure that identification of blocked tasks is using the live state.
  • Loading branch information...
stephenc committed Apr 15, 2015
2 parents b688047 + 57efbea commit 152d00ad09931c10f02fab4ac8a42e574d622bd3
Showing with 52 additions and 2 deletions.
  1. +16 −1 core/src/main/java/hudson/model/
  2. +36 −1 test/src/test/java/hudson/model/
@@ -1335,7 +1335,10 @@ public void maintain() {
final QueueSorter s = sorter;
if (s != null)

// Ensure that identification of blocked tasks is using the live state: JENKINS-27708 & JENKINS-27871

// allocate buildable jobs to executors
for (BuildableItem p : new ArrayList<BuildableItem>(
buildables)) {// copy as we'll mutate the list in the loop
@@ -1372,6 +1375,18 @@ public void maintain() {
LOGGER.log(Level.FINE, "BuildableItem {0} with empty work units!?", p);

// Ensure that identification of blocked tasks is using the live state: JENKINS-27708 & JENKINS-27871
// The creation of a snapshot itself should be relatively cheap given the expected rate of
// job execution. You probably would need 100's of jobs starting execution every iteration
// of maintain() before this could even start to become an issue and likely the calculation
// of isBuildBlocked(p) will become a bottleneck before updateSnapshot() will. Additionally
// since the snapshot itself only ever has at most one reference originating outside of the stack
// it should remain in the eden space and thus be cheap to GC.
// See
// or
// for alternative fixes of this issue.
} finally { updateSnapshot(); } } finally {
@@ -50,6 +50,7 @@
import hudson.slaves.DumbSlave;
import hudson.slaves.DummyCloudImpl;
import hudson.slaves.NodeProvisionerRule;
import hudson.tasks.BuildTrigger;
import hudson.tasks.Shell;
import hudson.triggers.SCMTrigger.SCMTriggerCause;
import hudson.triggers.TimerTrigger.TimerTriggerCause;
@@ -91,6 +92,7 @@
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.MockQueueItemAuthenticator;
import org.jvnet.hudson.test.SequenceLock;
import org.jvnet.hudson.test.SleepBuilder;
import org.jvnet.hudson.test.TestBuilder;
import org.mortbay.jetty.Server;
@@ -707,5 +709,38 @@ public void run() {
fail("Expected an CancellationException to be thrown");
} catch (CancellationException e) {}

@Test public void testBlockBuildWhenUpstreamBuildingLock() throws Exception {
final String prefix = "JENKINS-27871";

final FreeStyleProject projectA = r.createFreeStyleProject(prefix+"A");
projectA.getBuildersList().add(new SleepBuilder(5000));

final FreeStyleProject projectB = r.createFreeStyleProject(prefix+"B");
projectB.getBuildersList().add(new SleepBuilder(10000));

final FreeStyleProject projectC = r.createFreeStyleProject(prefix+"C");
projectC.getBuildersList().add(new SleepBuilder(10000));

projectA.getPublishersList().add(new BuildTrigger(Arrays.asList(projectB), Result.SUCCESS));
projectB.getPublishersList().add(new BuildTrigger(Arrays.asList(projectC), Result.SUCCESS));

final QueueTaskFuture<FreeStyleBuild> taskA = projectA.scheduleBuild2(0, new TimerTriggerCause());
final QueueTaskFuture<FreeStyleBuild> taskB = projectB.scheduleBuild2(0, new TimerTriggerCause());
final QueueTaskFuture<FreeStyleBuild> taskC = projectC.scheduleBuild2(0, new TimerTriggerCause());

final FreeStyleBuild buildA = taskA.get(60, TimeUnit.SECONDS);
final FreeStyleBuild buildB = taskB.get(60, TimeUnit.SECONDS);
final FreeStyleBuild buildC = taskC.get(60, TimeUnit.SECONDS);
long buildBEndTime = buildB.getStartTimeInMillis() + buildB.getDuration();
assertTrue("Project B build should be finished before the build of project C starts. " +
"B finished at " + buildBEndTime + ", C started at " + buildC.getStartTimeInMillis(),
buildC.getStartTimeInMillis() >= buildBEndTime);

0 comments on commit 152d00a

Please sign in to comment.
You can’t perform that action at this time.