| /* |
| * 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 "object_registry.h" |
| |
| #include "handle_scope-inl.h" |
| #include "jni_internal.h" |
| #include "mirror/class.h" |
| #include "mirror/throwable.h" |
| #include "obj_ptr-inl.h" |
| #include "scoped_thread_state_change-inl.h" |
| |
| namespace art { |
| |
| std::ostream& operator<<(std::ostream& os, const ObjectRegistryEntry& rhs) { |
| os << "ObjectRegistryEntry[" << rhs.jni_reference_type |
| << ",reference=" << rhs.jni_reference |
| << ",count=" << rhs.reference_count |
| << ",id=" << rhs.id << "]"; |
| return os; |
| } |
| |
| ObjectRegistry::ObjectRegistry() |
| : lock_("ObjectRegistry lock", kJdwpObjectRegistryLock), next_id_(1) { |
| Locks::AddToExpectedMutexesOnWeakRefAccess(&lock_); |
| } |
| |
| ObjectRegistry::~ObjectRegistry() { |
| Locks::RemoveFromExpectedMutexesOnWeakRefAccess(&lock_); |
| } |
| |
| JDWP::RefTypeId ObjectRegistry::AddRefType(ObjPtr<mirror::Class> c) { |
| return Add(c); |
| } |
| |
| JDWP::RefTypeId ObjectRegistry::AddRefType(Handle<mirror::Class> c_h) { |
| return Add(c_h); |
| } |
| |
| JDWP::ObjectId ObjectRegistry::Add(ObjPtr<mirror::Object> o) { |
| if (o == nullptr) { |
| return 0; |
| } |
| Thread* const self = Thread::Current(); |
| StackHandleScope<1> hs(self); |
| return InternalAdd(hs.NewHandle(o)); |
| } |
| |
| // Template instantiations must be declared below. |
| template<class T> |
| JDWP::ObjectId ObjectRegistry::Add(Handle<T> obj_h) { |
| if (obj_h == nullptr) { |
| return 0; |
| } |
| return InternalAdd(obj_h); |
| } |
| |
| // Explicit template instantiation. |
| template |
| REQUIRES_SHARED(Locks::mutator_lock_) |
| REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_) |
| JDWP::ObjectId ObjectRegistry::Add(Handle<mirror::Object> obj_h); |
| |
| template |
| REQUIRES_SHARED(Locks::mutator_lock_) |
| REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_) |
| JDWP::ObjectId ObjectRegistry::Add(Handle<mirror::Throwable> obj_h); |
| |
| template<class T> |
| JDWP::ObjectId ObjectRegistry::InternalAdd(Handle<T> obj_h) { |
| CHECK(obj_h != nullptr); |
| |
| Thread* const self = Thread::Current(); |
| self->AssertNoPendingException(); |
| // Object::IdentityHashCode may cause these locks to be held so check we do not already |
| // hold them. |
| Locks::thread_list_lock_->AssertNotHeld(self); |
| Locks::thread_suspend_count_lock_->AssertNotHeld(self); |
| |
| // Call IdentityHashCode here to avoid a lock level violation between lock_ and monitor_lock. |
| int32_t identity_hash_code = obj_h->IdentityHashCode(); |
| |
| ScopedObjectAccessUnchecked soa(self); |
| MutexLock mu(soa.Self(), lock_); |
| ObjectRegistryEntry* entry = nullptr; |
| if (ContainsLocked(soa.Self(), obj_h.Get(), identity_hash_code, &entry)) { |
| // This object was already in our map. |
| ++entry->reference_count; |
| } else { |
| entry = new ObjectRegistryEntry; |
| entry->jni_reference_type = JNIWeakGlobalRefType; |
| entry->jni_reference = nullptr; |
| entry->reference_count = 0; |
| entry->id = 0; |
| entry->identity_hash_code = identity_hash_code; |
| object_to_entry_.insert(std::make_pair(identity_hash_code, entry)); |
| |
| // This object isn't in the registry yet, so add it. |
| JNIEnv* env = soa.Env(); |
| |
| jobject local_reference = soa.AddLocalReference<jobject>(obj_h.Get()); |
| |
| entry->jni_reference_type = JNIWeakGlobalRefType; |
| entry->jni_reference = env->NewWeakGlobalRef(local_reference); |
| entry->reference_count = 1; |
| entry->id = next_id_++; |
| |
| id_to_entry_.Put(entry->id, entry); |
| |
| env->DeleteLocalRef(local_reference); |
| } |
| return entry->id; |
| } |
| |
| bool ObjectRegistry::ContainsLocked(Thread* self, |
| ObjPtr<mirror::Object> o, |
| int32_t identity_hash_code, |
| ObjectRegistryEntry** out_entry) { |
| DCHECK(o != nullptr); |
| for (auto it = object_to_entry_.lower_bound(identity_hash_code), end = object_to_entry_.end(); |
| it != end && it->first == identity_hash_code; ++it) { |
| ObjectRegistryEntry* entry = it->second; |
| if (o == self->DecodeJObject(entry->jni_reference)) { |
| if (out_entry != nullptr) { |
| *out_entry = entry; |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void ObjectRegistry::Clear() { |
| Thread* const self = Thread::Current(); |
| |
| // We must not hold the mutator lock exclusively if we want to delete weak global |
| // references. Otherwise this can lead to a deadlock with a running GC: |
| // 1. GC thread disables access to weak global references, then releases |
| // mutator lock. |
| // 2. JDWP thread takes mutator lock exclusively after suspending all |
| // threads. |
| // 3. GC thread waits for shared mutator lock which is held by JDWP |
| // thread. |
| // 4. JDWP thread clears weak global references but need to wait for GC |
| // thread to re-enable access to them. |
| Locks::mutator_lock_->AssertNotExclusiveHeld(self); |
| |
| MutexLock mu(self, lock_); |
| VLOG(jdwp) << "Object registry contained " << object_to_entry_.size() << " entries"; |
| // Delete all the JNI references. |
| JNIEnv* env = self->GetJniEnv(); |
| for (const auto& pair : object_to_entry_) { |
| const ObjectRegistryEntry* entry = pair.second; |
| if (entry->jni_reference_type == JNIWeakGlobalRefType) { |
| env->DeleteWeakGlobalRef(entry->jni_reference); |
| } else { |
| env->DeleteGlobalRef(entry->jni_reference); |
| } |
| delete entry; |
| } |
| // Clear the maps. |
| object_to_entry_.clear(); |
| id_to_entry_.clear(); |
| } |
| |
| mirror::Object* ObjectRegistry::InternalGet(JDWP::ObjectId id, JDWP::JdwpError* error) { |
| Thread* self = Thread::Current(); |
| MutexLock mu(self, lock_); |
| auto it = id_to_entry_.find(id); |
| if (it == id_to_entry_.end()) { |
| *error = JDWP::ERR_INVALID_OBJECT; |
| return nullptr; |
| } |
| ObjectRegistryEntry& entry = *it->second; |
| *error = JDWP::ERR_NONE; |
| return self->DecodeJObject(entry.jni_reference).Ptr(); |
| } |
| |
| jobject ObjectRegistry::GetJObject(JDWP::ObjectId id) { |
| if (id == 0) { |
| return nullptr; |
| } |
| Thread* self = Thread::Current(); |
| MutexLock mu(self, lock_); |
| auto it = id_to_entry_.find(id); |
| CHECK(it != id_to_entry_.end()) << id; |
| ObjectRegistryEntry& entry = *it->second; |
| return entry.jni_reference; |
| } |
| |
| void ObjectRegistry::DisableCollection(JDWP::ObjectId id) { |
| Thread* self = Thread::Current(); |
| MutexLock mu(self, lock_); |
| auto it = id_to_entry_.find(id); |
| CHECK(it != id_to_entry_.end()); |
| Promote(*it->second); |
| } |
| |
| void ObjectRegistry::EnableCollection(JDWP::ObjectId id) { |
| Thread* self = Thread::Current(); |
| MutexLock mu(self, lock_); |
| auto it = id_to_entry_.find(id); |
| CHECK(it != id_to_entry_.end()); |
| Demote(*it->second); |
| } |
| |
| void ObjectRegistry::Demote(ObjectRegistryEntry& entry) { |
| if (entry.jni_reference_type == JNIGlobalRefType) { |
| Thread* self = Thread::Current(); |
| JNIEnv* env = self->GetJniEnv(); |
| jobject global = entry.jni_reference; |
| entry.jni_reference = env->NewWeakGlobalRef(entry.jni_reference); |
| entry.jni_reference_type = JNIWeakGlobalRefType; |
| env->DeleteGlobalRef(global); |
| } |
| } |
| |
| void ObjectRegistry::Promote(ObjectRegistryEntry& entry) { |
| if (entry.jni_reference_type == JNIWeakGlobalRefType) { |
| Thread* self = Thread::Current(); |
| JNIEnv* env = self->GetJniEnv(); |
| jobject weak = entry.jni_reference; |
| entry.jni_reference = env->NewGlobalRef(entry.jni_reference); |
| entry.jni_reference_type = JNIGlobalRefType; |
| env->DeleteWeakGlobalRef(weak); |
| } |
| } |
| |
| bool ObjectRegistry::IsCollected(JDWP::ObjectId id) { |
| Thread* self = Thread::Current(); |
| MutexLock mu(self, lock_); |
| auto it = id_to_entry_.find(id); |
| CHECK(it != id_to_entry_.end()); |
| ObjectRegistryEntry& entry = *it->second; |
| if (entry.jni_reference_type == JNIWeakGlobalRefType) { |
| JNIEnv* env = self->GetJniEnv(); |
| return env->IsSameObject(entry.jni_reference, nullptr); // Has the jweak been collected? |
| } else { |
| return false; // We hold a strong reference, so we know this is live. |
| } |
| } |
| |
| void ObjectRegistry::DisposeObject(JDWP::ObjectId id, uint32_t reference_count) { |
| Thread* self = Thread::Current(); |
| MutexLock mu(self, lock_); |
| auto it = id_to_entry_.find(id); |
| if (it == id_to_entry_.end()) { |
| return; |
| } |
| ObjectRegistryEntry* entry = it->second; |
| entry->reference_count -= reference_count; |
| if (entry->reference_count <= 0) { |
| JNIEnv* env = self->GetJniEnv(); |
| // Erase the object from the maps. Note object may be null if it's |
| // a weak ref and the GC has cleared it. |
| int32_t hash_code = entry->identity_hash_code; |
| for (auto inner_it = object_to_entry_.lower_bound(hash_code), end = object_to_entry_.end(); |
| inner_it != end && inner_it->first == hash_code; ++inner_it) { |
| if (entry == inner_it->second) { |
| object_to_entry_.erase(inner_it); |
| break; |
| } |
| } |
| if (entry->jni_reference_type == JNIWeakGlobalRefType) { |
| env->DeleteWeakGlobalRef(entry->jni_reference); |
| } else { |
| env->DeleteGlobalRef(entry->jni_reference); |
| } |
| id_to_entry_.erase(id); |
| delete entry; |
| } |
| } |
| |
| } // namespace art |