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