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