diff options
author | 2018-07-26 08:28:36 -0700 | |
---|---|---|
committer | 2018-07-26 11:33:25 -0700 | |
commit | c98f83e301451cb7a62aab21a7dfcddc8eb17c42 (patch) | |
tree | 18d0477a315cf35352e4c44b3a66ce5d185a5fc4 | |
parent | f5dcd31d89282b6c9324fdc960e6e7e2281c16f1 (diff) |
Add raw_monitor_enter_no_suspend extension
In some circumstances it is useful to be able to lock a jvmti-monitor
without having to worry about the suspension state of the current
thread. This adds an extension
com.android.art.concurrent.raw_monitor_enter_no_suspend that allows
one to do that. This function will gain the monitor lock and return
even if the current thread is suspended. The normal RawMonitorLock
will not return unless the thread is not in a suspended state.
Test: ./test.py --host
Bug: 76205593
Change-Id: I9d9fcd586d1d2555f4adc8ac85597daa3dfcb0c4
-rw-r--r-- | openjdkjvmti/ti_extension.cc | 20 | ||||
-rw-r--r-- | openjdkjvmti/ti_monitor.cc | 30 | ||||
-rw-r--r-- | openjdkjvmti/ti_monitor.h | 2 | ||||
-rw-r--r-- | test/1951-monitor-enter-no-suspend/expected.txt | 1 | ||||
-rw-r--r-- | test/1951-monitor-enter-no-suspend/info.txt | 1 | ||||
-rw-r--r-- | test/1951-monitor-enter-no-suspend/raw_monitor.cc | 104 | ||||
-rwxr-xr-x | test/1951-monitor-enter-no-suspend/run | 17 | ||||
-rw-r--r-- | test/1951-monitor-enter-no-suspend/src/Main.java | 21 | ||||
-rw-r--r-- | test/1951-monitor-enter-no-suspend/src/art/Main.java | 32 | ||||
-rw-r--r-- | test/1951-monitor-enter-no-suspend/src/art/Suspension.java | 30 | ||||
-rw-r--r-- | test/1951-monitor-enter-no-suspend/src/art/Test1951.java | 65 | ||||
-rw-r--r-- | test/Android.bp | 1 | ||||
-rw-r--r-- | test/knownfailures.json | 3 |
13 files changed, 319 insertions, 8 deletions
diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc index 5b1a16c3ff..c61d6e585c 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -39,6 +39,7 @@ #include "ti_class.h" #include "ti_ddms.h" #include "ti_heap.h" +#include "ti_monitor.h" #include "thread-inl.h" namespace openjdkjvmti { @@ -252,6 +253,25 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, if (error != ERR(NONE)) { return error; } + + // Raw monitors no suspend + error = add_extension( + reinterpret_cast<jvmtiExtensionFunction>(MonitorUtil::RawMonitorEnterNoSuspend), + "com.android.art.concurrent.raw_monitor_enter_no_suspend", + "Normally entering a monitor will not return until both the monitor is locked and the" + " current thread is not suspended. This method will return once the monitor is locked" + " even if the thread is suspended. Note that using rawMonitorWait will wait until the" + " thread is not suspended again on wakeup and so should be avoided.", + { + { "raw_monitor", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, false }, + }, + { + ERR(NULL_POINTER), + ERR(INVALID_MONITOR), + }); + if (error != ERR(NONE)) { + return error; + } // Copy into output buffer. *extension_count_ptr = ext_vector.size(); diff --git a/openjdkjvmti/ti_monitor.cc b/openjdkjvmti/ti_monitor.cc index 1cfc64a61d..6d3a37e333 100644 --- a/openjdkjvmti/ti_monitor.cc +++ b/openjdkjvmti/ti_monitor.cc @@ -75,14 +75,16 @@ class JvmtiMonitor { return true; } - void MonitorEnter(art::Thread* self) NO_THREAD_SAFETY_ANALYSIS { + void MonitorEnter(art::Thread* self, bool suspend) NO_THREAD_SAFETY_ANALYSIS { // 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; + if (suspend) { + ThreadUtil::SuspendCheck(self); + if (ThreadUtil::WouldSuspendForUserCode(self)) { + continue; + } } // Check for recursive enter. @@ -100,7 +102,7 @@ class JvmtiMonitor { // 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)) { + if (!suspend || !ThreadUtil::WouldSuspendForUserCode(self)) { break; } else { // We got suspended in the middle of waiting for the mutex. We should release the mutex @@ -187,7 +189,8 @@ class JvmtiMonitor { } // Reaquire the mutex/monitor, also go to sleep if we were suspended. - MonitorEnter(self); + // TODO Give an extension to wait without suspension as well. + MonitorEnter(self, /*suspend*/ true); CHECK(owner_.load(std::memory_order_relaxed) == self); DCHECK_EQ(1u, count_); // Reset the count. @@ -249,6 +252,19 @@ jvmtiError MonitorUtil::DestroyRawMonitor(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMo return ERR(NONE); } +jvmtiError MonitorUtil::RawMonitorEnterNoSuspend(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) { + if (id == nullptr) { + return ERR(INVALID_MONITOR); + } + + JvmtiMonitor* monitor = DecodeMonitor(id); + art::Thread* self = art::Thread::Current(); + + monitor->MonitorEnter(self, /*suspend*/false); + + return ERR(NONE); +} + jvmtiError MonitorUtil::RawMonitorEnter(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) { if (id == nullptr) { return ERR(INVALID_MONITOR); @@ -257,7 +273,7 @@ jvmtiError MonitorUtil::RawMonitorEnter(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMoni JvmtiMonitor* monitor = DecodeMonitor(id); art::Thread* self = art::Thread::Current(); - monitor->MonitorEnter(self); + monitor->MonitorEnter(self, /*suspend*/true); return ERR(NONE); } diff --git a/openjdkjvmti/ti_monitor.h b/openjdkjvmti/ti_monitor.h index e0a865b9fa..5c361b43d0 100644 --- a/openjdkjvmti/ti_monitor.h +++ b/openjdkjvmti/ti_monitor.h @@ -43,6 +43,8 @@ class MonitorUtil { static jvmtiError DestroyRawMonitor(jvmtiEnv* env, jrawMonitorID monitor); + static jvmtiError RawMonitorEnterNoSuspend(jvmtiEnv* env, jrawMonitorID monitor); + static jvmtiError RawMonitorEnter(jvmtiEnv* env, jrawMonitorID monitor); static jvmtiError RawMonitorExit(jvmtiEnv* env, jrawMonitorID monitor); diff --git a/test/1951-monitor-enter-no-suspend/expected.txt b/test/1951-monitor-enter-no-suspend/expected.txt new file mode 100644 index 0000000000..35821117c8 --- /dev/null +++ b/test/1951-monitor-enter-no-suspend/expected.txt @@ -0,0 +1 @@ +Success diff --git a/test/1951-monitor-enter-no-suspend/info.txt b/test/1951-monitor-enter-no-suspend/info.txt new file mode 100644 index 0000000000..a608834793 --- /dev/null +++ b/test/1951-monitor-enter-no-suspend/info.txt @@ -0,0 +1 @@ +Tests the jvmti-extension to lock a monitor without regards to suspension. diff --git a/test/1951-monitor-enter-no-suspend/raw_monitor.cc b/test/1951-monitor-enter-no-suspend/raw_monitor.cc new file mode 100644 index 0000000000..0425e350fd --- /dev/null +++ b/test/1951-monitor-enter-no-suspend/raw_monitor.cc @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2013 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 "jvmti.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "scoped_local_ref.h" +#include "test_env.h" + +namespace art { +namespace Test1951MonitorEnterNoSuspend { + +typedef jvmtiError (*RawMonitorEnterNoSuspend)(jvmtiEnv* env, jrawMonitorID mon); + +template <typename T> +static void Dealloc(T* t) { + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(t)); +} + +template <typename T, typename ...Rest> +static void Dealloc(T* t, Rest... rs) { + Dealloc(t); + Dealloc(rs...); +} +static void DeallocParams(jvmtiParamInfo* params, jint n_params) { + for (jint i = 0; i < n_params; i++) { + Dealloc(params[i].name); + } +} + +RawMonitorEnterNoSuspend GetNoSuspendFunction(JNIEnv* env) { + // Get the extensions. + jint n_ext = 0; + jvmtiExtensionFunctionInfo* infos = nullptr; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionFunctions(&n_ext, &infos))) { + return nullptr; + } + RawMonitorEnterNoSuspend result = nullptr; + for (jint i = 0; i < n_ext; i++) { + jvmtiExtensionFunctionInfo* cur_info = &infos[i]; + if (strcmp("com.android.art.concurrent.raw_monitor_enter_no_suspend", cur_info->id) == 0) { + result = reinterpret_cast<RawMonitorEnterNoSuspend>(cur_info->func); + } + // Cleanup the cur_info + DeallocParams(cur_info->params, cur_info->param_count); + Dealloc(cur_info->id, cur_info->short_description, cur_info->params, cur_info->errors); + } + // Cleanup the array. + Dealloc(infos); + return result; +} + +static std::atomic<bool> started(false); +static std::atomic<bool> resumed(false); +static std::atomic<bool> progress(false); + +extern "C" JNIEXPORT void JNICALL Java_art_Test1951_otherThreadStart(JNIEnv* env, jclass) { + jrawMonitorID mon; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->CreateRawMonitor("test 1951", &mon))) { + return; + } + RawMonitorEnterNoSuspend enter_func = GetNoSuspendFunction(env); + if (enter_func == nullptr) { + return; + } + started = true; + while (!resumed) {} + jvmtiError err = enter_func(jvmti_env, mon); + CHECK_EQ(err, JVMTI_ERROR_NONE); + progress = true; + err = jvmti_env->RawMonitorExit(mon); + CHECK_EQ(err, JVMTI_ERROR_NONE); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1951_waitForStart(JNIEnv*, jclass) { + while (!started) {} +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1951_otherThreadResume(JNIEnv*, jclass) { + resumed = true; +} + +extern "C" JNIEXPORT jboolean JNICALL Java_art_Test1951_otherThreadProgressed(JNIEnv*, jclass) { + return progress; +} + +} // namespace Test1951MonitorEnterNoSuspend +} // namespace art diff --git a/test/1951-monitor-enter-no-suspend/run b/test/1951-monitor-enter-no-suspend/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/1951-monitor-enter-no-suspend/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 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/1951-monitor-enter-no-suspend/src/Main.java b/test/1951-monitor-enter-no-suspend/src/Main.java new file mode 100644 index 0000000000..3c5e1a2403 --- /dev/null +++ b/test/1951-monitor-enter-no-suspend/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016 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.Test1951.run(); + } +} diff --git a/test/1951-monitor-enter-no-suspend/src/art/Main.java b/test/1951-monitor-enter-no-suspend/src/art/Main.java new file mode 100644 index 0000000000..aa5498bd62 --- /dev/null +++ b/test/1951-monitor-enter-no-suspend/src/art/Main.java @@ -0,0 +1,32 @@ +/* + * 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; + +// Binder class so the agent's C code has something that can be bound and exposed to tests. +// In a package to separate cleanly and work around CTS reference issues (though this class +// should be replaced in the CTS version). +public class Main { + // Load the given class with the given classloader, and bind all native methods to corresponding + // C methods in the agent. Will abort if any of the steps fail. + public static native void bindAgentJNI(String className, ClassLoader classLoader); + // Same as above, giving the class directly. + public static native void bindAgentJNIForClass(Class<?> klass); + + // Common infrastructure. + public static native void setTag(Object o, long tag); + public static native long getTag(Object o); +} diff --git a/test/1951-monitor-enter-no-suspend/src/art/Suspension.java b/test/1951-monitor-enter-no-suspend/src/art/Suspension.java new file mode 100644 index 0000000000..16e62ccac9 --- /dev/null +++ b/test/1951-monitor-enter-no-suspend/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/1951-monitor-enter-no-suspend/src/art/Test1951.java b/test/1951-monitor-enter-no-suspend/src/art/Test1951.java new file mode 100644 index 0000000000..dc7ed9eb5a --- /dev/null +++ b/test/1951-monitor-enter-no-suspend/src/art/Test1951.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016 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.ref.WeakReference; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Semaphore; + +public class Test1951 { + + // Wait up to 1 minute for the other thread to make progress. + public static final long WAIT_TIME_MILLIS = 1000 * 60; + public static void run() throws Exception { + Thread t = new Thread(Test1951::otherThreadStart); + t.setDaemon(true); + t.start(); + waitForStart(); + Suspension.suspend(t); + otherThreadResume(); + long endTime = System.currentTimeMillis() + WAIT_TIME_MILLIS; + boolean otherProgressed = false; + while (true) { + if (otherThreadProgressed()) { + otherProgressed = true; + break; + } else if (System.currentTimeMillis() > endTime) { + break; + } else { + Thread.yield(); + } + } + Suspension.resume(t); + if (otherProgressed) { + t.join(1000); + } + if (otherProgressed) { + System.out.println("Success"); + } else { + System.out.println( + "Failure: other thread did not make progress in " + WAIT_TIME_MILLIS + " ms"); + } + return; + } + + public static native void otherThreadStart(); + public static native void waitForStart(); + public static native void otherThreadResume(); + public static native boolean otherThreadProgressed(); +} diff --git a/test/Android.bp b/test/Android.bp index 5d2ea1aa7f..a3de382059 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -289,6 +289,7 @@ art_cc_defaults { "1943-suspend-raw-monitor-wait/native_suspend_monitor.cc", "1946-list-descriptors/descriptors.cc", "1950-unprepared-transform/unprepared_transform.cc", + "1951-monitor-enter-no-suspend/raw_monitor.cc", ], // Use NDK-compatible headers for ctstiagent. header_libs: [ diff --git a/test/knownfailures.json b/test/knownfailures.json index 065706c842..ce4ebd76a5 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -968,7 +968,8 @@ "677-fsi2", "678-quickening", "679-locks", - "999-redefine-hiddenapi"], + "999-redefine-hiddenapi", + "1951-monitor-enter-no-suspend"], "variant": "jvm", "description": ["Doesn't run on RI."] }, |