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_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_ 18 #define ART_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_ 19 20 #include <android-base/logging.h> 21 22 #include "arena_allocator.h" 23 #include "debug_stack.h" 24 #include "globals.h" 25 #include "macros.h" 26 27 namespace art { 28 29 class ArenaStack; 30 class ScopedArenaAllocator; 31 32 template <typename T> 33 class ScopedArenaAllocatorAdapter; 34 35 // Tag associated with each allocation to help prevent double free. 36 enum class ArenaFreeTag : uint8_t { 37 // Allocation is used and has not yet been destroyed. 38 kUsed, 39 // Allocation has been destroyed. 40 kFree, 41 }; 42 43 // Holds a list of Arenas for use by ScopedArenaAllocator stack. 44 // The memory is returned to the ArenaPool when the ArenaStack is destroyed. 45 class ArenaStack : private DebugStackRefCounter, private ArenaAllocatorMemoryTool { 46 public: 47 explicit ArenaStack(ArenaPool* arena_pool); 48 ~ArenaStack(); 49 50 using ArenaAllocatorMemoryTool::IsRunningOnMemoryTool; 51 using ArenaAllocatorMemoryTool::MakeDefined; 52 using ArenaAllocatorMemoryTool::MakeUndefined; 53 using ArenaAllocatorMemoryTool::MakeInaccessible; 54 55 void Reset(); 56 PeakBytesAllocated()57 size_t PeakBytesAllocated() { 58 DebugStackRefCounter::CheckNoRefs(); 59 return PeakStats()->BytesAllocated(); 60 } 61 62 size_t ApproximatePeakBytes(); 63 64 MemStats GetPeakStats() const; 65 66 // Return the arena tag associated with a pointer. ArenaTagForAllocation(void * ptr)67 static ArenaFreeTag& ArenaTagForAllocation(void* ptr) { 68 DCHECK(kIsDebugBuild) << "Only debug builds have tags"; 69 return *(reinterpret_cast<ArenaFreeTag*>(ptr) - 1); 70 } 71 72 // The alignment guaranteed for individual allocations. 73 static constexpr size_t kAlignment = 8u; 74 75 private: 76 struct Peak; 77 struct Current; 78 template <typename Tag> struct TaggedStats : ArenaAllocatorStats { }; 79 struct StatsAndPool : TaggedStats<Peak>, TaggedStats<Current> { StatsAndPoolStatsAndPool80 explicit StatsAndPool(ArenaPool* arena_pool) : pool(arena_pool) { } 81 ArenaPool* const pool; 82 }; 83 PeakStats()84 ArenaAllocatorStats* PeakStats() { 85 return static_cast<TaggedStats<Peak>*>(&stats_and_pool_); 86 } 87 PeakStats()88 const ArenaAllocatorStats* PeakStats() const { 89 return static_cast<const TaggedStats<Peak>*>(&stats_and_pool_); 90 } 91 CurrentStats()92 ArenaAllocatorStats* CurrentStats() { 93 return static_cast<TaggedStats<Current>*>(&stats_and_pool_); 94 } 95 96 // Private - access via ScopedArenaAllocator or ScopedArenaAllocatorAdapter. Alloc(size_t bytes,ArenaAllocKind kind)97 void* Alloc(size_t bytes, ArenaAllocKind kind) ALWAYS_INLINE { 98 if (UNLIKELY(IsRunningOnMemoryTool())) { 99 return AllocWithMemoryTool(bytes, kind); 100 } 101 // Add kAlignment for the free or used tag. Required to preserve alignment. 102 size_t rounded_bytes = RoundUp(bytes + (kIsDebugBuild ? kAlignment : 0u), kAlignment); 103 uint8_t* ptr = top_ptr_; 104 if (UNLIKELY(static_cast<size_t>(top_end_ - ptr) < rounded_bytes)) { 105 ptr = AllocateFromNextArena(rounded_bytes); 106 } 107 CurrentStats()->RecordAlloc(bytes, kind); 108 top_ptr_ = ptr + rounded_bytes; 109 if (kIsDebugBuild) { 110 ptr += kAlignment; 111 ArenaTagForAllocation(ptr) = ArenaFreeTag::kUsed; 112 } 113 return ptr; 114 } 115 116 uint8_t* AllocateFromNextArena(size_t rounded_bytes); 117 void UpdatePeakStatsAndRestore(const ArenaAllocatorStats& restore_stats); 118 void UpdateBytesAllocated(); 119 void* AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind); 120 121 StatsAndPool stats_and_pool_; 122 Arena* bottom_arena_; 123 Arena* top_arena_; 124 uint8_t* top_ptr_; 125 uint8_t* top_end_; 126 127 friend class ScopedArenaAllocator; 128 template <typename T> 129 friend class ScopedArenaAllocatorAdapter; 130 131 DISALLOW_COPY_AND_ASSIGN(ArenaStack); 132 }; 133 134 // Fast single-threaded allocator. Allocated chunks are _not_ guaranteed to be zero-initialized. 135 // 136 // Unlike the ArenaAllocator, ScopedArenaAllocator is intended for relatively short-lived 137 // objects and allows nesting multiple allocators. Only the top allocator can be used but 138 // once it's destroyed, its memory can be reused by the next ScopedArenaAllocator on the 139 // stack. This is facilitated by returning the memory to the ArenaStack. 140 class ScopedArenaAllocator 141 : private DebugStackReference, private DebugStackRefCounter, private ArenaAllocatorStats { 142 public: 143 ScopedArenaAllocator(ScopedArenaAllocator&& other) noexcept; 144 explicit ScopedArenaAllocator(ArenaStack* arena_stack); 145 ~ScopedArenaAllocator(); 146 GetArenaStack()147 ArenaStack* GetArenaStack() const { 148 return arena_stack_; 149 } 150 151 void Reset(); 152 153 void* Alloc(size_t bytes, ArenaAllocKind kind = kArenaAllocMisc) ALWAYS_INLINE { 154 DebugStackReference::CheckTop(); 155 return arena_stack_->Alloc(bytes, kind); 156 } 157 158 template <typename T> 159 T* Alloc(ArenaAllocKind kind = kArenaAllocMisc) { 160 return AllocArray<T>(1, kind); 161 } 162 163 template <typename T> 164 T* AllocArray(size_t length, ArenaAllocKind kind = kArenaAllocMisc) { 165 return static_cast<T*>(Alloc(length * sizeof(T), kind)); 166 } 167 168 // Get adapter for use in STL containers. See scoped_arena_containers.h . 169 ScopedArenaAllocatorAdapter<void> Adapter(ArenaAllocKind kind = kArenaAllocSTL); 170 171 size_t ApproximatePeakBytes(); 172 173 // Allow a delete-expression to destroy but not deallocate allocators created by Create(). delete(void * ptr ATTRIBUTE_UNUSED)174 static void operator delete(void* ptr ATTRIBUTE_UNUSED) {} 175 176 private: 177 ArenaStack* arena_stack_; 178 Arena* mark_arena_; 179 uint8_t* mark_ptr_; 180 uint8_t* mark_end_; 181 182 void DoReset(); 183 184 template <typename T> 185 friend class ScopedArenaAllocatorAdapter; 186 187 DISALLOW_COPY_AND_ASSIGN(ScopedArenaAllocator); 188 }; 189 190 } // namespace art 191 192 #endif // ART_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_ 193