• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import java.lang.Thread.State;
18 import java.lang.reflect.Method;
19 import java.util.Arrays;
20 import java.util.LinkedList;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.concurrent.BrokenBarrierException;
24 import java.util.concurrent.CyclicBarrier;
25 import java.util.concurrent.ForkJoinPool;
26 
27 public class Main {
28 
29     static class Runner implements Runnable {
30         List<Object> locks;
31         List<CyclicBarrier> barriers;
32 
Runner(List<Object> locks, List<CyclicBarrier> barriers)33         public Runner(List<Object> locks, List<CyclicBarrier> barriers) {
34             this.locks = locks;
35             this.barriers = barriers;
36         }
37 
38         @Override
run()39         public void run() {
40             step(locks, barriers);
41         }
42 
step(List<Object> l, List<CyclicBarrier> b)43         private void step(List<Object> l, List<CyclicBarrier> b) {
44             if (l.isEmpty()) {
45                 // Nothing to do, sleep indefinitely.
46                 try {
47                     Thread.sleep(100000000);
48                 } catch (InterruptedException e) {
49                     throw new RuntimeException(e);
50                 }
51             } else {
52                 Object lockObject = l.remove(0);
53                 CyclicBarrier barrierObject = b.remove(0);
54 
55                 if (lockObject == null) {
56                     // No lock object: only take barrier, recurse.
57                     try {
58                         barrierObject.await();
59                     } catch (InterruptedException | BrokenBarrierException e) {
60                         throw new RuntimeException(e);
61                     }
62                     step(l, b);
63                 } else if (barrierObject != null) {
64                     // Have barrier: sync, wait and recurse.
65                     synchronized(lockObject) {
66                         try {
67                             barrierObject.await();
68                         } catch (InterruptedException | BrokenBarrierException e) {
69                             throw new RuntimeException(e);
70                         }
71                         step(l, b);
72                     }
73                 } else {
74                     // Sync, and get next step (which is assumed to have object and barrier).
75                     synchronized (lockObject) {
76                         Object lockObject2 = l.remove(0);
77                         CyclicBarrier barrierObject2 = b.remove(0);
78                         synchronized(lockObject2) {
79                             try {
80                                 barrierObject2.await();
81                             } catch (InterruptedException | BrokenBarrierException e) {
82                                 throw new RuntimeException(e);
83                             }
84                             step(l, b);
85                         }
86                     }
87                 }
88             }
89         }
90     }
91 
main(String[] args)92     public static void main(String[] args) throws Exception {
93         // Eagerly initialize ForkJoinPool as we've seen the class initialization code interfere
94         // with the logic of the test when waiting for threads to be non-runnable.
95         Class.forName(ForkJoinPool.class.getName(), true, ForkJoinPool.class.getClassLoader());
96         try {
97             testCluster1();
98         } catch (Exception e) {
99             Map<Thread,StackTraceElement[]> stacks = Thread.getAllStackTraces();
100             for (Map.Entry<Thread,StackTraceElement[]> entry : stacks.entrySet()) {
101                 System.out.println(entry.getKey());
102                 System.out.println(Arrays.toString(entry.getValue()));
103             }
104             throw e;
105         }
106     }
107 
testCluster1()108     private static void testCluster1() throws Exception {
109         // Test setup (at deadlock):
110         //
111         // Thread 1:
112         //   #0 step: synchornized(o3) { synchronized(o2) }
113         //   #1 step: synchronized(o1)
114         //
115         // Thread 2:
116         //   #0 step: synchronized(o1)
117         //   #1 step: synchronized(o4) { synchronized(o2) }
118         //
119         LinkedList<Object> l1 = new LinkedList<>();
120         LinkedList<CyclicBarrier> b1 = new LinkedList<>();
121         LinkedList<Object> l2 = new LinkedList<>();
122         LinkedList<CyclicBarrier> b2 = new LinkedList<>();
123 
124         Object o1 = new Object();
125         Object o2 = new Object();
126         Object o3 = new Object();
127         Object o4 = new Object();
128 
129         l1.add(o1);
130         l1.add(o3);
131         l1.add(o2);
132         l2.add(o4);
133         l2.add(o2);
134         l2.add(o1);
135 
136         CyclicBarrier c1 = new CyclicBarrier(3);
137         CyclicBarrier c2 = new CyclicBarrier(2);
138         b1.add(c1);
139         b1.add(null);
140         b1.add(c2);
141         b2.add(null);
142         b2.add(c1);
143         b2.add(c2);
144 
145         Thread t1 = new Thread(new Runner(l1, b1));
146         t1.setDaemon(true);
147         t1.start();
148         Thread t2 = new Thread(new Runner(l2, b2));
149         t2.setDaemon(true);
150         t2.start();
151 
152         c1.await();
153 
154         waitNotRunnable(t1);
155         waitNotRunnable(t2);
156         Thread.sleep(250);    // Unfortunately this seems necessary. :-(
157 
158         // Thread 1.
159         {
160             Object[] stack1 = getAnnotatedStack(t1);
161             assertBlockedOn(stack1[0], o2);              // Blocked on o2.
162             assertLocks(stack1[0], o3);                  // Locked o3.
163             assertStackTraceElementStep(stack1[0]);
164 
165             assertBlockedOn(stack1[1], null);            // Frame can't be blocked.
166             assertLocks(stack1[1], o1);                  // Locked o1.
167             assertStackTraceElementStep(stack1[1]);
168         }
169 
170         // Thread 2.
171         {
172             Object[] stack2 = getAnnotatedStack(t2);
173             assertBlockedOn(stack2[0], o1);              // Blocked on o1.
174             assertLocks(stack2[0]);                      // Nothing locked.
175             assertStackTraceElementStep(stack2[0]);
176 
177             assertBlockedOn(stack2[1], null);            // Frame can't be blocked.
178             assertLocks(stack2[1], o4, o2);              // Locked o4, o2.
179             assertStackTraceElementStep(stack2[1]);
180         }
181     }
182 
waitNotRunnable(Thread t)183     private static void waitNotRunnable(Thread t) throws InterruptedException {
184         while (t.getState() == State.RUNNABLE) {
185             Thread.sleep(100);
186         }
187     }
188 
getAnnotatedStack(Thread t)189     private static Object[] getAnnotatedStack(Thread t) throws Exception {
190         Class<?> vmStack = Class.forName("dalvik.system.VMStack");
191         Method m = vmStack.getDeclaredMethod("getAnnotatedThreadStackTrace", Thread.class);
192         return (Object[]) m.invoke(null, t);
193     }
194 
assertEquals(Object o1, Object o2)195     private static void assertEquals(Object o1, Object o2) {
196         if (o1 != o2) {
197             throw new RuntimeException("Expected " + o1 + " == " + o2);
198         }
199     }
assertLocks(Object fromTrace, Object... locks)200     private static void assertLocks(Object fromTrace, Object... locks) throws Exception {
201         Object fieldValue = fromTrace.getClass().getDeclaredMethod("getHeldLocks").
202                 invoke(fromTrace);
203         assertEquals((Object[]) fieldValue,
204                 (locks == null) ? null : (locks.length == 0 ? null : locks));
205     }
assertBlockedOn(Object fromTrace, Object block)206     private static void assertBlockedOn(Object fromTrace, Object block) throws Exception {
207         Object fieldValue = fromTrace.getClass().getDeclaredMethod("getBlockedOn").
208                 invoke(fromTrace);
209         assertEquals(fieldValue, block);
210     }
assertEquals(Object[] o1, Object[] o2)211     private static void assertEquals(Object[] o1, Object[] o2) {
212         if (!Arrays.equals(o1, o2)) {
213             throw new RuntimeException(
214                     "Expected " + Arrays.toString(o1) + " == " + Arrays.toString(o2));
215         }
216     }
assertStackTraceElementStep(Object o)217     private static void assertStackTraceElementStep(Object o) throws Exception {
218         Object fieldValue = o.getClass().getDeclaredMethod("getStackTraceElement").invoke(o);
219         if (fieldValue instanceof StackTraceElement) {
220             StackTraceElement elem = (StackTraceElement) fieldValue;
221             if (!elem.getMethodName().equals("step")) {
222                 throw new RuntimeException("Expected step method");
223             }
224             return;
225         }
226         throw new RuntimeException("Expected StackTraceElement " + fieldValue + " / " + o);
227     }
228 }
229 
230