xref: /aosp_15_r20/art/runtime/gc/reference_queue.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2013 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #include "reference_queue.h"
18*795d594fSAndroid Build Coastguard Worker 
19*795d594fSAndroid Build Coastguard Worker #include "accounting/card_table-inl.h"
20*795d594fSAndroid Build Coastguard Worker #include "base/mutex.h"
21*795d594fSAndroid Build Coastguard Worker #include "collector/concurrent_copying.h"
22*795d594fSAndroid Build Coastguard Worker #include "heap.h"
23*795d594fSAndroid Build Coastguard Worker #include "mirror/class-inl.h"
24*795d594fSAndroid Build Coastguard Worker #include "mirror/object-inl.h"
25*795d594fSAndroid Build Coastguard Worker #include "mirror/reference-inl.h"
26*795d594fSAndroid Build Coastguard Worker #include "object_callbacks.h"
27*795d594fSAndroid Build Coastguard Worker 
28*795d594fSAndroid Build Coastguard Worker namespace art HIDDEN {
29*795d594fSAndroid Build Coastguard Worker namespace gc {
30*795d594fSAndroid Build Coastguard Worker 
ReferenceQueue(Mutex * lock)31*795d594fSAndroid Build Coastguard Worker ReferenceQueue::ReferenceQueue(Mutex* lock) : lock_(lock), list_(nullptr) {
32*795d594fSAndroid Build Coastguard Worker }
33*795d594fSAndroid Build Coastguard Worker 
AtomicEnqueueIfNotEnqueued(Thread * self,ObjPtr<mirror::Reference> ref)34*795d594fSAndroid Build Coastguard Worker void ReferenceQueue::AtomicEnqueueIfNotEnqueued(Thread* self, ObjPtr<mirror::Reference> ref) {
35*795d594fSAndroid Build Coastguard Worker   DCHECK(ref != nullptr);
36*795d594fSAndroid Build Coastguard Worker   MutexLock mu(self, *lock_);
37*795d594fSAndroid Build Coastguard Worker   if (ref->IsUnprocessed()) {
38*795d594fSAndroid Build Coastguard Worker     EnqueueReference(ref);
39*795d594fSAndroid Build Coastguard Worker   }
40*795d594fSAndroid Build Coastguard Worker }
41*795d594fSAndroid Build Coastguard Worker 
EnqueueReference(ObjPtr<mirror::Reference> ref)42*795d594fSAndroid Build Coastguard Worker void ReferenceQueue::EnqueueReference(ObjPtr<mirror::Reference> ref) {
43*795d594fSAndroid Build Coastguard Worker   DCHECK(ref != nullptr);
44*795d594fSAndroid Build Coastguard Worker   CHECK(ref->IsUnprocessed());
45*795d594fSAndroid Build Coastguard Worker   if (IsEmpty()) {
46*795d594fSAndroid Build Coastguard Worker     // 1 element cyclic queue, ie: Reference ref = ..; ref.pendingNext = ref;
47*795d594fSAndroid Build Coastguard Worker     list_ = ref.Ptr();
48*795d594fSAndroid Build Coastguard Worker   } else {
49*795d594fSAndroid Build Coastguard Worker     // The list is owned by the GC, everything that has been inserted must already be at least
50*795d594fSAndroid Build Coastguard Worker     // gray.
51*795d594fSAndroid Build Coastguard Worker     ObjPtr<mirror::Reference> head = list_->GetPendingNext<kWithoutReadBarrier>();
52*795d594fSAndroid Build Coastguard Worker     DCHECK(head != nullptr);
53*795d594fSAndroid Build Coastguard Worker     ref->SetPendingNext(head);
54*795d594fSAndroid Build Coastguard Worker   }
55*795d594fSAndroid Build Coastguard Worker   // Add the reference in the middle to preserve the cycle.
56*795d594fSAndroid Build Coastguard Worker   list_->SetPendingNext(ref);
57*795d594fSAndroid Build Coastguard Worker }
58*795d594fSAndroid Build Coastguard Worker 
DequeuePendingReference()59*795d594fSAndroid Build Coastguard Worker ObjPtr<mirror::Reference> ReferenceQueue::DequeuePendingReference() {
60*795d594fSAndroid Build Coastguard Worker   DCHECK(!IsEmpty());
61*795d594fSAndroid Build Coastguard Worker   ObjPtr<mirror::Reference> ref = list_->GetPendingNext<kWithoutReadBarrier>();
62*795d594fSAndroid Build Coastguard Worker   DCHECK(ref != nullptr);
63*795d594fSAndroid Build Coastguard Worker   // Note: the following code is thread-safe because it is only called from ProcessReferences which
64*795d594fSAndroid Build Coastguard Worker   // is single threaded.
65*795d594fSAndroid Build Coastguard Worker   if (list_ == ref) {
66*795d594fSAndroid Build Coastguard Worker     list_ = nullptr;
67*795d594fSAndroid Build Coastguard Worker   } else {
68*795d594fSAndroid Build Coastguard Worker     ObjPtr<mirror::Reference> next = ref->GetPendingNext<kWithoutReadBarrier>();
69*795d594fSAndroid Build Coastguard Worker     list_->SetPendingNext(next);
70*795d594fSAndroid Build Coastguard Worker   }
71*795d594fSAndroid Build Coastguard Worker   ref->SetPendingNext(nullptr);
72*795d594fSAndroid Build Coastguard Worker   return ref;
73*795d594fSAndroid Build Coastguard Worker }
74*795d594fSAndroid Build Coastguard Worker 
75*795d594fSAndroid Build Coastguard Worker // This must be called whenever DequeuePendingReference is called.
DisableReadBarrierForReference(ObjPtr<mirror::Reference> ref,std::memory_order order)76*795d594fSAndroid Build Coastguard Worker void ReferenceQueue::DisableReadBarrierForReference(ObjPtr<mirror::Reference> ref,
77*795d594fSAndroid Build Coastguard Worker                                                     std::memory_order order) {
78*795d594fSAndroid Build Coastguard Worker   Heap* heap = Runtime::Current()->GetHeap();
79*795d594fSAndroid Build Coastguard Worker   if (kUseBakerReadBarrier && heap->CurrentCollectorType() == kCollectorTypeCC &&
80*795d594fSAndroid Build Coastguard Worker       heap->ConcurrentCopyingCollector()->IsActive()) {
81*795d594fSAndroid Build Coastguard Worker     // Change the gray ptr we left in ConcurrentCopying::ProcessMarkStackRef() to non-gray.
82*795d594fSAndroid Build Coastguard Worker     // We check IsActive() above because we don't want to do this when the zygote compaction
83*795d594fSAndroid Build Coastguard Worker     // collector (SemiSpace) is running.
84*795d594fSAndroid Build Coastguard Worker     CHECK(ref != nullptr);
85*795d594fSAndroid Build Coastguard Worker     collector::ConcurrentCopying* concurrent_copying = heap->ConcurrentCopyingCollector();
86*795d594fSAndroid Build Coastguard Worker     uint32_t rb_state = ref->GetReadBarrierState();
87*795d594fSAndroid Build Coastguard Worker     if (rb_state == ReadBarrier::GrayState()) {
88*795d594fSAndroid Build Coastguard Worker       ref->AtomicSetReadBarrierState(ReadBarrier::GrayState(), ReadBarrier::NonGrayState(), order);
89*795d594fSAndroid Build Coastguard Worker       CHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::NonGrayState());
90*795d594fSAndroid Build Coastguard Worker     } else {
91*795d594fSAndroid Build Coastguard Worker       // In ConcurrentCopying::ProcessMarkStackRef() we may leave a non-gray reference in the queue
92*795d594fSAndroid Build Coastguard Worker       // and find it here, which is OK.
93*795d594fSAndroid Build Coastguard Worker       CHECK_EQ(rb_state, ReadBarrier::NonGrayState()) << "ref=" << ref << " rb_state=" << rb_state;
94*795d594fSAndroid Build Coastguard Worker       ObjPtr<mirror::Object> referent = ref->GetReferent<kWithoutReadBarrier>();
95*795d594fSAndroid Build Coastguard Worker       // The referent could be null if it's cleared by a mutator (Reference.clear()).
96*795d594fSAndroid Build Coastguard Worker       if (referent != nullptr) {
97*795d594fSAndroid Build Coastguard Worker         CHECK(concurrent_copying->IsInToSpace(referent.Ptr()))
98*795d594fSAndroid Build Coastguard Worker             << "ref=" << ref << " rb_state=" << ref->GetReadBarrierState()
99*795d594fSAndroid Build Coastguard Worker             << " referent=" << referent;
100*795d594fSAndroid Build Coastguard Worker       }
101*795d594fSAndroid Build Coastguard Worker     }
102*795d594fSAndroid Build Coastguard Worker   }
103*795d594fSAndroid Build Coastguard Worker }
104*795d594fSAndroid Build Coastguard Worker 
Dump(std::ostream & os) const105*795d594fSAndroid Build Coastguard Worker void ReferenceQueue::Dump(std::ostream& os) const {
106*795d594fSAndroid Build Coastguard Worker   ObjPtr<mirror::Reference> cur = list_;
107*795d594fSAndroid Build Coastguard Worker   os << "Reference starting at list_=" << list_ << "\n";
108*795d594fSAndroid Build Coastguard Worker   if (cur == nullptr) {
109*795d594fSAndroid Build Coastguard Worker     return;
110*795d594fSAndroid Build Coastguard Worker   }
111*795d594fSAndroid Build Coastguard Worker   do {
112*795d594fSAndroid Build Coastguard Worker     ObjPtr<mirror::Reference> pending_next = cur->GetPendingNext();
113*795d594fSAndroid Build Coastguard Worker     os << "Reference= " << cur << " PendingNext=" << pending_next;
114*795d594fSAndroid Build Coastguard Worker     if (cur->IsFinalizerReferenceInstance()) {
115*795d594fSAndroid Build Coastguard Worker       os << " Zombie=" << cur->AsFinalizerReference()->GetZombie();
116*795d594fSAndroid Build Coastguard Worker     }
117*795d594fSAndroid Build Coastguard Worker     os << "\n";
118*795d594fSAndroid Build Coastguard Worker     cur = pending_next;
119*795d594fSAndroid Build Coastguard Worker   } while (cur != list_);
120*795d594fSAndroid Build Coastguard Worker }
121*795d594fSAndroid Build Coastguard Worker 
GetLength() const122*795d594fSAndroid Build Coastguard Worker size_t ReferenceQueue::GetLength() const {
123*795d594fSAndroid Build Coastguard Worker   size_t count = 0;
124*795d594fSAndroid Build Coastguard Worker   ObjPtr<mirror::Reference> cur = list_;
125*795d594fSAndroid Build Coastguard Worker   if (cur != nullptr) {
126*795d594fSAndroid Build Coastguard Worker     do {
127*795d594fSAndroid Build Coastguard Worker       ++count;
128*795d594fSAndroid Build Coastguard Worker       cur = cur->GetPendingNext();
129*795d594fSAndroid Build Coastguard Worker     } while (cur != list_);
130*795d594fSAndroid Build Coastguard Worker   }
131*795d594fSAndroid Build Coastguard Worker   return count;
132*795d594fSAndroid Build Coastguard Worker }
133*795d594fSAndroid Build Coastguard Worker 
ClearWhiteReferences(ReferenceQueue * cleared_references,collector::GarbageCollector * collector,bool report_cleared)134*795d594fSAndroid Build Coastguard Worker void ReferenceQueue::ClearWhiteReferences(ReferenceQueue* cleared_references,
135*795d594fSAndroid Build Coastguard Worker                                           collector::GarbageCollector* collector,
136*795d594fSAndroid Build Coastguard Worker                                           bool report_cleared) {
137*795d594fSAndroid Build Coastguard Worker   while (!IsEmpty()) {
138*795d594fSAndroid Build Coastguard Worker     ObjPtr<mirror::Reference> ref = DequeuePendingReference();
139*795d594fSAndroid Build Coastguard Worker     mirror::HeapReference<mirror::Object>* referent_addr = ref->GetReferentReferenceAddr();
140*795d594fSAndroid Build Coastguard Worker     // do_atomic_update is false because this happens during the reference processing phase where
141*795d594fSAndroid Build Coastguard Worker     // Reference.clear() would block.
142*795d594fSAndroid Build Coastguard Worker     if (!collector->IsNullOrMarkedHeapReference(referent_addr, /*do_atomic_update=*/false)) {
143*795d594fSAndroid Build Coastguard Worker       // Referent is white, clear it.
144*795d594fSAndroid Build Coastguard Worker       if (Runtime::Current()->IsActiveTransaction()) {
145*795d594fSAndroid Build Coastguard Worker         ref->ClearReferent<true>();
146*795d594fSAndroid Build Coastguard Worker       } else {
147*795d594fSAndroid Build Coastguard Worker         ref->ClearReferent<false>();
148*795d594fSAndroid Build Coastguard Worker       }
149*795d594fSAndroid Build Coastguard Worker       cleared_references->EnqueueReference(ref);
150*795d594fSAndroid Build Coastguard Worker       if (report_cleared) {
151*795d594fSAndroid Build Coastguard Worker         static bool already_reported = false;
152*795d594fSAndroid Build Coastguard Worker         if (!already_reported) {
153*795d594fSAndroid Build Coastguard Worker           // TODO: Maybe do this only if the queue is non-null?
154*795d594fSAndroid Build Coastguard Worker           LOG(WARNING)
155*795d594fSAndroid Build Coastguard Worker               << "Cleared Reference was only reachable from finalizer (only reported once)";
156*795d594fSAndroid Build Coastguard Worker           already_reported = true;
157*795d594fSAndroid Build Coastguard Worker         }
158*795d594fSAndroid Build Coastguard Worker       }
159*795d594fSAndroid Build Coastguard Worker     }
160*795d594fSAndroid Build Coastguard Worker     // Delay disabling the read barrier until here so that the ClearReferent call above in
161*795d594fSAndroid Build Coastguard Worker     // transaction mode will trigger the read barrier.
162*795d594fSAndroid Build Coastguard Worker     DisableReadBarrierForReference(ref, std::memory_order_relaxed);
163*795d594fSAndroid Build Coastguard Worker   }
164*795d594fSAndroid Build Coastguard Worker }
165*795d594fSAndroid Build Coastguard Worker 
EnqueueFinalizerReferences(ReferenceQueue * cleared_references,collector::GarbageCollector * collector)166*795d594fSAndroid Build Coastguard Worker FinalizerStats ReferenceQueue::EnqueueFinalizerReferences(ReferenceQueue* cleared_references,
167*795d594fSAndroid Build Coastguard Worker                                                 collector::GarbageCollector* collector) {
168*795d594fSAndroid Build Coastguard Worker   uint32_t num_refs(0), num_enqueued(0);
169*795d594fSAndroid Build Coastguard Worker   while (!IsEmpty()) {
170*795d594fSAndroid Build Coastguard Worker     ObjPtr<mirror::FinalizerReference> ref = DequeuePendingReference()->AsFinalizerReference();
171*795d594fSAndroid Build Coastguard Worker     ++num_refs;
172*795d594fSAndroid Build Coastguard Worker     mirror::HeapReference<mirror::Object>* referent_addr = ref->GetReferentReferenceAddr();
173*795d594fSAndroid Build Coastguard Worker     // do_atomic_update is false because this happens during the reference processing phase where
174*795d594fSAndroid Build Coastguard Worker     // Reference.clear() would block.
175*795d594fSAndroid Build Coastguard Worker     if (!collector->IsNullOrMarkedHeapReference(referent_addr, /*do_atomic_update=*/false)) {
176*795d594fSAndroid Build Coastguard Worker       ObjPtr<mirror::Object> forward_address = collector->MarkObject(referent_addr->AsMirrorPtr());
177*795d594fSAndroid Build Coastguard Worker       // Move the updated referent to the zombie field.
178*795d594fSAndroid Build Coastguard Worker       if (Runtime::Current()->IsActiveTransaction()) {
179*795d594fSAndroid Build Coastguard Worker         ref->SetZombie<true>(forward_address);
180*795d594fSAndroid Build Coastguard Worker         ref->ClearReferent<true>();
181*795d594fSAndroid Build Coastguard Worker       } else {
182*795d594fSAndroid Build Coastguard Worker         ref->SetZombie<false>(forward_address);
183*795d594fSAndroid Build Coastguard Worker         ref->ClearReferent<false>();
184*795d594fSAndroid Build Coastguard Worker       }
185*795d594fSAndroid Build Coastguard Worker       cleared_references->EnqueueReference(ref);
186*795d594fSAndroid Build Coastguard Worker       ++num_enqueued;
187*795d594fSAndroid Build Coastguard Worker     }
188*795d594fSAndroid Build Coastguard Worker     // Delay disabling the read barrier until here so that the ClearReferent call above in
189*795d594fSAndroid Build Coastguard Worker     // transaction mode will trigger the read barrier.
190*795d594fSAndroid Build Coastguard Worker     DisableReadBarrierForReference(ref->AsReference(), std::memory_order_relaxed);
191*795d594fSAndroid Build Coastguard Worker   }
192*795d594fSAndroid Build Coastguard Worker   return FinalizerStats(num_refs, num_enqueued);
193*795d594fSAndroid Build Coastguard Worker }
194*795d594fSAndroid Build Coastguard Worker 
ForwardSoftReferences(MarkObjectVisitor * visitor)195*795d594fSAndroid Build Coastguard Worker uint32_t ReferenceQueue::ForwardSoftReferences(MarkObjectVisitor* visitor) {
196*795d594fSAndroid Build Coastguard Worker   uint32_t num_refs(0);
197*795d594fSAndroid Build Coastguard Worker   Thread* self = Thread::Current();
198*795d594fSAndroid Build Coastguard Worker   static constexpr int SR_BUF_SIZE = 32;
199*795d594fSAndroid Build Coastguard Worker   ObjPtr<mirror::Reference> buf[SR_BUF_SIZE];
200*795d594fSAndroid Build Coastguard Worker   int n_entries;
201*795d594fSAndroid Build Coastguard Worker   bool empty;
202*795d594fSAndroid Build Coastguard Worker   do {
203*795d594fSAndroid Build Coastguard Worker     {
204*795d594fSAndroid Build Coastguard Worker       // Acquire lock only a few times and hold it as briefly as possible.
205*795d594fSAndroid Build Coastguard Worker       MutexLock mu(self, *lock_);
206*795d594fSAndroid Build Coastguard Worker       empty = IsEmpty();
207*795d594fSAndroid Build Coastguard Worker       for (n_entries = 0; n_entries < SR_BUF_SIZE && !empty; ++n_entries) {
208*795d594fSAndroid Build Coastguard Worker         // Dequeuing the Reference here means it could possibly be enqueued again during this GC.
209*795d594fSAndroid Build Coastguard Worker         // That's unlikely and benign.
210*795d594fSAndroid Build Coastguard Worker         buf[n_entries] = DequeuePendingReference();
211*795d594fSAndroid Build Coastguard Worker         empty = IsEmpty();
212*795d594fSAndroid Build Coastguard Worker       }
213*795d594fSAndroid Build Coastguard Worker     }
214*795d594fSAndroid Build Coastguard Worker     for (int i = 0; i < n_entries; ++i) {
215*795d594fSAndroid Build Coastguard Worker       mirror::HeapReference<mirror::Object>* referent_addr = buf[i]->GetReferentReferenceAddr();
216*795d594fSAndroid Build Coastguard Worker       if (referent_addr->AsMirrorPtr() != nullptr) {
217*795d594fSAndroid Build Coastguard Worker         visitor->MarkHeapReference(referent_addr, /*do_atomic_update=*/ true);
218*795d594fSAndroid Build Coastguard Worker         ++num_refs;
219*795d594fSAndroid Build Coastguard Worker       }
220*795d594fSAndroid Build Coastguard Worker       DisableReadBarrierForReference(buf[i]->AsReference(), std::memory_order_release);
221*795d594fSAndroid Build Coastguard Worker     }
222*795d594fSAndroid Build Coastguard Worker   } while (!empty);
223*795d594fSAndroid Build Coastguard Worker   return num_refs;
224*795d594fSAndroid Build Coastguard Worker }
225*795d594fSAndroid Build Coastguard Worker 
UpdateRoots(IsMarkedVisitor * visitor)226*795d594fSAndroid Build Coastguard Worker void ReferenceQueue::UpdateRoots(IsMarkedVisitor* visitor) {
227*795d594fSAndroid Build Coastguard Worker   if (list_ != nullptr) {
228*795d594fSAndroid Build Coastguard Worker     list_ = down_cast<mirror::Reference*>(visitor->IsMarked(list_));
229*795d594fSAndroid Build Coastguard Worker   }
230*795d594fSAndroid Build Coastguard Worker }
231*795d594fSAndroid Build Coastguard Worker 
232*795d594fSAndroid Build Coastguard Worker }  // namespace gc
233*795d594fSAndroid Build Coastguard Worker }  // namespace art
234