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 
checkChildren(ThreadGroup tg)93   private static void checkChildren(ThreadGroup tg) {
94     Object[] data = getThreadGroupChildren(tg);
95     Thread[] threads = (Thread[])data[0];
96     ThreadGroup[] groups = (ThreadGroup[])data[1];
97 
98     List<Thread> threadList = new ArrayList<>(Arrays.asList(threads));
99 
100     // Filter out JIT thread. It may or may not be there depending on configuration.
101     Iterator<Thread> it = threadList.iterator();
102     while (it.hasNext()) {
103       Thread t = it.next();
104       if (t.getName().startsWith("Jit thread pool worker")) {
105         it.remove();
106         break;
107       }
108     }
109 
110     Collections.sort(threadList, THREAD_COMP);
111 
112     Arrays.sort(groups, THREADGROUP_COMP);
113     System.out.println(tg.getName() + ":");
114     System.out.println("  " + threadList);
115     System.out.println("  " + Arrays.toString(groups));
116 
117     if (tg.getParent() != null) {
118       checkChildren(tg.getParent());
119     }
120   }
121 
waitGroupChildren(ThreadGroup tg, int expectedChildCount, int timeoutS)122   private static void waitGroupChildren(ThreadGroup tg, int expectedChildCount, int timeoutS)
123       throws Exception {
124     for (int i = 0; i <  timeoutS; i++) {
125       Object[] data = getThreadGroupChildren(tg);
126       Thread[] threads = (Thread[])data[0];
127       List<Thread> lthreads = new ArrayList<>(Arrays.asList(threads));
128       Iterator<Thread> it = lthreads.iterator();
129       while (it.hasNext()) {
130         Thread t = it.next();
131         if (t.getName().startsWith("Jit thread pool worker")) {
132           it.remove();
133           break;
134         }
135       }
136       if (lthreads.size() == expectedChildCount) {
137         return;
138       }
139       Thread.sleep(1000);
140     }
141 
142     Object[] data = getThreadGroupChildren(tg);
143     Thread[] threads = (Thread[])data[0];
144     System.out.println(Arrays.toString(threads));
145     throw new RuntimeException("Waited unsuccessfully for " + expectedChildCount + " children.");
146   }
147 
148   private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() {
149     public int compare(Thread o1, Thread o2) {
150       return o1.getName().compareTo(o2.getName());
151     }
152   };
153 
154   private final static Comparator<ThreadGroup> THREADGROUP_COMP = new Comparator<ThreadGroup>() {
155     public int compare(ThreadGroup o1, ThreadGroup o2) {
156       return o1.getName().compareTo(o2.getName());
157     }
158   };
159 
getTopThreadGroups()160   private static native ThreadGroup[] getTopThreadGroups();
getThreadGroupInfo(ThreadGroup tg)161   private static native Object[] getThreadGroupInfo(ThreadGroup tg);
162   // Returns an array where element 0 is an array of threads and element 1 is an array of groups.
getThreadGroupChildren(ThreadGroup tg)163   private static native Object[] getThreadGroupChildren(ThreadGroup tg);
164 }
165