diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/builders/ParallelBuildChainTest.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/builders/ParallelBuildChainTest.java index 3309eca97b6..5e1b78c1ec4 100644 --- a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/builders/ParallelBuildChainTest.java +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/builders/ParallelBuildChainTest.java @@ -141,6 +141,17 @@ public void testIndividualProjectBuilds_ProjectRelaxedRule() throws Exception { }); } + @Test + public void testIndividualProjectBuilds_WithManyProjects_ProjectRelaxedRule() throws Exception { + int numberOfParallelBuilds = 60; + var longRunningProjects = createMultipleTestProjects(numberOfParallelBuilds, BuildDurationType.LONG_RUNNING, + RuleType.CURRENT_PROJECT_RELAXED); + executeIndividualFullProjectBuilds(numberOfParallelBuilds, () -> { + assertBuildsToStart(getAllProjects()); + assertMinimumNumberOfSimultaneousBuilds(longRunningProjects.size()); + }); + } + @Test public void testWorkspaceBuild_NoConflictRule() throws Exception { int numberOfParallelBuilds = 3; diff --git a/runtime/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/ThreadJob.java b/runtime/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/ThreadJob.java index 6ed718fdcd1..9f2740acd49 100644 --- a/runtime/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/ThreadJob.java +++ b/runtime/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/ThreadJob.java @@ -15,8 +15,14 @@ import java.util.List; import org.eclipse.core.internal.runtime.RuntimeLog; -import org.eclipse.core.runtime.*; -import org.eclipse.core.runtime.jobs.*; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.jobs.LockListener; /** * Captures the implicit job state for a given thread. @@ -317,11 +323,19 @@ private static ThreadJob waitForRun(final ThreadJob threadJob, IProgressMonitor // The actual exit conditions are listed above at the beginning of // this while loop int state = blockingJob.getState(); - //ensure we don't wait forever if the blocker is waiting, because it might have yielded to me - if (state == Job.RUNNING && canBlock) { - blockingJob.jobStateLock.wait(); - } else if (state != Job.NONE) { - blockingJob.jobStateLock.wait(250); + // Check that blockingJob has not acquired a different, non-conflicting + // scheduling rule since checking for conflicts by JobManager. This can + // particularly happen if blockingJob is a ThreadJob that is reused for the + // same thread across the acquisition of different rules (see ThreadJob::recycle + // and ImplicitJob::newThreadJob) via JobManager::beginRule. + if (state != Job.NONE && blockingJob.isConflicting(threadJob)) { + // ensure we don't wait forever if the blocker is waiting, because it might have + // yielded to me + if (state == Job.RUNNING && canBlock) { + blockingJob.jobStateLock.wait(); + } else { + blockingJob.jobStateLock.wait(250); + } } } catch (InterruptedException e) { // This thread may be interrupted via two common scenarios. 1) If