diff options
18 files changed, 560 insertions, 31 deletions
diff --git a/openjdkjvmti/ti_monitor.cc b/openjdkjvmti/ti_monitor.cc index 1ec566b33d..adaa48c0cd 100644 --- a/openjdkjvmti/ti_monitor.cc +++ b/openjdkjvmti/ti_monitor.cc @@ -40,6 +40,7 @@ #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" +#include "ti_thread.h" namespace openjdkjvmti { @@ -51,8 +52,7 @@ namespace openjdkjvmti { class JvmtiMonitor { public: - JvmtiMonitor() : owner_(nullptr), count_(0) { - } + JvmtiMonitor() : owner_(nullptr), count_(0) { } static bool Destroy(art::Thread* self, JvmtiMonitor* monitor) NO_THREAD_SAFETY_ANALYSIS { // Check whether this thread holds the monitor, or nobody does. @@ -72,13 +72,41 @@ class JvmtiMonitor { } void MonitorEnter(art::Thread* self) NO_THREAD_SAFETY_ANALYSIS { - // Check for recursive enter. - if (IsOwner(self)) { - count_++; - return; - } - - mutex_.lock(); + // Perform a suspend-check. The spec doesn't require this but real-world agents depend on this + // behavior. We do this by performing a suspend-check then retrying if the thread is suspended + // before or after locking the internal mutex. + do { + ThreadUtil::SuspendCheck(self); + if (ThreadUtil::WouldSuspendForUserCode(self)) { + continue; + } + + // Check for recursive enter. + if (IsOwner(self)) { + count_++; + return; + } + + // Checking for user-code suspension takes acquiring 2 art::Mutexes so we want to avoid doing + // that if possible. To avoid it we try to get the internal mutex without sleeping. If we do + // this we don't bother doing another suspend check since it can linearize after the lock. + if (mutex_.try_lock()) { + break; + } else { + // Lock with sleep. We will need to check for suspension after this to make sure that agents + // won't deadlock. + mutex_.lock(); + if (!ThreadUtil::WouldSuspendForUserCode(self)) { + break; + } else { + // We got suspended in the middle of waiting for the mutex. We should release the mutex + // and try again so we can get it while not suspended. This lets some other + // (non-suspended) thread acquire the mutex in case it's waiting to wake us up. + mutex_.unlock(); + continue; + } + } + } while (true); DCHECK(owner_.load(std::memory_order_relaxed) == nullptr); owner_.store(self, std::memory_order_relaxed); @@ -123,7 +151,7 @@ class JvmtiMonitor { } private: - bool IsOwner(art::Thread* self) { + bool IsOwner(art::Thread* self) const { // There's a subtle correctness argument here for a relaxed load outside the critical section. // A thread is guaranteed to see either its own latest store or another thread's store. If a // thread sees another thread's store than it cannot be holding the lock. diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc index b0a1a8556a..27d01ea17d 100644 --- a/openjdkjvmti/ti_thread.cc +++ b/openjdkjvmti/ti_thread.cc @@ -85,7 +85,8 @@ struct ThreadCallback : public art::ThreadLifecycleCallback { if (name != "JDWP" && name != "Signal Catcher" && !android::base::StartsWith(name, "Jit thread pool")) { - LOG(FATAL) << "Unexpected thread before start: " << name; + LOG(FATAL) << "Unexpected thread before start: " << name << " id: " + << self->GetThreadId(); } } return; @@ -413,13 +414,24 @@ static jint GetJavaStateFromInternal(const InternalThreadState& state) { } // Suspends the current thread if it has any suspend requests on it. -static void SuspendCheck(art::Thread* self) - REQUIRES(!art::Locks::mutator_lock_, !art::Locks::user_code_suspension_lock_) { +void ThreadUtil::SuspendCheck(art::Thread* self) { art::ScopedObjectAccess soa(self); // Really this is only needed if we are in FastJNI and actually have the mutator_lock_ already. self->FullSuspendCheck(); } +bool ThreadUtil::WouldSuspendForUserCodeLocked(art::Thread* self) { + DCHECK(self == art::Thread::Current()); + art::MutexLock tscl_mu(self, *art::Locks::thread_suspend_count_lock_); + return self->GetUserCodeSuspendCount() != 0; +} + +bool ThreadUtil::WouldSuspendForUserCode(art::Thread* self) { + DCHECK(self == art::Thread::Current()); + art::MutexLock ucsl_mu(self, *art::Locks::user_code_suspension_lock_); + return WouldSuspendForUserCodeLocked(self); +} + jvmtiError ThreadUtil::GetThreadState(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread thread, jint* thread_state_ptr) { @@ -435,13 +447,10 @@ jvmtiError ThreadUtil::GetThreadState(jvmtiEnv* env ATTRIBUTE_UNUSED, do { SuspendCheck(self); art::MutexLock ucsl_mu(self, *art::Locks::user_code_suspension_lock_); - { - art::MutexLock tscl_mu(self, *art::Locks::thread_suspend_count_lock_); - if (self->GetUserCodeSuspendCount() != 0) { - // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ - // by a user-code suspension. We retry and do another SuspendCheck to clear this. - continue; - } + if (WouldSuspendForUserCodeLocked(self)) { + // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by + // a user-code suspension. We retry and do another SuspendCheck to clear this. + continue; } art::ScopedObjectAccess soa(self); art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_); @@ -657,6 +666,9 @@ jvmtiError ThreadUtil::RunAgentThread(jvmtiEnv* jvmti_env, jvmtiStartFunction proc, const void* arg, jint priority) { + if (!PhaseUtil::IsLivePhase()) { + return ERR(WRONG_PHASE); + } if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY) { return ERR(INVALID_PRIORITY); } @@ -703,15 +715,12 @@ jvmtiError ThreadUtil::SuspendOther(art::Thread* self, // before continuing. SuspendCheck(self); art::MutexLock mu(self, *art::Locks::user_code_suspension_lock_); - { - art::MutexLock thread_suspend_count_mu(self, *art::Locks::thread_suspend_count_lock_); + if (WouldSuspendForUserCodeLocked(self)) { // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by // a user-code suspension. We retry and do another SuspendCheck to clear this. - if (self->GetUserCodeSuspendCount() != 0) { - continue; - } - // We are not going to be suspended by user code from now on. + continue; } + // We are not going to be suspended by user code from now on. { art::ScopedObjectAccess soa(self); art::MutexLock thread_list_mu(self, *art::Locks::thread_list_lock_); @@ -798,13 +807,10 @@ jvmtiError ThreadUtil::ResumeThread(jvmtiEnv* env ATTRIBUTE_UNUSED, do { SuspendCheck(self); art::MutexLock ucsl_mu(self, *art::Locks::user_code_suspension_lock_); - { - art::MutexLock tscl_mu(self, *art::Locks::thread_suspend_count_lock_); + if (WouldSuspendForUserCodeLocked(self)) { // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by // a user-code suspension. We retry and do another SuspendCheck to clear this. - if (self->GetUserCodeSuspendCount() != 0) { - continue; - } + continue; } // From now on we know we cannot get suspended by user-code. { diff --git a/openjdkjvmti/ti_thread.h b/openjdkjvmti/ti_thread.h index a19974aa16..57b194362f 100644 --- a/openjdkjvmti/ti_thread.h +++ b/openjdkjvmti/ti_thread.h @@ -98,6 +98,22 @@ class ThreadUtil { REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(art::Locks::thread_list_lock_); + // Go to sleep if this thread is suspended. + static void SuspendCheck(art::Thread* self) + REQUIRES(!art::Locks::mutator_lock_, !art::Locks::user_code_suspension_lock_); + + // Returns true if the thread would be suspended if it locks the mutator-lock or calls + // SuspendCheck. This function is called with the user_code_suspension_lock already held. + static bool WouldSuspendForUserCodeLocked(art::Thread* self) + REQUIRES(art::Locks::user_code_suspension_lock_, + !art::Locks::thread_suspend_count_lock_); + + // Returns true if this thread would go to sleep if it locks the mutator-lock or calls + // SuspendCheck. + static bool WouldSuspendForUserCode(art::Thread* self) + REQUIRES(!art::Locks::user_code_suspension_lock_, + !art::Locks::thread_suspend_count_lock_); + private: // We need to make sure only one thread tries to suspend threads at a time so we can get the // 'suspend-only-once' behavior the spec requires. Internally, ART considers suspension to be a diff --git a/test/1920-suspend-native-monitor/expected.txt b/test/1920-suspend-native-monitor/expected.txt new file mode 100644 index 0000000000..2b1db089d9 --- /dev/null +++ b/test/1920-suspend-native-monitor/expected.txt @@ -0,0 +1,8 @@ +Resumer: isNativeThreadSpinning() = true +Resumer: isSuspended(spinner) = false +Resumer: Suspended spinner while native spinning +Resumer: isNativeThreadSpinning() = false +Resumer: isSuspended(spinner) = true +Resumer: resumed spinner while native spinning +Resumer: isNativeThreadSpinning() = true +Resumer: isSuspended(spinner) = false diff --git a/test/1920-suspend-native-monitor/info.txt b/test/1920-suspend-native-monitor/info.txt new file mode 100644 index 0000000000..6505972c88 --- /dev/null +++ b/test/1920-suspend-native-monitor/info.txt @@ -0,0 +1 @@ +Tests jvmti suspending of a thread that is interacting with a raw monitor. diff --git a/test/1920-suspend-native-monitor/native_suspend_monitor.cc b/test/1920-suspend-native-monitor/native_suspend_monitor.cc new file mode 100644 index 0000000000..9decfe68ea --- /dev/null +++ b/test/1920-suspend-native-monitor/native_suspend_monitor.cc @@ -0,0 +1,81 @@ +/* + * 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 <atomic> + +#include "android-base/logging.h" +#include "jni.h" +#include "scoped_local_ref.h" +#include "scoped_primitive_array.h" + +#include "jvmti.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "test_env.h" + +namespace art { +namespace Test1920NativeSuspendMonitor { + +std::atomic<bool> should_pause(false); +std::atomic<bool> paused(false); +std::atomic<bool> done(false); +std::atomic<bool> started(false); + +extern "C" JNIEXPORT void JNICALL Java_art_Test1920_nativeSpin(JNIEnv* env, jclass) { + jrawMonitorID mon; + if (JvmtiErrorToException( + env, jvmti_env, jvmti_env->CreateRawMonitor("Test1920 monitor", &mon))) { + return; + } + while (!done.load()) { + while (should_pause.load()) { + paused.store(true); + } + paused.store(false); + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(mon))) { + return; + } + started.store(true); + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon))) { + return; + } + } +} + +extern "C" JNIEXPORT jboolean JNICALL Java_art_Test1920_isNativeThreadSpinning(JNIEnv*, jclass) { + return started.load(); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1920_pause(JNIEnv*, jclass) { + should_pause.store(true); + paused.store(false); + while (!paused.load()) { } +} +extern "C" JNIEXPORT void JNICALL Java_art_Test1920_resume(JNIEnv*, jclass) { + should_pause.store(false); + while (paused.load()) { } +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1920_reset(JNIEnv*, jclass) { + started.store(false); +} +extern "C" JNIEXPORT void JNICALL Java_art_Test1920_nativeFinish(JNIEnv*, jclass) { + done.store(true); +} + +} // namespace Test1920NativeSuspendMonitor +} // namespace art diff --git a/test/1920-suspend-native-monitor/run b/test/1920-suspend-native-monitor/run new file mode 100755 index 0000000000..e92b873956 --- /dev/null +++ b/test/1920-suspend-native-monitor/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/1920-suspend-native-monitor/src/Main.java b/test/1920-suspend-native-monitor/src/Main.java new file mode 100644 index 0000000000..ad6643593b --- /dev/null +++ b/test/1920-suspend-native-monitor/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.Test1920.run(); + } +} diff --git a/test/1920-suspend-native-monitor/src/art/Suspension.java b/test/1920-suspend-native-monitor/src/art/Suspension.java new file mode 100644 index 0000000000..16e62ccac9 --- /dev/null +++ b/test/1920-suspend-native-monitor/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/1920-suspend-native-monitor/src/art/Test1920.java b/test/1920-suspend-native-monitor/src/art/Test1920.java new file mode 100644 index 0000000000..1dca7275b8 --- /dev/null +++ b/test/1920-suspend-native-monitor/src/art/Test1920.java @@ -0,0 +1,76 @@ +/* + * 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 Test1920 { + public static void run() throws Exception { + final Thread spinner = new Thread(() -> { + nativeSpin(); + }, "Spinner"); + + final Thread resumer = new Thread(() -> { + String me = Thread.currentThread().getName(); + + // wait for the other thread to start spinning. + while (!isNativeThreadSpinning()) { } + + System.out.println(me + ": isNativeThreadSpinning() = " + isNativeThreadSpinning()); + System.out.println(me + ": isSuspended(spinner) = " + Suspension.isSuspended(spinner)); + + // Make the native thread wait before calling monitor-enter. + pause(); + // Reset the marker bit. + reset(); + // Suspend it from java. + Suspension.suspend(spinner); + // Let the thread try to lock the monitor. + resume(); + + // Wait for the other thread to do something. + try { Thread.sleep(1000); } catch (Exception e) {} + + System.out.println(me + ": Suspended spinner while native spinning"); + System.out.println(me + ": isNativeThreadSpinning() = " + isNativeThreadSpinning()); + System.out.println(me + ": isSuspended(spinner) = " + Suspension.isSuspended(spinner)); + + // Resume it from java. It is still native spinning. + Suspension.resume(spinner); + + System.out.println(me + ": resumed spinner while native spinning"); + + // wait for the other thread to start spinning again. + while (!isNativeThreadSpinning()) { } + + System.out.println(me + ": isNativeThreadSpinning() = " + isNativeThreadSpinning()); + System.out.println(me + ": isSuspended(spinner) = " + Suspension.isSuspended(spinner)); + nativeFinish(); + }, "Resumer"); + + spinner.start(); + resumer.start(); + + spinner.join(); + resumer.join(); + } + + public static native void nativeSpin(); + public static native void nativeFinish(); + public static native void reset(); + public static native void pause(); + public static native void resume(); + public static native boolean isNativeThreadSpinning(); +} diff --git a/test/1921-suspend-native-recursive-monitor/expected.txt b/test/1921-suspend-native-recursive-monitor/expected.txt new file mode 100644 index 0000000000..2b1db089d9 --- /dev/null +++ b/test/1921-suspend-native-recursive-monitor/expected.txt @@ -0,0 +1,8 @@ +Resumer: isNativeThreadSpinning() = true +Resumer: isSuspended(spinner) = false +Resumer: Suspended spinner while native spinning +Resumer: isNativeThreadSpinning() = false +Resumer: isSuspended(spinner) = true +Resumer: resumed spinner while native spinning +Resumer: isNativeThreadSpinning() = true +Resumer: isSuspended(spinner) = false diff --git a/test/1921-suspend-native-recursive-monitor/info.txt b/test/1921-suspend-native-recursive-monitor/info.txt new file mode 100644 index 0000000000..c47f86f8a7 --- /dev/null +++ b/test/1921-suspend-native-recursive-monitor/info.txt @@ -0,0 +1,2 @@ +Tests JVMTI suspending of a thread that is interacting with a raw monitor +recursively. diff --git a/test/1921-suspend-native-recursive-monitor/native_suspend_recursive_monitor.cc b/test/1921-suspend-native-recursive-monitor/native_suspend_recursive_monitor.cc new file mode 100644 index 0000000000..b39f590852 --- /dev/null +++ b/test/1921-suspend-native-recursive-monitor/native_suspend_recursive_monitor.cc @@ -0,0 +1,89 @@ +/* + * 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 <atomic> + +#include "android-base/logging.h" +#include "jni.h" +#include "scoped_local_ref.h" +#include "scoped_primitive_array.h" + +#include "jvmti.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "test_env.h" + +namespace art { +namespace Test1921NativeSuspendMonitorRecursive { + +std::atomic<bool> should_pause(false); +std::atomic<bool> paused(false); +std::atomic<bool> done(false); +std::atomic<bool> started(false); + +extern "C" JNIEXPORT void JNICALL Java_art_Test1921_nativeSpin(JNIEnv* env, jclass) { + jrawMonitorID mon; + if (JvmtiErrorToException( + env, jvmti_env, jvmti_env->CreateRawMonitor("Test1921 monitor", &mon))) { + return; + } + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(mon))) { + return; + } + while (!done.load()) { + while (should_pause.load()) { + paused.store(true); + } + paused.store(false); + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(mon))) { + return; + } + started.store(true); + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon))) { + return; + } + } + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon))) { + return; + } +} + +extern "C" JNIEXPORT jboolean JNICALL Java_art_Test1921_isNativeThreadSpinning(JNIEnv*, jclass) { + return started.load(); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1921_pause(JNIEnv*, jclass) { + should_pause.store(true); + paused.store(false); + while (!paused.load()) { } +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1921_resume(JNIEnv*, jclass) { + should_pause.store(false); + while (paused.load()) { } +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1921_reset(JNIEnv*, jclass) { + started.store(false); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1921_nativeFinish(JNIEnv*, jclass) { + done.store(true); +} + +} // namespace Test1921NativeSuspendMonitorRecursive +} // namespace art diff --git a/test/1921-suspend-native-recursive-monitor/run b/test/1921-suspend-native-recursive-monitor/run new file mode 100755 index 0000000000..e92b873956 --- /dev/null +++ b/test/1921-suspend-native-recursive-monitor/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/1921-suspend-native-recursive-monitor/src/Main.java b/test/1921-suspend-native-recursive-monitor/src/Main.java new file mode 100644 index 0000000000..2523e50c79 --- /dev/null +++ b/test/1921-suspend-native-recursive-monitor/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.Test1921.run(); + } +} diff --git a/test/1921-suspend-native-recursive-monitor/src/art/Suspension.java b/test/1921-suspend-native-recursive-monitor/src/art/Suspension.java new file mode 100644 index 0000000000..16e62ccac9 --- /dev/null +++ b/test/1921-suspend-native-recursive-monitor/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/1921-suspend-native-recursive-monitor/src/art/Test1921.java b/test/1921-suspend-native-recursive-monitor/src/art/Test1921.java new file mode 100644 index 0000000000..b5fd24337a --- /dev/null +++ b/test/1921-suspend-native-recursive-monitor/src/art/Test1921.java @@ -0,0 +1,76 @@ +/* + * 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 Test1921 { + public static void run() throws Exception { + final Thread spinner = new Thread(() -> { + nativeSpin(); + }, "Spinner"); + + final Thread resumer = new Thread(() -> { + String me = Thread.currentThread().getName(); + + // wait for the other thread to start spinning. + while (!isNativeThreadSpinning()) { } + + System.out.println(me + ": isNativeThreadSpinning() = " + isNativeThreadSpinning()); + System.out.println(me + ": isSuspended(spinner) = " + Suspension.isSuspended(spinner)); + + // Make the native thread wait before calling monitor-enter. + pause(); + // Reset the marker bit. + reset(); + // Suspend it from java. + Suspension.suspend(spinner); + // Let the thread try to lock the monitor. + resume(); + + // Wait for the other thread to do something. + try { Thread.sleep(1000); } catch (Exception e) {} + + System.out.println(me + ": Suspended spinner while native spinning"); + System.out.println(me + ": isNativeThreadSpinning() = " + isNativeThreadSpinning()); + System.out.println(me + ": isSuspended(spinner) = " + Suspension.isSuspended(spinner)); + + // Resume it from java. It is still native spinning. + Suspension.resume(spinner); + + System.out.println(me + ": resumed spinner while native spinning"); + + // wait for the other thread to start spinning again. + while (!isNativeThreadSpinning()) { } + + System.out.println(me + ": isNativeThreadSpinning() = " + isNativeThreadSpinning()); + System.out.println(me + ": isSuspended(spinner) = " + Suspension.isSuspended(spinner)); + nativeFinish(); + }, "Resumer"); + + spinner.start(); + resumer.start(); + + spinner.join(); + resumer.join(); + } + + public static native void nativeSpin(); + public static native void nativeFinish(); + public static native void reset(); + public static native void pause(); + public static native void resume(); + public static native boolean isNativeThreadSpinning(); +} diff --git a/test/Android.bp b/test/Android.bp index f6f36c852e..4d82cbb444 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -292,6 +292,8 @@ art_cc_defaults { "1909-per-agent-tls/agent_tls.cc", "1914-get-local-instance/local_instance.cc", "1919-vminit-thread-start-timing/vminit.cc", + "1920-suspend-native-monitor/native_suspend_monitor.cc", + "1921-suspend-native-recursive-monitor/native_suspend_recursive_monitor.cc", ], shared_libs: [ "libbase", |