Emit JVMTI events for LockSupport.park
This restores the behavior from when park was implemented with
wait/notify, except that the blocker object is used as the monitor
rather than the Thread that is parking.
Bug: 28845097
Tested: ./test/run-test --jvm 1931
Change-Id: I523d719ca3e49a538c06f48f032b322fb91e147e
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 758917c..bf74816 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -151,6 +151,26 @@
Remove(cb, &monitor_callbacks_);
}
+void RuntimeCallbacks::ThreadParkStart(bool is_absolute, int64_t timeout) {
+ for (ParkCallback * cb : park_callbacks_) {
+ cb->ThreadParkStart(is_absolute, timeout);
+ }
+}
+
+void RuntimeCallbacks::ThreadParkFinished(bool timeout) {
+ for (ParkCallback * cb : park_callbacks_) {
+ cb->ThreadParkFinished(timeout);
+ }
+}
+
+void RuntimeCallbacks::AddParkCallback(ParkCallback* cb) {
+ park_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveParkCallback(ParkCallback* cb) {
+ Remove(cb, &park_callbacks_);
+}
+
void RuntimeCallbacks::RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
Remove(cb, &thread_callbacks_);
}
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index 9f0410d..4cce15e 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -115,6 +115,19 @@
virtual ~MonitorCallback() {}
};
+class ParkCallback {
+ public:
+ // Called on entry to the Unsafe.#park method
+ virtual void ThreadParkStart(bool is_absolute, int64_t millis_timeout)
+ REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+ // Called just after the thread has woken up from going to sleep for a park(). This will only be
+ // called for Unsafe.park() calls where the thread did (or at least could have) gone to sleep.
+ virtual void ThreadParkFinished(bool timed_out) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+ virtual ~ParkCallback() {}
+};
+
// A callback to let parts of the runtime note that they are currently relying on a particular
// method remaining in it's current state. Users should not rely on always being called. If multiple
// callbacks are added the runtime will short-circuit when the first one returns 'true'.
@@ -193,6 +206,11 @@
void AddMonitorCallback(MonitorCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
void RemoveMonitorCallback(MonitorCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
+ void ThreadParkStart(bool is_absolute, int64_t timeout) REQUIRES_SHARED(Locks::mutator_lock_);
+ void ThreadParkFinished(bool timed_out) REQUIRES_SHARED(Locks::mutator_lock_);
+ void AddParkCallback(ParkCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
+ void RemoveParkCallback(ParkCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
+
// Returns true if some MethodInspectionCallback indicates the method is being inspected/depended
// on by some code.
bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -243,6 +261,8 @@
GUARDED_BY(Locks::mutator_lock_);
std::vector<MonitorCallback*> monitor_callbacks_
GUARDED_BY(Locks::mutator_lock_);
+ std::vector<ParkCallback*> park_callbacks_
+ GUARDED_BY(Locks::mutator_lock_);
std::vector<MethodInspectionCallback*> method_inspection_callbacks_
GUARDED_BY(Locks::mutator_lock_);
std::vector<DdmCallback*> ddm_callbacks_
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 7a06be9..e9fed76 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -302,8 +302,9 @@
int old_state = tls32_.park_state_.fetch_add(1, std::memory_order_relaxed);
if (old_state == kNoPermit) {
// no permit was available. block thread until later.
- // TODO: Call to signal jvmti here
+ Runtime::Current()->GetRuntimeCallbacks()->ThreadParkStart(is_absolute, time);
int result = 0;
+ bool timed_out = false;
if (!is_absolute && time == 0) {
// Thread.getState() is documented to return waiting for untimed parks.
ScopedThreadSuspension sts(this, ThreadState::kWaiting);
@@ -351,8 +352,10 @@
}
if (result == -1) {
switch (errno) {
- case EAGAIN:
case ETIMEDOUT:
+ timed_out = true;
+ FALLTHROUGH_INTENDED;
+ case EAGAIN:
case EINTR: break; // park() is allowed to spuriously return
default: PLOG(FATAL) << "Failed to park";
}
@@ -360,6 +363,7 @@
// Mark as no longer waiting, and consume permit if there is one.
tls32_.park_state_.store(kNoPermit, std::memory_order_relaxed);
// TODO: Call to signal jvmti here
+ Runtime::Current()->GetRuntimeCallbacks()->ThreadParkFinished(timed_out);
} else {
// the fetch_add has consumed the permit. immediately return.
DCHECK_EQ(old_state, kPermitAvailable);
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 94faa62..65039bc 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -122,6 +122,7 @@
jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
+jfieldID WellKnownClasses::java_lang_Thread_parkBlocker;
jfieldID WellKnownClasses::java_lang_Thread_daemon;
jfieldID WellKnownClasses::java_lang_Thread_group;
jfieldID WellKnownClasses::java_lang_Thread_lock;
@@ -371,6 +372,7 @@
dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;");
dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(env, dalvik_system_VMRuntime, true, "nonSdkApiUsageConsumer", "Ljava/util/function/Consumer;");
+ java_lang_Thread_parkBlocker = CacheField(env, java_lang_Thread, false, "parkBlocker", "Ljava/lang/Object;");
java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
@@ -518,6 +520,7 @@
dalvik_system_DexPathList_dexElements = nullptr;
dalvik_system_DexPathList__Element_dexFile = nullptr;
dalvik_system_VMRuntime_nonSdkApiUsageConsumer = nullptr;
+ java_lang_Thread_parkBlocker = nullptr;
java_lang_Thread_daemon = nullptr;
java_lang_Thread_group = nullptr;
java_lang_Thread_lock = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 8c85228..130747c 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -131,6 +131,7 @@
static jfieldID dalvik_system_DexPathList_dexElements;
static jfieldID dalvik_system_DexPathList__Element_dexFile;
static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
+ static jfieldID java_lang_Thread_parkBlocker;
static jfieldID java_lang_Thread_daemon;
static jfieldID java_lang_Thread_group;
static jfieldID java_lang_Thread_lock;