blob: d006349cbb4dd450e6aa0f0cc452e6a247938e9f [file] [log] [blame]
Mathieu Chartier39e32612013-11-12 16:28:05 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "reference_queue.h"
18
19#include "accounting/card_table-inl.h"
20#include "heap.h"
21#include "mirror/class-inl.h"
22#include "mirror/object-inl.h"
23
24namespace art {
25namespace gc {
26
27ReferenceQueue::ReferenceQueue(Heap* heap)
28 : lock_("reference queue lock"),
29 heap_(heap),
30 list_(nullptr) {
31}
32
33void ReferenceQueue::AtomicEnqueueIfNotEnqueued(Thread* self, mirror::Object* ref) {
34 DCHECK(ref != NULL);
35 MutexLock mu(self, lock_);
36 if (!heap_->IsEnqueued(ref)) {
37 EnqueuePendingReference(ref);
38 }
39}
40
41void ReferenceQueue::EnqueueReference(mirror::Object* ref) {
42 CHECK(heap_->IsEnqueuable(ref));
43 EnqueuePendingReference(ref);
44}
45
46void ReferenceQueue::EnqueuePendingReference(mirror::Object* ref) {
47 DCHECK(ref != NULL);
48 MemberOffset pending_next_offset = heap_->GetReferencePendingNextOffset();
49 DCHECK_NE(pending_next_offset.Uint32Value(), 0U);
50 if (IsEmpty()) {
51 // 1 element cyclic queue, ie: Reference ref = ..; ref.pendingNext = ref;
52 ref->SetFieldObject(pending_next_offset, ref, false);
53 list_ = ref;
54 } else {
55 mirror::Object* head =
56 list_->GetFieldObject<mirror::Object*>(pending_next_offset, false);
57 ref->SetFieldObject(pending_next_offset, head, false);
58 list_->SetFieldObject(pending_next_offset, ref, false);
59 }
60}
61
62mirror::Object* ReferenceQueue::DequeuePendingReference() {
63 DCHECK(!IsEmpty());
64 MemberOffset pending_next_offset = heap_->GetReferencePendingNextOffset();
65 mirror::Object* head = list_->GetFieldObject<mirror::Object*>(pending_next_offset, false);
66 DCHECK(head != nullptr);
67 mirror::Object* ref;
68 // Note: the following code is thread-safe because it is only called from ProcessReferences which
69 // is single threaded.
70 if (list_ == head) {
71 ref = list_;
72 list_ = nullptr;
73 } else {
74 mirror::Object* next = head->GetFieldObject<mirror::Object*>(pending_next_offset, false);
75 list_->SetFieldObject(pending_next_offset, next, false);
76 ref = head;
77 }
78 ref->SetFieldObject(pending_next_offset, nullptr, false);
79 return ref;
80}
81
82void ReferenceQueue::Dump(std::ostream& os) const {
83 mirror::Object* cur = list_;
84 os << "Reference starting at list_=" << list_ << "\n";
85 while (cur != nullptr) {
86 mirror::Object* pending_next =
87 cur->GetFieldObject<mirror::Object*>(heap_->GetReferencePendingNextOffset(), false);
88 os << "PendingNext=" << pending_next;
89 if (cur->GetClass()->IsFinalizerReferenceClass()) {
90 os << " Zombie=" <<
91 cur->GetFieldObject<mirror::Object*>(heap_->GetFinalizerReferenceZombieOffset(), false);
92 }
93 os << "\n";
94 cur = pending_next;
95 }
96}
97
98void ReferenceQueue::ClearWhiteReferences(ReferenceQueue& cleared_references, RootVisitor visitor,
99 void* arg) {
100 while (!IsEmpty()) {
101 mirror::Object* ref = DequeuePendingReference();
102 mirror::Object* referent = heap_->GetReferenceReferent(ref);
103 if (referent != nullptr) {
104 mirror::Object* forward_address = visitor(referent, arg);
105 if (forward_address == nullptr) {
106 // Referent is white, clear it.
107 heap_->ClearReferenceReferent(ref);
108 if (heap_->IsEnqueuable(ref)) {
109 cleared_references.EnqueuePendingReference(ref);
110 }
111 } else if (referent != forward_address) {
112 // Object moved, need to updated the referrent.
113 heap_->SetReferenceReferent(ref, forward_address);
114 }
115 }
116 }
117}
118
119void ReferenceQueue::EnqueueFinalizerReferences(ReferenceQueue& cleared_references,
120 RootVisitor is_marked_callback,
121 RootVisitor recursive_mark_callback, void* arg) {
122 while (!IsEmpty()) {
123 mirror::Object* ref = DequeuePendingReference();
124 mirror::Object* referent = heap_->GetReferenceReferent(ref);
125 if (referent != nullptr) {
126 mirror::Object* forward_address = is_marked_callback(referent, arg);
127 // If the referent isn't marked, mark it and update the
128 if (forward_address == nullptr) {
129 forward_address = recursive_mark_callback(referent, arg);
130 // If the referent is non-null the reference must queuable.
131 DCHECK(heap_->IsEnqueuable(ref));
132 // Move the updated referent to the zombie field.
133 ref->SetFieldObject(heap_->GetFinalizerReferenceZombieOffset(), forward_address, false);
134 heap_->ClearReferenceReferent(ref);
135 cleared_references.EnqueueReference(ref);
136 } else if (referent != forward_address) {
137 heap_->SetReferenceReferent(ref, forward_address);
138 }
139 }
140 }
141}
142
143void ReferenceQueue::PreserveSomeSoftReferences(RootVisitor preserve_callback, void* arg) {
144 ReferenceQueue cleared(heap_);
145 while (!IsEmpty()) {
146 mirror::Object* ref = DequeuePendingReference();
147 mirror::Object* referent = heap_->GetReferenceReferent(ref);
148 if (referent != nullptr) {
149 mirror::Object* forward_address = preserve_callback(referent, arg);
150 if (forward_address == nullptr) {
151 // Either the reference isn't marked or we don't wish to preserve it.
152 cleared.EnqueuePendingReference(ref);
153 } else {
154 heap_->SetReferenceReferent(ref, forward_address);
155 }
156 }
157 }
158 list_ = cleared.GetList();
159}
160
161} // namespace gc
162} // namespace art
163