1 /* 2 * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @summary Test virtual threads using park/unpark 27 * @library /test/lib 28 * @run junit Parking 29 */ 30 31 import java.time.Duration; 32 import java.util.concurrent.TimeUnit; 33 import java.util.concurrent.locks.LockSupport; 34 35 import jdk.test.lib.thread.VThreadRunner; 36 import org.junit.jupiter.api.Test; 37 import static org.junit.jupiter.api.Assertions.*; 38 39 class Parking { 40 private static final Object lock = new Object(); 41 42 /** 43 * Park, unparked by platform thread. 44 */ 45 @Test 46 void testPark1() throws Exception { 47 var thread = Thread.ofVirtual().start(LockSupport::park); 48 Thread.sleep(1000); // give time for virtual thread to park 49 LockSupport.unpark(thread); 50 thread.join(); 51 } 52 53 /** 54 * Park, unparked by virtual thread. 55 */ 56 @Test 57 void testPark2() throws Exception { 58 var thread1 = Thread.ofVirtual().start(LockSupport::park); 59 Thread.sleep(1000); // give time for virtual thread to park 60 var thread2 = Thread.ofVirtual().start(() -> LockSupport.unpark(thread1)); 61 thread1.join(); 62 thread2.join(); 63 } 64 65 /** 66 * Park while holding monitor, unparked by platform thread. 67 */ 68 @Test 69 void testPark3() throws Exception { 70 var thread = Thread.ofVirtual().start(() -> { 71 synchronized (lock) { 72 LockSupport.park(); 73 } 74 }); 75 Thread.sleep(1000); // give time for virtual thread to park 76 LockSupport.unpark(thread); 77 thread.join(); 78 } 79 80 /** 81 * Park with native frame on stack. 82 */ 83 @Test 84 void testPark4() throws Exception { 85 // not implemented 86 } 87 88 /** 89 * Unpark before park. 90 */ 91 @Test 92 void testPark5() throws Exception { 93 var thread = Thread.ofVirtual().start(() -> { 94 LockSupport.unpark(Thread.currentThread()); 95 LockSupport.park(); 96 }); 97 thread.join(); 98 } 99 100 /** 101 * 2 x unpark before park. 102 */ 103 @Test 104 void testPark6() throws Exception { 105 var thread = Thread.ofVirtual().start(() -> { 106 Thread me = Thread.currentThread(); 107 LockSupport.unpark(me); 108 LockSupport.unpark(me); 109 LockSupport.park(); 110 LockSupport.park(); // should park 111 }); 112 Thread.sleep(1000); // give time for thread to park 113 LockSupport.unpark(thread); 114 thread.join(); 115 } 116 117 /** 118 * 2 x park and unpark by platform thread. 119 */ 120 @Test 121 void testPark7() throws Exception { 122 var thread = Thread.ofVirtual().start(() -> { 123 LockSupport.park(); 124 LockSupport.park(); 125 }); 126 127 Thread.sleep(1000); // give time for thread to park 128 129 // unpark, virtual thread should park again 130 LockSupport.unpark(thread); 131 Thread.sleep(1000); 132 assertTrue(thread.isAlive()); 133 134 // let it terminate 135 LockSupport.unpark(thread); 136 thread.join(); 137 } 138 139 /** 140 * Park with interrupt status set. 141 */ 142 @Test 143 void testPark8() throws Exception { 144 VThreadRunner.run(() -> { 145 Thread t = Thread.currentThread(); 146 t.interrupt(); 147 LockSupport.park(); 148 assertTrue(t.isInterrupted()); 149 }); 150 } 151 152 /** 153 * Thread interrupt when parked. 154 */ 155 @Test 156 void testPark9() throws Exception { 157 VThreadRunner.run(() -> { 158 Thread t = Thread.currentThread(); 159 scheduleInterrupt(t, 1000); 160 while (!Thread.currentThread().isInterrupted()) { 161 LockSupport.park(); 162 } 163 }); 164 } 165 166 /** 167 * Park while holding monitor and with interrupt status set. 168 */ 169 @Test 170 void testPark10() throws Exception { 171 VThreadRunner.run(() -> { 172 Thread t = Thread.currentThread(); 173 t.interrupt(); 174 synchronized (lock) { 175 LockSupport.park(); 176 } 177 assertTrue(t.isInterrupted()); 178 }); 179 } 180 181 /** 182 * Thread interrupt when parked while holding monitor 183 */ 184 @Test 185 void testPark11() throws Exception { 186 VThreadRunner.run(() -> { 187 Thread t = Thread.currentThread(); 188 scheduleInterrupt(t, 1000); 189 while (!Thread.currentThread().isInterrupted()) { 190 synchronized (lock) { 191 LockSupport.park(); 192 } 193 } 194 }); 195 } 196 197 /** 198 * parkNanos(-1) completes immediately 199 */ 200 @Test 201 void testParkNanos1() throws Exception { 202 VThreadRunner.run(() -> LockSupport.parkNanos(-1)); 203 } 204 205 /** 206 * parkNanos(0) completes immediately 207 */ 208 @Test 209 void testParkNanos2() throws Exception { 210 VThreadRunner.run(() -> LockSupport.parkNanos(0)); 211 } 212 213 /** 214 * parkNanos(1000ms) parks thread. 215 */ 216 @Test 217 void testParkNanos3() throws Exception { 218 VThreadRunner.run(() -> { 219 // park for 1000ms 220 long nanos = TimeUnit.NANOSECONDS.convert(1000, TimeUnit.MILLISECONDS); 221 long start = System.nanoTime(); 222 LockSupport.parkNanos(nanos); 223 224 // check that virtual thread parked for >= 900ms 225 long elapsed = TimeUnit.MILLISECONDS.convert(System.nanoTime() - start, 226 TimeUnit.NANOSECONDS); 227 assertTrue(elapsed >= 900); 228 }); 229 } 230 231 /** 232 * Park with parkNanos, unparked by platform thread. 233 */ 234 @Test 235 void testParkNanos4() throws Exception { 236 var thread = Thread.ofVirtual().start(() -> { 237 long nanos = TimeUnit.NANOSECONDS.convert(1, TimeUnit.DAYS); 238 LockSupport.parkNanos(nanos); 239 }); 240 Thread.sleep(100); // give time for virtual thread to park 241 LockSupport.unpark(thread); 242 thread.join(); 243 } 244 245 /** 246 * Park with parkNanos, unparked by virtual thread. 247 */ 248 @Test 249 void testParkNanos5() throws Exception { 250 var thread1 = Thread.ofVirtual().start(() -> { 251 long nanos = TimeUnit.NANOSECONDS.convert(1, TimeUnit.DAYS); 252 LockSupport.parkNanos(nanos); 253 }); 254 Thread.sleep(100); // give time for virtual thread to park 255 var thread2 = Thread.ofVirtual().start(() -> LockSupport.unpark(thread1)); 256 thread1.join(); 257 thread2.join(); 258 } 259 260 /** 261 * Unpark before parkNanos. 262 */ 263 @Test 264 void testParkNanos6() throws Exception { 265 VThreadRunner.run(() -> { 266 LockSupport.unpark(Thread.currentThread()); 267 long nanos = TimeUnit.NANOSECONDS.convert(1, TimeUnit.DAYS); 268 LockSupport.parkNanos(nanos); 269 }); 270 } 271 272 /** 273 * Unpark before parkNanos(0), should consume parking permit. 274 */ 275 @Test 276 void testParkNanos7() throws Exception { 277 var thread = Thread.ofVirtual().start(() -> { 278 LockSupport.unpark(Thread.currentThread()); 279 LockSupport.parkNanos(0); // should consume parking permit 280 LockSupport.park(); // should block 281 }); 282 boolean isAlive = thread.join(Duration.ofSeconds(2)); 283 assertTrue(isAlive); 284 LockSupport.unpark(thread); 285 thread.join(); 286 } 287 288 /** 289 * Park with parkNanos and interrupt status set. 290 */ 291 @Test 292 void testParkNanos8() throws Exception { 293 VThreadRunner.run(() -> { 294 Thread t = Thread.currentThread(); 295 t.interrupt(); 296 LockSupport.parkNanos(Duration.ofDays(1).toNanos()); 297 assertTrue(t.isInterrupted()); 298 }); 299 } 300 301 /** 302 * Thread interrupt when parked in parkNanos. 303 */ 304 @Test 305 void testParkNanos9() throws Exception { 306 VThreadRunner.run(() -> { 307 Thread t = Thread.currentThread(); 308 scheduleInterrupt(t, 1000); 309 while (!Thread.currentThread().isInterrupted()) { 310 LockSupport.parkNanos(Duration.ofDays(1).toNanos()); 311 } 312 }); 313 } 314 315 /** 316 * Park with parkNanos while holding monitor and with interrupt status set. 317 */ 318 @Test 319 void testParkNanos10() throws Exception { 320 VThreadRunner.run(() -> { 321 Thread t = Thread.currentThread(); 322 t.interrupt(); 323 synchronized (lock) { 324 LockSupport.parkNanos(Duration.ofDays(1).toNanos()); 325 } 326 assertTrue(t.isInterrupted()); 327 }); 328 } 329 330 /** 331 * Thread interrupt when parked in parkNanos and while holding monitor. 332 */ 333 @Test 334 void testParkNanos11() throws Exception { 335 VThreadRunner.run(() -> { 336 Thread t = Thread.currentThread(); 337 scheduleInterrupt(t, 1000); 338 while (!Thread.currentThread().isInterrupted()) { 339 synchronized (lock) { 340 LockSupport.parkNanos(Duration.ofDays(1).toNanos()); 341 } 342 } 343 }); 344 } 345 346 /** 347 * Schedule a thread to be interrupted after a delay. 348 */ 349 private static void scheduleInterrupt(Thread thread, long delay) { 350 Runnable interruptTask = () -> { 351 try { 352 Thread.sleep(delay); 353 thread.interrupt(); 354 } catch (Exception e) { 355 e.printStackTrace(); 356 } 357 }; 358 new Thread(interruptTask).start(); 359 } 360 }