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