Implement JVMTI can_signal_thread capability.
Implements the JVMTI can_signal_thread capability and all associated
methods and behaviors. This includes both the StopThread and
InterruptThread functions.
This CL contains the tests for the previous CL.
Test: ./test.py --host -j50
Test: stress --cpu 59 && while ./test/run-test --host 1934; do; done
Bug: 62821960
Bug: 34415266
Change-Id: I7b6fc37da0d2673caa993e486f078cf129d74c0f
diff --git a/test/1934-jvmti-signal-thread/expected.txt b/test/1934-jvmti-signal-thread/expected.txt
new file mode 100644
index 0000000..69a0e9e
--- /dev/null
+++ b/test/1934-jvmti-signal-thread/expected.txt
@@ -0,0 +1,27 @@
+Interrupt before start
+interrupting other thread before starting
+Caught exception java.lang.RuntimeException: JVMTI_ERROR_THREAD_NOT_ALIVE
+Stop before start
+stopping other thread before starting
+Caught exception java.lang.RuntimeException: JVMTI_ERROR_THREAD_NOT_ALIVE
+Interrupt recur
+Interrupting other thread recurring
+Other thread Interrupted. err: java.lang.Error: Interrupted!
+Stop Recur
+stopping other thread recurring
+Other thread Stopped by: java.lang.Error: AWESOME!
+Interrupt spinning
+Interrupting other thread spinning
+Other thread Interrupted.
+Stop spinning
+stopping other thread spinning
+Other thread Stopped by: java.lang.Error: AWESOME!
+Interrupt wait
+interrupting other thread waiting
+Other thread interrupted. err: java.lang.Error: Interrupted!
+Stop wait
+stopping other thread waiting
+Other thread Stopped by: java.lang.Error: AWESOME
+Stop in native
+stopping other thread
+Other thread Stopped by: java.lang.Error: AWESOME
diff --git a/test/1934-jvmti-signal-thread/info.txt b/test/1934-jvmti-signal-thread/info.txt
new file mode 100644
index 0000000..c8c9189
--- /dev/null
+++ b/test/1934-jvmti-signal-thread/info.txt
@@ -0,0 +1,3 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that the GetBytecodes function works as expected.
diff --git a/test/1934-jvmti-signal-thread/run b/test/1934-jvmti-signal-thread/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/1934-jvmti-signal-thread/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1934-jvmti-signal-thread/signal_threads.cc b/test/1934-jvmti-signal-thread/signal_threads.cc
new file mode 100644
index 0000000..726a7a86
--- /dev/null
+++ b/test/1934-jvmti-signal-thread/signal_threads.cc
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+
+#include <cstdio>
+#include <iostream>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "jvmti.h"
+
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1934SignalThreads {
+
+struct NativeMonitor {
+ jrawMonitorID continue_monitor;
+ bool should_continue;
+ jrawMonitorID start_monitor;
+ bool should_start;
+};
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_Test1934_allocNativeMonitor(JNIEnv* env, jclass) {
+ NativeMonitor* mon;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->Allocate(sizeof(NativeMonitor),
+ reinterpret_cast<unsigned char**>(&mon)))) {
+ return -1l;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->CreateRawMonitor("test-1934 start",
+ &mon->start_monitor))) {
+ return -1l;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->CreateRawMonitor("test-1934 continue",
+ &mon->continue_monitor))) {
+ return -1l;
+ }
+ mon->should_continue = false;
+ mon->should_start = false;
+ return static_cast<jlong>(reinterpret_cast<intptr_t>(mon));
+}
+
+extern "C" JNIEXPORT void Java_art_Test1934_nativeWaitForOtherThread(JNIEnv* env,
+ jclass,
+ jlong id) {
+ NativeMonitor* mon = reinterpret_cast<NativeMonitor*>(static_cast<intptr_t>(id));
+ // Start
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(mon->start_monitor))) {
+ return;
+ }
+ mon->should_start = true;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->RawMonitorNotifyAll(mon->start_monitor))) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon->start_monitor));
+ return;
+ }
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon->start_monitor))) {
+ return;
+ }
+
+ // Finish
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(mon->continue_monitor))) {
+ return;
+ }
+ while (!mon->should_continue) {
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->RawMonitorWait(mon->continue_monitor, -1l))) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon->continue_monitor));
+ return;
+ }
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon->continue_monitor));
+}
+
+extern "C" JNIEXPORT void Java_art_Test1934_nativeDoInterleaved(JNIEnv* env,
+ jclass,
+ jlong id,
+ jobject closure) {
+ NativeMonitor* mon = reinterpret_cast<NativeMonitor*>(static_cast<intptr_t>(id));
+ // Wait for start.
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(mon->start_monitor))) {
+ return;
+ }
+ while (!mon->should_start) {
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->RawMonitorWait(mon->start_monitor, -1l))) {
+ return;
+ }
+ }
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon->start_monitor))) {
+ return;
+ }
+
+ // Call closure.
+ ScopedLocalRef<jclass> runnable_klass(env, env->FindClass("java/lang/Runnable"));
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ jmethodID doRun = env->GetMethodID(runnable_klass.get(), "run", "()V");
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ env->CallVoidMethod(closure, doRun);
+
+ // Tell other thread to finish.
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(mon->continue_monitor))) {
+ return;
+ }
+ mon->should_continue = true;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->RawMonitorNotifyAll(mon->continue_monitor))) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon->continue_monitor));
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon->continue_monitor));
+}
+
+extern "C" JNIEXPORT void Java_art_Test1934_destroyNativeMonitor(JNIEnv*, jclass, jlong id) {
+ NativeMonitor* mon = reinterpret_cast<NativeMonitor*>(static_cast<intptr_t>(id));
+ jvmti_env->DestroyRawMonitor(mon->start_monitor);
+ jvmti_env->DestroyRawMonitor(mon->continue_monitor);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(mon));
+}
+
+} // namespace Test1934SignalThreads
+} // namespace art
+
diff --git a/test/1934-jvmti-signal-thread/src/Main.java b/test/1934-jvmti-signal-thread/src/Main.java
new file mode 100644
index 0000000..539763c
--- /dev/null
+++ b/test/1934-jvmti-signal-thread/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1934.run();
+ }
+}
diff --git a/test/1934-jvmti-signal-thread/src/art/Monitors.java b/test/1934-jvmti-signal-thread/src/art/Monitors.java
new file mode 100644
index 0000000..7fe2b60
--- /dev/null
+++ b/test/1934-jvmti-signal-thread/src/art/Monitors.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.atomic.*;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.Arrays;
+import java.util.Objects;
+
+public class Monitors {
+ public native static void setupMonitorEvents(
+ Class<?> method_klass,
+ Method monitor_contended_enter_event,
+ Method monitor_contended_entered_event,
+ Method monitor_wait_event,
+ Method monitor_waited_event,
+ Class<?> lock_klass,
+ Thread thr);
+ public native static void stopMonitorEvents();
+
+ public static class NamedLock {
+ public final String name;
+ private volatile int calledNotify;
+ public NamedLock(String name) {
+ this.name = name;
+ calledNotify = 0;
+ }
+
+ public String toString() {
+ return String.format("NamedLock[%s]", name);
+ }
+
+ public final void DoWait() throws Exception {
+ final int v = calledNotify;
+ while (v == calledNotify) {
+ wait();
+ }
+ }
+
+ public final void DoWait(long t) throws Exception {
+ final int v = calledNotify;
+ final long target = System.currentTimeMillis() + (t / 2);
+ while (v == calledNotify && (t < 0 || System.currentTimeMillis() < target)) {
+ wait(t);
+ }
+ }
+
+ public final void DoNotifyAll() throws Exception {
+ calledNotify++;
+ notifyAll();
+ }
+
+ public final void DoNotify() throws Exception {
+ calledNotify++;
+ notify();
+ }
+ }
+
+ public static final class MonitorUsage {
+ public final Object monitor;
+ public final Thread owner;
+ public final int entryCount;
+ public final Thread[] waiters;
+ public final Thread[] notifyWaiters;
+
+ public MonitorUsage(
+ Object monitor,
+ Thread owner,
+ int entryCount,
+ Thread[] waiters,
+ Thread[] notifyWaiters) {
+ this.monitor = monitor;
+ this.entryCount = entryCount;
+ this.owner = owner;
+ this.waiters = waiters;
+ this.notifyWaiters = notifyWaiters;
+ }
+
+ private static String toNameList(Thread[] ts) {
+ return Arrays.toString(Arrays.stream(ts).map((Thread t) -> t.getName()).toArray());
+ }
+
+ public String toString() {
+ return String.format(
+ "MonitorUsage{ monitor: %s, owner: %s, entryCount: %d, waiters: %s, notify_waiters: %s }",
+ monitor,
+ (owner != null) ? owner.getName() : "<NULL>",
+ entryCount,
+ toNameList(waiters),
+ toNameList(notifyWaiters));
+ }
+ }
+
+ public static native MonitorUsage getObjectMonitorUsage(Object monitor);
+ public static native Object getCurrentContendedMonitor(Thread thr);
+
+ public static class TestException extends Error {
+ public TestException() { super(); }
+ public TestException(String s) { super(s); }
+ public TestException(String s, Throwable c) { super(s, c); }
+ }
+
+ public static class LockController {
+ private static enum Action { HOLD, RELEASE, NOTIFY, NOTIFY_ALL, WAIT, TIMED_WAIT }
+
+ public final NamedLock lock;
+ public final long timeout;
+ private final AtomicStampedReference<Action> action;
+ private volatile Thread runner = null;
+ private volatile boolean started = false;
+ private volatile boolean held = false;
+ private static final AtomicInteger cnt = new AtomicInteger(0);
+ private volatile Throwable exe;
+
+ public LockController(NamedLock lock) {
+ this(lock, 10 * 1000);
+ }
+ public LockController(NamedLock lock, long timeout) {
+ this.lock = lock;
+ this.timeout = timeout;
+ this.action = new AtomicStampedReference(Action.HOLD, 0);
+ this.exe = null;
+ }
+
+ public boolean IsWorkerThread(Thread thd) {
+ return Objects.equals(runner, thd);
+ }
+
+ public boolean IsLocked() {
+ checkException();
+ return held;
+ }
+
+ public void checkException() {
+ if (exe != null) {
+ throw new TestException("Exception thrown by other thread!", exe);
+ }
+ }
+
+ private void setAction(Action a) {
+ int stamp = action.getStamp();
+ // Wait for it to be HOLD before updating.
+ while (!action.compareAndSet(Action.HOLD, a, stamp, stamp + 1)) {
+ stamp = action.getStamp();
+ }
+ }
+
+ public synchronized void suspendWorker() throws Exception {
+ checkException();
+ if (runner == null) {
+ throw new TestException("We don't have any runner holding " + lock);
+ }
+ Suspension.suspend(runner);
+ }
+
+ public Object getWorkerContendedMonitor() throws Exception {
+ checkException();
+ if (runner == null) {
+ return null;
+ }
+ return getCurrentContendedMonitor(runner);
+ }
+
+ public synchronized void DoLock() {
+ if (IsLocked()) {
+ throw new Error("lock is already acquired or being acquired.");
+ }
+ if (runner != null) {
+ throw new Error("Already have thread!");
+ }
+ runner = new Thread(() -> {
+ started = true;
+ try {
+ synchronized (lock) {
+ held = true;
+ int[] stamp_h = new int[] { -1 };
+ Action cur_action = Action.HOLD;
+ try {
+ while (true) {
+ cur_action = action.get(stamp_h);
+ int stamp = stamp_h[0];
+ if (cur_action == Action.RELEASE) {
+ // The other thread will deal with reseting action.
+ break;
+ }
+ try {
+ switch (cur_action) {
+ case HOLD:
+ Thread.yield();
+ break;
+ case NOTIFY:
+ lock.DoNotify();
+ break;
+ case NOTIFY_ALL:
+ lock.DoNotifyAll();
+ break;
+ case TIMED_WAIT:
+ lock.DoWait(timeout);
+ break;
+ case WAIT:
+ lock.DoWait();
+ break;
+ default:
+ throw new Error("Unknown action " + action);
+ }
+ } finally {
+ // reset action back to hold if it isn't something else.
+ action.compareAndSet(cur_action, Action.HOLD, stamp, stamp+1);
+ }
+ }
+ } catch (Exception e) {
+ throw new TestException("Got an error while performing action " + cur_action, e);
+ }
+ }
+ } finally {
+ held = false;
+ started = false;
+ }
+ }, "Locker thread " + cnt.getAndIncrement() + " for " + lock);
+ // Make sure we can get any exceptions this throws.
+ runner.setUncaughtExceptionHandler((t, e) -> { exe = e; });
+ runner.start();
+ }
+
+ public void waitForLockToBeHeld() throws Exception {
+ while (true) {
+ if (IsLocked() && Objects.equals(runner, Monitors.getObjectMonitorUsage(lock).owner)) {
+ return;
+ }
+ }
+ }
+
+ public synchronized void waitForNotifySleep() throws Exception {
+ if (runner == null) {
+ throw new Error("No thread trying to lock!");
+ }
+ do {
+ checkException();
+ } while (!started ||
+ !Arrays.asList(Monitors.getObjectMonitorUsage(lock).notifyWaiters).contains(runner));
+ }
+
+ public synchronized void waitForContendedSleep() throws Exception {
+ if (runner == null) {
+ throw new Error("No thread trying to lock!");
+ }
+ do {
+ checkException();
+ } while (!started ||
+ runner.getState() != Thread.State.BLOCKED ||
+ !Arrays.asList(Monitors.getObjectMonitorUsage(lock).waiters).contains(runner));
+ }
+
+ public synchronized void DoNotify() {
+ if (!IsLocked()) {
+ throw new Error("Not locked");
+ }
+ setAction(Action.NOTIFY);
+ }
+
+ public synchronized void DoNotifyAll() {
+ if (!IsLocked()) {
+ throw new Error("Not locked");
+ }
+ setAction(Action.NOTIFY_ALL);
+ }
+
+ public synchronized void DoTimedWait() throws Exception {
+ if (!IsLocked()) {
+ throw new Error("Not locked");
+ }
+ setAction(Action.TIMED_WAIT);
+ }
+
+ public synchronized void DoWait() throws Exception {
+ if (!IsLocked()) {
+ throw new Error("Not locked");
+ }
+ setAction(Action.WAIT);
+ }
+
+ public synchronized void interruptWorker() throws Exception {
+ if (!IsLocked()) {
+ throw new Error("Not locked");
+ }
+ runner.interrupt();
+ }
+
+ public synchronized void waitForActionToFinish() throws Exception {
+ checkException();
+ while (action.getReference() != Action.HOLD) { checkException(); }
+ }
+
+ public synchronized void DoUnlock() throws Exception {
+ Error throwing = null;
+ if (!IsLocked()) {
+ // We might just be racing some exception that was thrown by the worker thread. Cache the
+ // exception, we will throw one from the worker before this one.
+ throwing = new Error("Not locked!");
+ }
+ setAction(Action.RELEASE);
+ Thread run = runner;
+ runner = null;
+ while (held) {}
+ run.join();
+ action.set(Action.HOLD, 0);
+ // Make sure to throw any exception that occurred since it might not have unlocked due to our
+ // request.
+ checkException();
+ DoCleanup();
+ if (throwing != null) {
+ throw throwing;
+ }
+ }
+
+ public synchronized void DoCleanup() throws Exception {
+ if (runner != null) {
+ Thread run = runner;
+ runner = null;
+ while (held) {}
+ run.join();
+ }
+ action.set(Action.HOLD, 0);
+ exe = null;
+ }
+ }
+}
+
diff --git a/test/1934-jvmti-signal-thread/src/art/Suspension.java b/test/1934-jvmti-signal-thread/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1934-jvmti-signal-thread/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1934-jvmti-signal-thread/src/art/Test1934.java b/test/1934-jvmti-signal-thread/src/art/Test1934.java
new file mode 100644
index 0000000..552570a
--- /dev/null
+++ b/test/1934-jvmti-signal-thread/src/art/Test1934.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.concurrent.Semaphore;
+import java.util.Objects;
+
+public class Test1934 {
+ public static final boolean PRINT_STACK_TRACE = false;
+
+ public static void run() throws Exception {
+ System.out.println("Interrupt before start");
+ testInterruptBeforeStart();
+
+ System.out.println("Stop before start");
+ testStopBeforeStart();
+
+ System.out.println("Interrupt recur");
+ testInterruptRecur();
+
+ System.out.println("Stop Recur");
+ testStopRecur();
+
+ System.out.println("Interrupt spinning");
+ testInterruptSpinning();
+
+ System.out.println("Stop spinning");
+ testStopSpinning();
+
+ System.out.println("Interrupt wait");
+ testInterruptWait();
+
+ System.out.println("Stop wait");
+ testStopWait();
+
+ System.out.println("Stop in native");
+ testStopInNative();
+ }
+
+ public static void testStopBeforeStart() throws Exception {
+ final Throwable[] out_err = new Throwable[] { null, };
+ final Object tst = new Object();
+ Thread target = new Thread(() -> { while (true) { } }, "waiting thread!");
+ target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
+ System.out.println("stopping other thread before starting");
+ try {
+ Threads.stopThread(target, new Error("AWESOME"));
+ target.start();
+ target.join();
+ System.out.println("Other thread Stopped by: " + out_err[0]);
+ if (PRINT_STACK_TRACE && out_err[0] != null) {
+ out_err[0].printStackTrace();
+ }
+ } catch (Exception e) {
+ System.out.println("Caught exception " + e);
+ }
+ }
+
+ public static void testInterruptBeforeStart() throws Exception {
+ final Throwable[] out_err = new Throwable[] { null, };
+ final Object tst = new Object();
+ Thread target = new Thread(() -> { while (true) { } }, "waiting thread!");
+ target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
+ System.out.println("interrupting other thread before starting");
+ try {
+ Threads.interruptThread(target);
+ target.start();
+ target.join();
+ System.out.println("Other thread interrupted. err: " + out_err[0]);
+ if (PRINT_STACK_TRACE && out_err[0] != null) {
+ out_err[0].printStackTrace();
+ }
+ } catch (Exception e) {
+ System.out.println("Caught exception " + e);
+ }
+ }
+
+ public static void testStopWait() throws Exception {
+ final Throwable[] out_err = new Throwable[] { null, };
+ final Object tst = new Object();
+ final Semaphore sem = new Semaphore(0);
+ Thread target = new Thread(() -> {
+ sem.release();
+ while (true) {
+ try {
+ synchronized (tst) {
+ tst.wait();
+ }
+ } catch (InterruptedException e) { throw new Error("Interrupted!", e); }
+ }
+ }, "waiting thread!");
+ target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
+ target.start();
+ sem.acquire();
+ while (!Objects.equals(Monitors.getCurrentContendedMonitor(target), tst)) {}
+ System.out.println("stopping other thread waiting");
+ Threads.stopThread(target, new Error("AWESOME"));
+ target.join();
+ System.out.println("Other thread Stopped by: " + out_err[0]);
+ if (PRINT_STACK_TRACE && out_err[0] != null) {
+ out_err[0].printStackTrace();
+ }
+ }
+
+ public static void testInterruptWait() throws Exception {
+ final Throwable[] out_err = new Throwable[] { null, };
+ final Object tst = new Object();
+ final Semaphore sem = new Semaphore(0);
+ Thread target = new Thread(() -> {
+ sem.release();
+ while (true) {
+ try {
+ synchronized (tst) {
+ tst.wait();
+ }
+ } catch (InterruptedException e) { throw new Error("Interrupted!", e); }
+ }
+ }, "waiting thread!");
+ target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
+ target.start();
+ sem.acquire();
+ while (!Objects.equals(Monitors.getCurrentContendedMonitor(target), tst)) {}
+ System.out.println("interrupting other thread waiting");
+ Threads.interruptThread(target);
+ target.join();
+ System.out.println("Other thread interrupted. err: " + out_err[0]);
+ if (PRINT_STACK_TRACE && out_err[0] != null) {
+ out_err[0].printStackTrace();
+ }
+ }
+
+ public static void doNothing() {}
+ public static native long allocNativeMonitor();
+ public static native void nativeWaitForOtherThread(long id);
+ public static native void nativeDoInterleaved(long id, Runnable op);
+ public static native void destroyNativeMonitor(long id);
+ public static void testStopInNative() throws Exception {
+ final Throwable[] out_err = new Throwable[] { null, };
+ final long native_monitor_id = allocNativeMonitor();
+ final Semaphore sem = new Semaphore(0);
+ Thread target = new Thread(() -> {
+ sem.release();
+ nativeWaitForOtherThread(native_monitor_id);
+ // We need to make sure we do something that can get the exception to be actually noticed.
+ doNothing();
+ }, "native waiting thread!");
+ target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
+ target.start();
+ sem.acquire();
+ System.out.println("stopping other thread");
+ nativeDoInterleaved(
+ native_monitor_id,
+ () -> { Threads.stopThread(target, new Error("AWESOME")); });
+ target.join();
+ System.out.println("Other thread Stopped by: " + out_err[0]);
+ if (PRINT_STACK_TRACE && out_err[0] != null) {
+ out_err[0].printStackTrace();
+ }
+ destroyNativeMonitor(native_monitor_id);
+ }
+
+ public static void doRecur(Runnable r) {
+ if (r != null) {
+ r.run();
+ }
+ doRecur(r);
+ }
+
+ public static void testStopRecur() throws Exception {
+ final Throwable[] out_err = new Throwable[] { null, };
+ final Semaphore sem = new Semaphore(0);
+ Thread target = new Thread(() -> {
+ sem.release();
+ while (true) {
+ try {
+ doRecur(null);
+ } catch (StackOverflowError e) {}
+ }
+ }, "recuring thread!");
+ target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
+ target.start();
+ sem.acquire();
+ System.out.println("stopping other thread recurring");
+ Threads.stopThread(target, new Error("AWESOME!"));
+ target.join();
+ System.out.println("Other thread Stopped by: " + out_err[0]);
+ if (PRINT_STACK_TRACE && out_err[0] != null) {
+ out_err[0].printStackTrace();
+ }
+ }
+
+ public static void testInterruptRecur() throws Exception {
+ final Throwable[] out_err = new Throwable[] { null, };
+ final Semaphore sem = new Semaphore(0);
+ Thread target = new Thread(() -> {
+ sem.release();
+ while (true) {
+ try {
+ doRecur(() -> {
+ if (Thread.currentThread().isInterrupted()) { throw new Error("Interrupted!"); }
+ });
+ } catch (StackOverflowError e) { }
+ }
+ }, "recuring thread!");
+ target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
+ target.start();
+ sem.acquire();
+ System.out.println("Interrupting other thread recurring");
+ Threads.interruptThread(target);
+ target.join();
+ System.out.println("Other thread Interrupted. err: " + out_err[0]);
+ if (PRINT_STACK_TRACE && out_err[0] != null) {
+ out_err[0].printStackTrace();
+ }
+ }
+
+ public static void testStopSpinning() throws Exception {
+ final Throwable[] out_err = new Throwable[] { null, };
+ final Semaphore sem = new Semaphore(0);
+ Thread target = new Thread(() -> { sem.release(); while (true) {} }, "Spinning thread!");
+ target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
+ target.start();
+ sem.acquire();
+ System.out.println("stopping other thread spinning");
+ Threads.stopThread(target, new Error("AWESOME!"));
+ target.join();
+ System.out.println("Other thread Stopped by: " + out_err[0]);
+ if (PRINT_STACK_TRACE && out_err[0] != null) {
+ out_err[0].printStackTrace();
+ }
+ }
+
+ public static void testInterruptSpinning() throws Exception {
+ final Semaphore sem = new Semaphore(0);
+ Thread target = new Thread(() -> {
+ sem.release();
+ while (!Thread.currentThread().isInterrupted()) { }
+ }, "Spinning thread!");
+ target.start();
+ sem.acquire();
+ System.out.println("Interrupting other thread spinning");
+ Threads.interruptThread(target);
+ target.join();
+ System.out.println("Other thread Interrupted.");
+ }
+}
diff --git a/test/1934-jvmti-signal-thread/src/art/Threads.java b/test/1934-jvmti-signal-thread/src/art/Threads.java
new file mode 100644
index 0000000..266813b
--- /dev/null
+++ b/test/1934-jvmti-signal-thread/src/art/Threads.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Threads {
+ public static native void interruptThread(Thread t);
+ public static native void stopThread(Thread t, Throwable thr);
+}