/* * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 * (the "License"). You may not use this work except in compliance with the License, which is * available at www.apache.org/licenses/LICENSE-2.0 * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied, as more fully set forth in the License. * * See the NOTICE file distributed with this work for information regarding copyright ownership. */ package alluxio.client.concurrent; import org.junit.Assert; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * A set of utility methods for testing concurrency. */ public final class ConcurrencyTestUtils { private ConcurrencyTestUtils() {} // prevent instantiation /** * Tests the current operations of a list of runnables. Run all the operations at the same time to * maximize the chances of triggering a multithreading code error. Suggested by junit team at * https://github.com/junit-team/junit/wiki/Multithreaded-code-and-concurrency. * * @param runnables the list of runnables * @param maxTimeoutSeconds the maximal timeout in seconds * @throws InterruptedException if awaiting one of the threads fails */ public static void assertConcurrent(final List<? extends Runnable> runnables, final int maxTimeoutSeconds) throws InterruptedException { final int numThreads = runnables.size(); final List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<Throwable>()); final ExecutorService threadPool = Executors.newFixedThreadPool(numThreads); try { final CountDownLatch allExecutorThreadsReady = new CountDownLatch(numThreads); final CountDownLatch afterInitBlocker = new CountDownLatch(1); final CountDownLatch allDone = new CountDownLatch(numThreads); for (final Runnable submittedTestRunnable : runnables) { threadPool.submit(new Runnable() { @Override public void run() { allExecutorThreadsReady.countDown(); try { afterInitBlocker.await(); submittedTestRunnable.run(); } catch (final Throwable e) { exceptions.add(e); } finally { allDone.countDown(); } } }); } // wait until all threads are ready Assert.assertTrue("Timeout initializing threads!", allExecutorThreadsReady.await(runnables.size() * 10, TimeUnit.MILLISECONDS)); // start all test runners afterInitBlocker.countDown(); Assert.assertTrue("Timeout! More than " + maxTimeoutSeconds + " seconds", allDone.await(maxTimeoutSeconds, TimeUnit.SECONDS)); } finally { threadPool.shutdownNow(); } Assert.assertTrue("Failed with exception(s) " + exceptions, exceptions.isEmpty()); } }