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