| /********************************************************************** |
| * Copyright (c) 2003 IBM Corporation and others. All rights reserved. This |
| * program and the accompanying materials are made available under the terms of |
| * the Common Public License v1.0 which accompanies this distribution, and is |
| * available at http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM - Initial API and implementation |
| **********************************************************************/ |
| package org.eclipse.core.tests.runtime.jobs; |
| |
| import junit.framework.TestSuite; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.core.runtime.jobs.ISchedulingRule; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.core.tests.harness.FussyProgressMonitor; |
| |
| /** |
| * Tests API methods IJobManager.beginRule and IJobManager.endRule |
| */ |
| public class BeginEndRuleTest extends AbstractJobManagerTest { |
| |
| private class JobRuleRunner extends Job { |
| private ISchedulingRule rule; |
| private int[] status; |
| private int index; |
| private int numRepeats; |
| |
| /** |
| * This job will start applying the given rule in the manager |
| */ |
| public JobRuleRunner(String name, ISchedulingRule rule, int[] status, int index, int numRepeats) { |
| super(name); |
| this.status = status; |
| this.rule = rule; |
| this.index = index; |
| this.numRepeats = numRepeats; |
| } |
| |
| protected IStatus run(IProgressMonitor monitor) { |
| //begin executing the job |
| monitor.beginTask(getName(), numRepeats); |
| try { |
| //set the status flag to START |
| status[index] = StatusChecker.STATUS_START; |
| for (int i = 0; i < numRepeats; i++) { |
| //wait until the tester allows this job to run again |
| StatusChecker.waitForStatus(status, index, StatusChecker.STATUS_WAIT_FOR_RUN, 100); |
| //start the given rule in the manager |
| manager.beginRule(rule, null); |
| //set status to RUNNING |
| status[index] = StatusChecker.STATUS_RUNNING; |
| |
| if (monitor.isCanceled()) |
| return Status.CANCEL_STATUS; |
| |
| //wait until tester allows this job to finish |
| StatusChecker.waitForStatus(status, index, StatusChecker.STATUS_WAIT_FOR_DONE, 100); |
| //end the given rule |
| manager.endRule(rule); |
| //set status to DONE |
| status[index] = StatusChecker.STATUS_DONE; |
| |
| monitor.worked(1); |
| Thread.yield(); |
| } |
| |
| } finally { |
| monitor.done(); |
| Thread.yield(); |
| } |
| return Status.OK_STATUS; |
| } |
| |
| } |
| |
| /** |
| * This runnable will try to begin the given rule in the Job Manager. It will |
| * end the rule before returning. |
| */ |
| private class SimpleRuleRunner implements Runnable { |
| private ISchedulingRule rule; |
| private IProgressMonitor monitor; |
| private int[] status; |
| RuntimeException exception; |
| public SimpleRuleRunner(ISchedulingRule rule, int[] status, IProgressMonitor monitor) { |
| this.rule = rule; |
| this.monitor = monitor; |
| this.status = status; |
| this.exception = null; |
| } |
| public void run() { |
| //tell the caller that we have entered the run method |
| status[0] = StatusChecker.STATUS_RUNNING; |
| try { |
| try { |
| manager.beginRule(rule, monitor); |
| } finally { |
| manager.endRule(rule); |
| } |
| } catch (OperationCanceledException e) { |
| //ignore |
| } catch (RuntimeException e) { |
| exception = e; |
| } finally { |
| status[0] = StatusChecker.STATUS_DONE; |
| } |
| } |
| } /** |
| * This runnable will try to end the given rule in the Job Manager |
| */ |
| private class RuleEnder implements Runnable { |
| private ISchedulingRule rule; |
| |
| public RuleEnder(ISchedulingRule rule) { |
| this.rule = rule; |
| } |
| |
| public void run() { |
| try { |
| manager.endRule(rule); |
| fail("Ending Rule"); |
| |
| } catch (RuntimeException e) { |
| //should fail |
| } |
| |
| } |
| } |
| public static TestSuite suite() { |
| return new TestSuite(BeginEndRuleTest.class); |
| } |
| public BeginEndRuleTest() { |
| super(); |
| } |
| public BeginEndRuleTest(String name) { |
| super(name); |
| } |
| |
| public void _testComplexRuleStarting() { |
| //test how the manager reacts when several different threads try to begin conflicting rules |
| |
| //array to communicate with the launched threads |
| final int[] status = |
| { StatusChecker.STATUS_WAIT_FOR_START, StatusChecker.STATUS_WAIT_FOR_START, StatusChecker.STATUS_WAIT_FOR_START, StatusChecker.STATUS_WAIT_FOR_START, StatusChecker.STATUS_WAIT_FOR_START }; |
| //number of times to start each rule |
| int NUM_REPEATS = 10; |
| |
| RuleSetA.conflict = true; |
| Job[] jobs = new Job[5]; |
| |
| jobs[0] = new JobRuleRunner("Job1", new RuleSetB(), status, 0, NUM_REPEATS); |
| jobs[1] = new JobRuleRunner("Job2", new RuleSetB(), status, 1, NUM_REPEATS); |
| jobs[2] = new JobRuleRunner("Job3", new RuleSetC(), status, 2, NUM_REPEATS); |
| jobs[3] = new JobRuleRunner("Job4", new RuleSetD(), status, 3, NUM_REPEATS); |
| jobs[4] = new JobRuleRunner("Job5", new RuleSetE(), status, 4, NUM_REPEATS); |
| |
| //schedule the jobs |
| for (int i = 0; i < jobs.length; i++) { |
| jobs[i].schedule(); |
| } |
| |
| //by the time the fifth job starts, the rest should have already started |
| waitForStart(jobs[4]); |
| StatusChecker.waitForStatus(status, 4, StatusChecker.STATUS_START, 100); |
| |
| //all jobs should be running |
| //the status flag should be set to START |
| for (int i = 0; i < status.length; i++) { |
| assertTrue("1." + i, jobs[i].getState() == Job.RUNNING); |
| assertTrue("2." + i, status[i] == StatusChecker.STATUS_START); |
| } |
| |
| //the order that the jobs will be executed |
| int[] order = { 0, 1, 2, 3, 4 }; |
| |
| for (int j = 0; j < NUM_REPEATS; j++) { |
| //let the first job in the order run |
| status[order[0]] = StatusChecker.STATUS_WAIT_FOR_RUN; |
| //wait until the first job in the order reads the flag |
| StatusChecker.waitForStatus(status, order[0], StatusChecker.STATUS_RUNNING, 100); |
| |
| //let the second job run (it will be blocked), the other jobs will be waiting for the flag reset |
| status[order[1]] = StatusChecker.STATUS_WAIT_FOR_RUN; |
| assertTrue("3.0", status[order[0]] == StatusChecker.STATUS_RUNNING); |
| assertTrue("3.1", status[order[1]] == StatusChecker.STATUS_WAIT_FOR_RUN); |
| |
| //let the first job finish |
| status[order[0]] = StatusChecker.STATUS_WAIT_FOR_DONE; |
| //first job is done |
| StatusChecker.waitForStatus(status, order[0], StatusChecker.STATUS_DONE, 100); |
| |
| //let the second job run |
| StatusChecker.waitForStatus(status, order[1], StatusChecker.STATUS_RUNNING, 100); |
| status[order[2]] = StatusChecker.STATUS_WAIT_FOR_RUN; |
| |
| assertTrue("4.0", status[order[1]] == StatusChecker.STATUS_RUNNING); |
| assertTrue("4.1", status[order[2]] == StatusChecker.STATUS_WAIT_FOR_RUN); |
| |
| //let the second job finish |
| status[order[1]] = StatusChecker.STATUS_WAIT_FOR_DONE; |
| //second job is done |
| StatusChecker.waitForStatus(status, order[1], StatusChecker.STATUS_DONE, 100); |
| |
| //start the third job |
| StatusChecker.waitForStatus(status, order[2], StatusChecker.STATUS_RUNNING, 100); |
| status[order[3]] = StatusChecker.STATUS_WAIT_FOR_RUN; |
| |
| assertTrue("5.0", status[order[2]] == StatusChecker.STATUS_RUNNING); |
| assertTrue("5.1", status[order[3]] == StatusChecker.STATUS_WAIT_FOR_RUN); |
| |
| //let the third job finish |
| status[order[2]] = StatusChecker.STATUS_WAIT_FOR_DONE; |
| //third job is done |
| StatusChecker.waitForStatus(status, order[2], StatusChecker.STATUS_DONE, 100); |
| |
| //start the fourth job |
| StatusChecker.waitForStatus(status, order[3], StatusChecker.STATUS_RUNNING, 100); |
| status[order[4]] = StatusChecker.STATUS_WAIT_FOR_RUN; |
| |
| assertTrue("6.0", status[order[3]] == StatusChecker.STATUS_RUNNING); |
| assertTrue("6.1", status[order[4]] == StatusChecker.STATUS_WAIT_FOR_RUN); |
| |
| //let the fourth job finish |
| status[order[3]] = StatusChecker.STATUS_WAIT_FOR_DONE; |
| //fourth job is done |
| StatusChecker.waitForStatus(status, order[3], StatusChecker.STATUS_DONE, 100); |
| |
| //start the fifth job |
| StatusChecker.waitForStatus(status, order[4], StatusChecker.STATUS_RUNNING, 100); |
| |
| assertTrue("7.0", status[order[4]] == StatusChecker.STATUS_RUNNING); |
| |
| status[order[4]] = StatusChecker.STATUS_WAIT_FOR_DONE; |
| //fifth job is done |
| StatusChecker.waitForStatus(status, order[4], StatusChecker.STATUS_DONE, 100); |
| |
| if (j < 9) { |
| for (int i = 0; i < status.length; i++) { |
| assertTrue("7." + (i + 1), status[i] == StatusChecker.STATUS_DONE); |
| assertTrue("8." + (i + 1), jobs[i].getState() == Job.RUNNING); |
| } |
| } |
| |
| //change the order of the jobs, nothing should change in the execution |
| int temp = order[0]; |
| |
| order[0] = order[2]; |
| order[2] = order[4]; |
| order[4] = order[1]; |
| order[1] = order[3]; |
| order[3] = temp; |
| } |
| |
| for (int i = 0; i < jobs.length; i++) { |
| //check that the final status of all jobs is correct |
| assertTrue("9." + i, status[i] == StatusChecker.STATUS_DONE); |
| assertTrue("10." + i, jobs[i].getState() == Job.NONE); |
| assertTrue("11." + i, jobs[i].getResult().getSeverity() == Status.OK); |
| } |
| } |
| |
| public void testSimpleRuleStarting() { |
| //start two jobs, each of which will begin and end a rule several times |
| //while one job starts a rule, the second job's call to begin rule should block that thread |
| //until the first job calls end rule |
| final int[] status = { StatusChecker.STATUS_WAIT_FOR_START, StatusChecker.STATUS_WAIT_FOR_START }; |
| //number of repetitions of beginning and ending the rule |
| final int NUM_REPEATS = 10; |
| //set the two rules to conflict with each other |
| RuleSetA.conflict = true; |
| Job[] jobs = new Job[2]; |
| |
| jobs[0] = new JobRuleRunner("Job1", new RuleSetB(), status, 0, NUM_REPEATS); |
| jobs[1] = new JobRuleRunner("Job2", new RuleSetD(), status, 1, NUM_REPEATS); |
| |
| //schedule both jobs to start their execution |
| jobs[0].schedule(); |
| jobs[1].schedule(); |
| |
| //make sure both jobs are running and their respective run methods have been invoked |
| waitForStart(jobs[1]); |
| StatusChecker.waitForStatus(status, 1, StatusChecker.STATUS_START, 100); |
| |
| assertTrue("2.0", jobs[0].getState() == Job.RUNNING); |
| assertTrue("2.1", jobs[1].getState() == Job.RUNNING); |
| assertTrue("2.2", status[0] == StatusChecker.STATUS_START); |
| assertTrue("2.3", status[1] == StatusChecker.STATUS_START); |
| |
| //the order of execution of the jobs (by their index in the status array) |
| int first = 0; |
| int second = 1; |
| |
| //now both jobs are waiting for the STATUS_WAIT_FOR_RUN flag |
| for (int j = 0; j < NUM_REPEATS; j++) { |
| //let the first job start executing |
| status[first] = StatusChecker.STATUS_WAIT_FOR_RUN; |
| |
| //wait for the first job to read the flag |
| StatusChecker.waitForStatus(status, first, StatusChecker.STATUS_RUNNING, 100); |
| |
| //let the second job start, its thread will be blocked by the beginRule method |
| status[second] = StatusChecker.STATUS_WAIT_FOR_RUN; |
| |
| //only the first job should be running |
| //the other job should be blocked by the beginRule method |
| assertTrue("3.1", status[first] == StatusChecker.STATUS_RUNNING); |
| assertTrue("3.2", status[second] == StatusChecker.STATUS_WAIT_FOR_RUN); |
| |
| //let the first job finish execution and call endRule |
| //the second thread will then become unblocked |
| status[first] = StatusChecker.STATUS_WAIT_FOR_DONE; |
| |
| //wait until the first job is done |
| StatusChecker.waitForStatus(status, first, StatusChecker.STATUS_DONE, 100); |
| |
| //now wait until the second job begins execution |
| StatusChecker.waitForStatus(status, second, StatusChecker.STATUS_RUNNING, 100); |
| |
| //the first job is done, the second job is executing |
| assertTrue("4.1", status[first] == StatusChecker.STATUS_DONE); |
| assertTrue("4.2", status[second] == StatusChecker.STATUS_RUNNING); |
| |
| //let the second job finish execution |
| status[second] = StatusChecker.STATUS_WAIT_FOR_DONE; |
| |
| //wait until the second job is finished |
| StatusChecker.waitForStatus(status, second, StatusChecker.STATUS_DONE, 100); |
| |
| //both jobs are done now |
| assertTrue("5.1", status[first] == StatusChecker.STATUS_DONE); |
| assertTrue("5.2", status[second] == StatusChecker.STATUS_DONE); |
| |
| //flip the order of execution of the jobs |
| int temp = first; |
| first = second; |
| second = temp; |
| } |
| |
| //check that the final status of both jobs is correct |
| assertTrue("6.1", status[0] == StatusChecker.STATUS_DONE); |
| assertTrue("6.2", status[1] == StatusChecker.STATUS_DONE); |
| assertTrue("6.3", jobs[0].getState() == Job.NONE); |
| assertTrue("6.4", jobs[1].getState() == Job.NONE); |
| assertTrue("6.5", jobs[0].getResult().getSeverity() == Status.OK); |
| assertTrue("6.6", jobs[1].getResult().getSeverity() == Status.OK); |
| |
| } |
| public void testComplexRuleContainment() { |
| ISchedulingRule rules[] = new ISchedulingRule[4]; |
| |
| rules[0] = new RuleSetA(); |
| rules[1] = new RuleSetB(); |
| rules[2] = new RuleSetC(); |
| rules[3] = new RuleSetD(); |
| |
| //adding multiple rules in correct order |
| int RULE_REPEATS = 10; |
| try { |
| for (int i = 0; i < rules.length - 1; i++) { |
| for (int j = 0; j < RULE_REPEATS; j++) { |
| manager.beginRule(rules[i], null); |
| } |
| } |
| for (int i = rules.length - 1; i > 0; i--) { |
| for (int j = 0; j < RULE_REPEATS; j++) { |
| manager.endRule(rules[i - 1]); |
| } |
| } |
| } catch (RuntimeException e) { |
| fail("4.0"); |
| } |
| |
| //adding rules in proper order, then adding a rule from a bypassed branch |
| //trying to end previous rules should not work |
| for (int i = 0; i < rules.length; i++) { |
| manager.beginRule(rules[i], null); |
| } |
| try { |
| manager.endRule(rules[2]); |
| fail("4.1"); |
| } catch (RuntimeException e) { |
| //should fail |
| try { |
| manager.endRule(rules[1]); |
| fail("4.2"); |
| } catch (RuntimeException e1) { |
| //should fail |
| try { |
| manager.endRule(rules[0]); |
| fail("4.3"); |
| } catch (RuntimeException e2) { |
| //should fail |
| } |
| } |
| } |
| for (int i = rules.length; i > 0; i--) { |
| manager.endRule(rules[i - 1]); |
| } |
| } |
| public void _testEndNullRule() { |
| //see bug #43460 |
| //end null IScheduleRule without begin |
| try { |
| manager.endRule(null); |
| fail("1.1"); |
| } catch (RuntimeException e) { |
| //should fail |
| } |
| } |
| public void testFailureCase() { |
| ISchedulingRule rule1 = new IdentityRule(); |
| ISchedulingRule rule2 = new IdentityRule(); |
| |
| //end without begin |
| try { |
| manager.endRule(rule1); |
| fail("1.0"); |
| } catch (RuntimeException e) { |
| //should fail |
| } |
| //simple mismatched begin/end |
| manager.beginRule(rule1, null); |
| try { |
| manager.endRule(rule2); |
| fail("1.2"); |
| } catch (RuntimeException e) { |
| //should fail |
| } |
| //should still be able to end the original rule |
| manager.endRule(rule1); |
| |
| //mismatched begin/end, ending a null rule |
| manager.beginRule(rule1, null); |
| try { |
| manager.endRule(null); |
| fail("1.3"); |
| } catch (RuntimeException e) { |
| //should fail |
| } |
| //should still be able to end the original rule |
| manager.endRule(rule1); |
| } |
| public void testNestedCase() { |
| ISchedulingRule rule1 = new RuleSetA(); |
| ISchedulingRule rule2 = new RuleSetB(); |
| |
| //ending an outer rule before an inner one |
| manager.beginRule(rule1, null); |
| manager.beginRule(rule2, null); |
| try { |
| manager.endRule(rule1); |
| fail("2.0"); |
| } catch (RuntimeException e) { |
| //should fail |
| } |
| manager.endRule(rule2); |
| manager.endRule(rule1); |
| |
| //ending a rule that is not open |
| manager.beginRule(rule1, null); |
| manager.beginRule(rule2, null); |
| try { |
| manager.endRule(null); |
| fail("2.1"); |
| } catch (RuntimeException e) { |
| //should fail |
| } |
| manager.endRule(rule2); |
| manager.endRule(rule1); |
| |
| //adding rules starting with null rule |
| try { |
| manager.beginRule(null, null); |
| manager.beginRule(rule1, null); |
| manager.endRule(rule1); |
| manager.beginRule(rule2, null); |
| manager.endRule(rule2); |
| manager.endRule(null); |
| } catch (RuntimeException e) { |
| //should not fail |
| fail("2.2"); |
| } |
| |
| //adding numerous instances of the same rule |
| int NUM_ADDITIONS = 100; |
| try { |
| for (int i = 0; i < NUM_ADDITIONS; i++) { |
| manager.beginRule(rule1, null); |
| } |
| for (int i = 0; i < NUM_ADDITIONS; i++) { |
| manager.endRule(rule1); |
| } |
| } catch (RuntimeException e) { |
| //should not fail |
| fail("2.3"); |
| } |
| |
| //adding numerous instances of the null rule |
| try { |
| for (int i = 0; i < NUM_ADDITIONS; i++) { |
| manager.beginRule(null, null); |
| } |
| manager.beginRule(rule1, null); |
| manager.endRule(rule1); |
| for (int i = 0; i < NUM_ADDITIONS; i++) { |
| manager.endRule(null); |
| } |
| } catch (RuntimeException e) { |
| //should not fail |
| fail("2.4"); |
| } |
| } |
| /** |
| * Tests a failure where canceling an attempt to beginRule resulted in implicit jobs |
| * being recycled before they were finished. |
| */ |
| public void testBug44299() { |
| ISchedulingRule rule = new IdentityRule(); |
| FussyProgressMonitor monitor = new FussyProgressMonitor(); |
| manager.beginRule(rule, monitor); |
| int[] status = new int[1]; |
| SimpleRuleRunner runner = new SimpleRuleRunner(rule, status, monitor); |
| new Thread(runner).start(); |
| |
| //wait for the job to start |
| StatusChecker.waitForStatus(status, StatusChecker.STATUS_RUNNING); |
| |
| //give the job a chance to enter the wait loop |
| try { |
| Thread.sleep(100); |
| } catch (InterruptedException e) { |
| } |
| |
| //cancel the monitor |
| monitor.setCanceled(true); |
| |
| StatusChecker.waitForStatus(status, StatusChecker.STATUS_DONE); |
| if (runner.exception != null) |
| fail("1.0", runner.exception); |
| |
| //finally clear the rule |
| manager.endRule(rule); |
| } |
| public void testRuleContainment() { |
| ISchedulingRule rules[] = new ISchedulingRule[4]; |
| |
| rules[0] = new RuleSetA(); |
| rules[1] = new RuleSetB(); |
| rules[2] = new RuleSetC(); |
| rules[3] = new RuleSetD(); |
| |
| //simple addition of rules in incorrect containment order |
| manager.beginRule(rules[1], null); |
| try { |
| manager.beginRule(rules[0], null); |
| fail("3.0"); |
| } catch (RuntimeException e) { |
| //should fail |
| } finally { |
| //still need to end the rule |
| manager.endRule(rules[0]); |
| } |
| manager.endRule(rules[1]); |
| |
| //adding rules in proper order, then adding a rule from different hierarchy |
| manager.beginRule(rules[1], null); |
| manager.beginRule(rules[2], null); |
| try { |
| manager.beginRule(rules[3], null); |
| fail("3.2"); |
| } catch (RuntimeException e) { |
| //should fail |
| } finally { |
| manager.endRule(rules[3]); |
| } |
| //should still be able to end the rules |
| manager.endRule(rules[2]); |
| manager.endRule(rules[1]); |
| } |
| public void testSimpleOtherThreadAccess() { |
| //ending a rule started on this thread from another thread |
| ISchedulingRule rule1 = new IdentityRule(); |
| Thread endingThread = new Thread(new RuleEnder(rule1)); |
| manager.beginRule(rule1, null); |
| endingThread.start(); |
| |
| //should be able to end the rule from this thread |
| manager.endRule(rule1); |
| |
| //starting several rules on this thread, and trying to end them from other threads |
| ISchedulingRule rules[] = new ISchedulingRule[3]; |
| |
| rules[0] = new RuleSetA(); |
| rules[1] = new RuleSetB(); |
| rules[2] = new RuleSetC(); |
| |
| for (int i = 0; i < rules.length; i++) { |
| manager.beginRule(rules[i], null); |
| (new Thread(new RuleEnder(rules[i]))).start(); |
| } |
| |
| for (int i = 0; i < rules.length; i++) { |
| (new Thread(new RuleEnder(rules[i]))).start(); |
| } |
| |
| for (int i = rules.length; i > 0; i--) { |
| manager.endRule(rules[i - 1]); |
| (new Thread(new RuleEnder(rules[i - 1]))).start(); |
| } |
| } |
| |
| /** |
| * A job has been scheduled. Pause this thread so that a worker thread |
| * has a chance to pick up the new job. |
| */ |
| private void waitForStart(Job job) { |
| int i = 0; |
| while (job.getState() != Job.RUNNING) { |
| try { |
| //Thread.yield(); |
| Thread.sleep(100); |
| } catch (InterruptedException e) { |
| |
| } |
| |
| //sanity test to avoid hanging tests |
| assertTrue("Timeout waiting for job to start", i++ < 1000); |
| } |
| } |
| } |