diff options
Diffstat (limited to 'runtime/gc/reference_queue.cc')
-rw-r--r-- | runtime/gc/reference_queue.cc | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc new file mode 100644 index 0000000000..d006349cbb --- /dev/null +++ b/runtime/gc/reference_queue.cc @@ -0,0 +1,163 @@ +/* + * 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 "reference_queue.h" + +#include "accounting/card_table-inl.h" +#include "heap.h" +#include "mirror/class-inl.h" +#include "mirror/object-inl.h" + +namespace art { +namespace gc { + +ReferenceQueue::ReferenceQueue(Heap* heap) + : lock_("reference queue lock"), + heap_(heap), + list_(nullptr) { +} + +void ReferenceQueue::AtomicEnqueueIfNotEnqueued(Thread* self, mirror::Object* ref) { + DCHECK(ref != NULL); + MutexLock mu(self, lock_); + if (!heap_->IsEnqueued(ref)) { + EnqueuePendingReference(ref); + } +} + +void ReferenceQueue::EnqueueReference(mirror::Object* ref) { + CHECK(heap_->IsEnqueuable(ref)); + EnqueuePendingReference(ref); +} + +void ReferenceQueue::EnqueuePendingReference(mirror::Object* ref) { + DCHECK(ref != NULL); + MemberOffset pending_next_offset = heap_->GetReferencePendingNextOffset(); + DCHECK_NE(pending_next_offset.Uint32Value(), 0U); + if (IsEmpty()) { + // 1 element cyclic queue, ie: Reference ref = ..; ref.pendingNext = ref; + ref->SetFieldObject(pending_next_offset, ref, false); + list_ = ref; + } else { + mirror::Object* head = + list_->GetFieldObject<mirror::Object*>(pending_next_offset, false); + ref->SetFieldObject(pending_next_offset, head, false); + list_->SetFieldObject(pending_next_offset, ref, false); + } +} + +mirror::Object* ReferenceQueue::DequeuePendingReference() { + DCHECK(!IsEmpty()); + MemberOffset pending_next_offset = heap_->GetReferencePendingNextOffset(); + mirror::Object* head = list_->GetFieldObject<mirror::Object*>(pending_next_offset, false); + DCHECK(head != nullptr); + mirror::Object* ref; + // Note: the following code is thread-safe because it is only called from ProcessReferences which + // is single threaded. + if (list_ == head) { + ref = list_; + list_ = nullptr; + } else { + mirror::Object* next = head->GetFieldObject<mirror::Object*>(pending_next_offset, false); + list_->SetFieldObject(pending_next_offset, next, false); + ref = head; + } + ref->SetFieldObject(pending_next_offset, nullptr, false); + return ref; +} + +void ReferenceQueue::Dump(std::ostream& os) const { + mirror::Object* cur = list_; + os << "Reference starting at list_=" << list_ << "\n"; + while (cur != nullptr) { + mirror::Object* pending_next = + cur->GetFieldObject<mirror::Object*>(heap_->GetReferencePendingNextOffset(), false); + os << "PendingNext=" << pending_next; + if (cur->GetClass()->IsFinalizerReferenceClass()) { + os << " Zombie=" << + cur->GetFieldObject<mirror::Object*>(heap_->GetFinalizerReferenceZombieOffset(), false); + } + os << "\n"; + cur = pending_next; + } +} + +void ReferenceQueue::ClearWhiteReferences(ReferenceQueue& cleared_references, RootVisitor visitor, + void* arg) { + while (!IsEmpty()) { + mirror::Object* ref = DequeuePendingReference(); + mirror::Object* referent = heap_->GetReferenceReferent(ref); + if (referent != nullptr) { + mirror::Object* forward_address = visitor(referent, arg); + if (forward_address == nullptr) { + // Referent is white, clear it. + heap_->ClearReferenceReferent(ref); + if (heap_->IsEnqueuable(ref)) { + cleared_references.EnqueuePendingReference(ref); + } + } else if (referent != forward_address) { + // Object moved, need to updated the referrent. + heap_->SetReferenceReferent(ref, forward_address); + } + } + } +} + +void ReferenceQueue::EnqueueFinalizerReferences(ReferenceQueue& cleared_references, + RootVisitor is_marked_callback, + RootVisitor recursive_mark_callback, void* arg) { + while (!IsEmpty()) { + mirror::Object* ref = DequeuePendingReference(); + mirror::Object* referent = heap_->GetReferenceReferent(ref); + if (referent != nullptr) { + mirror::Object* forward_address = is_marked_callback(referent, arg); + // If the referent isn't marked, mark it and update the + if (forward_address == nullptr) { + forward_address = recursive_mark_callback(referent, arg); + // If the referent is non-null the reference must queuable. + DCHECK(heap_->IsEnqueuable(ref)); + // Move the updated referent to the zombie field. + ref->SetFieldObject(heap_->GetFinalizerReferenceZombieOffset(), forward_address, false); + heap_->ClearReferenceReferent(ref); + cleared_references.EnqueueReference(ref); + } else if (referent != forward_address) { + heap_->SetReferenceReferent(ref, forward_address); + } + } + } +} + +void ReferenceQueue::PreserveSomeSoftReferences(RootVisitor preserve_callback, void* arg) { + ReferenceQueue cleared(heap_); + while (!IsEmpty()) { + mirror::Object* ref = DequeuePendingReference(); + mirror::Object* referent = heap_->GetReferenceReferent(ref); + if (referent != nullptr) { + mirror::Object* forward_address = preserve_callback(referent, arg); + if (forward_address == nullptr) { + // Either the reference isn't marked or we don't wish to preserve it. + cleared.EnqueuePendingReference(ref); + } else { + heap_->SetReferenceReferent(ref, forward_address); + } + } + } + list_ = cleared.GetList(); +} + +} // namespace gc +} // namespace art + |