Home | History | Annotate | Download | only in dalvik
      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 tests.api.org.apache.harmony.kernel.dalvik;
     18 
     19 import java.lang.reflect.Field;
     20 
     21 import junit.framework.Assert;
     22 import junit.framework.TestCase;
     23 import sun.misc.Unsafe;
     24 
     25 /**
     26  * Tests for the <code>park()</code> functionality of {@link Unsafe}.
     27  */
     28 public class ThreadsTest extends TestCase {
     29     private static Unsafe UNSAFE = null;
     30     private static RuntimeException INITIALIZEFAILED = null;
     31 
     32     static {
     33         /*
     34          * Set up {@link #UNSAFE}. This subverts the access check to
     35          * get the unique Unsafe instance. We can do this because
     36          * there's no security manager installed when running the
     37          * test.
     38          */
     39         try {
     40             Field field = Unsafe.class.getDeclaredField("THE_ONE");
     41             field.setAccessible(true);
     42 
     43             UNSAFE = (Unsafe) field.get(null);
     44         } catch (NoSuchFieldException ex) {
     45             INITIALIZEFAILED = new RuntimeException(ex);
     46         } catch (IllegalAccessException ex) {
     47             INITIALIZEFAILED = new RuntimeException(ex);
     48         }
     49     }
     50 
     51     /** Test the case where the park times out. */
     52     public void test_parkFor_1() {
     53         Parker parker = new Parker(false, 500);
     54         Thread parkerThread = new Thread(parker);
     55         Thread waiterThread =
     56             new Thread(new WaitAndUnpark(1000, parkerThread));
     57 
     58         parkerThread.start();
     59         waiterThread.start();
     60         parker.assertDurationIsInRange(500);
     61     }
     62 
     63     /** Test the case where the unpark happens before the timeout. */
     64     public void test_parkFor_2() {
     65         Parker parker = new Parker(false, 1000);
     66         Thread parkerThread = new Thread(parker);
     67         Thread waiterThread =
     68             new Thread(new WaitAndUnpark(300, parkerThread));
     69 
     70         parkerThread.start();
     71         waiterThread.start();
     72         parker.assertDurationIsInRange(300);
     73     }
     74 
     75     /** Test the case where the thread is preemptively unparked. */
     76     public void test_parkFor_3() {
     77         Parker parker = new Parker(false, 1000);
     78         Thread parkerThread = new Thread(parker);
     79 
     80         UNSAFE.unpark(parkerThread);
     81         parkerThread.start();
     82         parker.assertDurationIsInRange(0);
     83     }
     84 
     85     /** Test the case where the park times out. */
     86     public void test_parkUntil_1() {
     87         Parker parker = new Parker(true, 500);
     88         Thread parkerThread = new Thread(parker);
     89         Thread waiterThread =
     90             new Thread(new WaitAndUnpark(1000, parkerThread));
     91 
     92         parkerThread.start();
     93         waiterThread.start();
     94         parker.assertDurationIsInRange(500);
     95     }
     96 
     97     /** Test the case where the unpark happens before the timeout. */
     98     public void test_parkUntil_2() {
     99         Parker parker = new Parker(true, 1000);
    100         Thread parkerThread = new Thread(parker);
    101         Thread waiterThread =
    102             new Thread(new WaitAndUnpark(300, parkerThread));
    103 
    104         parkerThread.start();
    105         waiterThread.start();
    106         parker.assertDurationIsInRange(300);
    107     }
    108 
    109     /** Test the case where the thread is preemptively unparked. */
    110     public void test_parkUntil_3() {
    111         Parker parker = new Parker(true, 1000);
    112         Thread parkerThread = new Thread(parker);
    113 
    114         UNSAFE.unpark(parkerThread);
    115         parkerThread.start();
    116         parker.assertDurationIsInRange(0);
    117     }
    118 
    119     // TODO: Add more tests.
    120 
    121     /**
    122      * Helper <code>Runnable</code> for tests, which parks for or until
    123      * the indicated value, noting the duration of time actually parked.
    124      */
    125     private static class Parker implements Runnable {
    126         /** whether {@link #amount} is milliseconds to wait in an
    127          * absolute fashion (<code>true</code>) or nanoseconds to wait
    128          * in a relative fashion (<code>false</code>) */
    129         private final boolean absolute;
    130 
    131         /** amount to wait (see above) */
    132         private final long amount;
    133 
    134         /** whether the run has completed */
    135         private boolean completed;
    136 
    137         /** recorded start time */
    138         private long startMillis;
    139 
    140         /** recorded end time */
    141         private long endMillis;
    142 
    143         /**
    144          * Construct an instance.
    145          *
    146          * @param absolute whether to use an absolute time or not; in
    147          * either case, this constructor takes a duration to park for
    148          * @param parkMillis the number of milliseconds to be parked
    149          */
    150         public Parker(boolean absolute, long parkMillis) {
    151             this.absolute = absolute;
    152 
    153             // Multiply by 1000000 because parkFor() takes nanoseconds.
    154             this.amount = absolute ? parkMillis : parkMillis * 1000000;
    155         }
    156 
    157         public void run() {
    158             boolean absolute = this.absolute;
    159             long amount = this.amount;
    160             long start = System.currentTimeMillis();
    161 
    162             if (absolute) {
    163                 UNSAFE.park(true, start + amount);
    164             } else {
    165                 UNSAFE.park(false, amount);
    166             }
    167 
    168             long end = System.currentTimeMillis();
    169 
    170             synchronized (this) {
    171                 startMillis = start;
    172                 endMillis = end;
    173                 completed = true;
    174                 notifyAll();
    175             }
    176         }
    177 
    178         /**
    179          * Wait for the test to complete and return the duration.
    180          *
    181          * @param maxWaitMillis the maximum amount of time to
    182          * wait for the test to complete
    183          * @return the duration in milliseconds
    184          */
    185         public long getDurationMillis(long maxWaitMillis) {
    186             synchronized (this) {
    187                 if (! completed) {
    188                     try {
    189                         wait(maxWaitMillis);
    190                     } catch (InterruptedException ex) {
    191                         // Ignore it.
    192                     }
    193                     if (! completed) {
    194                         Assert.fail("parker hanging");
    195                     }
    196                 }
    197 
    198                 return endMillis - startMillis;
    199             }
    200         }
    201 
    202         /**
    203          * Asserts that the actual duration is within 5% of the
    204          * given expected time.
    205          *
    206          * @param expectedMillis the expected duration, in milliseconds
    207          */
    208         public void assertDurationIsInRange(long expectedMillis) {
    209             /*
    210              * Allow a bit more slop for the maximum on "expected
    211              * instantaneous" results.
    212              */
    213             long minimum = (long) ((double) expectedMillis * 0.95);
    214             long maximum =
    215                 Math.max((long) ((double) expectedMillis * 1.05), 10);
    216             long waitMillis = Math.max(expectedMillis * 10, 10);
    217             long duration = getDurationMillis(waitMillis);
    218 
    219             if (duration < minimum) {
    220                 Assert.fail("expected duration: " + expectedMillis +
    221                         "; actual too short: " + duration);
    222             } else if (duration > maximum) {
    223                 Assert.fail("expected duration: " + expectedMillis +
    224                         "; actual too long: " + duration);
    225             }
    226         }
    227     }
    228 
    229     /**
    230      * Helper <code>Runnable</code> for tests, which waits for the
    231      * specified amount of time and then unparks an indicated thread.
    232      */
    233     private static class WaitAndUnpark implements Runnable {
    234         private final long waitMillis;
    235         private final Thread thread;
    236 
    237         public WaitAndUnpark(long waitMillis, Thread thread) {
    238             this.waitMillis = waitMillis;
    239             this.thread = thread;
    240         }
    241 
    242         public void run() {
    243             try {
    244                 Thread.sleep(waitMillis);
    245             } catch (InterruptedException ex) {
    246                 throw new RuntimeException("shouldn't happen", ex);
    247             }
    248 
    249             UNSAFE.unpark(thread);
    250         }
    251     }
    252 
    253     @Override
    254     protected void setUp() throws Exception {
    255         if (INITIALIZEFAILED != null) {
    256             throw INITIALIZEFAILED;
    257         }
    258     }
    259 }
    260