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