/* * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.util.concurrent; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import com.google.common.base.Stopwatch; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import org.junit.After; import org.junit.Test; /** * Tests various ThreadPoolExecutor implementations. * * @author Thomas Pantelis */ public class ThreadPoolExecutorTest { private ExecutorService executor; @After public void tearDown() { if (executor != null) { executor.shutdownNow(); } } @Test public void testFastThreadPoolExecution() throws Exception { testThreadPoolExecution( SpecialExecutors.newBoundedFastThreadPool( 50, 100000, "TestPool" ), 100000, "TestPool", 0 ); } @Test(expected = RejectedExecutionException.class) public void testFastThreadPoolRejectingTask() throws Exception { executor = SpecialExecutors.newBoundedFastThreadPool( 1, 1, "TestPool" ); for (int i = 0; i < 5; i++) { executor.execute( new Task( null, null, null, null, TimeUnit.MICROSECONDS.convert( 5, TimeUnit.SECONDS ) ) ); } } @Test public void testBlockingFastThreadPoolExecution() throws Exception { // With a queue capacity of 1, it should block at some point. testThreadPoolExecution( SpecialExecutors.newBlockingBoundedFastThreadPool( 2, 1, "TestPool" ), 1000, null, 10 ); } @Test public void testCachedThreadPoolExecution() throws Exception { testThreadPoolExecution( SpecialExecutors.newBoundedCachedThreadPool( 10, 100000, "TestPool" ), 100000, "TestPool", 0 ); } @Test(expected = RejectedExecutionException.class) public void testCachedThreadRejectingTask() throws Exception { ExecutorService executor = SpecialExecutors.newBoundedCachedThreadPool( 1, 1, "TestPool" ); for (int i = 0; i < 5; i++) { executor.execute( new Task( null, null, null, null, TimeUnit.MICROSECONDS.convert( 5, TimeUnit.SECONDS ) ) ); } } @Test public void testBlockingCachedThreadPoolExecution() throws Exception { testThreadPoolExecution( SpecialExecutors.newBlockingBoundedCachedThreadPool( 2, 1, "TestPool" ), 1000, null, 10 ); } void testThreadPoolExecution( final ExecutorService executor, final int numTasksToRun, final String expThreadPrefix, final long taskDelay ) throws Exception { this.executor = executor; System.out.println("\nTesting " + executor.getClass().getSimpleName() + " with " + numTasksToRun + " tasks."); final CountDownLatch tasksRunLatch = new CountDownLatch( numTasksToRun ); final ConcurrentMap<Thread, AtomicLong> taskCountPerThread = new ConcurrentHashMap<>(); final AtomicReference<AssertionError> threadError = new AtomicReference<>(); Stopwatch stopWatch = Stopwatch.createStarted(); new Thread() { @Override public void run() { for (int i = 0; i < numTasksToRun; i++) { // if (i%100 == 0) { // Uninterruptibles.sleepUninterruptibly( 20, TimeUnit.MICROSECONDS ); // } executor.execute( new Task( tasksRunLatch, taskCountPerThread, threadError, expThreadPrefix, taskDelay ) ); } } }.start(); boolean done = tasksRunLatch.await( 15, TimeUnit.SECONDS ); stopWatch.stop(); if (!done) { fail((numTasksToRun - tasksRunLatch.getCount()) + " tasks out of " + numTasksToRun + " executed"); } if (threadError.get() != null) { throw threadError.get(); } System.out.println( taskCountPerThread.size() + " threads used:" ); for (Map.Entry<Thread, AtomicLong> e : taskCountPerThread.entrySet()) { System.out.println( " " + e.getKey().getName() + " - " + e.getValue() + " tasks" ); } System.out.println( "\n" + executor ); System.out.println( "\nElapsed time: " + stopWatch ); System.out.println(); } static class Task implements Runnable { final CountDownLatch tasksRunLatch; final CountDownLatch blockLatch; final ConcurrentMap<Thread, AtomicLong> taskCountPerThread; final AtomicReference<AssertionError> threadError; final String expThreadPrefix; final long delay; Task( CountDownLatch tasksRunLatch, ConcurrentMap<Thread, AtomicLong> taskCountPerThread, AtomicReference<AssertionError> threadError, String expThreadPrefix, long delay ) { this.tasksRunLatch = tasksRunLatch; this.taskCountPerThread = taskCountPerThread; this.threadError = threadError; this.expThreadPrefix = expThreadPrefix; this.delay = delay; blockLatch = null; } Task( CountDownLatch tasksRunLatch, CountDownLatch blockLatch ) { this.tasksRunLatch = tasksRunLatch; this.blockLatch = blockLatch; this.taskCountPerThread = null; this.threadError = null; this.expThreadPrefix = null; this.delay = 0; } @Override public void run() { try { try { if (delay > 0) { TimeUnit.MICROSECONDS.sleep( delay ); } else if (blockLatch != null) { blockLatch.await(); } } catch (InterruptedException e) { } if (expThreadPrefix != null) { assertEquals( "Thread name starts with " + expThreadPrefix, true, Thread.currentThread().getName().startsWith( expThreadPrefix ) ); } if (taskCountPerThread != null) { AtomicLong count = taskCountPerThread.get( Thread.currentThread() ); if (count == null) { count = new AtomicLong( 0 ); AtomicLong prev = taskCountPerThread.putIfAbsent( Thread.currentThread(), count ); if (prev != null) { count = prev; } } count.incrementAndGet(); } } catch (AssertionError e) { if (threadError != null) { threadError.set( e ); } } finally { if (tasksRunLatch != null) { tasksRunLatch.countDown(); } } } } }