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/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index bac57f9..b30d45a 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -163,18 +163,16 @@
return ThreadUtil::ResumeThreadList(env, request_count, request_list, results);
}
- static jvmtiError StopThread(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jobject exception ATTRIBUTE_UNUSED) {
+ static jvmtiError StopThread(jvmtiEnv* env, jthread thread, jobject exception) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_signal_thread);
- return ERR(NOT_IMPLEMENTED);
+ return ThreadUtil::StopThread(env, thread, exception);
}
- static jvmtiError InterruptThread(jvmtiEnv* env, jthread thread ATTRIBUTE_UNUSED) {
+ static jvmtiError InterruptThread(jvmtiEnv* env, jthread thread) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_signal_thread);
- return ERR(NOT_IMPLEMENTED);
+ return ThreadUtil::InterruptThread(env, thread);
}
static jvmtiError GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadInfo* info_ptr) {
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 10ddfc1..ad405e8 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -229,7 +229,7 @@
.can_get_monitor_info = 1,
.can_pop_frame = 0,
.can_redefine_classes = 1,
- .can_signal_thread = 0,
+ .can_signal_thread = 1,
.can_get_source_file_name = 1,
.can_get_line_numbers = 1,
.can_get_source_debug_extension = 1,
diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc
index 907b515..9a809df 100644
--- a/openjdkjvmti/ti_thread.cc
+++ b/openjdkjvmti/ti_thread.cc
@@ -949,4 +949,65 @@
return OK;
}
+jvmtiError ThreadUtil::StopThread(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jthread thread,
+ jobject exception) {
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::StackHandleScope<1> hs(self);
+ if (exception == nullptr) {
+ return ERR(INVALID_OBJECT);
+ }
+ art::ObjPtr<art::mirror::Object> obj(soa.Decode<art::mirror::Object>(exception));
+ if (!obj->GetClass()->IsThrowableClass()) {
+ return ERR(INVALID_OBJECT);
+ }
+ art::Handle<art::mirror::Throwable> exc(hs.NewHandle(obj->AsThrowable()));
+ art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_);
+ art::Thread* target = nullptr;
+ jvmtiError err = ERR(INTERNAL);
+ if (!GetAliveNativeThread(thread, soa, &target, &err)) {
+ return err;
+ } else if (target->GetState() == art::ThreadState::kStarting || target->IsStillStarting()) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ struct StopThreadClosure : public art::Closure {
+ public:
+ explicit StopThreadClosure(art::Handle<art::mirror::Throwable> except) : exception_(except) { }
+
+ void Run(art::Thread* me) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ // Make sure the thread is prepared to notice the exception.
+ art::Runtime::Current()->GetInstrumentation()->InstrumentThreadStack(me);
+ me->SetAsyncException(exception_.Get());
+ // Wake up the thread if it is sleeping.
+ me->Notify();
+ }
+
+ private:
+ art::Handle<art::mirror::Throwable> exception_;
+ };
+ StopThreadClosure c(exc);
+ if (target->RequestSynchronousCheckpoint(&c)) {
+ return OK;
+ } else {
+ // Something went wrong, probably the thread died.
+ return ERR(THREAD_NOT_ALIVE);
+ }
+}
+
+jvmtiError ThreadUtil::InterruptThread(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread thread) {
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_);
+ art::Thread* target = nullptr;
+ jvmtiError err = ERR(INTERNAL);
+ if (!GetAliveNativeThread(thread, soa, &target, &err)) {
+ return err;
+ } else if (target->GetState() == art::ThreadState::kStarting || target->IsStillStarting()) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ target->Interrupt(self);
+ return OK;
+}
+
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_thread.h b/openjdkjvmti/ti_thread.h
index ceebff6..09b4cab 100644
--- a/openjdkjvmti/ti_thread.h
+++ b/openjdkjvmti/ti_thread.h
@@ -93,6 +93,9 @@
const jthread* threads,
jvmtiError* results);
+ static jvmtiError StopThread(jvmtiEnv* env, jthread thr, jobject exception);
+ static jvmtiError InterruptThread(jvmtiEnv* env, jthread thr);
+
// Returns true if we decoded the thread and it is alive, false otherwise with an appropriate
// error placed into 'err'. A thread is alive if it has had it's 'start' function called and has
// (or at least could have) executed managed code and has not yet returned past it's first managed