Merge "Add raw_monitor_enter_no_suspend extension"
diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc
index 5b1a16c..c61d6e5 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 @@
   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 1cfc64a..6d3a37e 100644
--- a/openjdkjvmti/ti_monitor.cc
+++ b/openjdkjvmti/ti_monitor.cc
@@ -75,14 +75,16 @@
     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 @@
         // 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 @@
     }
 
     // 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 @@
   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 @@
   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 e0a865b..5c361b4 100644
--- a/openjdkjvmti/ti_monitor.h
+++ b/openjdkjvmti/ti_monitor.h
@@ -43,6 +43,8 @@
 
   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 0000000..3582111
--- /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 0000000..a608834
--- /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 0000000..0425e35
--- /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 0000000..c6e62ae
--- /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 0000000..3c5e1a2
--- /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 0000000..aa5498b
--- /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 0000000..16e62cc
--- /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 0000000..dc7ed9e
--- /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 5d2ea1a..a3de382 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -289,6 +289,7 @@
         "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 065706c..ce4ebd7 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."]
     },