1 /*
2 * Copyright (c) 2023 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 "mempool.h"
17 #include <cstdio>
18 #include <cstdlib>
19 #include <cstring>
20 #include <iostream>
21 #include <mutex>
22 #include "thread_env.h"
23 #include "securec.h"
24 #include "mpl_logging.h"
25
26 namespace maple {
27 MemPoolCtrler memPoolCtrler;
28 bool MemPoolCtrler::freeMemInTime = false;
29
FreeMemBlocks(const MemPool & pool,MemBlock * fixedMemHead,MemBlock * bigMemHead)30 void MemPoolCtrler::FreeMemBlocks(const MemPool &pool, MemBlock *fixedMemHead, MemBlock *bigMemHead)
31 {
32 (void)(pool);
33
34 MemBlock *fixedTail = nullptr;
35
36 if (fixedMemHead != nullptr) {
37 fixedTail = fixedMemHead;
38 while (fixedTail->nextMemBlock != nullptr) {
39 fixedTail = fixedTail->nextMemBlock;
40 }
41 }
42
43 while (bigMemHead != nullptr) {
44 auto *cur = bigMemHead;
45 bigMemHead = bigMemHead->nextMemBlock;
46 free(cur->startPtr);
47 delete cur;
48 }
49
50 ParallelGuard guard(ctrlerMutex, HaveRace());
51 if (fixedTail != nullptr) {
52 fixedTail->nextMemBlock = fixedFreeMemBlocks;
53 DEBUG_ASSERT(fixedTail->nextMemBlock != fixedTail, "error");
54 fixedFreeMemBlocks = fixedMemHead;
55 }
56 }
57
58 // Destructor, free all allocated memories
~MemPoolCtrler()59 MemPoolCtrler::~MemPoolCtrler()
60 {
61 FreeMem();
62 }
63
FreeFixedSizeMemBlockMemory()64 void MemPoolCtrler::FreeFixedSizeMemBlockMemory()
65 {
66 FreeMem();
67 sysMemoryMgr->ReleaseMemory();
68 }
69
70 // Allocate a new memory pool and register it in controller
NewMemPool(const std::string & name,bool isLocalPool)71 MemPool *MemPoolCtrler::NewMemPool(const std::string &name, bool isLocalPool)
72 {
73 MemPool *memPool = nullptr;
74
75 if (isLocalPool) {
76 memPool = new ThreadLocalMemPool(*this, name);
77 } else {
78 memPool = new ThreadShareMemPool(*this, name);
79 }
80
81 return memPool;
82 }
83
84 // This function will be removed soon, DO NOT call it, just use delete memPool
DeleteMemPool(MemPool * memPool) const85 void MemPoolCtrler::DeleteMemPool(MemPool *memPool) const
86 {
87 delete memPool;
88 }
89
FreeMem()90 void MemPoolCtrler::FreeMem()
91 {
92 ParallelGuard guard(ctrlerMutex, HaveRace());
93
94 while (fixedFreeMemBlocks != nullptr) {
95 MemBlock *arena = fixedFreeMemBlocks;
96 fixedFreeMemBlocks = fixedFreeMemBlocks->nextMemBlock;
97 delete arena;
98 }
99 }
100
AllocMemBlock(const MemPool & pool,size_t size)101 MemBlock *MemPoolCtrler::AllocMemBlock(const MemPool &pool, size_t size)
102 {
103 if (size <= kMemBlockSizeMin) {
104 return AllocFixMemBlock(pool);
105 } else {
106 return AllocBigMemBlock(pool, size);
107 }
108 }
109
AllocFixMemBlock(const MemPool & pool)110 MemBlock *MemPoolCtrler::AllocFixMemBlock(const MemPool &pool)
111 {
112 (void)(pool);
113 MemBlock *ret = nullptr;
114
115 ParallelGuard guard(ctrlerMutex, HaveRace());
116 if (fixedFreeMemBlocks != nullptr) {
117 ret = fixedFreeMemBlocks;
118 fixedFreeMemBlocks = fixedFreeMemBlocks->nextMemBlock;
119 return ret;
120 }
121
122 uint8_t *ptr = sysMemoryMgr->RealAllocMemory(kMemBlockMalloc);
123 // leave one MemBlock to return
124 for (size_t i = 0; i < kMemBlockMalloc / kMemBlockSizeMin - 1; ++i) {
125 auto *block = new MemBlock(ptr, kMemBlockSizeMin);
126 ptr += kMemBlockSizeMin;
127 block->nextMemBlock = fixedFreeMemBlocks;
128 fixedFreeMemBlocks = block;
129 }
130
131 return new MemBlock(ptr, kMemBlockSizeMin);
132 }
133
AllocBigMemBlock(const MemPool & pool,size_t size) const134 MemBlock *MemPoolCtrler::AllocBigMemBlock(const MemPool &pool, size_t size) const
135 {
136 DEBUG_ASSERT(size > kMemBlockSizeMin, "Big memory block must be bigger than fixed memory block");
137 (void)(pool);
138
139 uint8_t *block = reinterpret_cast<uint8_t *>(malloc(size));
140 CHECK_FATAL(block != nullptr, "malloc failed");
141 return new MemBlock(block, size);
142 }
143
~MemPool()144 MemPool::~MemPool()
145 {
146 ctrler.FreeMemBlocks(*this, fixedMemHead, bigMemHead);
147 }
148
Malloc(size_t size)149 void *MemPool::Malloc(size_t size)
150 {
151 size = BITS_ALIGN(size);
152 DEBUG_ASSERT(endPtr >= curPtr, "endPtr should >= curPtr");
153 if (size > static_cast<size_t>(endPtr - curPtr)) {
154 return AllocNewMemBlock(size);
155 }
156 uint8_t *retPtr = curPtr;
157 curPtr += size;
158 return retPtr;
159 }
160
ReleaseContainingMem()161 void MemPool::ReleaseContainingMem()
162 {
163 ctrler.FreeMemBlocks(*this, fixedMemHead, bigMemHead);
164
165 fixedMemHead = nullptr;
166 bigMemHead = nullptr;
167 endPtr = nullptr;
168 curPtr = nullptr;
169 }
170
171 // Malloc size of memory from memory pool, then set 0
Calloc(size_t size)172 void *MemPool::Calloc(size_t size)
173 {
174 void *p = Malloc(BITS_ALIGN(size));
175 DEBUG_ASSERT(p != nullptr, "ERROR: Calloc error");
176 errno_t eNum = memset_s(p, BITS_ALIGN(size), 0, BITS_ALIGN(size));
177 CHECK_FATAL(eNum == EOK, "memset_s failed");
178 return p;
179 }
180
181 // Realloc new size of memory
Realloc(const void * ptr,size_t oldSize,size_t newSize)182 void *MemPool::Realloc(const void *ptr, size_t oldSize, size_t newSize)
183 {
184 void *result = Malloc(newSize);
185 DEBUG_ASSERT(result != nullptr, "ERROR: Realloc error");
186 size_t copySize = ((newSize > oldSize) ? oldSize : newSize);
187 if (copySize != 0 && ptr != nullptr) {
188 errno_t eNum = memcpy_s(result, copySize, ptr, copySize);
189 CHECK_FATAL(eNum == EOK, "memcpy_s failed");
190 }
191 return result;
192 }
193
AllocNewMemBlock(size_t size)194 uint8_t *MemPool::AllocNewMemBlock(size_t size)
195 {
196 MemBlock **head = nullptr;
197 MemBlock *newMemBlock = ctrler.AllocMemBlock(*this, size);
198 if (newMemBlock->memSize <= kMemBlockSizeMin) {
199 head = &fixedMemHead;
200 } else {
201 head = &bigMemHead;
202 }
203
204 newMemBlock->nextMemBlock = *head;
205 *head = newMemBlock;
206 CHECK_FATAL(newMemBlock->nextMemBlock != newMemBlock, "error");
207
208 curPtr = newMemBlock->startPtr + size;
209 endPtr = newMemBlock->startPtr + newMemBlock->memSize;
210 DEBUG_ASSERT(curPtr <= endPtr, "must be");
211
212 return newMemBlock->startPtr;
213 }
214
Malloc(size_t size)215 void *StackMemPool::Malloc(size_t size)
216 {
217 size = BITS_ALIGN(size);
218 uint8_t **curPtrPtr = nullptr;
219 uint8_t *curEndPtr = nullptr;
220 if (size <= kMemBlockSizeMin) {
221 curPtrPtr = &curPtr;
222 curEndPtr = endPtr;
223 } else {
224 curPtrPtr = &bigCurPtr;
225 curEndPtr = bigEndPtr;
226 }
227 uint8_t *retPtr = *curPtrPtr;
228 DEBUG_ASSERT(curEndPtr >= *curPtrPtr, "endPtr should >= curPtr");
229 if (size > static_cast<size_t>(curEndPtr - *curPtrPtr)) {
230 retPtr = AllocTailMemBlock(size);
231 }
232 *curPtrPtr = retPtr + size;
233 return retPtr;
234 }
235
236 // scoped mem pool don't use big mem block for small size, different with normal mempool
AllocMemBlockBySize(size_t size)237 MemBlock *StackMemPool::AllocMemBlockBySize(size_t size)
238 {
239 if (size <= kMemBlockSizeMin) {
240 return ctrler.AllocFixMemBlock(*this);
241 } else {
242 return ctrler.AllocBigMemBlock(*this, size);
243 }
244 }
245
ResetStackTop(const LocalMapleAllocator * alloc,uint8_t * fixedCurPtrMark,MemBlock * fixedStackTopMark,uint8_t * bigCurPtrMark,MemBlock * bigStackTopMark)246 void StackMemPool::ResetStackTop(const LocalMapleAllocator *alloc, uint8_t *fixedCurPtrMark,
247 MemBlock *fixedStackTopMark, uint8_t *bigCurPtrMark,
248 MemBlock *bigStackTopMark) noexcept
249 {
250 CheckTopAllocator(alloc);
251 PopAllocator();
252
253 if (fixedStackTopMark != nullptr) {
254 fixedMemStackTop = fixedStackTopMark;
255 curPtr = fixedCurPtrMark;
256 endPtr = fixedMemStackTop->EndPtr();
257 } else if (fixedMemHead != nullptr) {
258 fixedMemStackTop = fixedMemHead;
259 curPtr = fixedMemStackTop->startPtr;
260 endPtr = fixedMemStackTop->EndPtr();
261 }
262
263 if (bigStackTopMark != nullptr) {
264 bigMemStackTop = bigStackTopMark;
265 bigCurPtr = bigCurPtrMark;
266 bigEndPtr = bigMemStackTop->EndPtr();
267 } else if (bigMemHead != nullptr) {
268 bigMemStackTop = bigMemHead;
269 bigCurPtr = bigMemStackTop->startPtr;
270 bigEndPtr = bigMemStackTop->EndPtr();
271 }
272 }
273
AllocTailMemBlock(size_t size)274 uint8_t *StackMemPool::AllocTailMemBlock(size_t size)
275 {
276 MemBlock **head = nullptr;
277 MemBlock **stackTop = nullptr;
278 uint8_t **endPtrPtr = nullptr;
279
280 if (size <= kMemBlockSizeMin) {
281 head = &fixedMemHead;
282 stackTop = &fixedMemStackTop;
283 endPtrPtr = &endPtr;
284 } else {
285 head = &bigMemHead;
286 stackTop = &bigMemStackTop;
287 endPtrPtr = &bigEndPtr;
288 }
289
290 if (*stackTop == nullptr) {
291 MemBlock *newMemBlock = AllocMemBlockBySize(size);
292 *stackTop = newMemBlock;
293 *head = newMemBlock;
294 (*stackTop)->nextMemBlock = nullptr;
295 } else {
296 if ((*stackTop)->nextMemBlock != nullptr && (*stackTop)->nextMemBlock->memSize >= size) {
297 *stackTop = (*stackTop)->nextMemBlock;
298 } else {
299 MemBlock *newMemBlock = AllocMemBlockBySize(size);
300 auto *tmp = (*stackTop)->nextMemBlock;
301 (*stackTop)->nextMemBlock = newMemBlock;
302 *stackTop = newMemBlock;
303 newMemBlock->nextMemBlock = tmp;
304 }
305 }
306 *endPtrPtr = (*stackTop)->EndPtr();
307 return (*stackTop)->startPtr;
308 }
309 } // namespace maple
310