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 CHECK_NOT_NULL(arenas_);
32 AllocArenaMemStats(DEFAULT_ARENA_SIZE);
33 }
34 }
35
36 template <bool use_oom_handler>
ArenaAllocatorT(OOMHandler oom_handler,SpaceType space_type,BaseMemStats * mem_stats,bool limit_alloc_size_by_pool)37 ArenaAllocatorT<use_oom_handler>::ArenaAllocatorT(OOMHandler oom_handler, SpaceType space_type, BaseMemStats *mem_stats,
38 bool limit_alloc_size_by_pool)
39 : memStats_(mem_stats),
40 space_type_(space_type),
41 oom_handler_(oom_handler),
42 limit_alloc_size_by_pool_(limit_alloc_size_by_pool)
43 {
44 ASSERT(use_oom_handler);
45 if (!ON_STACK_ALLOCATION_ENABLED) {
46 arenas_ = PoolManager::AllocArena(DEFAULT_ARENA_SIZE, space_type_, AllocatorType::ARENA_ALLOCATOR, this);
47 CHECK_NOT_NULL(arenas_);
48 AllocArenaMemStats(DEFAULT_ARENA_SIZE);
49 }
50 }
51
52 template <bool use_oom_handler>
~ArenaAllocatorT()53 ArenaAllocatorT<use_oom_handler>::~ArenaAllocatorT()
54 {
55 Arena *cur = arenas_;
56 while (cur != nullptr) {
57 Arena *tmp;
58 tmp = cur->GetNextArena();
59 PoolManager::FreeArena(cur);
60 cur = tmp;
61 }
62 }
63
64 template <bool use_oom_handler>
AllocateAndAddNewPool(size_t size,Alignment alignment)65 inline void *ArenaAllocatorT<use_oom_handler>::AllocateAndAddNewPool(size_t size, Alignment alignment)
66 {
67 void *mem = arenas_->Alloc(size, alignment);
68 if (mem == nullptr) {
69 bool add_new_pool = false;
70 if (limit_alloc_size_by_pool_) {
71 add_new_pool = AddArenaFromPool(std::max(AlignUp(size, alignment) + sizeof(Arena), DEFAULT_ARENA_SIZE));
72 } else {
73 add_new_pool = AddArenaFromPool(DEFAULT_ARENA_SIZE);
74 }
75 if (UNLIKELY(!add_new_pool)) {
76 LOG(DEBUG, ALLOC) << "Can not add new pool for " << SpaceTypeToString(space_type_);
77 return nullptr;
78 }
79 mem = arenas_->Alloc(size, alignment);
80 ASSERT(!limit_alloc_size_by_pool_ || mem != nullptr);
81 }
82 return mem;
83 }
84
85 template <bool use_oom_handler>
Alloc(size_t size,Alignment align)86 void *ArenaAllocatorT<use_oom_handler>::Alloc(size_t size, Alignment align)
87 {
88 trace::ScopedTrace scoped_trace("ArenaAllocator allocate");
89 LOG(DEBUG, ALLOC) << "ArenaAllocator: try to alloc " << size << " with align " << align;
90 void *ret = nullptr;
91 if (ON_STACK_ALLOCATION_ENABLED && UNLIKELY(!arenas_)) {
92 LOG(DEBUG, ALLOC) << "\tTry to allocate from stack";
93 ret = buff_.Alloc(size, align);
94 LOG_IF(ret, DEBUG, ALLOC) << "\tallocate from stack buffer";
95 if (ret == nullptr) {
96 ret = AllocateAndAddNewPool(size, align);
97 }
98 } else {
99 ret = AllocateAndAddNewPool(size, align);
100 }
101 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
102 if constexpr (use_oom_handler) {
103 if (ret == nullptr) {
104 oom_handler_();
105 }
106 }
107 LOG(DEBUG, ALLOC) << "ArenaAllocator: allocated " << size << " bytes aligned by " << align;
108 AllocArenaMemStats(size);
109 return ret;
110 }
111
112 template <bool use_oom_handler>
Resize(size_t new_size)113 void ArenaAllocatorT<use_oom_handler>::Resize(size_t new_size)
114 {
115 LOG(DEBUG, ALLOC) << "ArenaAllocator: resize to new size " << new_size;
116 // TODO(aemelenko): we have O(2n) here in the worst case
117 size_t cur_size = GetAllocatedSize();
118 if (cur_size <= new_size) {
119 LOG_IF(cur_size < new_size, FATAL, ALLOC) << "ArenaAllocator: resize to bigger size than we have. Do nothing";
120 return;
121 }
122
123 size_t bytes_to_delete = cur_size - new_size;
124
125 // Try to delete unused arenas
126 while ((arenas_ != nullptr) && (bytes_to_delete != 0)) {
127 Arena *next = arenas_->GetNextArena();
128 size_t cur_arena_size = arenas_->GetOccupiedSize();
129 if (cur_arena_size < bytes_to_delete) {
130 // We need to free the whole arena
131 PoolManager::FreeArena(arenas_);
132 arenas_ = next;
133 bytes_to_delete -= cur_arena_size;
134 } else {
135 arenas_->Resize(cur_arena_size - bytes_to_delete);
136 bytes_to_delete = 0;
137 }
138 }
139 if ((ON_STACK_ALLOCATION_ENABLED) && (bytes_to_delete > 0)) {
140 size_t stack_size = buff_.GetOccupiedSize();
141 ASSERT(stack_size >= bytes_to_delete);
142 buff_.Resize(stack_size - bytes_to_delete);
143 bytes_to_delete = 0;
144 }
145 ASSERT(bytes_to_delete == 0);
146 }
147
148 template <bool use_oom_handler>
AddArenaFromPool(size_t pool_size)149 bool ArenaAllocatorT<use_oom_handler>::AddArenaFromPool(size_t pool_size)
150 {
151 ASSERT(pool_size != 0);
152 pool_size = AlignUp(pool_size, PANDA_POOL_ALIGNMENT_IN_BYTES);
153 Arena *new_arena = PoolManager::AllocArena(pool_size, space_type_, GetAllocatorType(), this);
154 if (UNLIKELY(new_arena == nullptr)) {
155 return false;
156 }
157 new_arena->LinkTo(arenas_);
158 arenas_ = new_arena;
159 return true;
160 }
161
162 template <bool use_oom_handler>
GetAllocatedSize() const163 size_t ArenaAllocatorT<use_oom_handler>::GetAllocatedSize() const
164 {
165 size_t size = 0;
166 if (ON_STACK_ALLOCATION_ENABLED) {
167 size += buff_.GetOccupiedSize();
168 }
169 for (Arena *cur = arenas_; cur != nullptr; cur = cur->GetNextArena()) {
170 size += cur->GetOccupiedSize();
171 }
172 return size;
173 }
174
175 template class ArenaAllocatorT<true>;
176 template class ArenaAllocatorT<false>;
177
178 } // namespace panda
179