• 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 <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