Home | History | Annotate | Download | only in core
      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