1 /* 2 * Copyright (C) 2012 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_GC_ACCOUNTING_ATOMIC_STACK_H_ 18 #define ART_RUNTIME_GC_ACCOUNTING_ATOMIC_STACK_H_ 19 20 #include <algorithm> 21 #include <memory> 22 #include <string> 23 24 #include "atomic.h" 25 #include "base/logging.h" 26 #include "base/macros.h" 27 #include "mem_map.h" 28 #include "utils.h" 29 30 namespace art { 31 namespace gc { 32 namespace accounting { 33 34 template <typename T> 35 class AtomicStack { 36 public: 37 // Capacity is how many elements we can store in the stack. 38 static AtomicStack* Create(const std::string& name, size_t growth_limit, size_t capacity) { 39 std::unique_ptr<AtomicStack> mark_stack(new AtomicStack(name, growth_limit, capacity)); 40 mark_stack->Init(); 41 return mark_stack.release(); 42 } 43 44 ~AtomicStack() {} 45 46 void Reset() { 47 DCHECK(mem_map_.get() != nullptr); 48 DCHECK(begin_ != NULL); 49 front_index_.StoreRelaxed(0); 50 back_index_.StoreRelaxed(0); 51 debug_is_sorted_ = true; 52 mem_map_->MadviseDontNeedAndZero(); 53 } 54 55 // Beware: Mixing atomic pushes and atomic pops will cause ABA problem. 56 57 // Returns false if we overflowed the stack. 58 bool AtomicPushBackIgnoreGrowthLimit(const T& value) { 59 return AtomicPushBackInternal(value, capacity_); 60 } 61 62 // Returns false if we overflowed the stack. 63 bool AtomicPushBack(const T& value) { 64 return AtomicPushBackInternal(value, growth_limit_); 65 } 66 67 // Atomically bump the back index by the given number of 68 // slots. Returns false if we overflowed the stack. 69 bool AtomicBumpBack(size_t num_slots, T** start_address, T** end_address) { 70 if (kIsDebugBuild) { 71 debug_is_sorted_ = false; 72 } 73 int32_t index; 74 int32_t new_index; 75 do { 76 index = back_index_.LoadRelaxed(); 77 new_index = index + num_slots; 78 if (UNLIKELY(static_cast<size_t>(new_index) >= growth_limit_)) { 79 // Stack overflow. 80 return false; 81 } 82 } while (!back_index_.CompareExchangeWeakRelaxed(index, new_index)); 83 *start_address = &begin_[index]; 84 *end_address = &begin_[new_index]; 85 if (kIsDebugBuild) { 86 // Sanity check that the memory is zero. 87 for (int32_t i = index; i < new_index; ++i) { 88 DCHECK_EQ(begin_[i], static_cast<T>(0)) 89 << "i=" << i << " index=" << index << " new_index=" << new_index; 90 } 91 } 92 return true; 93 } 94 95 void AssertAllZero() { 96 if (kIsDebugBuild) { 97 for (size_t i = 0; i < capacity_; ++i) { 98 DCHECK_EQ(begin_[i], static_cast<T>(0)) << "i=" << i; 99 } 100 } 101 } 102 103 void PushBack(const T& value) { 104 if (kIsDebugBuild) { 105 debug_is_sorted_ = false; 106 } 107 int32_t index = back_index_.LoadRelaxed(); 108 DCHECK_LT(static_cast<size_t>(index), growth_limit_); 109 back_index_.StoreRelaxed(index + 1); 110 begin_[index] = value; 111 } 112 113 T PopBack() { 114 DCHECK_GT(back_index_.LoadRelaxed(), front_index_.LoadRelaxed()); 115 // Decrement the back index non atomically. 116 back_index_.StoreRelaxed(back_index_.LoadRelaxed() - 1); 117 return begin_[back_index_.LoadRelaxed()]; 118 } 119 120 // Take an item from the front of the stack. 121 T PopFront() { 122 int32_t index = front_index_.LoadRelaxed(); 123 DCHECK_LT(index, back_index_.LoadRelaxed()); 124 front_index_.StoreRelaxed(index + 1); 125 return begin_[index]; 126 } 127 128 // Pop a number of elements. 129 void PopBackCount(int32_t n) { 130 DCHECK_GE(Size(), static_cast<size_t>(n)); 131 back_index_.FetchAndSubSequentiallyConsistent(n); 132 } 133 134 bool IsEmpty() const { 135 return Size() == 0; 136 } 137 138 size_t Size() const { 139 DCHECK_LE(front_index_.LoadRelaxed(), back_index_.LoadRelaxed()); 140 return back_index_.LoadRelaxed() - front_index_.LoadRelaxed(); 141 } 142 143 T* Begin() const { 144 return const_cast<T*>(begin_ + front_index_.LoadRelaxed()); 145 } 146 147 T* End() const { 148 return const_cast<T*>(begin_ + back_index_.LoadRelaxed()); 149 } 150 151 size_t Capacity() const { 152 return capacity_; 153 } 154 155 // Will clear the stack. 156 void Resize(size_t new_capacity) { 157 capacity_ = new_capacity; 158 growth_limit_ = new_capacity; 159 Init(); 160 } 161 162 void Sort() { 163 int32_t start_back_index = back_index_.LoadRelaxed(); 164 int32_t start_front_index = front_index_.LoadRelaxed(); 165 std::sort(Begin(), End()); 166 CHECK_EQ(start_back_index, back_index_.LoadRelaxed()); 167 CHECK_EQ(start_front_index, front_index_.LoadRelaxed()); 168 if (kIsDebugBuild) { 169 debug_is_sorted_ = true; 170 } 171 } 172 173 bool ContainsSorted(const T& value) const { 174 DCHECK(debug_is_sorted_); 175 return std::binary_search(Begin(), End(), value); 176 } 177 178 bool Contains(const T& value) const { 179 return std::find(Begin(), End(), value) != End(); 180 } 181 182 private: 183 AtomicStack(const std::string& name, size_t growth_limit, size_t capacity) 184 : name_(name), 185 back_index_(0), 186 front_index_(0), 187 begin_(nullptr), 188 growth_limit_(growth_limit), 189 capacity_(capacity), 190 debug_is_sorted_(true) { 191 } 192 193 // Returns false if we overflowed the stack. 194 bool AtomicPushBackInternal(const T& value, size_t limit) ALWAYS_INLINE { 195 if (kIsDebugBuild) { 196 debug_is_sorted_ = false; 197 } 198 int32_t index; 199 do { 200 index = back_index_.LoadRelaxed(); 201 if (UNLIKELY(static_cast<size_t>(index) >= limit)) { 202 // Stack overflow. 203 return false; 204 } 205 } while (!back_index_.CompareExchangeWeakRelaxed(index, index + 1)); 206 begin_[index] = value; 207 return true; 208 } 209 210 // Size in number of elements. 211 void Init() { 212 std::string error_msg; 213 mem_map_.reset(MemMap::MapAnonymous(name_.c_str(), NULL, capacity_ * sizeof(T), 214 PROT_READ | PROT_WRITE, false, &error_msg)); 215 CHECK(mem_map_.get() != NULL) << "couldn't allocate mark stack.\n" << error_msg; 216 byte* addr = mem_map_->Begin(); 217 CHECK(addr != NULL); 218 debug_is_sorted_ = true; 219 begin_ = reinterpret_cast<T*>(addr); 220 Reset(); 221 } 222 223 // Name of the mark stack. 224 std::string name_; 225 // Memory mapping of the atomic stack. 226 std::unique_ptr<MemMap> mem_map_; 227 // Back index (index after the last element pushed). 228 AtomicInteger back_index_; 229 // Front index, used for implementing PopFront. 230 AtomicInteger front_index_; 231 // Base of the atomic stack. 232 T* begin_; 233 // Current maximum which we can push back to, must be <= capacity_. 234 size_t growth_limit_; 235 // Maximum number of elements. 236 size_t capacity_; 237 // Whether or not the stack is sorted, only updated in debug mode to avoid performance overhead. 238 bool debug_is_sorted_; 239 240 DISALLOW_COPY_AND_ASSIGN(AtomicStack); 241 }; 242 243 typedef AtomicStack<mirror::Object*> ObjectStack; 244 245 } // namespace accounting 246 } // namespace gc 247 } // namespace art 248 249 #endif // ART_RUNTIME_GC_ACCOUNTING_ATOMIC_STACK_H_ 250