test final fix for bug 46894
diff --git a/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/jobs/DeadlockDetectionTest.java b/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/jobs/DeadlockDetectionTest.java
index 8533467..4b3c03b 100644
--- a/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/jobs/DeadlockDetectionTest.java
+++ b/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/jobs/DeadlockDetectionTest.java
@@ -1,5 +1,6 @@
package org.eclipse.core.tests.runtime.jobs;
+import java.util.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
@@ -8,12 +9,14 @@
import org.eclipse.core.internal.jobs.LockManager;
import org.eclipse.core.internal.jobs.OrderedLock;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IProgressMonitorWithBlocking;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ILock;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.tests.harness.FussyProgressMonitor;
+import org.eclipse.core.tests.harness.TestProgressMonitor;
import junit.framework.TestCase;
@@ -50,6 +53,27 @@
r.kill();
}
}
+ /**
+ * A monitor that will receive the event when a thread is blocked on a rule
+ *
+ */
+ private class BlockingMonitor extends TestProgressMonitor implements IProgressMonitorWithBlocking {
+ int[] status;
+ int index;
+
+ public BlockingMonitor(int[] status, int index) {
+ this.status = status;
+ this.index = index;
+ }
+
+ public void setBlocked(IStatus reason) {
+ status[index] = StatusChecker.STATUS_BLOCKED;
+ }
+
+ public void clearBlocked() {
+ //leave empty for now
+ }
+ }
public void testComplex() {
ArrayList allRunnables = new ArrayList();
@@ -261,6 +285,215 @@
//the underlying array has to be empty
assertTrue("Jobs not removed from graph.", manager.getLockManager().isEmpty());
}
+ /**
+ * Regression test for bug 46894. Stale rules left over in graph.
+ */
+ public void testRuleHierarchyLockInteraction() {
+ final JobManager manager = JobManager.getInstance();
+ final int NUM_JOBS = 5;
+ final int[] status = new int[NUM_JOBS];
+ Arrays.fill(status, StatusChecker.STATUS_WAIT_FOR_START);
+ final ISchedulingRule[] rules = {new RuleHierarchy(), new RuleHierarchy(), new RuleHierarchy()};
+ Job[] jobs = new Job[NUM_JOBS];
+
+ jobs[0] = new Job("Test 0") {
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ monitor.beginTask("Testing", 1);
+ manager.beginRule(rules[1], null);
+ status[0] = StatusChecker.STATUS_WAIT_FOR_RUN;
+ StatusChecker.waitForStatus(status, 0, StatusChecker.STATUS_RUNNING, 100);
+ manager.endRule(rules[1]);
+ monitor.worked(1);
+ } finally {
+ monitor.done();
+ }
+ return Status.OK_STATUS;
+ }
+ };
+
+ jobs[1] = new Job("Test 1") {
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ monitor.beginTask("Testing", 1);
+ StatusChecker.waitForStatus(status, 1, StatusChecker.STATUS_START, 100);
+ manager.beginRule(rules[2], null);
+ status[1] = StatusChecker.STATUS_WAIT_FOR_RUN;
+ StatusChecker.waitForStatus(status, 1, StatusChecker.STATUS_RUNNING, 100);
+ manager.endRule(rules[2]);
+ monitor.worked(1);
+ } finally {
+ monitor.done();
+ }
+ return Status.OK_STATUS;
+ }
+ };
+
+ jobs[2] = new Job("Test 2") {
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ monitor.beginTask("Testing", 1);
+ StatusChecker.waitForStatus(status, 2, StatusChecker.STATUS_START, 100);
+ manager.beginRule(rules[0], new BlockingMonitor(status, 2));
+ status[2] = StatusChecker.STATUS_WAIT_FOR_RUN;
+ StatusChecker.waitForStatus(status, 2, StatusChecker.STATUS_RUNNING, 100);
+ manager.endRule(rules[0]);
+ monitor.worked(1);
+ } finally {
+ monitor.done();
+ }
+ return Status.OK_STATUS;
+ }
+ };
+
+ jobs[3] = new Job("Test 3") {
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ monitor.beginTask("Testing", 1);
+ StatusChecker.waitForStatus(status, 3, StatusChecker.STATUS_START, 100);
+ manager.beginRule(rules[2], new BlockingMonitor(status, 3));
+ status[3] = StatusChecker.STATUS_WAIT_FOR_RUN;
+ StatusChecker.waitForStatus(status, 3, StatusChecker.STATUS_RUNNING, 100);
+ manager.endRule(rules[2]);
+ monitor.worked(1);
+ } finally {
+ monitor.done();
+ }
+ return Status.OK_STATUS;
+ }
+ };
+
+ jobs[4] = new Job("Test 4") {
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ monitor.beginTask("Testing", 1);
+ StatusChecker.waitForStatus(status, 4, StatusChecker.STATUS_START, 100);
+ manager.beginRule(rules[2], new BlockingMonitor(status, 4));
+ status[4] = StatusChecker.STATUS_WAIT_FOR_RUN;
+ StatusChecker.waitForStatus(status, 4, StatusChecker.STATUS_RUNNING, 100);
+ manager.endRule(rules[2]);
+ monitor.worked(1);
+ } finally {
+ monitor.done();
+ }
+ return Status.OK_STATUS;
+ }
+ };
+
+ for (int i = 0; i < jobs.length; i++) {
+ jobs[i].schedule();
+ }
+ //wait until the first job starts
+ StatusChecker.waitForStatus(status, 0, StatusChecker.STATUS_WAIT_FOR_RUN, 100);
+ //now let the second job start
+ status[1] = StatusChecker.STATUS_START;
+ StatusChecker.waitForStatus(status, 1, StatusChecker.STATUS_WAIT_FOR_RUN, 100);
+
+ //let the third job register the wait
+ status[2] = StatusChecker.STATUS_START;
+ //wait until the job is blocked on the scheduling rule
+ StatusChecker.waitForStatus(status, 2, StatusChecker.STATUS_BLOCKED, 100);
+
+ //let the fourth job register the wait
+ status[3] = StatusChecker.STATUS_START;
+ //wait until the job is blocked on the scheduling rule
+ StatusChecker.waitForStatus(status, 3, StatusChecker.STATUS_BLOCKED, 100);
+
+ //end the first job, and the second job
+ status[0] = StatusChecker.STATUS_RUNNING;
+ status[1] = StatusChecker.STATUS_RUNNING;
+
+ //wait until the third job gets the rule
+ StatusChecker.waitForStatus(status, 2, StatusChecker.STATUS_WAIT_FOR_RUN, 100);
+
+ //let the fifth job start its wait
+ status[4] = StatusChecker.STATUS_START;
+ StatusChecker.waitForStatus(status, 4, StatusChecker.STATUS_BLOCKED, 100);
+
+ //let the third job finish
+ status[2] = StatusChecker.STATUS_RUNNING;
+
+ //wait until the fourth job gets the rule
+ StatusChecker.waitForStatus(status, 3, StatusChecker.STATUS_WAIT_FOR_RUN, 100);
+ //let the fourth job end
+ status[3] = StatusChecker.STATUS_RUNNING;
+
+ //wait until the fifth job gets the rule
+ StatusChecker.waitForStatus(status, 4, StatusChecker.STATUS_WAIT_FOR_RUN, 100);
+ //let the fifth job end
+ status[4] = StatusChecker.STATUS_RUNNING;
+
+ for(int i = 0; i < jobs.length; i++) {
+ waitForCompletion(jobs[i]);
+ }
+
+ for(int i = 0; i < jobs.length; i++) {
+ assertEquals("10." + i, Job.NONE, jobs[i].getState());
+ assertEquals("10." + i, Status.OK_STATUS, jobs[i].getResult());
+ }
+ //the underlying graph has to be empty
+ assertTrue("Jobs not removed from graph.", manager.getLockManager().isEmpty());
+ }
+
+ public void testVeryComplex() {
+ ArrayList allRunnables = new ArrayList();
+ LockManager manager = new LockManager();
+ OrderedLock lock1 = manager.newLock();
+ OrderedLock lock2 = manager.newLock();
+ OrderedLock lock3 = manager.newLock();
+ OrderedLock lock4 = manager.newLock();
+ OrderedLock lock5 = manager.newLock();
+ OrderedLock lock6 = manager.newLock();
+ createRunnables(new ILock[] { lock1, lock2, lock3 }, 10, allRunnables, true);
+ createRunnables(new ILock[] { lock2, lock3, lock4 }, 10, allRunnables, true);
+ createRunnables(new ILock[] { lock3, lock4, lock5 }, 10, allRunnables, true);
+ createRunnables(new ILock[] { lock4, lock5, lock6 }, 10, allRunnables, true);
+ createRunnables(new ILock[] { lock5, lock6, lock1 }, 10, allRunnables, true);
+ createRunnables(new ILock[] { lock6, lock1, lock2 }, 10, allRunnables, true);
+ start(allRunnables);
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ }
+ kill(allRunnables);
+
+ for(int i = 0; i < allRunnables.size(); i++) {
+ try {
+ ((Thread)allRunnables.get(i)).join(100000);
+ } catch (InterruptedException e1) {
+ }
+ assertTrue("1." + i, !((Thread)allRunnables.get(i)).isAlive());
+ }
+ //the underlying array has to be empty
+ assertTrue("Locks not removed from graph.", manager.isEmpty());
+ }
+ /**
+ * Spin until the given job completes
+ */
+ private void waitForCompletion(Job job) {
+ int i = 0;
+ while(job.getState() != Job.NONE) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ }
+ assertTrue("Timeout waiting for job to end.", ++i < 100);
+ }
+ }
+ /**
+ * Spin until the given thread dies
+ */
+ private void waitForThreadDeath(Thread thread) {
+ int i = 0;
+ while(thread.isAlive()) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ }
+ assertTrue("Timeout waiting for job to end.", ++i < 100);
+ }
+ }
+
public void testComplexRuleLockInteraction() {
final JobManager manager = JobManager.getInstance();
final int NUM_LOCKS = 5;
@@ -328,7 +561,7 @@
assertTrue("Timeout waiting for jobs to finish.", ++j < 1000);
}
}
-
+
for(int i = 0; i < jobs.length; i++) {
assertEquals("10." + i, Job.NONE, jobs[i].getState());
assertEquals("10." + i, Status.OK_STATUS, jobs[i].getResult());
@@ -337,65 +570,6 @@
assertTrue("Jobs not removed from graph.", manager.getLockManager().isEmpty());
}
- public void testVeryComplex() {
- ArrayList allRunnables = new ArrayList();
- LockManager manager = new LockManager();
- OrderedLock lock1 = manager.newLock();
- OrderedLock lock2 = manager.newLock();
- OrderedLock lock3 = manager.newLock();
- OrderedLock lock4 = manager.newLock();
- OrderedLock lock5 = manager.newLock();
- OrderedLock lock6 = manager.newLock();
- createRunnables(new ILock[] { lock1, lock2, lock3 }, 10, allRunnables, true);
- createRunnables(new ILock[] { lock2, lock3, lock4 }, 10, allRunnables, true);
- createRunnables(new ILock[] { lock3, lock4, lock5 }, 10, allRunnables, true);
- createRunnables(new ILock[] { lock4, lock5, lock6 }, 10, allRunnables, true);
- createRunnables(new ILock[] { lock5, lock6, lock1 }, 10, allRunnables, true);
- createRunnables(new ILock[] { lock6, lock1, lock2 }, 10, allRunnables, true);
- start(allRunnables);
- try {
- Thread.sleep(5000);
- } catch (InterruptedException e) {
- }
- kill(allRunnables);
-
- for(int i = 0; i < allRunnables.size(); i++) {
- try {
- ((Thread)allRunnables.get(i)).join(100000);
- } catch (InterruptedException e1) {
- }
- assertTrue("1." + i, !((Thread)allRunnables.get(i)).isAlive());
- }
- //the underlying array has to be empty
- assertTrue("Locks not removed from graph.", manager.isEmpty());
- }
- /**
- * Spin until the given job completes
- */
- private void waitForCompletion(Job job) {
- int i = 0;
- while(job.getState() != Job.NONE) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- }
- assertTrue("Timeout waiting for job to end.", ++i < 100);
- }
- }
- /**
- * Spin until the given thread dies
- */
- private void waitForThreadDeath(Thread thread) {
- int i = 0;
- while(thread.isAlive()) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- }
- assertTrue("Timeout waiting for job to end.", ++i < 100);
- }
- }
-
public void testJobRuleCancellation() {
final JobManager manager = JobManager.getInstance();
final ISchedulingRule rule = new IdentityRule();
diff --git a/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/jobs/RuleHierarchy.java b/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/jobs/RuleHierarchy.java
new file mode 100644
index 0000000..6358c32
--- /dev/null
+++ b/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/jobs/RuleHierarchy.java
@@ -0,0 +1,39 @@
+
+package org.eclipse.core.tests.runtime.jobs;
+
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * Form a rule hierarchy for testing
+ */
+public class RuleHierarchy implements ISchedulingRule {
+ private static int ruleNumber = 1;
+ public final int rule = ruleNumber++;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+ */
+ public boolean contains(ISchedulingRule rule) {
+ if((rule instanceof RuleHierarchy) && (((RuleHierarchy)rule).rule >= this.rule)
+ && ((((RuleHierarchy)rule).rule%2 == this.rule%2) || (this.rule < 2))) {
+ return true;
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)
+ */
+ public boolean isConflicting(ISchedulingRule rule) {
+ if(contains(rule))
+ return true;
+ if(rule.contains(this))
+ return true;
+
+ return false;
+ }
+
+ public String toString() {
+ return "HierarchyRule (" + rule + ")";
+ }
+}