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