| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license |
| * agreements. See the NOTICE file distributed with this work for additional information regarding |
| * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the License. You may obtain a |
| * copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package org.apache.geode.test.dunit; |
| |
| import static org.apache.geode.test.dunit.Jitter.jitterInterval; |
| import static org.junit.Assert.fail; |
| |
| import org.apache.logging.log4j.Logger; |
| |
| import org.apache.geode.internal.OSProcess; |
| import org.apache.geode.internal.logging.LogService; |
| |
| /** |
| * <code>ThreadUtils</code> provides static utility methods to perform thread related actions such |
| * as dumping thread stacks. |
| * |
| * These methods can be used directly: <code>ThreadUtils.dumpAllStacks()</code>, however, they are |
| * intended to be referenced through static import: |
| * |
| * <pre> |
| * import static org.apache.geode.test.dunit.ThreadUtils.*; |
| * ... |
| * dumpAllStacks(); |
| * </pre> |
| * |
| * Extracted from DistributedTestCase. |
| */ |
| public class ThreadUtils { |
| |
| private static final Logger logger = LogService.getLogger(); |
| |
| protected ThreadUtils() {} |
| |
| /** |
| * Print stack dumps for all vms. |
| * |
| * @since GemFire 5.0 |
| */ |
| public static void dumpAllStacks() { |
| for (int h = 0; h < Host.getHostCount(); h++) { |
| dumpStack(Host.getHost(h)); |
| } |
| } |
| |
| /** |
| * Dump all thread stacks |
| */ |
| public static void dumpMyThreads() { |
| OSProcess.printStacks(0, false); |
| } |
| |
| /** |
| * Print a stack dump for this vm. |
| * |
| * @since GemFire 5.0 |
| */ |
| public static void dumpStack() { |
| OSProcess.printStacks(0, false); |
| } |
| |
| /** |
| * Print stack dumps for all vms on the given host. |
| * |
| * @since GemFire 5.0 |
| */ |
| public static void dumpStack(final Host host) { |
| for (int v = 0; v < host.getVMCount(); v++) { |
| host.getVM(v).invoke(org.apache.geode.test.dunit.ThreadUtils.class, "dumpStack"); |
| } |
| } |
| |
| /** |
| * Print a stack dump for the given vm. |
| * |
| * @since GemFire 5.0 |
| */ |
| public static void dumpStack(final VM vm) { |
| vm.invoke(org.apache.geode.test.dunit.ThreadUtils.class, "dumpStack"); |
| } |
| |
| public static void dumpStackTrace(final Thread thread, final StackTraceElement[] stackTrace) { |
| StringBuilder msg = new StringBuilder(); |
| msg.append("Thread=<").append(thread).append("> stackDump:\n"); |
| for (int i = 0; i < stackTrace.length; i++) { |
| msg.append("\t").append(stackTrace[i]).append("\n"); |
| } |
| logger.info(msg.toString()); |
| } |
| |
| /** |
| * Wait for a thread to join. |
| * |
| * @param async async invocation to wait on |
| * @param timeoutMilliseconds maximum time to wait |
| * @throws AssertionError if the thread does not terminate |
| */ |
| public static void join(final AsyncInvocation<?> async, final long timeoutMilliseconds) { |
| join(async.getThread(), timeoutMilliseconds); |
| } |
| |
| /** |
| * Wait for a thread to join. |
| * |
| * @param thread thread to wait on |
| * @param timeoutMilliseconds maximum time to wait |
| * @throws AssertionError if the thread does not terminate |
| */ |
| public static void join(final Thread thread, final long timeoutMilliseconds) { |
| final long tilt = System.currentTimeMillis() + timeoutMilliseconds; |
| final long incrementalWait = jitterInterval(timeoutMilliseconds); |
| final long start = System.currentTimeMillis(); |
| for (;;) { |
| // I really do *not* understand why this check is necessary |
| // but it is, at least with JDK 1.6. According to the source code |
| // and the javadocs, one would think that join() would exit immediately |
| // if the thread is dead. However, I can tell you from experimentation |
| // that this is not the case. :-( djp 2008-12-08 |
| if (!thread.isAlive()) { |
| break; |
| } |
| try { |
| thread.join(incrementalWait); |
| } catch (InterruptedException e) { |
| fail("interrupted"); |
| } |
| if (System.currentTimeMillis() >= tilt) { |
| break; |
| } |
| } // for |
| if (thread.isAlive()) { |
| logger.info("HUNG THREAD"); |
| ThreadUtils.dumpStackTrace(thread, thread.getStackTrace()); |
| ThreadUtils.dumpMyThreads(); |
| thread.interrupt(); // We're in trouble! |
| fail("Thread did not terminate after " + timeoutMilliseconds + " ms: " + thread); |
| } |
| long elapsedMs = (System.currentTimeMillis() - start); |
| if (elapsedMs > 0) { |
| String msg = "Thread " + thread + " took " + elapsedMs + " ms to exit."; |
| logger.info(msg); |
| } |
| } |
| } |