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 Object.wait/notifyAll
 27  * @modules java.base/java.lang:+open
 28  * @library /test/lib
 29  * @run junit MonitorWaitNotify
 30  */
 31 
 32 import java.util.concurrent.Semaphore;
 33 
 34 import jdk.test.lib.thread.VThreadRunner;
 35 import org.junit.jupiter.api.Test;
 36 import static org.junit.jupiter.api.Assertions.*;
 37 
 38 class MonitorWaitNotify {
 39 
 40     /**
 41      * Test virtual thread waits, notified by platform thread.
 42      */
 43     @Test
 44     void testWaitNotify1() throws Exception {
 45         var lock = new Object();
 46         var ready = new Semaphore(0);
 47         var thread = Thread.ofVirtual().start(() -> {
 48             synchronized (lock) {
 49                 ready.release();
 50                 try {
 51                     lock.wait();
 52                 } catch (InterruptedException e) { }
 53             }
 54         });
 55         // thread invokes notify
 56         ready.acquire();
 57         synchronized (lock) {
 58             lock.notifyAll();
 59         }
 60         thread.join();
 61     }
 62 
 63     /**
 64      * Test platform thread waits, notified by virtual thread.
 65      */
 66     @Test
 67     void testWaitNotify2() throws Exception {
 68         var lock = new Object();
 69         var ready = new Semaphore(0);
 70         var thread = Thread.ofVirtual().start(() -> {
 71             ready.acquireUninterruptibly();
 72             synchronized (lock) {
 73                 lock.notifyAll();
 74             }
 75         });
 76         synchronized (lock) {
 77             ready.release();
 78             lock.wait();
 79         }
 80         thread.join();
 81     }
 82 
 83     /**
 84      * Test virtual thread waits, notified by another virtual thread.
 85      */
 86     @Test
 87     void testWaitNotify3() throws Exception {
 88         // need at least two carrier threads due to pinning
 89         int previousParallelism = VThreadRunner.ensureParallelism(2);
 90         try {
 91             var lock = new Object();
 92             var ready = new Semaphore(0);
 93             var thread1 = Thread.ofVirtual().start(() -> {
 94                 synchronized (lock) {
 95                     ready.release();
 96                     try {
 97                         lock.wait();
 98                     } catch (InterruptedException e) { }
 99                 }
100             });
101             var thread2 = Thread.ofVirtual().start(() -> {
102                 ready.acquireUninterruptibly();
103                 synchronized (lock) {
104                     lock.notifyAll();
105                 }
106             });
107             thread1.join();
108             thread2.join();
109         } finally {
110             // restore
111             VThreadRunner.setParallelism(previousParallelism);
112         }
113     }
114 
115     /**
116      * Test interrupt status set when calling Object.wait.
117      */
118     @Test
119     void testWaitNotify4() throws Exception {
120         VThreadRunner.run(() -> {
121             Thread t = Thread.currentThread();
122             t.interrupt();
123             Object lock = new Object();
124             synchronized (lock) {
125                 try {
126                     lock.wait();
127                     fail();
128                 } catch (InterruptedException e) {
129                     // interrupt status should be cleared
130                     assertFalse(t.isInterrupted());
131                 }
132             }
133         });
134     }
135 
136     /**
137      * Test interrupt when blocked in Object.wait.
138      */
139     @Test
140     void testWaitNotify5() throws Exception {
141         VThreadRunner.run(() -> {
142             Thread t = Thread.currentThread();
143             scheduleInterrupt(t, 1000);
144             Object lock = new Object();
145             synchronized (lock) {
146                 try {
147                     lock.wait();
148                     fail();
149                 } catch (InterruptedException e) {
150                     // interrupt status should be cleared
151                     assertFalse(t.isInterrupted());
152                 }
153             }
154         });
155     }
156 
157     /**
158      * Schedule a thread to be interrupted after a delay.
159      */
160     private static void scheduleInterrupt(Thread thread, long delay) {
161         Runnable interruptTask = () -> {
162             try {
163                 Thread.sleep(delay);
164                 thread.interrupt();
165             } catch (Exception e) {
166                 e.printStackTrace();
167             }
168         };
169         new Thread(interruptTask).start();
170     }
171 }