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 Object.wait/notifyAll
27 * @modules java.base/java.lang:+open jdk.management
28 * @library /test/lib
29 * @run junit/othervm --enable-native-access=ALL-UNNAMED MonitorWaitNotify
30 */
31
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Set;
35 import java.util.concurrent.CountDownLatch;
36 import java.util.concurrent.Executors;
37 import java.util.concurrent.ExecutorService;
38 import java.util.concurrent.ThreadFactory;
39 import java.util.concurrent.TimeUnit;
40 import java.util.concurrent.atomic.AtomicBoolean;
41 import java.util.concurrent.atomic.AtomicInteger;
42 import java.util.concurrent.atomic.AtomicReference;
43 import java.util.concurrent.locks.LockSupport;
44 import java.util.stream.IntStream;
45 import java.util.stream.Stream;
46 import java.util.stream.Collectors;
47
48 import jdk.test.lib.thread.VThreadScheduler;
49 import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management
50 import jdk.test.lib.thread.VThreadPinner;
51 import org.junit.jupiter.api.Test;
52 import org.junit.jupiter.api.BeforeAll;
53 import org.junit.jupiter.params.ParameterizedTest;
54 import org.junit.jupiter.params.provider.ValueSource;
55 import static org.junit.jupiter.api.Assertions.*;
56 import static org.junit.jupiter.api.Assumptions.*;
57
58 class MonitorWaitNotify {
59
60 @BeforeAll
61 static void setup() {
62 // need >=2 carriers for testing pinning
63 VThreadRunner.ensureParallelism(2);
64 }
65
66 /**
67 * Test virtual thread waits, notified by platform thread.
68 */
69 @ParameterizedTest
70 @ValueSource(booleans = { true, false })
71 void testWaitNotify1(boolean pinned) throws Exception {
72 var lock = new Object();
73 var ready = new AtomicBoolean();
186 });
187
188 try {
189 // wait for thread to start and wait
190 awaitTrue(ready);
191 Thread.State expectedState = timeout > 0
192 ? Thread.State.TIMED_WAITING
193 : Thread.State.WAITING;
194 await(thread, expectedState);
195
196 // poll thread state again, it should still be waiting
197 Thread.sleep(10);
198 assertEquals(thread.getState(), expectedState);
199 } finally {
200 synchronized (lock) {
201 lock.notifyAll();
202 }
203 thread.join();
204 }
205 }
206 /**
207 * Test duration of timed Object.wait.
208 */
209 @Test
210 void testTimedWaitDuration1() throws Exception {
211 var lock = new Object();
212
213 var durationRef = new AtomicReference<Long>();
214 var thread = Thread.ofVirtual().start(() -> {
215 try {
216 synchronized (lock) {
217 long start = millisTime();
218 lock.wait(2000);
219 durationRef.set(millisTime() - start);
220 }
221 } catch (InterruptedException e) { }
222 });
223
224 thread.join();
225
472 // wait for thread to start and wait
473 started.await();
474 await(vthread, Thread.State.WAITING);
475
476 // wakeup thread
477 synchronized (lock) {
478 lock.notifyAll();
479 }
480
481 // thread should park
482 readyToPark.await();
483 await(vthread, Thread.State.WAITING);
484
485 LockSupport.unpark(vthread);
486
487 // thread should terminate
488 vthread.join();
489 assertTrue(completed.get());
490 }
491
492 /**
493 * Test that wait(long) throws IAE when timeout is negative.
494 */
495 @Test
496 void testIllegalArgumentException() throws Exception {
497 VThreadRunner.run(() -> {
498 Object obj = new Object();
499 synchronized (obj) {
500 assertThrows(IllegalArgumentException.class, () -> obj.wait(-1L));
501 assertThrows(IllegalArgumentException.class, () -> obj.wait(-1000L));
502 assertThrows(IllegalArgumentException.class, () -> obj.wait(Long.MIN_VALUE));
503 }
504 });
505 }
506
507 /**
508 * Test that wait throws IMSE when not owner.
509 */
510 @Test
511 void testIllegalMonitorStateException() throws Exception {
|
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 Object.wait/notifyAll
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/othervm --enable-native-access=ALL-UNNAMED MonitorWaitNotify
32 */
33
34 /*
35 * @test id=LM_LEGACY
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 -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
41 */
42
43 /*
44 * @test id=LM_LIGHTWEIGHT
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 -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
50 */
51
52 /*
53 * @test id=Xint-LM_LEGACY
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 -Xint -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
59 */
60
61 /*
62 * @test id=Xint-LM_LIGHTWEIGHT
63 * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
64 * @modules java.base/java.lang:+open jdk.management
65 * @library /test/lib
66 * @build LockingMode
67 * @run junit/othervm -Xint -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
68 */
69
70 /*
71 * @test id=Xcomp-LM_LEGACY
72 * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
73 * @modules java.base/java.lang:+open jdk.management
74 * @library /test/lib
75 * @build LockingMode
76 * @run junit/othervm -Xcomp -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
77 */
78
79 /*
80 * @test id=Xcomp-LM_LIGHTWEIGHT
81 * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
82 * @modules java.base/java.lang:+open jdk.management
83 * @library /test/lib
84 * @build LockingMode
85 * @run junit/othervm -Xcomp -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
86 */
87
88 /*
89 * @test id=Xcomp-TieredStopAtLevel1-LM_LEGACY
90 * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
91 * @modules java.base/java.lang:+open jdk.management
92 * @library /test/lib
93 * @build LockingMode
94 * @run junit/othervm -Xcomp -XX:TieredStopAtLevel=1 -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
95 */
96
97 /*
98 * @test id=Xcomp-TieredStopAtLevel1-LM_LIGHTWEIGHT
99 * @modules java.base/java.lang:+open jdk.management
100 * @library /test/lib
101 * @build LockingMode
102 * @run junit/othervm -Xcomp -XX:TieredStopAtLevel=1 -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
103 */
104
105 /*
106 * @test id=Xcomp-noTieredCompilation-LM_LEGACY
107 * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
108 * @modules java.base/java.lang:+open jdk.management
109 * @library /test/lib
110 * @build LockingMode
111 * @run junit/othervm -Xcomp -XX:-TieredCompilation -XX:LockingMode=1 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
112 */
113
114 /*
115 * @test id=Xcomp-noTieredCompilation-LM_LIGHTWEIGHT
116 * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
117 * @modules java.base/java.lang:+open jdk.management
118 * @library /test/lib
119 * @build LockingMode
120 * @run junit/othervm -Xcomp -XX:-TieredCompilation -XX:LockingMode=2 --enable-native-access=ALL-UNNAMED MonitorWaitNotify
121 */
122
123 import java.util.ArrayList;
124 import java.util.List;
125 import java.util.Set;
126 import java.util.concurrent.CountDownLatch;
127 import java.util.concurrent.Executors;
128 import java.util.concurrent.ExecutorService;
129 import java.util.concurrent.ThreadFactory;
130 import java.util.concurrent.TimeUnit;
131 import java.util.concurrent.atomic.AtomicBoolean;
132 import java.util.concurrent.atomic.AtomicInteger;
133 import java.util.concurrent.atomic.AtomicReference;
134 import java.util.concurrent.locks.LockSupport;
135 import java.util.stream.IntStream;
136 import java.util.stream.Stream;
137 import java.util.stream.Collectors;
138
139 import jdk.test.lib.thread.VThreadScheduler;
140 import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management
141 import jdk.test.lib.thread.VThreadPinner;
142 import org.junit.jupiter.api.Test;
143 import org.junit.jupiter.api.BeforeAll;
144 import org.junit.jupiter.api.condition.DisabledIf;
145 import org.junit.jupiter.params.provider.Arguments;
146 import org.junit.jupiter.params.ParameterizedTest;
147 import org.junit.jupiter.params.provider.MethodSource;
148 import org.junit.jupiter.params.provider.ValueSource;
149 import static org.junit.jupiter.api.Assertions.*;
150 import static org.junit.jupiter.api.Assumptions.*;
151
152 class MonitorWaitNotify {
153
154 @BeforeAll
155 static void setup() {
156 // need >=2 carriers for testing pinning
157 VThreadRunner.ensureParallelism(2);
158 }
159
160 /**
161 * Test virtual thread waits, notified by platform thread.
162 */
163 @ParameterizedTest
164 @ValueSource(booleans = { true, false })
165 void testWaitNotify1(boolean pinned) throws Exception {
166 var lock = new Object();
167 var ready = new AtomicBoolean();
280 });
281
282 try {
283 // wait for thread to start and wait
284 awaitTrue(ready);
285 Thread.State expectedState = timeout > 0
286 ? Thread.State.TIMED_WAITING
287 : Thread.State.WAITING;
288 await(thread, expectedState);
289
290 // poll thread state again, it should still be waiting
291 Thread.sleep(10);
292 assertEquals(thread.getState(), expectedState);
293 } finally {
294 synchronized (lock) {
295 lock.notifyAll();
296 }
297 thread.join();
298 }
299 }
300
301 /**
302 * Returns a stream of elements that are ordered pairs of platform and virtual thread
303 * counts. 0,2,4,..8 platform threads. 2,4,6,..16 virtual threads.
304 */
305 static Stream<Arguments> threadCounts() {
306 return IntStream.range(0, 9)
307 .filter(i -> i % 2 == 0)
308 .mapToObj(i -> i)
309 .flatMap(np -> IntStream.range(2, 17)
310 .filter(i -> i % 2 == 0)
311 .mapToObj(vp -> Arguments.of(np, vp)));
312 }
313
314 /**
315 * Test notify wakes only one thread when platform and virtual threads are waiting.
316 */
317 @ParameterizedTest
318 @MethodSource("threadCounts")
319 @DisabledIf("LockingMode#isLegacy")
320 void testNotifyOneThread(int nPlatformThreads, int nVirtualThreads) throws Exception {
321 int nThreads = nPlatformThreads + nVirtualThreads;
322
323 var lock = new Object();
324 var ready = new CountDownLatch(nThreads);
325 var notified = new AtomicInteger();
326
327 Runnable waitTask = () -> {
328 synchronized (lock) {
329 try {
330 ready.countDown();
331 lock.wait();
332 notified.incrementAndGet();
333 } catch (InterruptedException e) { }
334 }
335 };
336
337 var threads = new ArrayList<Thread>();
338 try {
339 for (int i = 0; i < nPlatformThreads; i++) {
340 threads.add(Thread.ofPlatform().start(waitTask));
341 }
342 for (int i = 0; i < nVirtualThreads; i++) {
343 threads.add(Thread.ofVirtual().start(waitTask));
344 }
345
346 // wait for all threads to wait
347 ready.await();
348
349 // wake threads, one by one
350 for (int i = 0; i < threads.size(); i++) {
351
352 // wake one thread
353 synchronized (lock) {
354 lock.notify();
355 }
356
357 // one thread should have awoken
358 int expectedWakeups = i + 1;
359 while (notified.get() < expectedWakeups) {
360 Thread.sleep(10);
361 }
362 assertEquals(expectedWakeups, notified.get());
363 }
364 } finally {
365 for (Thread t : threads) {
366 t.interrupt();
367 t.join();
368 }
369 }
370 }
371
372 /**
373 * Test notifyAll wakes all threads.
374 */
375 @ParameterizedTest
376 @MethodSource("threadCounts")
377 @DisabledIf("LockingMode#isLegacy")
378 void testNotifyAllThreads(int nPlatformThreads, int nVirtualThreads) throws Exception {
379 int nThreads = nPlatformThreads + nVirtualThreads;
380
381 var lock = new Object();
382 var ready = new CountDownLatch(nThreads);
383 var notified = new CountDownLatch(nThreads);
384
385 Runnable waitTask = () -> {
386 synchronized (lock) {
387 try {
388 ready.countDown();
389 lock.wait();
390 notified.countDown();
391 } catch (InterruptedException e) { }
392 }
393 };
394
395 var threads = new ArrayList<Thread>();
396 try {
397 for (int i = 0; i < nPlatformThreads; i++) {
398 threads.add(Thread.ofPlatform().start(waitTask));
399 }
400 for (int i = 0; i < nVirtualThreads; i++) {
401 threads.add(Thread.ofVirtual().start(waitTask));
402 }
403
404 // wait for all threads to wait
405 ready.await();
406
407 // wakeup all threads
408 synchronized (lock) {
409 lock.notifyAll();
410 }
411
412 // wait for all threads to have awoken
413 notified.await();
414
415 } finally {
416 for (Thread t : threads) {
417 t.interrupt();
418 t.join();
419 }
420 }
421 }
422
423 /**
424 * Test duration of timed Object.wait.
425 */
426 @Test
427 void testTimedWaitDuration1() throws Exception {
428 var lock = new Object();
429
430 var durationRef = new AtomicReference<Long>();
431 var thread = Thread.ofVirtual().start(() -> {
432 try {
433 synchronized (lock) {
434 long start = millisTime();
435 lock.wait(2000);
436 durationRef.set(millisTime() - start);
437 }
438 } catch (InterruptedException e) { }
439 });
440
441 thread.join();
442
689 // wait for thread to start and wait
690 started.await();
691 await(vthread, Thread.State.WAITING);
692
693 // wakeup thread
694 synchronized (lock) {
695 lock.notifyAll();
696 }
697
698 // thread should park
699 readyToPark.await();
700 await(vthread, Thread.State.WAITING);
701
702 LockSupport.unpark(vthread);
703
704 // thread should terminate
705 vthread.join();
706 assertTrue(completed.get());
707 }
708
709 /**
710 * Test that Object.wait releases the carrier. This test uses a custom scheduler
711 * with one carrier thread.
712 */
713 @ParameterizedTest
714 @ValueSource(ints = { 0, 30000, Integer.MAX_VALUE })
715 @DisabledIf("LockingMode#isLegacy")
716 void testReleaseWhenWaiting1(int timeout) throws Exception {
717 assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers");
718 try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) {
719 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler);
720
721 var lock = new Object();
722 var ready = new AtomicBoolean();
723 var completed = new AtomicBoolean();
724
725 var vthread1 = factory.newThread(() -> {
726 synchronized (lock) {
727 try {
728 ready.set(true);
729 if (timeout > 0) {
730 lock.wait(timeout);
731 } else {
732 lock.wait();
733 }
734 } catch (InterruptedException e) {
735 fail("wait interrupted");
736 }
737 }
738 completed.set(true);
739 });
740 vthread1.start();
741
742 // wait for vthread1 to start and wait
743 awaitTrue(ready);
744 await(vthread1, timeout > 0 ? Thread.State.TIMED_WAITING : Thread.State.WAITING);
745
746 // carrier should be released, use it for another thread
747 var executed = new AtomicBoolean();
748 var vthread2 = factory.newThread(() -> {
749 executed.set(true);
750 });
751 vthread2.start();
752 vthread2.join();
753 assertTrue(executed.get());
754
755 // wakeup vthread1
756 synchronized (lock) {
757 lock.notifyAll();
758 }
759
760 vthread1.join();
761 assertTrue(completed.get());
762 }
763 }
764
765 /**
766 * Test that Object.wait releases the carrier. This test arranges for 4*ncores - 1
767 * virtual threads to wait. For long timeout and no timeout cases, all virtual threads
768 * will wait until they are notified.
769 */
770 @ParameterizedTest
771 @ValueSource(ints = { 0, 10, 20, 100, 500, 30000, Integer.MAX_VALUE })
772 @DisabledIf("LockingMode#isLegacy")
773 void testReleaseWhenWaiting2(int timeout) throws Exception {
774 int VTHREAD_COUNT = 4 * Runtime.getRuntime().availableProcessors();
775 CountDownLatch latch = new CountDownLatch(VTHREAD_COUNT);
776 Object lock = new Object();
777 AtomicInteger counter = new AtomicInteger(0);
778
779 for (int i = 0; i < VTHREAD_COUNT; i++) {
780 Thread.ofVirtual().name("vthread-" + i).start(() -> {
781 synchronized (lock) {
782 if (counter.incrementAndGet() == VTHREAD_COUNT) {
783 lock.notifyAll();
784 } else {
785 try {
786 if (timeout > 0) {
787 lock.wait(timeout);
788 } else {
789 lock.wait();
790 }
791 } catch (InterruptedException e) {}
792 }
793 }
794 latch.countDown();
795 });
796 }
797 latch.await();
798 }
799
800 /**
801 * Test that wait(long) throws IAE when timeout is negative.
802 */
803 @Test
804 void testIllegalArgumentException() throws Exception {
805 VThreadRunner.run(() -> {
806 Object obj = new Object();
807 synchronized (obj) {
808 assertThrows(IllegalArgumentException.class, () -> obj.wait(-1L));
809 assertThrows(IllegalArgumentException.class, () -> obj.wait(-1000L));
810 assertThrows(IllegalArgumentException.class, () -> obj.wait(Long.MIN_VALUE));
811 }
812 });
813 }
814
815 /**
816 * Test that wait throws IMSE when not owner.
817 */
818 @Test
819 void testIllegalMonitorStateException() throws Exception {
|