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 enter.
28  * @requires vm.jvmti
29  * @library /test/lib
30  * @compile SuspendWithObjectMonitorEnter.java
31  * @run main/othervm/native -agentlib:SuspendWithObjectMonitorEnter SuspendWithObjectMonitorEnter
32  */
33 
34 import java.io.PrintStream;
35 
36 //
37 // main               blocker           contender            resumer
38 // =================  ================  ===================  ================
39 // launch blocker
40 // <launch returns>   blocker running
41 // launch contender   enter threadLock
42 // <launch returns>   wait for notify   contender running
43 // launch resumer     :                 block on threadLock
44 // <launch returns>   :                 :                    resumer running
45 // suspend contender  :                 <suspended>          wait for notify
46 // <ready to test>    :                 :                    :
47 // :                  :                 :                    :
48 // notify blocker     wait finishes     :                    :
49 // notify resumer     exit threadLock   :                    wait finishes
50 // join blocker       :                 :                    enter threadLock
51 // <join returns>     blocker exits     <resumed>            resume contender
52 // join resumer                         :                    exit threadLock
53 // <join returns>                       enter threadLock     resumer exits
54 // join contender                       exit threadLock
55 // <join returns>                       contender exits
56 //
57 
58 public class SuspendWithObjectMonitorEnter {
59     private static final String AGENT_LIB = "SuspendWithObjectMonitorEnter";
60     private static final int exit_delta   = 95;
61 
62     private static final int DEF_TIME_MAX = 60;    // default max # secs to test
63     private static final int JOIN_MAX     = 30;    // max # secs to wait for join
64 
65     public static final int TS_INIT              = 1;  // initial testState
66     public static final int TS_BLOCKER_RUNNING   = 2;  // blocker is running
67     public static final int TS_CONTENDER_RUNNING = 3;  // contender is running
68     public static final int TS_RESUMER_RUNNING   = 4;  // resumer is running
69     public static final int TS_CALL_SUSPEND      = 5;  // call suspend on contender
70     public static final int TS_DONE_BLOCKING     = 6;  // done blocking threadLock
71     public static final int TS_READY_TO_RESUME   = 7;  // ready to resume contender
72     public static final int TS_CALL_RESUME       = 8;  // call resume on contender
73     public static final int TS_CONTENDER_DONE    = 9;  // contender has run; done
74 
75     public static Object barrierLaunch = new Object();   // controls thread launch
76     public static Object barrierBlocker = new Object();  // controls blocker
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(SuspendWithObjectMonitorEnterWorker thr)86     native static int suspendThread(SuspendWithObjectMonitorEnterWorker thr);
wait4ContendedEnter(SuspendWithObjectMonitorEnterWorker thr)87     native static int wait4ContendedEnter(SuspendWithObjectMonitorEnterWorker 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 SuspendWithObjectMonitorEnter()).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         SuspendWithObjectMonitorEnterWorker blocker;    // blocker thread
158         SuspendWithObjectMonitorEnterWorker contender;  // contender thread
159         SuspendWithObjectMonitorEnterWorker resumer;    // resumer thread
160 
161         System.out.println("About to execute for " + timeMax + " seconds.");
162 
163         long start_time = System.currentTimeMillis();
164         while (System.currentTimeMillis() < start_time + (timeMax * 1000)) {
165             count++;
166             testState = TS_INIT;  // starting the test loop
167 
168             // launch the blocker thread
169             synchronized (barrierLaunch) {
170                 blocker = new SuspendWithObjectMonitorEnterWorker("blocker");
171                 blocker.start();
172 
173                 while (testState != TS_BLOCKER_RUNNING) {
174                     try {
175                         barrierLaunch.wait(0);  // wait until it is running
176                     } catch (InterruptedException ex) {
177                     }
178                 }
179             }
180 
181             // launch the contender thread
182             synchronized (barrierLaunch) {
183                 contender = new SuspendWithObjectMonitorEnterWorker("contender");
184                 contender.start();
185 
186                 while (testState != TS_CONTENDER_RUNNING) {
187                     try {
188                         barrierLaunch.wait(0);  // wait until it is running
189                     } catch (InterruptedException ex) {
190                     }
191                 }
192             }
193 
194             // launch the resumer thread
195             synchronized (barrierLaunch) {
196                 resumer = new SuspendWithObjectMonitorEnterWorker("resumer", contender);
197                 resumer.start();
198 
199                 while (testState != TS_RESUMER_RUNNING) {
200                     try {
201                         barrierLaunch.wait(0);  // wait until it is running
202                     } catch (InterruptedException ex) {
203                     }
204                 }
205             }
206 
207             // wait for the contender thread to block
208             logDebug("before contended enter wait");
209             int retCode = wait4ContendedEnter(contender);
210             if (retCode != 0) {
211                 throw new RuntimeException("error in JVMTI GetThreadState: " +
212                                            "retCode=" + retCode);
213             }
214             logDebug("done contended enter wait");
215 
216             checkTestState(TS_RESUMER_RUNNING);
217             testState = TS_CALL_SUSPEND;
218             logDebug("before suspend thread");
219             retCode = suspendThread(contender);
220             if (retCode != 0) {
221                 throw new RuntimeException("error in JVMTI SuspendThread: " +
222                                            "retCode=" + retCode);
223             }
224             logDebug("suspended thread");
225 
226             //
227             // At this point, all of the child threads are running
228             // and we can get to meat of the test:
229             //
230             // - suspended threadLock contender
231             // - a threadLock exit in the blocker thread
232             // - a threadLock enter in the resumer thread
233             // - resumption of the contender thread
234             // - a threadLock enter in the freshly resumed contender thread
235             //
236             synchronized (barrierBlocker) {
237                 checkTestState(TS_CALL_SUSPEND);
238 
239                 // tell blocker thread to exit threadLock
240                 testState = TS_DONE_BLOCKING;
241                 barrierBlocker.notify();
242             }
243 
244             synchronized (barrierResumer) {
245                 // tell resumer thread to resume contender thread
246                 testState = TS_READY_TO_RESUME;
247                 barrierResumer.notify();
248 
249                 // Can't call checkTestState() here because the
250                 // resumer thread may have already resumed the
251                 // contender thread.
252             }
253 
254             try {
255                 blocker.join();
256                 resumer.join(JOIN_MAX * 1000);
257                 if (resumer.isAlive()) {
258                     System.err.println("Failure at " + count + " loops.");
259                     throw new InternalError("resumer thread is stuck");
260                 }
261                 contender.join(JOIN_MAX * 1000);
262                 if (contender.isAlive()) {
263                     System.err.println("Failure at " + count + " loops.");
264                     throw new InternalError("contender thread is stuck");
265                 }
266             } catch (InterruptedException ex) {
267             }
268 
269             checkTestState(TS_CONTENDER_DONE);
270         }
271 
272         System.out.println("Executed " + count + " loops in " + timeMax +
273                            " seconds.");
274 
275         return 0;
276     }
277 }
278 
279 class SuspendWithObjectMonitorEnterWorker extends Thread {
280     private SuspendWithObjectMonitorEnterWorker target;  // target for resume operation
281 
SuspendWithObjectMonitorEnterWorker(String name)282     public SuspendWithObjectMonitorEnterWorker(String name) {
283         super(name);
284     }
285 
SuspendWithObjectMonitorEnterWorker(String name, SuspendWithObjectMonitorEnterWorker target)286     public SuspendWithObjectMonitorEnterWorker(String name, SuspendWithObjectMonitorEnterWorker target) {
287         super(name);
288         this.target = target;
289     }
290 
resumeThread(SuspendWithObjectMonitorEnterWorker thr)291     native static int resumeThread(SuspendWithObjectMonitorEnterWorker thr);
292 
run()293     public void run() {
294         SuspendWithObjectMonitorEnter.logDebug("thread running");
295 
296         //
297         // Launch the blocker thread:
298         // - grabs threadLock
299         // - holds threadLock until we tell it let go
300         // - releases threadLock
301         //
302         if (getName().equals("blocker")) {
303             // grab threadLock before we tell main we are running
304             SuspendWithObjectMonitorEnter.logDebug("before enter threadLock");
305             synchronized(SuspendWithObjectMonitorEnter.threadLock) {
306                 SuspendWithObjectMonitorEnter.logDebug("enter threadLock");
307 
308                 SuspendWithObjectMonitorEnter.checkTestState(SuspendWithObjectMonitorEnter.TS_INIT);
309 
310                 synchronized(SuspendWithObjectMonitorEnter.barrierBlocker) {
311                     synchronized(SuspendWithObjectMonitorEnter.barrierLaunch) {
312                         // tell main we are running
313                         SuspendWithObjectMonitorEnter.testState = SuspendWithObjectMonitorEnter.TS_BLOCKER_RUNNING;
314                         SuspendWithObjectMonitorEnter.barrierLaunch.notify();
315                     }
316                     SuspendWithObjectMonitorEnter.logDebug("thread waiting");
317                     // TS_READY_TO_RESUME is set right after TS_DONE_BLOCKING
318                     // is set so either can get the blocker thread out of
319                     // this wait() wrapper:
320                     while (SuspendWithObjectMonitorEnter.testState != SuspendWithObjectMonitorEnter.TS_DONE_BLOCKING &&
321                            SuspendWithObjectMonitorEnter.testState != SuspendWithObjectMonitorEnter.TS_READY_TO_RESUME) {
322                         try {
323                             // wait for main to tell us when to exit threadLock
324                             SuspendWithObjectMonitorEnter.barrierBlocker.wait(0);
325                         } catch (InterruptedException ex) {
326                         }
327                     }
328                 }
329                 SuspendWithObjectMonitorEnter.logDebug("exit threadLock");
330             }
331         }
332         //
333         // Launch the contender thread:
334         // - tries to grab the threadLock
335         // - grabs threadLock
336         // - releases threadLock
337         //
338         else if (getName().equals("contender")) {
339             synchronized(SuspendWithObjectMonitorEnter.barrierLaunch) {
340                 // tell main we are running
341                 SuspendWithObjectMonitorEnter.testState = SuspendWithObjectMonitorEnter.TS_CONTENDER_RUNNING;
342                 SuspendWithObjectMonitorEnter.barrierLaunch.notify();
343             }
344 
345             SuspendWithObjectMonitorEnter.logDebug("before enter threadLock");
346             synchronized(SuspendWithObjectMonitorEnter.threadLock) {
347                 SuspendWithObjectMonitorEnter.logDebug("enter threadLock");
348 
349                 SuspendWithObjectMonitorEnter.checkTestState(SuspendWithObjectMonitorEnter.TS_CALL_RESUME);
350                 SuspendWithObjectMonitorEnter.testState = SuspendWithObjectMonitorEnter.TS_CONTENDER_DONE;
351 
352                 SuspendWithObjectMonitorEnter.logDebug("exit threadLock");
353             }
354         }
355         //
356         // Launch the resumer thread:
357         // - tries to grab the threadLock (should not block!)
358         // - grabs threadLock
359         // - resumes the contended thread
360         // - releases threadLock
361         //
362         else if (getName().equals("resumer")) {
363             synchronized(SuspendWithObjectMonitorEnter.barrierResumer) {
364                 synchronized(SuspendWithObjectMonitorEnter.barrierLaunch) {
365                     // tell main we are running
366                     SuspendWithObjectMonitorEnter.testState = SuspendWithObjectMonitorEnter.TS_RESUMER_RUNNING;
367                     SuspendWithObjectMonitorEnter.barrierLaunch.notify();
368                 }
369                 SuspendWithObjectMonitorEnter.logDebug("thread waiting");
370                 while (SuspendWithObjectMonitorEnter.testState != SuspendWithObjectMonitorEnter.TS_READY_TO_RESUME) {
371                     try {
372                         // wait for main to tell us when to continue
373                         SuspendWithObjectMonitorEnter.barrierResumer.wait(0);
374                     } catch (InterruptedException ex) {
375                     }
376                 }
377             }
378 
379             SuspendWithObjectMonitorEnter.logDebug("before enter threadLock");
380             synchronized(SuspendWithObjectMonitorEnter.threadLock) {
381                 SuspendWithObjectMonitorEnter.logDebug("enter threadLock");
382 
383                 SuspendWithObjectMonitorEnter.checkTestState(SuspendWithObjectMonitorEnter.TS_READY_TO_RESUME);
384                 SuspendWithObjectMonitorEnter.testState = SuspendWithObjectMonitorEnter.TS_CALL_RESUME;
385 
386                 // resume the contender thread so contender.join() can work
387                 SuspendWithObjectMonitorEnter.logDebug("before resume thread");
388                 int retCode = resumeThread(target);
389                 if (retCode != 0) {
390                     throw new RuntimeException("error in JVMTI ResumeThread: " +
391                                                "retCode=" + retCode);
392                 }
393                 SuspendWithObjectMonitorEnter.logDebug("resumed thread");
394 
395                 SuspendWithObjectMonitorEnter.logDebug("exit threadLock");
396             }
397         }
398     }
399 }
400