public InterruptedException shutDownAll() { pools.forEach( ( group, pool ) -> pool.cancelAllJobs() ); pools.forEach( ( group, pool ) -> pool.shutDown() ); return pools.values().stream() .map( ThreadPool::getShutdownException ) .reduce( null, Exceptions::chain ); } }
ThreadPoolManager( ThreadGroup topLevelGroup ) { pools = new ConcurrentHashMap<>(); poolBuilder = group -> new ThreadPool( group, topLevelGroup ); }
@Override public ThreadFactory threadFactory( Group group ) { return pools.getThreadPool( group ).getThreadFactory(); }
@Test public void mustNotStartRecurringTasksWherePriorExecutionHasNotYetFinished() { Runnable runnable = () -> { counter.incrementAndGet(); semaphore.acquireUninterruptibly(); }; scheduler.submit( Group.STORAGE_MAINTENANCE, runnable, 100, 100 ); for ( int i = 0; i < 4; i++ ) { scheduler.tick(); clock.forward( 100, TimeUnit.NANOSECONDS ); } semaphore.release( Integer.MAX_VALUE ); pools.getThreadPool( Group.STORAGE_MAINTENANCE ).shutDown(); assertThat( counter.get(), is( 1 ) ); }
JobHandle submit( Group group, Runnable job ) { ThreadPool threadPool = getThreadPool( group ); return threadPool.submit( job ); }
@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 ) ); }
JobHandle submit( Group group, Runnable job ) { ThreadPool threadPool = getThreadPool( group ); return threadPool.submit( job ); }
public InterruptedException shutDownAll() { pools.forEach( ( group, pool ) -> pool.cancelAllJobs() ); pools.forEach( ( group, pool ) -> pool.shutDown() ); return pools.values().stream() .map( ThreadPool::getShutdownException ) .reduce( null, Exceptions::chain ); } }
@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. } }
ThreadPoolManager( ThreadGroup topLevelGroup ) { pools = new ConcurrentHashMap<>(); poolBuilder = group -> new ThreadPool( group, topLevelGroup ); }
@Override public ThreadFactory threadFactory( Group group ) { return pools.getThreadPool( group ).getThreadFactory(); }
@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 ) ); }