mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
track thread leaks in tests: each test should not leave newly started alive threads behind
This commit is contained in:
@@ -29,7 +29,7 @@
|
||||
<orderEntry type="module" module-name="indexing-impl" />
|
||||
<orderEntry type="module" module-name="projectModel-impl" />
|
||||
<orderEntry type="library" name="Snappy-Java" level="project" />
|
||||
<orderEntry type="library" name="Netty" level="project" />
|
||||
<orderEntry type="library" exported="" name="Netty" level="project" />
|
||||
<orderEntry type="library" name="Sanselan" level="project" />
|
||||
<orderEntry type="library" name="Guava" level="project" />
|
||||
<orderEntry type="module" module-name="jps-model-impl" />
|
||||
|
||||
@@ -33,8 +33,10 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import io.netty.handler.codec.http.cors.CorsConfig;
|
||||
import io.netty.handler.codec.http.cors.CorsHandler;
|
||||
import io.netty.handler.stream.ChunkedWriteHandler;
|
||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.concurrency.AsyncPromise;
|
||||
import org.jetbrains.concurrency.Promise;
|
||||
import org.jetbrains.ide.PooledThreadExecutor;
|
||||
@@ -45,6 +47,7 @@ import java.net.ConnectException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public final class NettyUtil {
|
||||
public static final int MAX_CONTENT_LENGTH = 100 * 1024 * 1024;
|
||||
@@ -226,4 +229,18 @@ public final class NettyUtil {
|
||||
context.fireExceptionCaught(cause);
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public static void awaitQuiescenceOfGlobalEventExecutor(long timeout, @NotNull TimeUnit unit) {
|
||||
try {
|
||||
@NotNull GlobalEventExecutor executor = GlobalEventExecutor.INSTANCE;
|
||||
executor.awaitInactivity(timeout, unit);
|
||||
}
|
||||
catch (InterruptedException ignored) {
|
||||
|
||||
}
|
||||
catch (IllegalStateException ignored) {
|
||||
// thread did not start
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,14 +15,24 @@
|
||||
*/
|
||||
package com.intellij.testFramework;
|
||||
|
||||
import com.intellij.execution.process.BaseOSProcessHandler;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.project.impl.ProjectManagerImpl;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.util.ReflectionUtil;
|
||||
import com.intellij.util.TimeoutUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.ContainerUtilRt;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.io.NettyUtil;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author cdr
|
||||
@@ -31,50 +41,69 @@ public class ThreadTracker {
|
||||
private final Collection<Thread> before;
|
||||
private final boolean myDefaultProjectInitialized;
|
||||
|
||||
@TestOnly
|
||||
public ThreadTracker() {
|
||||
before = getThreads();
|
||||
myDefaultProjectInitialized = ((ProjectManagerImpl)ProjectManager.getInstance()).isDefaultProjectInitialized();
|
||||
}
|
||||
|
||||
private static final Method getThreads = ReflectionUtil.getDeclaredMethod(Thread.class, "getThreads");
|
||||
|
||||
@NotNull
|
||||
private static Collection<Thread> getThreads() {
|
||||
//todo enable
|
||||
return Collections.emptyList(); //Thread.getAllStackTraces().keySet();
|
||||
Thread[] threads;
|
||||
try {
|
||||
// faster than Thread.getAllStackTraces().keySet()
|
||||
threads = (Thread[])getThreads.invoke(null);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return ContainerUtilRt.newArrayList(threads);
|
||||
}
|
||||
|
||||
private static final Set<String> wellKnownOffenders = new THashSet<String>(){{
|
||||
add("Alarm pool(own)");
|
||||
add("Alarm pool(shared)");
|
||||
add("ApplicationImpl pooled thread");
|
||||
add("AWT-Shutdown");
|
||||
add("AWT-Windows");
|
||||
add("CompilerThread0");
|
||||
add("Finalizer");
|
||||
add("FS Synchronizer");
|
||||
add("IDEA Test Case Thread");
|
||||
add("Image Fetcher 0");
|
||||
add("Java2D Disposer");
|
||||
add("Low Memory Detector");
|
||||
add("main");
|
||||
add("Monitor Ctrl-Break");
|
||||
add("Periodic tasks thread");
|
||||
add("Reference Handler");
|
||||
add("Signal Dispatcher");
|
||||
add("SimpleTimer");
|
||||
add("timed reference disposer");
|
||||
add("timer-int"); //serverImpl
|
||||
add("timer-sys"); //clientimpl
|
||||
add("TimerQueue");
|
||||
add("UserActivityMonitor thread");
|
||||
add("VM Periodic Task Thread");
|
||||
add("VM Thread");
|
||||
add("YJPAgent-Telemetry");
|
||||
private static final Set<String> wellKnownOffenders = new THashSet<String>();
|
||||
static {
|
||||
wellKnownOffenders.add("Action Updater"); // todo remove
|
||||
wellKnownOffenders.add("Alarm pool(own)");
|
||||
wellKnownOffenders.add("Alarm pool(shared)");
|
||||
wellKnownOffenders.add("ApplicationImpl pooled thread");
|
||||
wellKnownOffenders.add("AWT-EventQueue-");
|
||||
wellKnownOffenders.add("AWT-Shutdown");
|
||||
wellKnownOffenders.add("AWT-Windows");
|
||||
wellKnownOffenders.add("Change List Updater"); //todo remove
|
||||
wellKnownOffenders.add("CompilerThread0");
|
||||
wellKnownOffenders.add("Document commit thread");
|
||||
wellKnownOffenders.add("Finalizer");
|
||||
wellKnownOffenders.add("FS Synchronizer"); //todo remove
|
||||
wellKnownOffenders.add("IDEA Test Case Thread");
|
||||
wellKnownOffenders.add("Image Fetcher ");
|
||||
wellKnownOffenders.add("Java2D Disposer");
|
||||
wellKnownOffenders.add("JobScheduler FJ pool ");
|
||||
wellKnownOffenders.add("JPS thread pool");
|
||||
wellKnownOffenders.add("Keep-Alive-Timer");
|
||||
wellKnownOffenders.add("Low Memory Detector");
|
||||
wellKnownOffenders.add("main");
|
||||
wellKnownOffenders.add("Monitor Ctrl-Break");
|
||||
wellKnownOffenders.add("Periodic tasks thread");
|
||||
wellKnownOffenders.add("Reference Handler");
|
||||
wellKnownOffenders.add("RMI TCP Connection");
|
||||
wellKnownOffenders.add("Signal Dispatcher");
|
||||
wellKnownOffenders.add("Netty ");
|
||||
wellKnownOffenders.add("timed reference disposer");
|
||||
wellKnownOffenders.add("timer-int"); //serverImpl
|
||||
wellKnownOffenders.add("timer-sys"); //clientimpl
|
||||
wellKnownOffenders.add("TimerQueue");
|
||||
wellKnownOffenders.add("UserActivityMonitor thread");
|
||||
wellKnownOffenders.add("VM Periodic Task Thread");
|
||||
wellKnownOffenders.add("VM Thread");
|
||||
wellKnownOffenders.add("YJPAgent-Telemetry");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
add("Change List Updater");
|
||||
}};
|
||||
@TestOnly
|
||||
public void checkLeak() throws AssertionError {
|
||||
BaseOSProcessHandler.awaitQuiescence(100, TimeUnit.SECONDS);
|
||||
NettyUtil.awaitQuiescenceOfGlobalEventExecutor(100, TimeUnit.SECONDS);
|
||||
try {
|
||||
if (myDefaultProjectInitialized != ((ProjectManagerImpl)ProjectManager.getInstance()).isDefaultProjectInitialized()) return;
|
||||
|
||||
@@ -85,15 +114,27 @@ public class ThreadTracker {
|
||||
if (thread == Thread.currentThread()) continue;
|
||||
ThreadGroup group = thread.getThreadGroup();
|
||||
if (group != null && "system".equals(group.getName()))continue;
|
||||
String name = thread.getName();
|
||||
if (name.startsWith("AWT-EventQueue-0")) continue;
|
||||
if (name.startsWith("JobScheduler pool ")) continue;
|
||||
if (wellKnownOffenders.contains(name)) continue;
|
||||
final String name = thread.getName();
|
||||
if (ContainerUtil.exists(wellKnownOffenders, new Condition<String>() {
|
||||
@Override
|
||||
public boolean value(String pattern) {
|
||||
return name.contains(pattern);
|
||||
}
|
||||
})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String trace = "Thread leaked: " + thread+": "+ name +";\n ";
|
||||
if (!thread.isAlive()) continue;
|
||||
if (thread.getStackTrace().length == 0) {
|
||||
TimeoutUtil.sleep(10000);
|
||||
if (!thread.isAlive()) continue;
|
||||
}
|
||||
|
||||
String trace = "Thread leaked: " + thread+"; " + thread.getState()+" ("+ thread.isAlive()+")\n--- its stacktrace:\n";
|
||||
for (final StackTraceElement stackTraceElement : thread.getStackTrace()) {
|
||||
trace += " at "+stackTraceElement +"\n";
|
||||
}
|
||||
trace += "---\n";
|
||||
Assert.fail(trace);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user