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.concurrent.CountDownLatch; 20 import java.util.ArrayList; 21 import java.util.Iterator; 22 import java.util.List; 23 24 public class Test923 { run()25 public static void run() throws Exception { 26 doTest(); 27 } 28 doTest()29 private static void doTest() throws Exception { 30 // Start a watchdog, to make sure on deadlocks etc the test dies. 31 startWatchdog(); 32 33 sharedId = createRawMonitor(); 34 35 output = new ArrayList<String>(100); 36 37 simpleTests(sharedId); 38 39 for (String s : output) { 40 System.out.println(s); 41 } 42 output.clear(); 43 44 threadTests(sharedId); 45 46 destroyRawMonitor(sharedId); 47 } 48 simpleTests(long id)49 private static void simpleTests(long id) { 50 unlock(id); // Should fail. 51 52 lock(id); 53 unlock(id); 54 unlock(id); // Should fail. 55 56 lock(id); 57 lock(id); 58 unlock(id); 59 unlock(id); 60 unlock(id); // Should fail. 61 62 rawWait(id, 0); // Should fail. 63 rawWait(id, -1); // Should fail. 64 rawWait(id, 1); // Should fail. 65 66 lock(id); 67 rawWait(id, 50); 68 unlock(id); 69 unlock(id); // Should fail. 70 71 rawNotify(id); // Should fail. 72 lock(id); 73 rawNotify(id); 74 unlock(id); 75 unlock(id); // Should fail. 76 77 rawNotifyAll(id); // Should fail. 78 lock(id); 79 rawNotifyAll(id); 80 unlock(id); 81 unlock(id); // Should fail. 82 } 83 threadTests(final long id)84 private static void threadTests(final long id) throws Exception { 85 final int N = 10; 86 87 final CountDownLatch waitLatch = new CountDownLatch(N); 88 final CountDownLatch wait2Latch = new CountDownLatch(1); 89 90 Runnable r = new Runnable() { 91 @Override 92 public void run() { 93 lock(id); 94 waitLatch.countDown(); 95 rawWait(id, 0); 96 firstAwakened = Thread.currentThread(); 97 appendToLog("Awakened"); 98 unlock(id); 99 wait2Latch.countDown(); 100 } 101 }; 102 103 List<Thread> threads = new ArrayList<Thread>(); 104 for (int i = 0; i < N; i++) { 105 Thread t = new Thread(r); 106 threads.add(t); 107 t.start(); 108 } 109 110 // Wait till all threads have been started. 111 waitLatch.await(); 112 113 // Hopefully enough time for all the threads to progress into wait. 114 Thread.yield(); 115 Thread.sleep(500); 116 117 // Wake up one. 118 lock(id); 119 rawNotify(id); 120 unlock(id); 121 122 wait2Latch.await(); 123 124 // Wait a little bit more to see stragglers. This is flaky - spurious wakeups could 125 // make the test fail. 126 Thread.yield(); 127 Thread.sleep(500); 128 if (firstAwakened != null) { 129 firstAwakened.join(); 130 } 131 132 // Wake up everyone else. 133 lock(id); 134 rawNotifyAll(id); 135 unlock(id); 136 137 // Wait for everyone to die. 138 for (Thread t : threads) { 139 t.join(); 140 } 141 142 // Check threaded output. 143 Iterator<String> it = output.iterator(); 144 // 1) Start with N locks and Waits. 145 { 146 int locks = 0; 147 int waits = 0; 148 for (int i = 0; i < 2*N; i++) { 149 String s = it.next(); 150 if (s.equals("Lock")) { 151 locks++; 152 } else if (s.equals("Wait")) { 153 if (locks <= waits) { 154 System.out.println(output); 155 throw new RuntimeException("Wait before Lock"); 156 } 157 waits++; 158 } else { 159 System.out.println(output); 160 throw new RuntimeException("Unexpected operation: " + s); 161 } 162 } 163 } 164 165 // 2) Expect Lock + Notify + Unlock. 166 expect("Lock", it, output); 167 expect("Notify", it, output); 168 expect("Unlock", it, output); 169 170 // 3) A single thread wakes up, runs, and dies. 171 expect("Awakened", it, output); 172 expect("Unlock", it, output); 173 174 // 4) Expect Lock + NotifyAll + Unlock. 175 expect("Lock", it, output); 176 expect("NotifyAll", it, output); 177 expect("Unlock", it, output); 178 179 // 5) N-1 threads wake up, run, and die. 180 { 181 int expectedUnlocks = 0; 182 int ops = 2 * (N-1); 183 for (int i = 0; i < ops; i++) { 184 String s = it.next(); 185 if (s.equals("Awakened")) { 186 expectedUnlocks++; 187 } else if (s.equals("Unlock")) { 188 expectedUnlocks--; 189 if (expectedUnlocks < 0) { 190 System.out.println(output); 191 throw new RuntimeException("Unexpected unlock"); 192 } 193 } 194 } 195 } 196 197 // 6) That should be it. 198 if (it.hasNext()) { 199 System.out.println(output); 200 throw new RuntimeException("Unexpected trailing output, starting with " + it.next()); 201 } 202 203 output.clear(); 204 System.out.println("Done"); 205 } 206 expect(String s, Iterator<String> it, List<String> output)207 private static void expect(String s, Iterator<String> it, List<String> output) { 208 String t = it.next(); 209 if (!s.equals(t)) { 210 System.out.println(output); 211 throw new RuntimeException("Expected " + s + " but got " + t); 212 } 213 } 214 lock(long id)215 private static void lock(long id) { 216 appendToLog("Lock"); 217 rawMonitorEnter(id); 218 } 219 unlock(long id)220 private static void unlock(long id) { 221 appendToLog("Unlock"); 222 try { 223 rawMonitorExit(id); 224 } catch (RuntimeException e) { 225 appendToLog(e.getMessage()); 226 } 227 } 228 rawWait(long id, long millis)229 private static void rawWait(long id, long millis) { 230 appendToLog("Wait"); 231 try { 232 rawMonitorWait(id, millis); 233 } catch (RuntimeException e) { 234 appendToLog(e.getMessage()); 235 } 236 } 237 rawNotify(long id)238 private static void rawNotify(long id) { 239 appendToLog("Notify"); 240 try { 241 rawMonitorNotify(id); 242 } catch (RuntimeException e) { 243 appendToLog(e.getMessage()); 244 } 245 } 246 rawNotifyAll(long id)247 private static void rawNotifyAll(long id) { 248 appendToLog("NotifyAll"); 249 try { 250 rawMonitorNotifyAll(id); 251 } catch (RuntimeException e) { 252 appendToLog(e.getMessage()); 253 } 254 } 255 appendToLog(String s)256 private static synchronized void appendToLog(String s) { 257 output.add(s); 258 } 259 startWatchdog()260 private static void startWatchdog() { 261 Runnable r = new Runnable() { 262 @Override 263 public void run() { 264 long start = System.currentTimeMillis(); 265 // Give it a minute. 266 long end = 60 * 1000 + start; 267 for (;;) { 268 long delta = end - System.currentTimeMillis(); 269 if (delta <= 0) { 270 break; 271 } 272 273 try { 274 Thread.currentThread().sleep(delta); 275 } catch (Exception e) { 276 } 277 } 278 System.out.println("TIMEOUT!"); 279 System.exit(1); 280 } 281 }; 282 Thread t = new Thread(r); 283 t.setDaemon(true); 284 t.start(); 285 } 286 287 static volatile long sharedId; 288 static List<String> output; 289 static Thread firstAwakened; 290 createRawMonitor()291 private static native long createRawMonitor(); destroyRawMonitor(long id)292 private static native void destroyRawMonitor(long id); rawMonitorEnter(long id)293 private static native void rawMonitorEnter(long id); rawMonitorExit(long id)294 private static native void rawMonitorExit(long id); rawMonitorWait(long id, long millis)295 private static native void rawMonitorWait(long id, long millis); rawMonitorNotify(long id)296 private static native void rawMonitorNotify(long id); rawMonitorNotifyAll(long id)297 private static native void rawMonitorNotifyAll(long id); 298 } 299