Revert "Revert "Make JVMTI DisposeEnvironment and GetEnv thread safe.""
This reverts commit af9341087aab0146b8323ece156bde8130948465.
We needed to allow TopLockLevel locks to be acquired when the
mutator_lock_ is exclusive held. This is required for spec
conformance. To ensure there are no deadlocks the mutator_lock_ is the
only lock level with this exception and one cannot acquire the
mutator_lock_ when one holds any kTopLockLevel locks.
Reason for revert: Fixed issue causing test 913 failure in art-gc-gss-tlab
Test: ART_DEFAULT_GC_TYPE=GSS \
ART_USE_TLAB=true \
ART_USE_READ_BARRIER=false
./test.py --host -j50
Bug: 69465262
Change-Id: Ic1a4d9bb3ff64382ba7ae22ba27a4f44628ed095
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 682b82b..e8e62c2 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -94,6 +94,10 @@
static ArtJvmTiEnv* AsArtJvmTiEnv(jvmtiEnv* env) {
return art::down_cast<ArtJvmTiEnv*>(env);
}
+
+ // Top level lock. Nothing can be held when we get this except for mutator lock for full
+ // thread-suspension.
+ static art::Mutex *gEnvMutex ACQUIRED_AFTER(art::Locks::mutator_lock_);
};
// Macro and constexpr to make error values less annoying to write.
diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h
index 5344e0f..007669b 100644
--- a/openjdkjvmti/events-inl.h
+++ b/openjdkjvmti/events-inl.h
@@ -46,6 +46,45 @@
namespace impl {
+// Helper for ensuring that the dispatch environment is sane. Events with JNIEnvs need to stash
+// pending exceptions since they can cause new ones to be thrown. In accordance with the JVMTI
+// specification we allow exceptions originating from events to overwrite the current exception,
+// including exceptions originating from earlier events.
+class ScopedEventDispatchEnvironment FINAL : public art::ValueObject {
+ public:
+ ScopedEventDispatchEnvironment() : env_(nullptr), throw_(nullptr, nullptr) {
+ DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative);
+ }
+
+ explicit ScopedEventDispatchEnvironment(JNIEnv* env)
+ : env_(env),
+ throw_(env_, env_->ExceptionOccurred()) {
+ DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative);
+ // The spec doesn't say how much local data should be there, so we just give 128 which seems
+ // likely to be enough for most cases.
+ env_->PushLocalFrame(128);
+ env_->ExceptionClear();
+ }
+
+ ~ScopedEventDispatchEnvironment() {
+ if (env_ != nullptr) {
+ if (throw_.get() != nullptr && !env_->ExceptionCheck()) {
+ // TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list
+ // of the newest exception.
+ env_->Throw(throw_.get());
+ }
+ env_->PopLocalFrame(nullptr);
+ }
+ DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative);
+ }
+
+ private:
+ JNIEnv* env_;
+ ScopedLocalRef<jthrowable> throw_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedEventDispatchEnvironment);
+};
+
// Infrastructure to achieve type safety for event dispatch.
#define FORALL_EVENT_TYPES(fn) \
@@ -97,27 +136,68 @@
#undef EVENT_FN_TYPE
-template <ArtJvmtiEvent kEvent>
-ALWAYS_INLINE inline typename EventFnType<kEvent>::type GetCallback(ArtJvmTiEnv* env);
+#define MAKE_EVENT_HANDLER_FUNC(name, enum_name) \
+template<> \
+struct EventHandlerFunc<enum_name> { \
+ using EventFnType = typename impl::EventFnType<enum_name>::type; \
+ explicit EventHandlerFunc(ArtJvmTiEnv* env) \
+ : env_(env), \
+ fn_(env_->event_callbacks == nullptr ? nullptr : env_->event_callbacks->name) { } \
+ \
+ template <typename ...Args> \
+ ALWAYS_INLINE \
+ void ExecuteCallback(JNIEnv* jnienv, Args... args) const { \
+ if (fn_ != nullptr) { \
+ ScopedEventDispatchEnvironment sede(jnienv); \
+ DoExecute(jnienv, args...); \
+ } \
+ } \
+ \
+ template <typename ...Args> \
+ ALWAYS_INLINE \
+ void ExecuteCallback(Args... args) const { \
+ if (fn_ != nullptr) { \
+ ScopedEventDispatchEnvironment sede; \
+ DoExecute(args...); \
+ } \
+ } \
+ \
+ private: \
+ template <typename ...Args> \
+ ALWAYS_INLINE \
+ inline void DoExecute(Args... args) const { \
+ static_assert(std::is_same<EventFnType, void(*)(jvmtiEnv*, Args...)>::value, \
+ "Unexpected different type of ExecuteCallback"); \
+ fn_(env_, args...); \
+ } \
+ \
+ public: \
+ ArtJvmTiEnv* env_; \
+ EventFnType fn_; \
+};
-#define GET_CALLBACK(name, enum_name) \
-template <> \
-ALWAYS_INLINE inline EventFnType<enum_name>::type GetCallback<enum_name>( \
- ArtJvmTiEnv* env) { \
- if (env->event_callbacks == nullptr) { \
- return nullptr; \
- } \
- return env->event_callbacks->name; \
-}
+FORALL_EVENT_TYPES(MAKE_EVENT_HANDLER_FUNC)
-FORALL_EVENT_TYPES(GET_CALLBACK)
-
-#undef GET_CALLBACK
+#undef MAKE_EVENT_HANDLER_FUNC
#undef FORALL_EVENT_TYPES
} // namespace impl
+template <ArtJvmtiEvent kEvent, typename ...Args>
+inline std::vector<impl::EventHandlerFunc<kEvent>> EventHandler::CollectEvents(art::Thread* thread,
+ Args... args) const {
+ art::MutexLock mu(thread, envs_lock_);
+ std::vector<impl::EventHandlerFunc<kEvent>> handlers;
+ for (ArtJvmTiEnv* env : envs) {
+ if (ShouldDispatch<kEvent>(env, thread, args...)) {
+ impl::EventHandlerFunc<kEvent> h(env);
+ handlers.push_back(h);
+ }
+ }
+ return handlers;
+}
+
// C++ does not allow partial template function specialization. The dispatch for our separated
// ClassFileLoadHook event types is the same, so use this helper for code deduplication.
template <ArtJvmtiEvent kEvent>
@@ -131,29 +211,37 @@
const unsigned char* class_data,
jint* new_class_data_len,
unsigned char** new_class_data) const {
+ art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative);
static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event");
DCHECK(*new_class_data == nullptr);
jint current_len = class_data_len;
unsigned char* current_class_data = const_cast<unsigned char*>(class_data);
+ std::vector<impl::EventHandlerFunc<kEvent>> handlers =
+ CollectEvents<kEvent>(thread,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ class_data_len,
+ class_data,
+ new_class_data_len,
+ new_class_data);
ArtJvmTiEnv* last_env = nullptr;
- for (ArtJvmTiEnv* env : envs) {
- if (env == nullptr) {
- continue;
- }
+ for (const impl::EventHandlerFunc<kEvent>& event : handlers) {
jint new_len = 0;
unsigned char* new_data = nullptr;
- DispatchEventOnEnv<kEvent>(env,
- thread,
- jnienv,
- class_being_redefined,
- loader,
- name,
- protection_domain,
- current_len,
- static_cast<const unsigned char*>(current_class_data),
- &new_len,
- &new_data);
+ ExecuteCallback<kEvent>(event,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ current_len,
+ static_cast<const unsigned char*>(current_class_data),
+ &new_len,
+ &new_data);
if (new_data != nullptr && new_data != current_class_data) {
// Destroy the data the last transformer made. We skip this if the previous state was the
// initial one since we don't know here which jvmtiEnv allocated it.
@@ -162,7 +250,7 @@
if (last_env != nullptr) {
last_env->Deallocate(current_class_data);
}
- last_env = env;
+ last_env = event.env_;
current_class_data = new_data;
current_len = new_len;
}
@@ -177,69 +265,27 @@
// exactly the argument types of the corresponding Jvmti kEvent function pointer.
template <ArtJvmtiEvent kEvent, typename ...Args>
-inline void EventHandler::ExecuteCallback(ArtJvmTiEnv* env, Args... args) {
- using FnType = typename impl::EventFnType<kEvent>::type;
- FnType callback = impl::GetCallback<kEvent>(env);
- if (callback != nullptr) {
- (*callback)(env, args...);
- }
-}
-
-template <ArtJvmtiEvent kEvent, typename ...Args>
inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const {
+ art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative);
static_assert(!std::is_same<JNIEnv*,
typename std::decay_t<
std::tuple_element_t<0, std::tuple<Args..., nullptr_t>>>>::value,
"Should be calling DispatchEvent with explicit JNIEnv* argument!");
DCHECK(thread == nullptr || !thread->IsExceptionPending());
- for (ArtJvmTiEnv* env : envs) {
- if (env != nullptr) {
- DispatchEventOnEnv<kEvent, Args...>(env, thread, args...);
- }
+ std::vector<impl::EventHandlerFunc<kEvent>> events = CollectEvents<kEvent>(thread, args...);
+ for (auto event : events) {
+ ExecuteCallback<kEvent>(event, args...);
}
}
-// Helper for ensuring that the dispatch environment is sane. Events with JNIEnvs need to stash
-// pending exceptions since they can cause new ones to be thrown. In accordance with the JVMTI
-// specification we allow exceptions originating from events to overwrite the current exception,
-// including exceptions originating from earlier events.
-class ScopedEventDispatchEnvironment FINAL : public art::ValueObject {
- public:
- explicit ScopedEventDispatchEnvironment(JNIEnv* env)
- : env_(env),
- thr_(env_, env_->ExceptionOccurred()),
- suspend_(art::Thread::Current(), art::kNative) {
- // The spec doesn't say how much local data should be there, so we just give 128 which seems
- // likely to be enough for most cases.
- env_->PushLocalFrame(128);
- env_->ExceptionClear();
- UNUSED(suspend_);
- }
-
- ~ScopedEventDispatchEnvironment() {
- if (thr_.get() != nullptr && !env_->ExceptionCheck()) {
- // TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list
- // of the newest exception.
- env_->Throw(thr_.get());
- }
- env_->PopLocalFrame(nullptr);
- }
-
- private:
- JNIEnv* env_;
- ScopedLocalRef<jthrowable> thr_;
- // Not actually unused. The destructor/constructor does important work.
- art::ScopedThreadStateChange suspend_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedEventDispatchEnvironment);
-};
-
template <ArtJvmtiEvent kEvent, typename ...Args>
inline void EventHandler::DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const {
- for (ArtJvmTiEnv* env : envs) {
- if (env != nullptr) {
- DispatchEventOnEnv<kEvent, Args...>(env, thread, jnienv, args...);
- }
+ art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative);
+ std::vector<impl::EventHandlerFunc<kEvent>> events = CollectEvents<kEvent>(thread,
+ jnienv,
+ args...);
+ for (auto event : events) {
+ ExecuteCallback<kEvent>(event, jnienv, args...);
}
}
@@ -248,8 +294,9 @@
ArtJvmTiEnv* env, art::Thread* thread, JNIEnv* jnienv, Args... args) const {
DCHECK(env != nullptr);
if (ShouldDispatch<kEvent, JNIEnv*, Args...>(env, thread, jnienv, args...)) {
- ScopedEventDispatchEnvironment sede(jnienv);
- ExecuteCallback<kEvent, JNIEnv*, Args...>(env, jnienv, args...);
+ art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative);
+ impl::EventHandlerFunc<kEvent> func(env);
+ ExecuteCallback<kEvent>(func, jnienv, args...);
}
}
@@ -260,11 +307,26 @@
typename std::decay_t<
std::tuple_element_t<0, std::tuple<Args..., nullptr_t>>>>::value,
"Should be calling DispatchEventOnEnv with explicit JNIEnv* argument!");
- if (ShouldDispatch<kEvent>(env, thread, args...)) {
- ExecuteCallback<kEvent, Args...>(env, args...);
+ DCHECK(env != nullptr);
+ if (ShouldDispatch<kEvent, Args...>(env, thread, args...)) {
+ art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative);
+ impl::EventHandlerFunc<kEvent> func(env);
+ ExecuteCallback<kEvent>(func, args...);
}
}
+template <ArtJvmtiEvent kEvent, typename ...Args>
+inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc<kEvent> handler, Args... args) {
+ handler.ExecuteCallback(args...);
+}
+
+template <ArtJvmtiEvent kEvent, typename ...Args>
+inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc<kEvent> handler,
+ JNIEnv* jnienv,
+ Args... args) {
+ handler.ExecuteCallback(jnienv, args...);
+}
+
// Events that need custom logic for if we send the event but are otherwise normal. This includes
// the kBreakpoint, kFramePop, kFieldAccess, and kFieldModification events.
@@ -347,14 +409,13 @@
// something.
template <>
inline void EventHandler::ExecuteCallback<ArtJvmtiEvent::kFramePop>(
- ArtJvmTiEnv* env,
+ impl::EventHandlerFunc<ArtJvmtiEvent::kFramePop> event,
JNIEnv* jnienv,
jthread jni_thread,
jmethodID jmethod,
jboolean is_exception,
const art::ShadowFrame* frame ATTRIBUTE_UNUSED) {
- ExecuteCallback<ArtJvmtiEvent::kFramePop>(
- env, jnienv, jni_thread, jmethod, is_exception);
+ ExecuteCallback<ArtJvmtiEvent::kFramePop>(event, jnienv, jni_thread, jmethod, is_exception);
}
// Need to give a custom specialization for NativeMethodBind since it has to deal with an out
@@ -366,20 +427,25 @@
jmethodID method,
void* cur_method,
void** new_method) const {
+ art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative);
+ std::vector<impl::EventHandlerFunc<ArtJvmtiEvent::kNativeMethodBind>> events =
+ CollectEvents<ArtJvmtiEvent::kNativeMethodBind>(thread,
+ jnienv,
+ jni_thread,
+ method,
+ cur_method,
+ new_method);
*new_method = cur_method;
- for (ArtJvmTiEnv* env : envs) {
- if (env != nullptr) {
- *new_method = cur_method;
- DispatchEventOnEnv<ArtJvmtiEvent::kNativeMethodBind>(env,
- thread,
- jnienv,
- jni_thread,
- method,
- cur_method,
- new_method);
- if (*new_method != nullptr) {
- cur_method = *new_method;
- }
+ for (auto event : events) {
+ *new_method = cur_method;
+ ExecuteCallback<ArtJvmtiEvent::kNativeMethodBind>(event,
+ jnienv,
+ jni_thread,
+ method,
+ cur_method,
+ new_method);
+ if (*new_method != nullptr) {
+ cur_method = *new_method;
}
}
*new_method = cur_method;
@@ -439,7 +505,7 @@
}
template <ArtJvmtiEvent kEvent>
-inline bool EventHandler::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) {
+inline bool EventHandler::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) const {
bool dispatch = env->event_masks.global_event_mask.Test(kEvent);
if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) {
@@ -461,6 +527,11 @@
}
inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) {
+ art::MutexLock mu(art::Thread::Current(), envs_lock_);
+ RecalculateGlobalEventMaskLocked(event);
+}
+
+inline void EventHandler::RecalculateGlobalEventMaskLocked(ArtJvmtiEvent event) {
bool union_value = false;
for (const ArtJvmTiEnv* stored_env : envs) {
if (stored_env == nullptr) {
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index d1d606d..be4ebbc 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -193,25 +193,21 @@
}
void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) {
- // Since we never shrink this array we might as well try to fill gaps.
- auto it = std::find(envs.begin(), envs.end(), nullptr);
- if (it != envs.end()) {
- *it = env;
- } else {
- envs.push_back(env);
- }
+ art::MutexLock mu(art::Thread::Current(), envs_lock_);
+ envs.push_back(env);
}
void EventHandler::RemoveArtJvmTiEnv(ArtJvmTiEnv* env) {
+ art::MutexLock mu(art::Thread::Current(), envs_lock_);
// Since we might be currently iterating over the envs list we cannot actually erase elements.
// Instead we will simply replace them with 'nullptr' and skip them manually.
auto it = std::find(envs.begin(), envs.end(), env);
if (it != envs.end()) {
- *it = nullptr;
+ envs.erase(it);
for (size_t i = static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal);
i <= static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal);
++i) {
- RecalculateGlobalEventMask(static_cast<ArtJvmtiEvent>(i));
+ RecalculateGlobalEventMaskLocked(static_cast<ArtJvmtiEvent>(i));
}
}
}
@@ -431,11 +427,11 @@
finish_enabled_(false) {}
void StartPause() OVERRIDE {
- handler_->DispatchEvent<ArtJvmtiEvent::kGarbageCollectionStart>(nullptr);
+ handler_->DispatchEvent<ArtJvmtiEvent::kGarbageCollectionStart>(art::Thread::Current());
}
void EndPause() OVERRIDE {
- handler_->DispatchEvent<ArtJvmtiEvent::kGarbageCollectionFinish>(nullptr);
+ handler_->DispatchEvent<ArtJvmtiEvent::kGarbageCollectionFinish>(art::Thread::Current());
}
bool IsEnabled() {
@@ -1176,7 +1172,8 @@
art::Runtime::Current()->GetInstrumentation()->RemoveListener(method_trace_listener_.get(), ~0);
}
-EventHandler::EventHandler() {
+EventHandler::EventHandler() : envs_lock_("JVMTI Environment List Lock",
+ art::LockLevel::kTopLockLevel) {
alloc_listener_.reset(new JvmtiAllocationListener(this));
ddm_listener_.reset(new JvmtiDdmChunkListener(this));
gc_pause_listener_.reset(new JvmtiGcPauseListener(this));
diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h
index a99ed7b..c73215f 100644
--- a/openjdkjvmti/events.h
+++ b/openjdkjvmti/events.h
@@ -158,6 +158,10 @@
void HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added);
};
+namespace impl {
+template <ArtJvmtiEvent kEvent> struct EventHandlerFunc { };
+} // namespace impl
+
// Helper class for event handling.
class EventHandler {
public:
@@ -169,10 +173,10 @@
// Register an env. It is assumed that this happens on env creation, that is, no events are
// enabled, yet.
- void RegisterArtJvmTiEnv(ArtJvmTiEnv* env);
+ void RegisterArtJvmTiEnv(ArtJvmTiEnv* env) REQUIRES(!envs_lock_);
// Remove an env.
- void RemoveArtJvmTiEnv(ArtJvmTiEnv* env);
+ void RemoveArtJvmTiEnv(ArtJvmTiEnv* env) REQUIRES(!envs_lock_);
bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const {
if (!EventMask::EventIsInRange(event)) {
@@ -184,13 +188,15 @@
jvmtiError SetEvent(ArtJvmTiEnv* env,
art::Thread* thread,
ArtJvmtiEvent event,
- jvmtiEventMode mode);
+ jvmtiEventMode mode)
+ REQUIRES(!envs_lock_);
// Dispatch event to all registered environments. Since this one doesn't have a JNIEnv* it doesn't
// matter if it has the mutator_lock.
template <ArtJvmtiEvent kEvent, typename ...Args>
ALWAYS_INLINE
- inline void DispatchEvent(art::Thread* thread, Args... args) const;
+ inline void DispatchEvent(art::Thread* thread, Args... args) const
+ REQUIRES(!envs_lock_);
// Dispatch event to all registered environments stashing exceptions as needed. This works since
// JNIEnv* is always the second argument if it is passed to an event. Needed since C++ does not
@@ -200,7 +206,8 @@
// the event to allocate local references.
template <ArtJvmtiEvent kEvent, typename ...Args>
ALWAYS_INLINE
- inline void DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const;
+ inline void DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const
+ REQUIRES(!envs_lock_);
// Tell the event handler capabilities were added/lost so it can adjust the sent events.If
// caps_added is true then caps is all the newly set capabilities of the jvmtiEnv. If it is false
@@ -208,30 +215,50 @@
ALWAYS_INLINE
inline void HandleChangedCapabilities(ArtJvmTiEnv* env,
const jvmtiCapabilities& caps,
- bool added);
+ bool added)
+ REQUIRES(!envs_lock_);
// Dispatch event to the given environment, only.
template <ArtJvmtiEvent kEvent, typename ...Args>
ALWAYS_INLINE
- inline void DispatchEventOnEnv(
- ArtJvmTiEnv* env, art::Thread* thread, JNIEnv* jnienv, Args... args) const;
+ inline void DispatchEventOnEnv(ArtJvmTiEnv* env,
+ art::Thread* thread,
+ JNIEnv* jnienv,
+ Args... args) const
+ REQUIRES(!envs_lock_);
// Dispatch event to the given environment, only.
template <ArtJvmtiEvent kEvent, typename ...Args>
ALWAYS_INLINE
- inline void DispatchEventOnEnv(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const;
+ inline void DispatchEventOnEnv(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const
+ REQUIRES(!envs_lock_);
private:
+ template <ArtJvmtiEvent kEvent, typename ...Args>
+ ALWAYS_INLINE
+ inline std::vector<impl::EventHandlerFunc<kEvent>> CollectEvents(art::Thread* thread,
+ Args... args) const
+ REQUIRES(!envs_lock_);
+
template <ArtJvmtiEvent kEvent>
ALWAYS_INLINE
- static inline bool ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread);
+ inline bool ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) const;
template <ArtJvmtiEvent kEvent, typename ...Args>
ALWAYS_INLINE
- static inline void ExecuteCallback(ArtJvmTiEnv* env, Args... args);
+ static inline void ExecuteCallback(impl::EventHandlerFunc<kEvent> handler,
+ JNIEnv* env,
+ Args... args)
+ REQUIRES(!envs_lock_);
template <ArtJvmtiEvent kEvent, typename ...Args>
ALWAYS_INLINE
+ static inline void ExecuteCallback(impl::EventHandlerFunc<kEvent> handler, Args... args)
+ REQUIRES(!envs_lock_);
+
+ // Public for use to collect dispatches
+ template <ArtJvmtiEvent kEvent, typename ...Args>
+ ALWAYS_INLINE
inline bool ShouldDispatch(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const;
ALWAYS_INLINE
@@ -241,7 +268,9 @@
// Recalculates the event mask for the given event.
ALWAYS_INLINE
- inline void RecalculateGlobalEventMask(ArtJvmtiEvent event);
+ inline void RecalculateGlobalEventMask(ArtJvmtiEvent event) REQUIRES(!envs_lock_);
+ ALWAYS_INLINE
+ inline void RecalculateGlobalEventMaskLocked(ArtJvmtiEvent event) REQUIRES(envs_lock_);
template <ArtJvmtiEvent kEvent>
ALWAYS_INLINE inline void DispatchClassFileLoadHookEvent(art::Thread* thread,
@@ -253,7 +282,8 @@
jint class_data_len,
const unsigned char* class_data,
jint* new_class_data_len,
- unsigned char** new_class_data) const;
+ unsigned char** new_class_data) const
+ REQUIRES(!envs_lock_);
void HandleEventType(ArtJvmtiEvent event, bool enable);
void HandleLocalAccessCapabilityAdded();
@@ -261,10 +291,13 @@
bool OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event);
- // List of all JvmTiEnv objects that have been created, in their creation order.
- // NB Some elements might be null representing envs that have been deleted. They should be skipped
- // anytime this list is used.
- std::vector<ArtJvmTiEnv*> envs;
+ // List of all JvmTiEnv objects that have been created, in their creation order. It is a std::list
+ // since we mostly access it by iterating over the entire thing, only ever append to the end, and
+ // need to be able to remove arbitrary elements from it.
+ std::list<ArtJvmTiEnv*> envs GUARDED_BY(envs_lock_);
+
+ // Top level lock. Nothing at all should be held when we lock this.
+ mutable art::Mutex envs_lock_ ACQUIRED_BEFORE(art::Locks::instrument_entrypoints_lock_);
// A union of all enabled events, anywhere.
EventMask global_mask;
diff --git a/openjdkjvmti/object_tagging.cc b/openjdkjvmti/object_tagging.cc
index 6ba7165..ba242ef 100644
--- a/openjdkjvmti/object_tagging.cc
+++ b/openjdkjvmti/object_tagging.cc
@@ -61,7 +61,8 @@
return event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kObjectFree);
}
void ObjectTagTable::HandleNullSweep(jlong tag) {
- event_handler_->DispatchEventOnEnv<ArtJvmtiEvent::kObjectFree>(jvmti_env_, nullptr, tag);
+ event_handler_->DispatchEventOnEnv<ArtJvmtiEvent::kObjectFree>(
+ jvmti_env_, art::Thread::Current(), tag);
}
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_dump.cc b/openjdkjvmti/ti_dump.cc
index 809a5e4..253580e 100644
--- a/openjdkjvmti/ti_dump.cc
+++ b/openjdkjvmti/ti_dump.cc
@@ -47,7 +47,7 @@
void SigQuit() OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
art::Thread* thread = art::Thread::Current();
art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
- event_handler->DispatchEvent<ArtJvmtiEvent::kDataDumpRequest>(nullptr);
+ event_handler->DispatchEvent<ArtJvmtiEvent::kDataDumpRequest>(art::Thread::Current());
}
EventHandler* event_handler = nullptr;
diff --git a/openjdkjvmti/ti_phase.cc b/openjdkjvmti/ti_phase.cc
index 23df27f..7157974 100644
--- a/openjdkjvmti/ti_phase.cc
+++ b/openjdkjvmti/ti_phase.cc
@@ -57,6 +57,7 @@
}
void NextRuntimePhase(RuntimePhase phase) REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE {
+ art::Thread* self = art::Thread::Current();
switch (phase) {
case RuntimePhase::kInitialAgents:
PhaseUtil::current_phase_ = JVMTI_PHASE_PRIMORDIAL;
@@ -64,8 +65,7 @@
case RuntimePhase::kStart:
{
PhaseUtil::current_phase_ = JVMTI_PHASE_START;
- art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
- event_handler->DispatchEvent<ArtJvmtiEvent::kVmStart>(nullptr, GetJniEnv());
+ event_handler->DispatchEvent<ArtJvmtiEvent::kVmStart>(self, GetJniEnv());
}
break;
case RuntimePhase::kInit:
@@ -74,9 +74,7 @@
PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE;
{
ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread());
- art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
- event_handler->DispatchEvent<ArtJvmtiEvent::kVmInit>(
- nullptr, GetJniEnv(), thread.get());
+ event_handler->DispatchEvent<ArtJvmtiEvent::kVmInit>(self, GetJniEnv(), thread.get());
}
// We need to have these events be ordered to match behavior expected by some real-world
// agents. The spec does not really require this but compatibility is a useful property to
@@ -86,8 +84,7 @@
break;
case RuntimePhase::kDeath:
{
- art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
- event_handler->DispatchEvent<ArtJvmtiEvent::kVmDeath>(nullptr, GetJniEnv());
+ event_handler->DispatchEvent<ArtJvmtiEvent::kVmDeath>(self, GetJniEnv());
PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD;
}
// TODO: Block events now.