Skip to content

Commit

Permalink
8231595: [TEST] develop a test case for SuspendThreadList including c…
Browse files Browse the repository at this point in the history
…urrent thread

Extend test coverage for SuspendThreadList

Backport-of: 4091899
  • Loading branch information
GoeLin committed Feb 14, 2023
1 parent 8773d46 commit 4c27fa0
Show file tree
Hide file tree
Showing 2 changed files with 431 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

1 comment on commit 4c27fa0

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.