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