test/hotspot/jtreg/serviceability/jvmti/SuspendWithCurrentThread/SuspendWithCurrentThread.java
changeset 58530 865c889ce351
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithCurrentThread/SuspendWithCurrentThread.java	Thu Oct 10 01:10:26 2019 +0000
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8231595
+ * @summary [TEST] develop a test case for SuspendThreadList including current thread
+ * @library /test/lib
+ * @compile SuspendWithCurrentThread.java
+ * @run main/othervm/native -agentlib:SuspendWithCurrentThread SuspendWithCurrentThread SuspenderIndex=first
+ * @run main/othervm/native -agentlib:SuspendWithCurrentThread SuspendWithCurrentThread SuspenderIndex=last
+ */
+
+import java.io.PrintStream;
+
+public class SuspendWithCurrentThread {
+    private static final String AGENT_LIB = "SuspendWithCurrentThread";
+    private static final String SUSPENDER_OPT = "SuspenderIndex=";
+    private static final int THREADS_COUNT = 10;
+
+    private static void log(String msg) { System.out.println(msg); }
+
+    private static native void    registerTestedThreads(Thread[] threads);
+    private static native boolean checkTestedThreadsSuspended();
+    private static native void    resumeTestedThreads();
+    private static native void    releaseTestedThreadsInfo();
+
+    // The suspender thread index defines the thread which has to suspend
+    // the tested threads including itself with the JVMTI SuspendThreadList
+    private static int suspenderIndex;
+
+    public static void main(String args[]) throws Exception {
+        try {
+            System.loadLibrary(AGENT_LIB);
+            log("Loaded library: " + AGENT_LIB);
+        } catch (UnsatisfiedLinkError ule) {
+            log("Failed to load library: " + AGENT_LIB);
+            log("java.library.path: " + System.getProperty("java.library.path"));
+            throw ule;
+        }
+        if (args.length != 1) {
+            throw new RuntimeException("Main: wrong arguments count: " + args.length + ", expected: 1");
+        }
+        String arg = args[0];
+        if (arg.equals(SUSPENDER_OPT + "first")) {
+            suspenderIndex = 0;
+        } else if (arg.equals(SUSPENDER_OPT + "last")) {
+            suspenderIndex = THREADS_COUNT - 1;
+        } else {
+            throw new RuntimeException("Main: wrong argument: " + arg + ", expected: SuspenderIndex={first|last}");
+        }
+        log("Main: suspenderIndex: " + suspenderIndex);
+
+        SuspendWithCurrentThread test = new SuspendWithCurrentThread();
+        test.run();
+    }
+
+    private ThreadToSuspend[] startTestedThreads(int threadsCount) throws RuntimeException  {
+        ThreadToSuspend[]threads = new ThreadToSuspend[threadsCount];
+
+        // create tested threads
+        for (int i = 0; i < threads.length; i++) {
+            threads[i] = new ThreadToSuspend("ThreadToSuspend#" + i,
+                                             i == suspenderIndex // isSuspender
+                                            );
+        }
+        log("Main: starting tested threads");
+        for (int i = 0; i < threads.length; i++) {
+            threads[i].start();
+            if (!threads[i].checkReady()) {
+                throw new RuntimeException("Main: unable to prepare tested thread: " + threads[i]);
+            }
+        }
+        log("Main: tested threads started");
+
+        registerTestedThreads(threads);
+        return threads;
+    }
+
+    private boolean checkSuspendedStatus() throws RuntimeException  {
+        log("Main: checking all tested threads have been suspended");
+        return checkTestedThreadsSuspended();
+    }
+
+    /* The test does the following steps:
+     *  - main thread starts several (THREADS_COUNT) ThreadToSuspend tested threads
+     *  - main thread waits for threads to be ready with the thread.checkReady()
+     *  - main thread registers tested threads within the native agent library
+     *    with the native method registerTestedThreads()
+     *  - main thread triggers the suspender tested thread with the
+     *    ThreadToSuspend.setAllThreadsReady() to suspend tested threads
+     *  - suspender thread suspends tested threads including itself with the native
+     *    method suspendTestedThreads() (uses the JVMTI SuspendThreadList function)
+     *  - main thread checks tested threads suspended status with the native method
+     *    checkSuspendedStatus(); the tested threads are expected to have suspended status
+     *  - main thread resumes tested threads with the native method resumeTestedThreads()
+     *  - main thread releases tested threads with the native method releaseTestedThreads()
+     *  - main thread triggers the tested threads to finish with the thread.letFinish()
+     */
+    private void run() throws Exception {
+        ThreadToSuspend[] threads = null; // tested threads
+
+        log("Main: started");
+        try {
+            threads = startTestedThreads(THREADS_COUNT);
+
+            log("Main: trigger " + threads[suspenderIndex].getName() +
+                " to suspend all tested threads including itself");
+            ThreadToSuspend.setAllThreadsReady();
+
+            if (!checkSuspendedStatus()) {
+                throw new RuntimeException("Main: FAILED status returned from checkTestedThreadsSuspended");
+            }
+
+            log("Main: resuming all tested threads");
+            resumeTestedThreads();
+        } finally {
+            // let threads to finish
+            for (int i = 0; i < threads.length; i++) {
+                threads[i].letFinish();
+            }
+            log("Main: tested threads finished");
+        }
+
+        // wait for threads to finish
+        log("Main: joining tested threads");
+        try {
+            for (int i = 0; i < threads.length; i++) {
+                threads[i].join();
+            }
+            log("Main: tested thread joined");
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+        log("Main: releasing tested threads native info");
+        releaseTestedThreadsInfo();
+
+        log("Main: finished");
+    }
+}
+
+/* =================================================================== */
+
+// tested threads
+class ThreadToSuspend extends Thread {
+    private static void log(String msg) { System.out.println(msg); }
+
+    private static native void init();
+    private static native void suspendTestedThreads();
+    private static volatile boolean allThreadsReady = false;
+
+    public static void setAllThreadsReady() {
+        allThreadsReady = true;
+    }
+
+    private volatile boolean threadReady = false;
+    private volatile boolean shouldFinish = false;
+    private boolean isSuspender = false;
+
+    // make thread with specific name
+    public ThreadToSuspend(String name, boolean isSuspender) {
+        super(name);
+        this.isSuspender = isSuspender;
+    }
+
+    // run thread continuously
+    public void run() {
+        boolean needSuspend = true;
+
+        if (isSuspender) {
+            init();
+        }
+        threadReady = true;
+
+        // run in a loop
+        while (!shouldFinish) {
+            if (isSuspender && needSuspend && allThreadsReady) {
+                log(getName() + ": before suspending all tested threads including myself");
+                needSuspend = false;
+                suspendTestedThreads();
+                log(getName() + ": after suspending all tested threads including myself");
+            }
+        }
+    }
+
+    // check if thread is ready
+    public boolean checkReady() {
+        try {
+            while (!threadReady) {
+                sleep(1);
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException("checkReady: sleep was interrupted\n\t" + e);
+        }
+        return threadReady;
+    }
+
+    // let thread to finish
+    public void letFinish() {
+        shouldFinish = true;
+    }
+}