diff --git a/runtime/bundles/org.eclipse.core.jobs/META-INF/MANIFEST.MF b/runtime/bundles/org.eclipse.core.jobs/META-INF/MANIFEST.MF index 1991fbccb97..be708b1eead 100644 --- a/runtime/bundles/org.eclipse.core.jobs/META-INF/MANIFEST.MF +++ b/runtime/bundles/org.eclipse.core.jobs/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.core.jobs; singleton:=true -Bundle-Version: 3.15.700.qualifier +Bundle-Version: 3.15.800.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Export-Package: org.eclipse.core.internal.jobs;x-internal:=true, diff --git a/runtime/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobManager.java b/runtime/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobManager.java index ef4005f7069..88e2a2d5ac5 100644 --- a/runtime/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobManager.java +++ b/runtime/bundles/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobManager.java @@ -1486,9 +1486,6 @@ protected boolean scheduleInternal(InternalJob job, long delay, boolean reschedu } Assert.isNotNull(job, "Job is null"); //$NON-NLS-1$ Assert.isLegal(delay >= 0, "Scheduling delay is negative"); //$NON-NLS-1$ - if (!reschedule) { - job.setAboutToRunCanceled(false); - } // if the job is already running, set it to be rescheduled when done if (job.getState() == Job.RUNNING) { job.setStartTime(delay); // XXX delay used as time @@ -1498,6 +1495,10 @@ protected boolean scheduleInternal(InternalJob job, long delay, boolean reschedu if (job.internalGetState() != Job.NONE) { return false; } + // Clear the about to run canceled flag when actually scheduling. + // A new explicit schedule() call should override any previous cancel() call. + // See https://github.com/eclipse-platform/eclipse.platform/issues/160 + job.setAboutToRunCanceled(false); if (JobManager.DEBUG) { JobManager.debug("Scheduling job: " + job); //$NON-NLS-1$ } diff --git a/runtime/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/jobs/Bug_550738.java b/runtime/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/jobs/Bug_550738.java index 230b8c060fc..1ebbc844b34 100644 --- a/runtime/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/jobs/Bug_550738.java +++ b/runtime/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/jobs/Bug_550738.java @@ -134,4 +134,41 @@ public void testReportDoneOncePerSchedule() throws InterruptedException { waitForCompletion(job); } } + + /** + * Test for issue #160: Job.schedule() should work after cancel() when job is running. + * This tests the specific scenario where: + * 1. Job is running + * 2. cancel() is called + * 3. schedule() is called immediately (while still running) + * 4. Job finishes and should reschedule + */ + @Test + public void testCancelThenScheduleWhileRunning() throws InterruptedException { + BusyLoopJob job = new BusyLoopJob(); + try { + for (int i = 0; i < 100; i++) { + CountDownLatch startedLatch = new CountDownLatch(1); + job.started = startedLatch::countDown; + job.schedule(); + assertTrue("Job should start after schedule. Iteration " + i, + startedLatch.await(5, TimeUnit.SECONDS)); + + // While job is running, cancel it and then immediately schedule it again + Thread.sleep(i); + job.cancel(); + job.schedule(); + + // The job should reschedule and not remain in NONE state + // Wait a bit to ensure the rescheduling happens + Thread.sleep(50); + int state = job.getState(); + assertTrue("Job should not be in NONE state after cancel+schedule. Iteration " + i + ", state: " + state, + state != org.eclipse.core.runtime.jobs.Job.NONE); + } + } finally { + job.cancelWithoutRelyingOnFramework = true; + waitForCompletion(job); + } + } }