package jetbrains.mps.testbench.util; /*Generated by MPS */ import java.util.regex.Pattern; import gnu.trove.TLongObjectHashMap; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.lang.management.ManagementFactory; /** * fyodor, Sep 8, 2010 */ public class ThreadWatcher implements Output { private ThreadWatcher.ThreadState myState; private String myErrors; private String myDescription = "threads difference"; private static class ThreadState { private static Pattern IGNORED_THREAD = Pattern.compile("(AWT\\-.*)|(Image Fetch.*)|(Progress Cancel Checker)|(Flushing thread)" + "|(Keep\\-Alive.*)|(Finalizer.*)|(MPS interrupting thread)" + "|(caret blinker.*)|(ApplicationImpl.*)|(AnimatorThread)" + "|(MPS EDT Executor Thread.*)|(ChangesManager command queue)|(TimerQueue)" + "|(Change List.*)|(Reference.*)|(Periodic task.*)" + "|(Java2D.*)|(LowMemoryWatcher janitor)|(Timer\\-.*)|(FS Sync.*)" + "|(timed reference disposer)|(Alarm pool\\(own\\))|(Alarm pool\\(shared\\))" + "|(Poller SunPKCS11-Darwin)|(MPS interrupt.*)|(process reaper)" + "|(RefCountingStorage.*)|(Encoding detection thread)" + "|(ProcessWaitFor: .*git(\\.exe)?.*)|(BaseDataReader: error stream of .*git(\\.exe)?.*)" + "|(BaseDataReader: output stream of .*git(\\.exe)?.*)" + "|(Process I/O pool [0-9]+)"); private TLongObjectHashMap<ThreadInfo> myAllThreads = new TLongObjectHashMap<ThreadInfo>(); private TLongObjectHashMap<ThreadInfo> myRunningThreads = new TLongObjectHashMap<ThreadInfo>(); private ThreadState() { } private void captureState() { myAllThreads.clear(); myRunningThreads.clear(); long cid = Thread.currentThread().getId(); ThreadMXBean bean = ManagementFactory.getThreadMXBean(); for (ThreadInfo info : bean.getThreadInfo(bean.getAllThreadIds())) { if (info != null && cid != info.getThreadId() && info.getThreadState() != Thread.State.NEW && info.getThreadState() != Thread.State.TERMINATED && !(IGNORED_THREAD.matcher(info.getThreadName()).matches())) { myAllThreads.put(info.getThreadId(), info); if (Thread.State.RUNNABLE == info.getThreadState()) { myRunningThreads.put(info.getThreadId(), info); } } } } public void dump(StringBuilder sb, String pref) { String sep = ""; for (long id : myRunningThreads.keys()) { sb.append(sep).append(pref).append("Running thread ").append(id).append(" \"").append(myRunningThreads.get(id).getThreadName()).append("\""); sep = "\n"; } for (long id : myAllThreads.keys()) { if (!(myRunningThreads.contains(id))) { sb.append(sep).append(pref).append("Inactive thread ").append(id).append(" \"").append(myAllThreads.get(id).getThreadName()).append("\""); sep = "\n"; } } } public ThreadWatcher.ThreadState[] diff(ThreadWatcher.ThreadState baseLine) { ThreadWatcher.ThreadState newDiff = new ThreadWatcher.ThreadState(); ThreadWatcher.ThreadState oldDiff = new ThreadWatcher.ThreadState(); for (long id : this.myAllThreads.keys()) { if (!((baseLine.myAllThreads.containsKey(id)))) { newDiff.myAllThreads.put(id, this.myAllThreads.get(id)); if (this.myRunningThreads.containsKey(id)) { newDiff.myRunningThreads.put(id, myRunningThreads.get(id)); } } } for (long id : baseLine.myAllThreads.keys()) { if (!((this.myAllThreads.containsKey(id)))) { oldDiff.myAllThreads.put(id, baseLine.myAllThreads.get(id)); if (baseLine.myRunningThreads.containsKey(id)) { oldDiff.myRunningThreads.put(id, baseLine.myRunningThreads.get(id)); } } } for (long id : this.myRunningThreads.keys()) { if (!((baseLine.myRunningThreads.containsKey(id)))) { newDiff.myRunningThreads.put(id, this.myRunningThreads.get(id)); newDiff.myAllThreads.put(id, this.myAllThreads.get(id)); } } for (long id : baseLine.myRunningThreads.keys()) { if (!((this.myRunningThreads.containsKey(id)))) { oldDiff.myRunningThreads.put(id, baseLine.myRunningThreads.get(id)); oldDiff.myAllThreads.put(id, baseLine.myAllThreads.get(id)); } } return new ThreadWatcher.ThreadState[]{newDiff, oldDiff}; } } public ThreadWatcher(boolean capture) { if (capture) { this.myState = new ThreadWatcher.ThreadState(); myState.captureState(); } } @Override public boolean isNotEmpty() { return myErrors != null; } @Override public String getDescription() { return myDescription; } @Override public String getText() { return myErrors; } public boolean waitUntilSettled(long millis) { if (myState == null) { return true; } if (myErrors != null) { throw new IllegalStateException("Settled already"); } ThreadWatcher.ThreadState current = new ThreadWatcher.ThreadState(); ThreadWatcher.ThreadState[] diff; long step = 100; long leftMillis = millis; do { current.captureState(); diff = current.diff(myState); if (diff[0].myAllThreads.isEmpty() && diff[1].myAllThreads.isEmpty()) { return true; } if (diff[0].myAllThreads.isEmpty()) { break; } try { Thread.sleep(step); } catch (InterruptedException ignore) { } leftMillis -= step; } while (leftMillis >= 0); StringBuilder sb = new StringBuilder(); sb.append("After ").append(millis).append(" ms. --\n"); StringBuilder sb2 = new StringBuilder(); String sep2 = ""; String pr2 = "no"; if (!(diff[0].myAllThreads.isEmpty())) { sb2.append(String.valueOf(diff[0].myAllThreads.size())).append(" new"); sep2 = ", "; pr2 = ""; sb.append(" New:\n"); diff[0].dump(sb, " "); sb.append("\n"); } if (!(diff[1].myAllThreads.isEmpty())) { sb2.append(sep2).append(String.valueOf(diff[1].myAllThreads.size())).append(" killed"); pr2 = ""; sb.append(" Killed:\n"); diff[1].dump(sb, " "); sb.append("\n"); } sb2.append(pr2).append(" threads"); this.myDescription = sb2.toString(); this.myErrors = sb.toString(); return false; } }