private static CentralJobScheduler createCentralScheduler() { return new CentralJobScheduler(); } }
@Override public Executor executor( Group group ) { return job -> schedule( group, job ); }
public static JobScheduler createInitialisedScheduler() { CentralJobScheduler scheduler = createCentralScheduler(); scheduler.init(); return scheduler; }
@Override public void shutdown() { started = false; // First shut down the scheduler, so no new tasks are queued up in the pools. InterruptedException exception = shutDownScheduler(); // Then shut down the thread pools. This involves cancelling jobs which hasn't been cancelled already, // so we avoid having to wait the full maximum wait time on the executor service shut-downs. exception = Exceptions.chain( exception, pools.shutDownAll() ); // Finally, we shut the work-stealing executors down. for ( ExecutorService workStealingExecutor : workStealingExecutors.values() ) { exception = shutdownPool( workStealingExecutor, exception ); } workStealingExecutors.clear(); if ( exception != null ) { throw new RuntimeException( "Unable to shut down job scheduler properly.", exception ); } }
@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( 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() ); }
@Test( timeout = 10_000 ) public void scheduledTasksThatThrowsShouldStop() throws Exception { CentralJobScheduler scheduler = new CentralJobScheduler(); scheduler.init(); BinaryLatch triggerLatch = new BinaryLatch(); RuntimeException boom = new RuntimeException( "boom" ); AtomicInteger triggerCounter = new AtomicInteger(); Runnable job = () -> { triggerCounter.incrementAndGet(); triggerLatch.release(); throw boom; }; scheduler.scheduleRecurring( Group.INDEX_POPULATION, job, 1, TimeUnit.MILLISECONDS ); triggerLatch.await(); Thread.sleep( 50 ); assertThat( triggerCounter.get(), is( 1 ) ); }
handles.add( scheduler.schedule( Group.INDEX_POPULATION, task, 0, TimeUnit.MILLISECONDS ) ); handles.add( scheduler.scheduleRecurring( Group.INDEX_POPULATION, task, Integer.MAX_VALUE, TimeUnit.MILLISECONDS ) ); handles.add( scheduler.scheduleRecurring( Group.INDEX_POPULATION, task, 0, Integer.MAX_VALUE, TimeUnit.MILLISECONDS ) );
@Test( timeout = 10_000 ) public void shouldRunRecurringJob() throws Throwable { // Given long period = 10; int count = 5; life.start(); // When scheduler.scheduleRecurring( Group.INDEX_POPULATION, countInvocationsJob, period, MILLISECONDS ); awaitInvocationCount( count ); scheduler.shutdown(); // Then assert that the recurring job was stopped (when the scheduler was shut down) int actualInvocations = invocations.get(); sleep( period * 5 ); assertThat( invocations.get(), equalTo( actualInvocations ) ); }
@Override public JobHandle scheduleRecurring( Group group, final Runnable runnable, long period, TimeUnit timeUnit ) { return scheduleRecurring( group, runnable, 0, period, timeUnit ); }
@Override public void close() { shutdown(); }
@Override public ExecutorService workStealingExecutor( Group group, int parallelism ) { return workStealingExecutor( group, parallelism, false ); }
private ExecutorService workStealingExecutor( Group group, int parallelism, boolean asyncMode ) { return workStealingExecutors.computeIfAbsent( group, g -> createNewWorkStealingExecutor( g, parallelism, asyncMode ) ); }
@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 ) ); } }
@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 ) ) ); }
@Override public void close() { shutdown(); }
@Override public void shutdown() { started = false; // First shut down the scheduler, so no new tasks are queued up in the pools. InterruptedException exception = shutDownScheduler(); // Then shut down the thread pools. This involves cancelling jobs which hasn't been cancelled already, // so we avoid having to wait the full maximum wait time on the executor service shut-downs. exception = Exceptions.chain( exception, pools.shutDownAll() ); // Finally, we shut the work-stealing executors down. for ( ExecutorService workStealingExecutor : workStealingExecutors.values() ) { exception = shutdownPool( workStealingExecutor, exception ); } workStealingExecutors.clear(); if ( exception != null ) { throw new RuntimeException( "Unable to shut down job scheduler properly.", exception ); } }
@Override public ExecutorService workStealingExecutorAsyncMode( Group group, int parallelism ) { return workStealingExecutor( group, parallelism, true ); }
private ExecutorService workStealingExecutor( Group group, int parallelism, boolean asyncMode ) { return workStealingExecutors.computeIfAbsent( group, g -> createNewWorkStealingExecutor( g, parallelism, asyncMode ) ); }
@Override public JobHandle schedule( Group group, Runnable job ) { return super.schedule( group, slowRunnable( job ) ); } };