@Override public void stop() { if ( monitorJobHandle != null ) { monitorJobHandle.cancel( true ); } } }
@Override public void waitTermination() throws ExecutionException, InterruptedException { handleRelease.await(); JobHandle handleDelegate = this.latestHandle; if ( handleDelegate != null ) { handleDelegate.waitTermination(); } if ( get() == FAILED ) { Throwable exception = this.lastException; if ( exception != null ) { throw new ExecutionException( exception ); } else { throw new CancellationException(); } } }
@Test public void delayedTasksMustNotRunIfCancelledFirst() throws Exception { List<Boolean> cancelListener = new ArrayList<>(); JobHandle handle = scheduler.submit( Group.STORAGE_MAINTENANCE, counter::incrementAndGet, 100, 0 ); handle.registerCancelListener( cancelListener::add ); clock.forward( 90, TimeUnit.NANOSECONDS ); scheduler.tick(); handle.cancel( false ); clock.forward( 10, TimeUnit.NANOSECONDS ); scheduler.tick(); pools.getThreadPool( Group.STORAGE_MAINTENANCE ).shutDown(); assertThat( counter.get(), is( 0 ) ); assertThat( cancelListener, contains( Boolean.FALSE ) ); try { handle.waitTermination(); fail( "waitTermination should have thrown a CancellationException." ); } catch ( CancellationException ignore ) { // Good stuff. } }
@Test public void shouldCancelRecurringJob() throws Exception { // Given long period = 2; life.start(); JobHandle jobHandle = scheduler.scheduleRecurring( Group.INDEX_POPULATION, countInvocationsJob, period, MILLISECONDS ); awaitFirstInvocation(); // When jobHandle.cancel( false ); try { jobHandle.waitTermination(); fail( "Task should be terminated" ); } catch ( CancellationException ignored ) { // task should be canceled } // Then int recorded = invocations.get(); sleep( period * 100 ); // we can have task that is already running during cancellation so lets count it as well assertThat( invocations.get(), both( greaterThanOrEqualTo( recorded ) ).and( lessThanOrEqualTo( recorded + 1 ) ) ); }
@Test public void shouldNotifyCancelListeners() { // GIVEN CentralJobScheduler centralJobScheduler = new CentralJobScheduler(); centralJobScheduler.init(); // WHEN AtomicBoolean halted = new AtomicBoolean(); Runnable job = () -> { while ( !halted.get() ) { LockSupport.parkNanos( MILLISECONDS.toNanos( 10 ) ); } }; JobHandle handle = centralJobScheduler.schedule( Group.INDEX_POPULATION, job ); handle.registerCancelListener( mayBeInterrupted -> halted.set( true ) ); handle.cancel( false ); // THEN assertTrue( halted.get() ); centralJobScheduler.shutdown(); }
@Test public void recurringTasksMustStopWhenCancelled() throws InterruptedException { List<Boolean> cancelListener = new ArrayList<>(); Runnable recurring = () -> { counter.incrementAndGet(); semaphore.release(); }; JobHandle handle = scheduler.submit( Group.STORAGE_MAINTENANCE, recurring, 100, 100 ); handle.registerCancelListener( cancelListener::add ); clock.forward( 100, TimeUnit.NANOSECONDS ); scheduler.tick(); assertSemaphoreAcquire(); clock.forward( 100, TimeUnit.NANOSECONDS ); scheduler.tick(); assertSemaphoreAcquire(); handle.cancel( true ); clock.forward( 100, TimeUnit.NANOSECONDS ); scheduler.tick(); clock.forward( 100, TimeUnit.NANOSECONDS ); scheduler.tick(); pools.getThreadPool( Group.STORAGE_MAINTENANCE ).shutDown(); assertThat( counter.get(), is( 2 ) ); assertThat( cancelListener, contains( Boolean.TRUE ) ); }
@Override public void stop() { jobHandle.cancel( false ); }
flush.waitTermination();
@Override public void stop() { if ( featureDecayJob != null ) { featureDecayJob.cancel( false ); } }
@Test( timeout = 10_000 ) public void waitTerminationOnDelayedJobMustWaitUntilJobCompletion() throws Exception { CentralJobScheduler scheduler = new CentralJobScheduler(); scheduler.init(); AtomicBoolean triggered = new AtomicBoolean(); Runnable job = () -> { LockSupport.parkNanos( TimeUnit.MILLISECONDS.toNanos( 10 ) ); triggered.set( true ); }; JobHandle handle = scheduler.schedule( Group.INDEX_POPULATION, job, 10, TimeUnit.MILLISECONDS ); handle.waitTermination(); assertTrue( triggered.get() ); }
public void stop() { if ( backgroundSamplingHandle != null ) { backgroundSamplingHandle.cancel( true ); } jobTracker.stopAndAwaitAllJobs(); } }
@Test public void mustOnlyScheduleTasksThatAreDue() throws Exception { JobHandle handle1 = scheduler.submit( Group.STORAGE_MAINTENANCE, () -> counter.addAndGet( 10 ), 100, 0 ); JobHandle handle2 = scheduler.submit( Group.STORAGE_MAINTENANCE, () -> counter.addAndGet( 100 ), 200, 0 ); scheduler.tick(); assertThat( counter.get(), is( 0 ) ); clock.forward( 199, TimeUnit.NANOSECONDS ); scheduler.tick(); handle1.waitTermination(); assertThat( counter.get(), is( 10 ) ); clock.forward( 1, TimeUnit.NANOSECONDS ); scheduler.tick(); handle2.waitTermination(); assertThat( counter.get(), is( 110 ) ); }
@Override public void stop() { stopped = true; if ( handle != null ) { handle.cancel( false ); } waitOngoingCheckpointCompletion(); }
@Test( timeout = 10_000 ) public void scheduledTasksThatThrowsMustPropagateException() throws Exception { CentralJobScheduler scheduler = new CentralJobScheduler(); scheduler.init(); RuntimeException boom = new RuntimeException( "boom" ); AtomicInteger triggerCounter = new AtomicInteger(); Runnable job = () -> { triggerCounter.incrementAndGet(); throw boom; }; JobHandle handle = scheduler.scheduleRecurring( Group.INDEX_POPULATION, job, 1, TimeUnit.MILLISECONDS ); try { handle.waitTermination(); fail( "waitTermination should have failed." ); } catch ( ExecutionException e ) { assertThat( e.getCause(), is( boom ) ); } }
public void stop() { log.debug( "Stopping VM pause monitor" ); checkState( job != null, "VM pause monitor is not started" ); job.cancel( true ); job = null; }
@Test public void mustDelayExecution() throws Exception { JobHandle handle = scheduler.submit( Group.STORAGE_MAINTENANCE, counter::incrementAndGet, 100, 0 ); scheduler.tick(); assertThat( counter.get(), is( 0 ) ); clock.forward( 99, TimeUnit.NANOSECONDS ); scheduler.tick(); assertThat( counter.get(), is( 0 ) ); clock.forward( 1, TimeUnit.NANOSECONDS ); scheduler.tick(); handle.waitTermination(); assertThat( counter.get(), is( 1 ) ); }
@Override public void cancel( boolean mayInterruptIfRunning ) { set( FAILED ); JobHandle handle = latestHandle; if ( handle != null ) { handle.cancel( mayInterruptIfRunning ); } for ( CancelListener cancelListener : cancelListeners ) { cancelListener.cancelled( mayInterruptIfRunning ); } // Release the handle to allow waitTermination() to observe the cancellation. handleRelease.release(); }
@Test public void mustNotRescheduleDelayedTasks() throws Exception { JobHandle handle = scheduler.submit( Group.STORAGE_MAINTENANCE, counter::incrementAndGet, 100, 0 ); clock.forward( 100, TimeUnit.NANOSECONDS ); scheduler.tick(); handle.waitTermination(); assertThat( counter.get(), is( 1 ) ); clock.forward( 100, TimeUnit.NANOSECONDS ); scheduler.tick(); handle.waitTermination(); pools.getThreadPool( Group.STORAGE_MAINTENANCE ).shutDown(); assertThat( counter.get(), is( 1 ) ); }
@Test public void testRestart() { monitor.start(); monitor.stop(); monitor.start(); verify( jobScheduler, times( 2 ) ).schedule( any( Group.class ), any( Runnable.class ) ); verify( jobHandle ).cancel( eq( true ) ); }
@Test public void mustNotRescheduleRecurringTasksThatThrows() throws Exception { Runnable runnable = () -> { semaphore.release(); throw new RuntimeException( "boom" ); }; JobHandle handle = scheduler.submit( Group.STORAGE_MAINTENANCE, runnable, 100, 100 ); clock.forward( 100, TimeUnit.NANOSECONDS ); scheduler.tick(); assertSemaphoreAcquire(); clock.forward( 100, TimeUnit.NANOSECONDS ); scheduler.tick(); try { handle.waitTermination(); fail( "waitTermination should have thrown because the task should have failed." ); } catch ( ExecutionException e ) { assertThat( e.getCause().getMessage(), is( "boom" ) ); } assertThat( semaphore.drainPermits(), is( 0 ) ); }