Home | History | Annotate | Download | only in runtime
      1 /*
      2  * Copyright (C) 2014 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 #ifndef ART_RUNTIME_READ_BARRIER_INL_H_
     18 #define ART_RUNTIME_READ_BARRIER_INL_H_
     19 
     20 #include "read_barrier.h"
     21 
     22 #include "gc/collector/concurrent_copying-inl.h"
     23 #include "gc/heap.h"
     24 #include "mirror/object_reference.h"
     25 #include "mirror/reference.h"
     26 #include "runtime.h"
     27 #include "utils.h"
     28 
     29 namespace art {
     30 
     31 template <typename MirrorType, ReadBarrierOption kReadBarrierOption, bool kAlwaysUpdateField>
     32 inline MirrorType* ReadBarrier::Barrier(
     33     mirror::Object* obj, MemberOffset offset, mirror::HeapReference<MirrorType>* ref_addr) {
     34   constexpr bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
     35   if (kUseReadBarrier && with_read_barrier) {
     36     if (kIsDebugBuild) {
     37       Thread* const self = Thread::Current();
     38       if (self != nullptr) {
     39         CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
     40       }
     41     }
     42     if (kUseBakerReadBarrier) {
     43       // The higher bits of the rb_ptr, rb_ptr_high_bits (must be zero)
     44       // is used to create artificial data dependency from the is_gray
     45       // load to the ref field (ptr) load to avoid needing a load-load
     46       // barrier between the two.
     47       uintptr_t rb_ptr_high_bits;
     48       bool is_gray = HasGrayReadBarrierPointer(obj, &rb_ptr_high_bits);
     49       ref_addr = reinterpret_cast<mirror::HeapReference<MirrorType>*>(
     50           rb_ptr_high_bits | reinterpret_cast<uintptr_t>(ref_addr));
     51       MirrorType* ref = ref_addr->AsMirrorPtr();
     52       MirrorType* old_ref = ref;
     53       if (is_gray) {
     54         // Slow-path.
     55         ref = reinterpret_cast<MirrorType*>(Mark(ref));
     56         // If kAlwaysUpdateField is true, update the field atomically. This may fail if mutator
     57         // updates before us, but it's ok.
     58         if (kAlwaysUpdateField && ref != old_ref) {
     59           obj->CasFieldStrongRelaxedObjectWithoutWriteBarrier<false, false>(
     60               offset, old_ref, ref);
     61         }
     62       }
     63       if (kEnableReadBarrierInvariantChecks) {
     64         CHECK_EQ(rb_ptr_high_bits, 0U) << obj << " rb_ptr=" << obj->GetReadBarrierPointer();
     65       }
     66       AssertToSpaceInvariant(obj, offset, ref);
     67       return ref;
     68     } else if (kUseBrooksReadBarrier) {
     69       // To be implemented.
     70       return ref_addr->AsMirrorPtr();
     71     } else if (kUseTableLookupReadBarrier) {
     72       MirrorType* ref = ref_addr->AsMirrorPtr();
     73       MirrorType* old_ref = ref;
     74       // The heap or the collector can be null at startup. TODO: avoid the need for this null check.
     75       gc::Heap* heap = Runtime::Current()->GetHeap();
     76       if (heap != nullptr && heap->GetReadBarrierTable()->IsSet(old_ref)) {
     77         ref = reinterpret_cast<MirrorType*>(Mark(old_ref));
     78         // Update the field atomically. This may fail if mutator updates before us, but it's ok.
     79         if (ref != old_ref) {
     80           obj->CasFieldStrongRelaxedObjectWithoutWriteBarrier<false, false>(
     81               offset, old_ref, ref);
     82         }
     83       }
     84       AssertToSpaceInvariant(obj, offset, ref);
     85       return ref;
     86     } else {
     87       LOG(FATAL) << "Unexpected read barrier type";
     88       UNREACHABLE();
     89     }
     90   } else {
     91     // No read barrier.
     92     return ref_addr->AsMirrorPtr();
     93   }
     94 }
     95 
     96 template <typename MirrorType, ReadBarrierOption kReadBarrierOption>
     97 inline MirrorType* ReadBarrier::BarrierForRoot(MirrorType** root,
     98                                                GcRootSource* gc_root_source) {
     99   MirrorType* ref = *root;
    100   const bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
    101   if (kUseReadBarrier && with_read_barrier) {
    102     if (kIsDebugBuild) {
    103       Thread* const self = Thread::Current();
    104       if (self != nullptr) {
    105         CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
    106       }
    107     }
    108     if (kUseBakerReadBarrier) {
    109       // TODO: separate the read barrier code from the collector code more.
    110       Thread* self = Thread::Current();
    111       if (self != nullptr && self->GetIsGcMarking()) {
    112         ref = reinterpret_cast<MirrorType*>(Mark(ref));
    113       }
    114       AssertToSpaceInvariant(gc_root_source, ref);
    115       return ref;
    116     } else if (kUseBrooksReadBarrier) {
    117       // To be implemented.
    118       return ref;
    119     } else if (kUseTableLookupReadBarrier) {
    120       Thread* self = Thread::Current();
    121       if (self != nullptr &&
    122           self->GetIsGcMarking() &&
    123           Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) {
    124         MirrorType* old_ref = ref;
    125         ref = reinterpret_cast<MirrorType*>(Mark(old_ref));
    126         // Update the field atomically. This may fail if mutator updates before us, but it's ok.
    127         if (ref != old_ref) {
    128           Atomic<mirror::Object*>* atomic_root = reinterpret_cast<Atomic<mirror::Object*>*>(root);
    129           atomic_root->CompareExchangeStrongRelaxed(old_ref, ref);
    130         }
    131       }
    132       AssertToSpaceInvariant(gc_root_source, ref);
    133       return ref;
    134     } else {
    135       LOG(FATAL) << "Unexpected read barrier type";
    136       UNREACHABLE();
    137     }
    138   } else {
    139     return ref;
    140   }
    141 }
    142 
    143 // TODO: Reduce copy paste
    144 template <typename MirrorType, ReadBarrierOption kReadBarrierOption>
    145 inline MirrorType* ReadBarrier::BarrierForRoot(mirror::CompressedReference<MirrorType>* root,
    146                                                GcRootSource* gc_root_source) {
    147   MirrorType* ref = root->AsMirrorPtr();
    148   const bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
    149   if (with_read_barrier && kUseBakerReadBarrier) {
    150     // TODO: separate the read barrier code from the collector code more.
    151     Thread* self = Thread::Current();
    152     if (self != nullptr && self->GetIsGcMarking()) {
    153       ref = reinterpret_cast<MirrorType*>(Mark(ref));
    154     }
    155     AssertToSpaceInvariant(gc_root_source, ref);
    156     return ref;
    157   } else if (with_read_barrier && kUseBrooksReadBarrier) {
    158     // To be implemented.
    159     return ref;
    160   } else if (with_read_barrier && kUseTableLookupReadBarrier) {
    161     Thread* self = Thread::Current();
    162     if (self != nullptr &&
    163         self->GetIsGcMarking() &&
    164         Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) {
    165       auto old_ref = mirror::CompressedReference<MirrorType>::FromMirrorPtr(ref);
    166       ref = reinterpret_cast<MirrorType*>(Mark(ref));
    167       auto new_ref = mirror::CompressedReference<MirrorType>::FromMirrorPtr(ref);
    168       // Update the field atomically. This may fail if mutator updates before us, but it's ok.
    169       if (new_ref.AsMirrorPtr() != old_ref.AsMirrorPtr()) {
    170         auto* atomic_root =
    171             reinterpret_cast<Atomic<mirror::CompressedReference<MirrorType>>*>(root);
    172         atomic_root->CompareExchangeStrongRelaxed(old_ref, new_ref);
    173       }
    174     }
    175     AssertToSpaceInvariant(gc_root_source, ref);
    176     return ref;
    177   } else {
    178     return ref;
    179   }
    180 }
    181 
    182 inline bool ReadBarrier::IsDuringStartup() {
    183   gc::Heap* heap = Runtime::Current()->GetHeap();
    184   if (heap == nullptr) {
    185     // During startup, the heap can be null.
    186     return true;
    187   }
    188   if (heap->CurrentCollectorType() != gc::kCollectorTypeCC) {
    189     // CC isn't running.
    190     return true;
    191   }
    192   gc::collector::ConcurrentCopying* collector = heap->ConcurrentCopyingCollector();
    193   if (collector == nullptr) {
    194     // During startup, the collector can be null.
    195     return true;
    196   }
    197   return false;
    198 }
    199 
    200 inline void ReadBarrier::AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset,
    201                                                 mirror::Object* ref) {
    202   if (kEnableToSpaceInvariantChecks || kIsDebugBuild) {
    203     if (ref == nullptr || IsDuringStartup()) {
    204       return;
    205     }
    206     Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->
    207         AssertToSpaceInvariant(obj, offset, ref);
    208   }
    209 }
    210 
    211 inline void ReadBarrier::AssertToSpaceInvariant(GcRootSource* gc_root_source,
    212                                                 mirror::Object* ref) {
    213   if (kEnableToSpaceInvariantChecks || kIsDebugBuild) {
    214     if (ref == nullptr || IsDuringStartup()) {
    215       return;
    216     }
    217     Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->
    218         AssertToSpaceInvariant(gc_root_source, ref);
    219   }
    220 }
    221 
    222 inline mirror::Object* ReadBarrier::Mark(mirror::Object* obj) {
    223   return Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->Mark(obj);
    224 }
    225 
    226 inline bool ReadBarrier::HasGrayReadBarrierPointer(mirror::Object* obj,
    227                                                    uintptr_t* out_rb_ptr_high_bits) {
    228   mirror::Object* rb_ptr = obj->GetReadBarrierPointer();
    229   uintptr_t rb_ptr_bits = reinterpret_cast<uintptr_t>(rb_ptr);
    230   uintptr_t rb_ptr_low_bits = rb_ptr_bits & rb_ptr_mask_;
    231   if (kEnableReadBarrierInvariantChecks) {
    232     CHECK(rb_ptr_low_bits == white_ptr_ || rb_ptr_low_bits == gray_ptr_ ||
    233           rb_ptr_low_bits == black_ptr_)
    234         << "obj=" << obj << " rb_ptr=" << rb_ptr << " " << PrettyTypeOf(obj);
    235   }
    236   bool is_gray = rb_ptr_low_bits == gray_ptr_;
    237   // The high bits are supposed to be zero. We check this on the caller side.
    238   *out_rb_ptr_high_bits = rb_ptr_bits & ~rb_ptr_mask_;
    239   return is_gray;
    240 }
    241 
    242 }  // namespace art
    243 
    244 #endif  // ART_RUNTIME_READ_BARRIER_INL_H_
    245