1 /* 2 * Copyright (C) 2007 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 android.core; 18 19 import junit.framework.TestCase; 20 import android.test.suitebuilder.annotation.MediumTest; 21 import android.test.suitebuilder.annotation.LargeTest; 22 import android.test.suitebuilder.annotation.SmallTest; 23 24 public class MonitorTest extends TestCase { 25 26 @MediumTest 27 public void testWaitArgumentsTest() throws Exception { 28 /* Try some valid arguments. These should all 29 * return very quickly. 30 */ 31 try { 32 synchronized (this) { 33 /* millisecond version */ 34 wait(1); 35 wait(10); 36 37 /* millisecond + nanosecond version */ 38 wait(0, 1); 39 wait(0, 999999); 40 wait(1, 1); 41 wait(1, 999999); 42 } 43 } catch (InterruptedException ex) { 44 throw new RuntimeException("good Object.wait() interrupted", 45 ex); 46 } catch (Exception ex) { 47 throw new RuntimeException("Unexpected exception when calling" + 48 "Object.wait() with good arguments", ex); 49 } 50 51 /* Try some invalid arguments. 52 */ 53 boolean sawException = false; 54 try { 55 synchronized (this) { 56 wait(-1); 57 } 58 } catch (InterruptedException ex) { 59 throw new RuntimeException("bad Object.wait() interrupted", ex); 60 } catch (IllegalArgumentException ex) { 61 sawException = true; 62 } catch (Exception ex) { 63 throw new RuntimeException("Unexpected exception when calling" + 64 "Object.wait() with bad arguments", ex); 65 } 66 if (!sawException) { 67 throw new RuntimeException("bad call to Object.wait() should " + 68 "have thrown IllegalArgumentException"); 69 } 70 71 sawException = false; 72 try { 73 synchronized (this) { 74 wait(0, -1); 75 } 76 } catch (InterruptedException ex) { 77 throw new RuntimeException("bad Object.wait() interrupted", ex); 78 } catch (IllegalArgumentException ex) { 79 sawException = true; 80 } catch (Exception ex) { 81 throw new RuntimeException("Unexpected exception when calling" + 82 "Object.wait() with bad arguments", ex); 83 } 84 if (!sawException) { 85 throw new RuntimeException("bad call to Object.wait() should " + 86 "have thrown IllegalArgumentException"); 87 } 88 89 sawException = false; 90 try { 91 synchronized (this) { 92 /* The legal range of nanos is 0-999999. */ 93 wait(0, 1000000); 94 } 95 } catch (InterruptedException ex) { 96 throw new RuntimeException("bad Object.wait() interrupted", ex); 97 } catch (IllegalArgumentException ex) { 98 sawException = true; 99 } catch (Exception ex) { 100 throw new RuntimeException("Unexpected exception when calling" + 101 "Object.wait() with bad arguments", ex); 102 } 103 if (!sawException) { 104 throw new RuntimeException("bad call to Object.wait() should " + 105 "have thrown IllegalArgumentException"); 106 } 107 } 108 109 private class Interrupter extends Thread { 110 Waiter waiter; 111 112 Interrupter(String name, Waiter waiter) { 113 super(name); 114 this.waiter = waiter; 115 } 116 117 public void run() { 118 try { 119 run_inner(); 120 } catch (Throwable t) { 121 MonitorTest.errorException = t; 122 MonitorTest.testThread.interrupt(); 123 } 124 } 125 126 void run_inner() { 127 waiter.spin = true; 128 // System.out.println("InterruptTest: starting waiter"); 129 waiter.start(); 130 131 try { 132 Thread.currentThread().sleep(500); 133 } catch (InterruptedException ex) { 134 throw new RuntimeException("Test sleep interrupted.", ex); 135 } 136 137 /* Waiter is spinning, and its monitor should still be thin. 138 */ 139 // System.out.println("Test interrupting waiter"); 140 waiter.interrupt(); 141 waiter.spin = false; 142 143 for (int i = 0; i < 3; i++) { 144 /* Wait for the waiter to start waiting. 145 */ 146 synchronized (waiter.interrupterLock) { 147 try { 148 waiter.interrupterLock.wait(); 149 } catch (InterruptedException ex) { 150 throw new RuntimeException("Test wait interrupted.", ex); 151 } 152 } 153 154 /* Before interrupting, grab the waiter lock, which 155 * guarantees that the waiter is already sitting in wait(). 156 */ 157 synchronized (waiter) { 158 //System.out.println("Test interrupting waiter (" + i + ")"); 159 waiter.interrupt(); 160 } 161 } 162 163 // System.out.println("Test waiting for waiter to die."); 164 try { 165 waiter.join(); 166 } catch (InterruptedException ex) { 167 throw new RuntimeException("Test join interrupted.", ex); 168 } 169 // System.out.println("InterruptTest done."); 170 } 171 } 172 173 private class Waiter extends Thread { 174 Object interrupterLock = new Object(); 175 Boolean spin = false; 176 177 Waiter(String name) { 178 super(name); 179 } 180 181 public void run() { 182 try { 183 run_inner(); 184 } catch (Throwable t) { 185 MonitorTest.errorException = t; 186 MonitorTest.testThread.interrupt(); 187 } 188 } 189 190 void run_inner() { 191 // System.out.println("Waiter spinning"); 192 while (spin) { 193 // We're going to get interrupted while we spin. 194 } 195 if (interrupted()) { 196 // System.out.println("Waiter done spinning; interrupted."); 197 } else { 198 throw new RuntimeException("Thread not interrupted " + 199 "during spin"); 200 } 201 202 synchronized (this) { 203 Boolean sawEx = false; 204 205 try { 206 synchronized (interrupterLock) { 207 interrupterLock.notify(); 208 } 209 // System.out.println("Waiter calling wait()"); 210 this.wait(); 211 } catch (InterruptedException ex) { 212 sawEx = true; 213 // System.out.println("wait(): Waiter caught " + ex); 214 } 215 // System.out.println("wait() finished"); 216 217 if (!sawEx) { 218 throw new RuntimeException("Thread not interrupted " + 219 "during wait()"); 220 } 221 } 222 synchronized (this) { 223 Boolean sawEx = false; 224 225 try { 226 synchronized (interrupterLock) { 227 interrupterLock.notify(); 228 } 229 // System.out.println("Waiter calling wait(1000)"); 230 this.wait(1000); 231 } catch (InterruptedException ex) { 232 sawEx = true; 233 // System.out.println("wait(1000): Waiter caught " + ex); 234 } 235 // System.out.println("wait(1000) finished"); 236 237 if (!sawEx) { 238 throw new RuntimeException("Thread not interrupted " + 239 "during wait(1000)"); 240 } 241 } 242 synchronized (this) { 243 Boolean sawEx = false; 244 245 try { 246 synchronized (interrupterLock) { 247 interrupterLock.notify(); 248 } 249 // System.out.println("Waiter calling wait(1000, 5000)"); 250 this.wait(1000, 5000); 251 } catch (InterruptedException ex) { 252 sawEx = true; 253 // System.out.println("wait(1000, 5000): Waiter caught " + ex); 254 } 255 // System.out.println("wait(1000, 5000) finished"); 256 257 if (!sawEx) { 258 throw new RuntimeException("Thread not interrupted " + 259 "during wait(1000, 5000)"); 260 } 261 } 262 263 // System.out.println("Waiter returning"); 264 } 265 } 266 267 private static Throwable errorException; 268 private static Thread testThread; 269 270 // TODO: Flaky test. Add back MediumTest annotation once fixed 271 public void testInterruptTest() throws Exception { 272 273 274 testThread = Thread.currentThread(); 275 errorException = null; 276 277 Waiter waiter = new Waiter("InterruptTest Waiter"); 278 Interrupter interrupter = 279 new Interrupter("InterruptTest Interrupter", waiter); 280 interrupter.start(); 281 282 try { 283 interrupter.join(); 284 waiter.join(); 285 } catch (InterruptedException ex) { 286 throw new RuntimeException("Test join interrupted.", ex); 287 } 288 289 if (errorException != null) { 290 throw new RuntimeException("InterruptTest failed", 291 errorException); 292 } 293 294 295 296 297 } 298 299 private static void deepWait(int depth, Object lock) { 300 synchronized (lock) { 301 if (depth > 0) { 302 deepWait(depth - 1, lock); 303 } else { 304 String threadName = Thread.currentThread().getName(); 305 try { 306 // System.out.println(threadName + " waiting"); 307 lock.wait(); 308 // System.out.println(threadName + " done waiting"); 309 } catch (InterruptedException ex) { 310 // System.out.println(threadName + " interrupted."); 311 } 312 } 313 } 314 } 315 316 private class Worker extends Thread { 317 Object lock; 318 int id; 319 320 Worker(int id, Object lock) { 321 super("Worker(" + id + ")"); 322 this.id = id; 323 this.lock = lock; 324 } 325 326 public void run() { 327 int iterations = 0; 328 329 while (MonitorTest.running) { 330 MonitorTest.deepWait(id, lock); 331 iterations++; 332 } 333 // System.out.println(getName() + " done after " + iterations + " iterations."); 334 } 335 } 336 337 private static Object commonLock = new Object(); 338 private static Boolean running = false; 339 340 341 @LargeTest 342 public void testNestedMonitors() throws Exception { 343 final int NUM_WORKERS = 5; 344 345 Worker w[] = new Worker[NUM_WORKERS]; 346 int i; 347 348 for (i = 0; i < NUM_WORKERS; i++) { 349 w[i] = new Worker(i * 2 - 1, new Object()); 350 } 351 352 running = true; 353 354 // System.out.println("NestedMonitors: starting workers"); 355 for (i = 0; i < NUM_WORKERS; i++) { 356 w[i].start(); 357 } 358 359 try { 360 Thread.currentThread().sleep(1000); 361 } catch (InterruptedException ex) { 362 // System.out.println("Test sleep interrupted."); 363 } 364 365 for (i = 0; i < 100; i++) { 366 for (int j = 0; j < NUM_WORKERS; j++) { 367 synchronized (w[j].lock) { 368 w[j].lock.notify(); 369 } 370 } 371 } 372 373 // System.out.println("NesterMonitors: stopping workers"); 374 running = false; 375 for (i = 0; i < NUM_WORKERS; i++) { 376 synchronized (w[i].lock) { 377 w[i].lock.notifyAll(); 378 } 379 } 380 } 381 382 private static class CompareAndExchange extends Thread { 383 static Object toggleLock = null; 384 static int toggle = -1; 385 static Boolean running = false; 386 387 public void run() { 388 toggleLock = new Object(); 389 toggle = -1; 390 391 Worker w1 = new Worker(0, 1); 392 Worker w2 = new Worker(2, 3); 393 Worker w3 = new Worker(4, 5); 394 Worker w4 = new Worker(6, 7); 395 396 running = true; 397 398 // System.out.println("CompareAndExchange: starting workers"); 399 400 w1.start(); 401 w2.start(); 402 w3.start(); 403 w4.start(); 404 405 try { 406 this.sleep(10000); 407 } catch (InterruptedException ex) { 408 // System.out.println(getName() + " interrupted."); 409 } 410 411 // System.out.println("MonitorTest: stopping workers"); 412 running = false; 413 414 toggleLock = null; 415 } 416 417 class Worker extends Thread { 418 int i1; 419 int i2; 420 421 Worker(int i1, int i2) { 422 super("Worker(" + i1 + ", " + i2 + ")"); 423 this.i1 = i1; 424 this.i2 = i2; 425 } 426 427 public void run() { 428 int iterations = 0; 429 430 /* Latch this because run() may set the static field to 431 * null at some point. 432 */ 433 Object toggleLock = CompareAndExchange.toggleLock; 434 435 // System.out.println(getName() + " running"); 436 try { 437 while (CompareAndExchange.running) { 438 synchronized (toggleLock) { 439 int test; 440 int check; 441 442 if (CompareAndExchange.toggle == i1) { 443 this.sleep(5 + i2); 444 CompareAndExchange.toggle = test = i2; 445 } else { 446 this.sleep(5 + i1); 447 CompareAndExchange.toggle = test = i1; 448 } 449 if ((check = CompareAndExchange.toggle) != test) { 450 // System.out.println("Worker(" + i1 + ", " + 451 // i2 + ") " + "test " + test + 452 // " != toggle " + check); 453 throw new RuntimeException( 454 "locked value changed"); 455 } 456 } 457 458 iterations++; 459 } 460 } catch (InterruptedException ex) { 461 // System.out.println(getName() + " interrupted."); 462 } 463 464 // System.out.println(getName() + " done after " + 465 // iterations + " iterations."); 466 } 467 } 468 } 469 } 470