1 /**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "arena_allocator.h"
17
18 #include "pool_manager.h"
19 #include "trace/trace.h"
20
21 namespace panda {
22
23 template <bool use_oom_handler>
ArenaAllocatorT(SpaceType space_type,BaseMemStats * mem_stats,bool limit_alloc_size_by_pool)24 ArenaAllocatorT<use_oom_handler>::ArenaAllocatorT(SpaceType space_type, BaseMemStats *mem_stats,
25 bool limit_alloc_size_by_pool)
26 : memStats_(mem_stats), space_type_(space_type), limit_alloc_size_by_pool_(limit_alloc_size_by_pool)
27 {
28 ASSERT(!use_oom_handler);
29 if (!ON_STACK_ALLOCATION_ENABLED) {
30 arenas_ = PoolManager::AllocArena(DEFAULT_ARENA_SIZE, space_type_, AllocatorType::ARENA_ALLOCATOR, this);
31 if (UNLIKELY(arenas_ == nullptr)) {
32 std::cerr << "CHECK FAILED: : arenas_ == nullptr IN: " << __FILE__ << ":" << __LINE__ <<
33 " in " << __FUNCTION__ << std::endl <<
34 "Solutions: > Free up system memory by closing unnecessary applications" <<
35 "> Consider reducing memory usage or implementing memory cleanup" << std::endl;
36 panda::PrintStack(std::cerr);
37 std::abort();
38 }
39 AllocArenaMemStats(DEFAULT_ARENA_SIZE);
40 }
41 }
42
43 template <bool use_oom_handler>
ArenaAllocatorT(OOMHandler oom_handler,SpaceType space_type,BaseMemStats * mem_stats,bool limit_alloc_size_by_pool)44 ArenaAllocatorT<use_oom_handler>::ArenaAllocatorT(OOMHandler oom_handler, SpaceType space_type, BaseMemStats *mem_stats,
45 bool limit_alloc_size_by_pool)
46 : memStats_(mem_stats),
47 space_type_(space_type),
48 oom_handler_(oom_handler),
49 limit_alloc_size_by_pool_(limit_alloc_size_by_pool)
50 {
51 ASSERT(use_oom_handler);
52 if (!ON_STACK_ALLOCATION_ENABLED) {
53 arenas_ = PoolManager::AllocArena(DEFAULT_ARENA_SIZE, space_type_, AllocatorType::ARENA_ALLOCATOR, this);
54 CHECK_NOT_NULL(arenas_);
55 AllocArenaMemStats(DEFAULT_ARENA_SIZE);
56 }
57 }
58
59 template <bool use_oom_handler>
~ArenaAllocatorT()60 ArenaAllocatorT<use_oom_handler>::~ArenaAllocatorT()
61 {
62 Arena *cur = arenas_;
63 while (cur != nullptr) {
64 Arena *tmp;
65 tmp = cur->GetNextArena();
66 PoolManager::FreeArena(cur);
67 cur = tmp;
68 }
69 }
70
71 template <bool use_oom_handler>
AllocateAndAddNewPool(size_t size,Alignment alignment)72 inline void *ArenaAllocatorT<use_oom_handler>::AllocateAndAddNewPool(size_t size, Alignment alignment)
73 {
74 void *mem = arenas_->Alloc(size, alignment);
75 if (mem == nullptr) {
76 bool add_new_pool = false;
77 if (limit_alloc_size_by_pool_) {
78 add_new_pool = AddArenaFromPool(std::max(AlignUp(size, alignment) + sizeof(Arena), DEFAULT_ARENA_SIZE));
79 } else {
80 add_new_pool = AddArenaFromPool(DEFAULT_ARENA_SIZE);
81 }
82 if (UNLIKELY(!add_new_pool)) {
83 LOG(DEBUG, ALLOC) << "Can not add new pool for " << SpaceTypeToString(space_type_);
84 return nullptr;
85 }
86 mem = arenas_->Alloc(size, alignment);
87 ASSERT(!limit_alloc_size_by_pool_ || mem != nullptr);
88 }
89 return mem;
90 }
91
92 template <bool use_oom_handler>
Alloc(size_t size,Alignment align)93 void *ArenaAllocatorT<use_oom_handler>::Alloc(size_t size, Alignment align)
94 {
95 trace::ScopedTrace scoped_trace("ArenaAllocator allocate");
96 LOG(DEBUG, ALLOC) << "ArenaAllocator: try to alloc " << size << " with align " << align;
97 void *ret = nullptr;
98 if (ON_STACK_ALLOCATION_ENABLED && UNLIKELY(!arenas_)) {
99 LOG(DEBUG, ALLOC) << "\tTry to allocate from stack";
100 ret = buff_.Alloc(size, align);
101 LOG_IF(ret, DEBUG, ALLOC) << "\tallocate from stack buffer";
102 if (ret == nullptr) {
103 ret = AllocateAndAddNewPool(size, align);
104 }
105 } else {
106 ret = AllocateAndAddNewPool(size, align);
107 }
108 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
109 if constexpr (use_oom_handler) {
110 if (ret == nullptr) {
111 oom_handler_();
112 }
113 }
114 LOG(DEBUG, ALLOC) << "ArenaAllocator: allocated " << size << " bytes aligned by " << align;
115 AllocArenaMemStats(size);
116 return ret;
117 }
118
119 template <bool use_oom_handler>
Resize(size_t new_size)120 void ArenaAllocatorT<use_oom_handler>::Resize(size_t new_size)
121 {
122 LOG(DEBUG, ALLOC) << "ArenaAllocator: resize to new size " << new_size;
123 // TODO(aemelenko): we have O(2n) here in the worst case
124 size_t cur_size = GetAllocatedSize();
125 if (cur_size <= new_size) {
126 LOG_IF(cur_size < new_size, FATAL, ALLOC) << "ArenaAllocator: resize to bigger size than we have. Do nothing";
127 return;
128 }
129
130 size_t bytes_to_delete = cur_size - new_size;
131
132 // Try to delete unused arenas
133 while ((arenas_ != nullptr) && (bytes_to_delete != 0)) {
134 Arena *next = arenas_->GetNextArena();
135 size_t cur_arena_size = arenas_->GetOccupiedSize();
136 if (cur_arena_size < bytes_to_delete) {
137 // We need to free the whole arena
138 PoolManager::FreeArena(arenas_);
139 arenas_ = next;
140 bytes_to_delete -= cur_arena_size;
141 } else {
142 arenas_->Resize(cur_arena_size - bytes_to_delete);
143 bytes_to_delete = 0;
144 }
145 }
146 if ((ON_STACK_ALLOCATION_ENABLED) && (bytes_to_delete > 0)) {
147 size_t stack_size = buff_.GetOccupiedSize();
148 ASSERT(stack_size >= bytes_to_delete);
149 buff_.Resize(stack_size - bytes_to_delete);
150 bytes_to_delete = 0;
151 }
152 ASSERT(bytes_to_delete == 0);
153 }
154
155 template <bool use_oom_handler>
AddArenaFromPool(size_t pool_size)156 bool ArenaAllocatorT<use_oom_handler>::AddArenaFromPool(size_t pool_size)
157 {
158 ASSERT(pool_size != 0);
159 pool_size = AlignUp(pool_size, PANDA_POOL_ALIGNMENT_IN_BYTES);
160 Arena *new_arena = PoolManager::AllocArena(pool_size, space_type_, GetAllocatorType(), this);
161 if (UNLIKELY(new_arena == nullptr)) {
162 return false;
163 }
164 new_arena->LinkTo(arenas_);
165 arenas_ = new_arena;
166 return true;
167 }
168
169 template <bool use_oom_handler>
GetAllocatedSize() const170 size_t ArenaAllocatorT<use_oom_handler>::GetAllocatedSize() const
171 {
172 size_t size = 0;
173 if (ON_STACK_ALLOCATION_ENABLED) {
174 size += buff_.GetOccupiedSize();
175 }
176 for (Arena *cur = arenas_; cur != nullptr; cur = cur->GetNextArena()) {
177 size += cur->GetOccupiedSize();
178 }
179 return size;
180 }
181
182 template class ArenaAllocatorT<true>;
183 template class ArenaAllocatorT<false>;
184
185 } // namespace panda
186