1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package art;
18 
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Comparator;
22 import java.util.Collections;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.concurrent.CountDownLatch;
26 
27 public class Test925 {
run()28   public static void run() throws Exception {
29     doTest();
30   }
31 
doTest()32   private static void doTest() throws Exception {
33     Thread t1 = Thread.currentThread();
34     ThreadGroup curGroup = t1.getThreadGroup();
35 
36     ThreadGroup rootGroup = curGroup;
37     while (rootGroup.getParent() != null) {
38       rootGroup = rootGroup.getParent();
39     }
40 
41     ThreadGroup topGroups[] = getTopThreadGroups();
42     if (topGroups == null || topGroups.length != 1 || topGroups[0] != rootGroup) {
43       System.out.println(Arrays.toString(topGroups));
44       throw new RuntimeException("Unexpected topGroups");
45     }
46 
47     printThreadGroupInfo(curGroup);
48     printThreadGroupInfo(rootGroup);
49 
50     waitGroupChildren(rootGroup, 5 /* # daemons */, 30 /* timeout in seconds */);
51 
52     checkChildren(curGroup);
53 
54     // Test custom groups
55     ThreadGroup testGroup = new CustomThreadGroup(curGroup, "TEST GROUP");
56     final CountDownLatch cdl = new CountDownLatch(1);
57     final CountDownLatch startup = new CountDownLatch(1);
58     Thread t2 = new Thread(testGroup, "Test Thread") {
59       public void run() {
60         startup.countDown();
61         try {
62           cdl.await();
63         } catch (Exception e) {}
64       }
65     };
66     t2.start();
67     startup.await();
68     printThreadGroupInfo(testGroup);
69     cdl.countDown();
70     t2.join();
71   }
72 
73   private static final class CustomThreadGroup extends ThreadGroup {
CustomThreadGroup(ThreadGroup parent, String name)74     public CustomThreadGroup(ThreadGroup parent, String name) {
75       super(parent, name);
76     }
77   }
78 
printThreadGroupInfo(ThreadGroup tg)79   private static void printThreadGroupInfo(ThreadGroup tg) {
80     Object[] threadGroupInfo = getThreadGroupInfo(tg);
81     if (threadGroupInfo == null || threadGroupInfo.length != 4) {
82       System.out.println(Arrays.toString(threadGroupInfo));
83       throw new RuntimeException("threadGroupInfo length wrong");
84     }
85 
86     System.out.println(tg);
87     System.out.println("  " + threadGroupInfo[0]);  // Parent
88     System.out.println("  " + threadGroupInfo[1]);  // Name
89     System.out.println("  " + threadGroupInfo[2]);  // Priority
90     System.out.println("  " + threadGroupInfo[3]);  // Daemon
91   }
92 
filteredThread(Thread[] threads)93   private static ArrayList<Thread> filteredThread(Thread[] threads) {
94     ArrayList<Thread> list = new ArrayList<>(Arrays.asList(threads));
95 
96     // Filter out JIT and reporting thread. They may or may not be there depending on configuration.
97     Iterator<Thread> it = list.iterator();
98     while (it.hasNext()) {
99       Thread t = it.next();
100       if (t.getName().startsWith("Jit thread pool worker") ||
101           t.getName().startsWith("Metrics Background Reporting Thread")) {
102         it.remove();
103       }
104     }
105     return list;
106   }
107 
checkChildren(ThreadGroup tg)108   private static void checkChildren(ThreadGroup tg) {
109     Object[] data = getThreadGroupChildren(tg);
110     Thread[] threads = (Thread[])data[0];
111     ThreadGroup[] groups = (ThreadGroup[])data[1];
112 
113     List<Thread> threadList = filteredThread(threads);
114 
115     Collections.sort(threadList, THREAD_COMP);
116 
117     Arrays.sort(groups, THREADGROUP_COMP);
118     System.out.println(tg.getName() + ":");
119     System.out.println("  " + threadList);
120     System.out.println("  " + Arrays.toString(groups));
121 
122     if (tg.getParent() != null) {
123       checkChildren(tg.getParent());
124     }
125   }
126 
waitGroupChildren(ThreadGroup tg, int expectedChildCount, int timeoutS)127   private static void waitGroupChildren(ThreadGroup tg, int expectedChildCount, int timeoutS)
128       throws Exception {
129     for (int i = 0; i <  timeoutS; i++) {
130       Object[] data = getThreadGroupChildren(tg);
131       Thread[] threads = (Thread[])data[0];
132       List<Thread> lthreads = filteredThread(threads);
133       if (lthreads.size() == expectedChildCount) {
134         return;
135       }
136       Thread.sleep(1000);
137     }
138 
139     Object[] data = getThreadGroupChildren(tg);
140     Thread[] threads = (Thread[])data[0];
141     System.out.println(Arrays.toString(threads));
142     throw new RuntimeException("Waited unsuccessfully for " + expectedChildCount + " children.");
143   }
144 
145   private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() {
146     public int compare(Thread o1, Thread o2) {
147       return o1.getName().compareTo(o2.getName());
148     }
149   };
150 
151   private final static Comparator<ThreadGroup> THREADGROUP_COMP = new Comparator<ThreadGroup>() {
152     public int compare(ThreadGroup o1, ThreadGroup o2) {
153       return o1.getName().compareTo(o2.getName());
154     }
155   };
156 
getTopThreadGroups()157   private static native ThreadGroup[] getTopThreadGroups();
getThreadGroupInfo(ThreadGroup tg)158   private static native Object[] getThreadGroupInfo(ThreadGroup tg);
159   // Returns an array where element 0 is an array of threads and element 1 is an array of groups.
getThreadGroupChildren(ThreadGroup tg)160   private static native Object[] getThreadGroupChildren(ThreadGroup tg);
161 }
162