File: concurrent_copying-inl.h

package info (click to toggle)
android-platform-art 11.0.0%2Br48-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 78,932 kB
  • sloc: cpp: 459,858; java: 163,268; asm: 22,644; python: 9,815; sh: 6,330; ansic: 4,117; xml: 2,855; perl: 77; makefile: 73
file content (275 lines) | stat: -rw-r--r-- 12,681 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*
 * Copyright (C) 2015 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.
 */

#ifndef ART_RUNTIME_GC_COLLECTOR_CONCURRENT_COPYING_INL_H_
#define ART_RUNTIME_GC_COLLECTOR_CONCURRENT_COPYING_INL_H_

#include "concurrent_copying.h"

#include "gc/accounting/atomic_stack.h"
#include "gc/accounting/space_bitmap-inl.h"
#include "gc/heap.h"
#include "gc/space/region_space-inl.h"
#include "gc/verification.h"
#include "lock_word.h"
#include "mirror/class.h"
#include "mirror/object-readbarrier-inl.h"

namespace art {
namespace gc {
namespace collector {

inline mirror::Object* ConcurrentCopying::MarkUnevacFromSpaceRegion(
    Thread* const self,
    mirror::Object* ref,
    accounting::ContinuousSpaceBitmap* bitmap) {
  if (use_generational_cc_ && !done_scanning_.load(std::memory_order_acquire)) {
    // Everything in the unevac space should be marked for young generation CC,
    // except for large objects.
    DCHECK(!young_gen_ || region_space_bitmap_->Test(ref) || region_space_->IsLargeObject(ref))
        << ref << " "
        << ref->GetClass<kVerifyNone, kWithoutReadBarrier>()->PrettyClass();
    // Since the mark bitmap is still filled in from last GC (or from marking phase of 2-phase CC,
    // we can not use that or else the mutator may see references to the from space. Instead, use
    // the baker pointer itself as the mark bit.
    if (ref->AtomicSetReadBarrierState(ReadBarrier::NonGrayState(), ReadBarrier::GrayState())) {
      // TODO: We don't actually need to scan this object later, we just need to clear the gray
      // bit.
      // TODO: We could also set the mark bit here for "free" since this case comes from the
      // read barrier.
      PushOntoMarkStack(self, ref);
    }
    DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState());
    return ref;
  }
  // For the Baker-style RB, in a rare case, we could incorrectly change the object from non-gray
  // (black) to gray even though the object has already been marked through. This happens if a
  // mutator thread gets preempted before the AtomicSetReadBarrierState below, GC marks through the
  // object (changes it from non-gray (white) to gray and back to non-gray (black)), and the thread
  // runs and incorrectly changes it from non-gray (black) to gray. If this happens, the object
  // will get added to the mark stack again and get changed back to non-gray (black) after it is
  // processed.
  if (kUseBakerReadBarrier) {
    // Test the bitmap first to avoid graying an object that has already been marked through most
    // of the time.
    if (bitmap->Test(ref)) {
      return ref;
    }
  }
  // This may or may not succeed, which is ok because the object may already be gray.
  bool success = false;
  if (kUseBakerReadBarrier) {
    // GC will mark the bitmap when popping from mark stack. If only the GC is touching the bitmap
    // we can avoid an expensive CAS.
    // For the baker case, an object is marked if either the mark bit marked or the bitmap bit is
    // set.
    success = ref->AtomicSetReadBarrierState(/* expected_rb_state= */ ReadBarrier::NonGrayState(),
                                             /* rb_state= */ ReadBarrier::GrayState());
  } else {
    success = !bitmap->AtomicTestAndSet(ref);
  }
  if (success) {
    // Newly marked.
    if (kUseBakerReadBarrier) {
      DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState());
    }
    PushOntoMarkStack(self, ref);
  }
  return ref;
}

template<bool kGrayImmuneObject>
inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(Thread* const self,
                                                          mirror::Object* ref) {
  if (kUseBakerReadBarrier) {
    // The GC-running thread doesn't (need to) gray immune objects except when updating thread roots
    // in the thread flip on behalf of suspended threads (when gc_grays_immune_objects_ is
    // true). Also, a mutator doesn't (need to) gray an immune object after GC has updated all
    // immune space objects (when updated_all_immune_objects_ is true).
    if (kIsDebugBuild) {
      if (self == thread_running_gc_) {
        DCHECK(!kGrayImmuneObject ||
               updated_all_immune_objects_.load(std::memory_order_relaxed) ||
               gc_grays_immune_objects_);
      } else {
        DCHECK(kGrayImmuneObject);
      }
    }
    if (!kGrayImmuneObject || updated_all_immune_objects_.load(std::memory_order_relaxed)) {
      return ref;
    }
    // This may or may not succeed, which is ok because the object may already be gray.
    bool success =
        ref->AtomicSetReadBarrierState(/* expected_rb_state= */ ReadBarrier::NonGrayState(),
                                       /* rb_state= */ ReadBarrier::GrayState());
    if (success) {
      MutexLock mu(self, immune_gray_stack_lock_);
      immune_gray_stack_.push_back(ref);
    }
  }
  return ref;
}

template<bool kGrayImmuneObject, bool kNoUnEvac, bool kFromGCThread>
inline mirror::Object* ConcurrentCopying::Mark(Thread* const self,
                                               mirror::Object* from_ref,
                                               mirror::Object* holder,
                                               MemberOffset offset) {
  // Cannot have `kNoUnEvac` when Generational CC collection is disabled.
  DCHECK(!kNoUnEvac || use_generational_cc_);
  if (from_ref == nullptr) {
    return nullptr;
  }
  DCHECK(heap_->collector_type_ == kCollectorTypeCC);
  if (kFromGCThread) {
    DCHECK(is_active_);
    DCHECK_EQ(self, thread_running_gc_);
  } else if (UNLIKELY(kUseBakerReadBarrier && !is_active_)) {
    // In the lock word forward address state, the read barrier bits
    // in the lock word are part of the stored forwarding address and
    // invalid. This is usually OK as the from-space copy of objects
    // aren't accessed by mutators due to the to-space
    // invariant. However, during the dex2oat image writing relocation
    // and the zygote compaction, objects can be in the forward
    // address state (to store the forward/relocation addresses) and
    // they can still be accessed and the invalid read barrier bits
    // are consulted. If they look like gray but aren't really, the
    // read barriers slow path can trigger when it shouldn't. To guard
    // against this, return here if the CC collector isn't running.
    return from_ref;
  }
  DCHECK(region_space_ != nullptr) << "Read barrier slow path taken when CC isn't running?";
  if (region_space_->HasAddress(from_ref)) {
    space::RegionSpace::RegionType rtype = region_space_->GetRegionTypeUnsafe(from_ref);
    switch (rtype) {
      case space::RegionSpace::RegionType::kRegionTypeToSpace:
        // It's already marked.
        return from_ref;
      case space::RegionSpace::RegionType::kRegionTypeFromSpace: {
        mirror::Object* to_ref = GetFwdPtr(from_ref);
        if (to_ref == nullptr) {
          // It isn't marked yet. Mark it by copying it to the to-space.
          to_ref = Copy(self, from_ref, holder, offset);
        }
        // The copy should either be in a to-space region, or in the
        // non-moving space, if it could not fit in a to-space region.
        DCHECK(region_space_->IsInToSpace(to_ref) || heap_->non_moving_space_->HasAddress(to_ref))
            << "from_ref=" << from_ref << " to_ref=" << to_ref;
        return to_ref;
      }
      case space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace:
        if (kNoUnEvac && use_generational_cc_ && !region_space_->IsLargeObject(from_ref)) {
          if (!kFromGCThread) {
            DCHECK(IsMarkedInUnevacFromSpace(from_ref)) << "Returning unmarked object to mutator";
          }
          return from_ref;
        }
        return MarkUnevacFromSpaceRegion(self, from_ref, region_space_bitmap_);
      default:
        // The reference is in an unused region. Remove memory protection from
        // the region space and log debugging information.
        region_space_->Unprotect();
        LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(holder, offset, from_ref);
        region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT));
        heap_->GetVerification()->LogHeapCorruption(holder, offset, from_ref, /* fatal= */ true);
        UNREACHABLE();
    }
  } else {
    if (immune_spaces_.ContainsObject(from_ref)) {
      return MarkImmuneSpace<kGrayImmuneObject>(self, from_ref);
    } else {
      return MarkNonMoving(self, from_ref, holder, offset);
    }
  }
}

inline mirror::Object* ConcurrentCopying::MarkFromReadBarrier(mirror::Object* from_ref) {
  mirror::Object* ret;
  Thread* const self = Thread::Current();
  // We can get here before marking starts since we gray immune objects before the marking phase.
  if (from_ref == nullptr || !self->GetIsGcMarking()) {
    return from_ref;
  }
  // TODO: Consider removing this check when we are done investigating slow paths. b/30162165
  if (UNLIKELY(mark_from_read_barrier_measurements_)) {
    ret = MarkFromReadBarrierWithMeasurements(self, from_ref);
  } else {
    ret = Mark</*kGrayImmuneObject=*/true, /*kNoUnEvac=*/false, /*kFromGCThread=*/false>(self,
                                                                                         from_ref);
  }
  // Only set the mark bit for baker barrier.
  if (kUseBakerReadBarrier && LIKELY(!rb_mark_bit_stack_full_ && ret->AtomicSetMarkBit(0, 1))) {
    // If the mark stack is full, we may temporarily go to mark and back to unmarked. Seeing both
    // values are OK since the only race is doing an unnecessary Mark.
    if (!rb_mark_bit_stack_->AtomicPushBack(ret)) {
      // Mark stack is full, set the bit back to zero.
      CHECK(ret->AtomicSetMarkBit(1, 0));
      // Set rb_mark_bit_stack_full_, this is racy but OK since AtomicPushBack is thread safe.
      rb_mark_bit_stack_full_ = true;
    }
  }
  return ret;
}

inline mirror::Object* ConcurrentCopying::GetFwdPtr(mirror::Object* from_ref) {
  DCHECK(region_space_->IsInFromSpace(from_ref));
  LockWord lw = from_ref->GetLockWord(false);
  if (lw.GetState() == LockWord::kForwardingAddress) {
    mirror::Object* fwd_ptr = reinterpret_cast<mirror::Object*>(lw.ForwardingAddress());
    DCHECK(fwd_ptr != nullptr);
    return fwd_ptr;
  } else {
    return nullptr;
  }
}

inline bool ConcurrentCopying::IsMarkedInUnevacFromSpace(mirror::Object* from_ref) {
  // Use load-acquire on the read barrier pointer to ensure that we never see a black (non-gray)
  // read barrier state with an unmarked bit due to reordering.
  DCHECK(region_space_->IsInUnevacFromSpace(from_ref));
  if (kUseBakerReadBarrier && from_ref->GetReadBarrierStateAcquire() == ReadBarrier::GrayState()) {
    return true;
  } else if (!use_generational_cc_ || done_scanning_.load(std::memory_order_acquire)) {
    // If the card table scanning is not finished yet, then only read-barrier
    // state should be checked. Checking the mark bitmap is unreliable as there
    // may be some objects - whose corresponding card is dirty - which are
    // marked in the mark bitmap, but cannot be considered marked unless their
    // read-barrier state is set to Gray.
    //
    // Why read read-barrier state before checking done_scanning_?
    // If the read-barrier state was read *after* done_scanning_, then there
    // exists a concurrency race due to which even after the object is marked,
    // read-barrier state is checked *after* that, this function will return
    // false. The following scenario may cause the race:
    //
    // 1. Mutator thread reads done_scanning_ and upon finding it false, gets
    // suspended before reading the object's read-barrier state.
    // 2. GC thread finishes card-table scan and then sets done_scanning_ to
    // true.
    // 3. GC thread grays the object, scans it, marks in the bitmap, and then
    // changes its read-barrier state back to non-gray.
    // 4. Mutator thread resumes, reads the object's read-barrier state and
    // returns false.
    return region_space_bitmap_->Test(from_ref);
  }
  return false;
}

}  // namespace collector
}  // namespace gc
}  // namespace art

#endif  // ART_RUNTIME_GC_COLLECTOR_CONCURRENT_COPYING_INL_H_