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