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));
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 }
|
1 /*
2 * Copyright (c) 2019, 2024, 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 id=default
26 * @summary Test virtual threads using park/unpark
27 * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
28 * @modules java.base/java.lang:+open jdk.management
29 * @library /test/lib
30 * @build LockingMode
31 * @run junit Parking
32 */
33
34 /*
35 * @test id=Xint
36 * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
37 * @modules java.base/java.lang:+open jdk.management
38 * @library /test/lib
39 * @build LockingMode
40 * @run junit/othervm -Xint Parking
41 */
42
43 /*
44 * @test id=Xcomp
45 * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
46 * @modules java.base/java.lang:+open jdk.management
47 * @library /test/lib
48 * @build LockingMode
49 * @run junit/othervm -Xcomp Parking
50 */
51
52 /*
53 * @test id=Xcomp-noTieredCompilation
54 * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
55 * @modules java.base/java.lang:+open jdk.management
56 * @library /test/lib
57 * @build LockingMode
58 * @run junit/othervm -Xcomp -XX:-TieredCompilation Parking
59 */
60
61 import java.time.Duration;
62 import java.util.concurrent.Executors;
63 import java.util.concurrent.ExecutorService;
64 import java.util.concurrent.CountDownLatch;
65 import java.util.concurrent.ThreadFactory;
66 import java.util.concurrent.TimeUnit;
67 import java.util.concurrent.atomic.AtomicBoolean;
68 import java.util.concurrent.locks.LockSupport;
69
70 import jdk.test.lib.thread.VThreadRunner;
71 import jdk.test.lib.thread.VThreadScheduler;
72 import org.junit.jupiter.api.Test;
73 import org.junit.jupiter.api.condition.DisabledIf;
74 import org.junit.jupiter.params.ParameterizedTest;
75 import org.junit.jupiter.params.provider.ValueSource;
76 import static org.junit.jupiter.api.Assertions.*;
77 import static org.junit.jupiter.api.Assumptions.*;
78
79 class Parking {
80 static final int MAX_VTHREAD_COUNT = 4 * Runtime.getRuntime().availableProcessors();
81 static final Object lock = new Object();
82
83 /**
84 * Park, unparked by platform thread.
85 */
86 @Test
87 void testPark1() throws Exception {
88 var thread = Thread.ofVirtual().start(LockSupport::park);
89 Thread.sleep(1000); // give time for virtual thread to park
90 LockSupport.unpark(thread);
91 thread.join();
92 }
93
94 /**
95 * Park, unparked by virtual thread.
96 */
97 @Test
98 void testPark2() throws Exception {
99 var thread1 = Thread.ofVirtual().start(LockSupport::park);
100 Thread.sleep(1000); // give time for virtual thread to park
101 var thread2 = Thread.ofVirtual().start(() -> LockSupport.unpark(thread1));
367 assertTrue(t.isInterrupted());
368 });
369 }
370
371 /**
372 * Thread interrupt when parked in parkNanos and while holding monitor.
373 */
374 @Test
375 void testParkNanos11() throws Exception {
376 VThreadRunner.run(() -> {
377 Thread t = Thread.currentThread();
378 scheduleInterrupt(t, 1000);
379 while (!Thread.currentThread().isInterrupted()) {
380 synchronized (lock) {
381 LockSupport.parkNanos(Duration.ofDays(1).toNanos());
382 }
383 }
384 });
385 }
386
387 /**
388 * Test that parking while holding a monitor releases the carrier.
389 */
390 @ParameterizedTest
391 @ValueSource(booleans = { true, false })
392 @DisabledIf("LockingMode#isLegacy")
393 void testParkWhenHoldingMonitor(boolean reenter) throws Exception {
394 assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers");
395 try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) {
396 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler);
397
398 var lock = new Object();
399
400 // thread enters (and maybe reenters) a monitor and parks
401 var started = new CountDownLatch(1);
402 var vthread1 = factory.newThread(() -> {
403 started.countDown();
404 synchronized (lock) {
405 if (reenter) {
406 synchronized (lock) {
407 LockSupport.park();
408 }
409 } else {
410 LockSupport.park();
411 }
412 }
413 });
414
415 vthread1.start();
416 try {
417 // wait for thread to start and park
418 started.await();
419 await(vthread1, Thread.State.WAITING);
420
421 // carrier should be released, use it for another thread
422 var executed = new AtomicBoolean();
423 var vthread2 = factory.newThread(() -> {
424 executed.set(true);
425 });
426 vthread2.start();
427 vthread2.join();
428 assertTrue(executed.get());
429 } finally {
430 LockSupport.unpark(vthread1);
431 vthread1.join();
432 }
433 }
434 }
435
436 /**
437 * Test lots of virtual threads parked while holding a monitor. If the number of
438 * virtual threads exceeds the number of carrier threads then this test will hang if
439 * parking doesn't release the carrier.
440 */
441 @Test
442 @DisabledIf("LockingMode#isLegacy")
443 void testManyParkedWhenHoldingMonitor() throws Exception {
444 Thread[] vthreads = new Thread[MAX_VTHREAD_COUNT];
445 var done = new AtomicBoolean();
446 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) {
447 var lock = new Object();
448 var started = new CountDownLatch(1);
449 var vthread = Thread.ofVirtual().start(() -> {
450 started.countDown();
451 synchronized (lock) {
452 while (!done.get()) {
453 LockSupport.park();
454 }
455 }
456 });
457 // wait for thread to start and park
458 started.await();
459 await(vthread, Thread.State.WAITING);
460 vthreads[i] = vthread;
461 }
462
463 // cleanup
464 done.set(true);
465 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) {
466 var vthread = vthreads[i];
467 LockSupport.unpark(vthread);
468 vthread.join();
469 }
470 }
471
472 /**
473 * Schedule a thread to be interrupted after a delay.
474 */
475 private static void scheduleInterrupt(Thread thread, long delay) {
476 Runnable interruptTask = () -> {
477 try {
478 Thread.sleep(delay);
479 thread.interrupt();
480 } catch (Exception e) {
481 e.printStackTrace();
482 }
483 };
484 new Thread(interruptTask).start();
485 }
486
487 /**
488 * Waits for the given thread to reach a given state.
489 */
490 private void await(Thread thread, Thread.State expectedState) throws InterruptedException {
491 Thread.State state = thread.getState();
492 while (state != expectedState) {
493 assertTrue(state != Thread.State.TERMINATED, "Thread has terminated");
494 Thread.sleep(10);
495 state = thread.getState();
496 }
497 }
498 }
|