1 /*
2  * Copyright (c) 2001, 2021, 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  * @bug 4413752 8262881
27  * @summary Test SuspendThread with ObjectMonitor wait.
28  * @requires vm.jvmti
29  * @library /test/lib
30  * @compile SuspendWithObjectMonitorWait.java
31  * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait
32  */
33 
34 import java.io.PrintStream;
35 
36 //
37 // main               waiter              resumer
38 // =================  ==================  ===================
39 // launch waiter
40 // <launch returns>   waiter running
41 // launch resumer     enter threadLock
42 // <launch returns>   threadLock.wait()   resumer running
43 // enter threadLock   :                   wait for notify
44 // threadLock.notify  wait finishes       :
45 // :                  reenter blocks      :
46 // suspend waiter     <suspended>         :
47 // exit threadLock    :                   :
48 // <ready to test>    :                   :
49 // :                  :                   :
50 // notify resumer     :                   wait finishes
51 // join resumer       :                   enter threadLock
52 // :                  <resumed>           resume waiter
53 // :                  :                   exit threadLock
54 // :                  reenter threadLock  :
55 // <join returns>     :                   resumer exits
56 // join waiter        :
57 // <join returns>     waiter exits
58 //
59 
60 public class SuspendWithObjectMonitorWait {
61     private static final String AGENT_LIB = "SuspendWithObjectMonitorWait";
62     private static final int exit_delta   = 95;
63 
64     private static final int DEF_TIME_MAX = 60;    // default max # secs to test
65     private static final int JOIN_MAX     = 30;    // max # secs to wait for join
66 
67     public static final int TS_INIT            = 1;  // initial testState
68     public static final int TS_WAITER_RUNNING  = 2;  // waiter is running
69     public static final int TS_RESUMER_RUNNING = 3;  // resumer is running
70     public static final int TS_READY_TO_NOTIFY = 4;  // ready to notify threadLock
71     public static final int TS_CALL_SUSPEND    = 5;  // call suspend on contender
72     public static final int TS_READY_TO_RESUME = 6;  // ready to resume waiter
73     public static final int TS_CALL_RESUME     = 7;  // call resume on waiter
74     public static final int TS_WAITER_DONE     = 8;  // waiter has run; done
75 
76     public static Object barrierLaunch = new Object();   // controls thread launch
77     public static Object barrierResumer = new Object();  // controls resumer
78     public static Object threadLock = new Object();      // testing object
79 
80     public static long count = 0;
81     public static boolean printDebug = false;
82     public volatile static int testState;
83 
log(String msg)84     private static void log(String msg) { System.out.println(msg); }
85 
suspendThread(SuspendWithObjectMonitorWaitWorker thr)86     native static int suspendThread(SuspendWithObjectMonitorWaitWorker thr);
wait4ContendedEnter(SuspendWithObjectMonitorWaitWorker thr)87     native static int wait4ContendedEnter(SuspendWithObjectMonitorWaitWorker thr);
88 
main(String[] args)89     public static void main(String[] args) throws Exception {
90         try {
91             System.loadLibrary(AGENT_LIB);
92             log("Loaded library: " + AGENT_LIB);
93         } catch (UnsatisfiedLinkError ule) {
94             log("Failed to load library: " + AGENT_LIB);
95             log("java.library.path: " + System.getProperty("java.library.path"));
96             throw ule;
97         }
98 
99         int timeMax = 0;
100         if (args.length == 0) {
101             timeMax = DEF_TIME_MAX;
102         } else {
103             int argIndex = 0;
104             int argsLeft = args.length;
105             if (args[0].equals("-p")) {
106                 printDebug = true;
107                 argIndex = 1;
108                 argsLeft--;
109             }
110             if (argsLeft == 0) {
111                 timeMax = DEF_TIME_MAX;
112             } else if (argsLeft == 1) {
113                 try {
114                     timeMax = Integer.parseUnsignedInt(args[argIndex]);
115                 } catch (NumberFormatException nfe) {
116                     System.err.println("'" + args[argIndex] +
117                                        "': invalid timeMax value.");
118                     usage();
119                 }
120             } else {
121                 usage();
122             }
123         }
124 
125         System.exit(run(timeMax, System.out) + exit_delta);
126     }
127 
logDebug(String mesg)128     public static void logDebug(String mesg) {
129         if (printDebug) {
130             System.err.println(Thread.currentThread().getName() + ": " + mesg);
131         }
132     }
133 
usage()134     public static void usage() {
135         System.err.println("Usage: " + AGENT_LIB + " [-p][time_max]");
136         System.err.println("where:");
137         System.err.println("    -p       ::= print debug info");
138         System.err.println("    time_max ::= max looping time in seconds");
139         System.err.println("                 (default is " + DEF_TIME_MAX +
140                            " seconds)");
141         System.exit(1);
142     }
143 
run(int timeMax, PrintStream out)144     public static int run(int timeMax, PrintStream out) {
145         return (new SuspendWithObjectMonitorWait()).doWork(timeMax, out);
146     }
147 
checkTestState(int exp)148     public static void checkTestState(int exp) {
149         if (testState != exp) {
150             System.err.println("Failure at " + count + " loops.");
151             throw new InternalError("Unexpected test state value: "
152                 + "expected=" + exp + " actual=" + testState);
153         }
154     }
155 
doWork(int timeMax, PrintStream out)156     public int doWork(int timeMax, PrintStream out) {
157         SuspendWithObjectMonitorWaitWorker waiter;    // waiter thread
158         SuspendWithObjectMonitorWaitWorker resumer;    // resumer thread
159 
160         System.out.println("About to execute for " + timeMax + " seconds.");
161 
162         long start_time = System.currentTimeMillis();
163         while (System.currentTimeMillis() < start_time + (timeMax * 1000)) {
164             count++;
165             testState = TS_INIT;  // starting the test loop
166 
167             // launch the waiter thread
168             synchronized (barrierLaunch) {
169                 waiter = new SuspendWithObjectMonitorWaitWorker("waiter");
170                 waiter.start();
171 
172                 while (testState != TS_WAITER_RUNNING) {
173                     try {
174                         barrierLaunch.wait(0);  // wait until it is running
175                     } catch (InterruptedException ex) {
176                     }
177                 }
178             }
179 
180             // launch the resumer thread
181             synchronized (barrierLaunch) {
182                 resumer = new SuspendWithObjectMonitorWaitWorker("resumer", waiter);
183                 resumer.start();
184 
185                 while (testState != TS_RESUMER_RUNNING) {
186                     try {
187                         barrierLaunch.wait(0);  // wait until it is running
188                     } catch (InterruptedException ex) {
189                     }
190                 }
191             }
192 
193             checkTestState(TS_RESUMER_RUNNING);
194 
195             // The waiter thread was synchronized on threadLock before it
196             // set TS_WAITER_RUNNING and notified barrierLaunch above so
197             // we cannot enter threadLock until the waiter thread calls
198             // threadLock.wait().
199             synchronized (threadLock) {
200                 // notify waiter thread so it can try to reenter threadLock
201                 testState = TS_READY_TO_NOTIFY;
202                 threadLock.notify();
203 
204                 // wait for the waiter thread to block
205                 logDebug("before contended enter wait");
206                 int retCode = wait4ContendedEnter(waiter);
207                 if (retCode != 0) {
208                     throw new RuntimeException("error in JVMTI GetThreadState: "
209                                                + "retCode=" + retCode);
210                 }
211                 logDebug("done contended enter wait");
212 
213                 checkTestState(TS_READY_TO_NOTIFY);
214                 testState = TS_CALL_SUSPEND;
215                 logDebug("before suspend thread");
216                 retCode = suspendThread(waiter);
217                 if (retCode != 0) {
218                     throw new RuntimeException("error in JVMTI SuspendThread: "
219                                                + "retCode=" + retCode);
220                 }
221                 logDebug("suspended thread");
222             }
223 
224             //
225             // At this point, all of the child threads are running
226             // and we can get to meat of the test:
227             //
228             // - suspended threadLock waiter (trying to reenter)
229             // - a threadLock enter in the resumer thread
230             // - resumption of the waiter thread
231             // - a threadLock enter in the freshly resumed waiter thread
232             //
233 
234             synchronized (barrierResumer) {
235                 checkTestState(TS_CALL_SUSPEND);
236 
237                 // tell resumer thread to resume waiter thread
238                 testState = TS_READY_TO_RESUME;
239                 barrierResumer.notify();
240 
241                 // Can't call checkTestState() here because the
242                 // resumer thread may have already resumed the
243                 // waiter thread.
244             }
245 
246             try {
247                 resumer.join(JOIN_MAX * 1000);
248                 if (resumer.isAlive()) {
249                     System.err.println("Failure at " + count + " loops.");
250                     throw new InternalError("resumer thread is stuck");
251                 }
252                 waiter.join(JOIN_MAX * 1000);
253                 if (waiter.isAlive()) {
254                     System.err.println("Failure at " + count + " loops.");
255                     throw new InternalError("waiter thread is stuck");
256                 }
257             } catch (InterruptedException ex) {
258             }
259 
260             checkTestState(TS_WAITER_DONE);
261         }
262 
263         System.out.println("Executed " + count + " loops in " + timeMax +
264                            " seconds.");
265 
266         return 0;
267     }
268 }
269 
270 class SuspendWithObjectMonitorWaitWorker extends Thread {
271     private SuspendWithObjectMonitorWaitWorker target;  // target for resume operation
272 
SuspendWithObjectMonitorWaitWorker(String name)273     public SuspendWithObjectMonitorWaitWorker(String name) {
274         super(name);
275     }
276 
SuspendWithObjectMonitorWaitWorker(String name, SuspendWithObjectMonitorWaitWorker target)277     public SuspendWithObjectMonitorWaitWorker(String name, SuspendWithObjectMonitorWaitWorker target) {
278         super(name);
279         this.target = target;
280     }
281 
resumeThread(SuspendWithObjectMonitorWaitWorker thr)282     native static int resumeThread(SuspendWithObjectMonitorWaitWorker thr);
283 
run()284     public void run() {
285         SuspendWithObjectMonitorWait.logDebug("thread running");
286 
287         //
288         // Launch the waiter thread:
289         // - grab the threadLock
290         // - threadLock.wait()
291         // - releases threadLock
292         //
293         if (getName().equals("waiter")) {
294             // grab threadLock before we tell main we are running
295             SuspendWithObjectMonitorWait.logDebug("before enter threadLock");
296             synchronized(SuspendWithObjectMonitorWait.threadLock) {
297                 SuspendWithObjectMonitorWait.logDebug("enter threadLock");
298 
299                 SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_INIT);
300 
301                 synchronized(SuspendWithObjectMonitorWait.barrierLaunch) {
302                     // tell main we are running
303                     SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_WAITER_RUNNING;
304                     SuspendWithObjectMonitorWait.barrierLaunch.notify();
305                 }
306 
307                 SuspendWithObjectMonitorWait.logDebug("before wait");
308 
309                 // TS_READY_TO_NOTIFY is set after the main thread has
310                 // entered threadLock so a spurious wakeup can't get the
311                 // waiter thread out of this threadLock.wait(0) call:
312                 while (SuspendWithObjectMonitorWait.testState <= SuspendWithObjectMonitorWait.TS_READY_TO_NOTIFY) {
313                     try {
314                         SuspendWithObjectMonitorWait.threadLock.wait(0);
315                     } catch (InterruptedException ex) {
316                     }
317                 }
318 
319                 SuspendWithObjectMonitorWait.logDebug("after wait");
320 
321                 SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_CALL_RESUME);
322                 SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_WAITER_DONE;
323 
324                 SuspendWithObjectMonitorWait.logDebug("exit threadLock");
325             }
326         }
327         //
328         // Launch the resumer thread:
329         // - tries to grab the threadLock (should not block!)
330         // - grabs threadLock
331         // - resumes the waiter thread
332         // - releases threadLock
333         //
334         else if (getName().equals("resumer")) {
335             synchronized(SuspendWithObjectMonitorWait.barrierResumer) {
336                 synchronized(SuspendWithObjectMonitorWait.barrierLaunch) {
337                     // tell main we are running
338                     SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_RESUMER_RUNNING;
339                     SuspendWithObjectMonitorWait.barrierLaunch.notify();
340                 }
341                 SuspendWithObjectMonitorWait.logDebug("thread waiting");
342                 while (SuspendWithObjectMonitorWait.testState != SuspendWithObjectMonitorWait.TS_READY_TO_RESUME) {
343                     try {
344                         // wait for main to tell us when to continue
345                         SuspendWithObjectMonitorWait.barrierResumer.wait(0);
346                     } catch (InterruptedException ex) {
347                     }
348                 }
349             }
350 
351             SuspendWithObjectMonitorWait.logDebug("before enter threadLock");
352             synchronized(SuspendWithObjectMonitorWait.threadLock) {
353                 SuspendWithObjectMonitorWait.logDebug("enter threadLock");
354 
355                 SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_READY_TO_RESUME);
356                 SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_CALL_RESUME;
357 
358                 // resume the waiter thread so waiter.join() can work
359                 SuspendWithObjectMonitorWait.logDebug("before resume thread");
360                 int retCode = resumeThread(target);
361                 if (retCode != 0) {
362                     throw new RuntimeException("error in JVMTI ResumeThread: " +
363                                                "retCode=" + retCode);
364                 }
365                 SuspendWithObjectMonitorWait.logDebug("resumed thread");
366 
367                 SuspendWithObjectMonitorWait.logDebug("exit threadLock");
368             }
369         }
370     }
371 }
372