• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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