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 #ifndef MEMPOOL_INCLUDE_MEMPOOL_H
17 #define MEMPOOL_INCLUDE_MEMPOOL_H
18 #include <list>
19 #include <set>
20 #include <forward_list>
21 #include <unordered_set>
22 #include <stack>
23 #include <map>
24 #include <string>
25 #include <mutex>
26 #include <memory>
27 #include "mir_config.h"
28 #include "mpl_logging.h"
29 
30 namespace maple {
31 #define BITS_ALIGN(size) (((size) + 7) & (0xFFFFFFF8))
32 
33 constexpr size_t kMemBlockSizeMin = 2 * 1024;
34 constexpr size_t kMemBlockMalloc = 1024 * 1024;
35 static_assert((kMemBlockMalloc > kMemBlockSizeMin) && ((kMemBlockMalloc % kMemBlockSizeMin) == 0), "mempool error");
36 
37 struct MemBlock {
MemBlockmaple::MemBlock38     MemBlock(uint8_t *startPtr, size_t size) : startPtr(startPtr), memSize(size) {}
39     ~MemBlock() = default;
40 
EndPtrmaple::MemBlock41     uint8_t *EndPtr() const
42     {
43         return startPtr + memSize;
44     }
45 
46     uint8_t *startPtr = nullptr;
47     size_t memSize = 0;
48     MemBlock *nextMemBlock = nullptr;
49 };
50 
51 // Class declaration
52 class MemPool;
53 class StackMemPool;
54 class MemPoolCtrler;
55 extern MemPoolCtrler memPoolCtrler;
56 
57 // memory backend
58 class SysMemoryManager {
59 public:
60     virtual ~SysMemoryManager() = default;
61     virtual uint8_t *RealAllocMemory(size_t size) = 0;
62     virtual void ReleaseMemory() = 0;
63 };
64 
65 class MallocSysMemoryManager : public SysMemoryManager {
66 public:
67     uint8_t *RealAllocMemory(size_t size) override
68     {
69         if (size == 0) {
70             return nullptr;
71         }
72         void *block = malloc(size);
73         CHECK_FATAL(block != nullptr, "malloc failed");
74 
75         mallocMemories.push_front(block);
76         return reinterpret_cast<uint8_t *>(block);
77     }
78     ~MallocSysMemoryManager() override
79     {
80         for (void *ptr : mallocMemories) {
81             free(ptr);
82         }
83         mallocMemories.clear();
84     }
85     void ReleaseMemory() override
86     {
87         for (void *ptr : mallocMemories) {
88             free(ptr);
89         }
90         mallocMemories.clear();
91     }
92     std::forward_list<void *> mallocMemories;
93 };
94 
95 // memory middle end
96 class MemPoolCtrler {
97     friend MemPool;
98 
99 public:
100     static bool freeMemInTime;
MemPoolCtrler()101     MemPoolCtrler() : sysMemoryMgr(new MallocSysMemoryManager()) {}
102 
103     ~MemPoolCtrler();
104 
105     MemPool *NewMemPool(const std::string &, bool isLocalPool);
106     void DeleteMemPool(MemPool *memPool) const;
107 
108     MemBlock *AllocMemBlock(const MemPool &pool, size_t size);
109     MemBlock *AllocFixMemBlock(const MemPool &pool);
110     MemBlock *AllocBigMemBlock(const MemPool &pool, size_t size) const;
111     void FreeFixedSizeMemBlockMemory();
112 
113 private:
114     struct MemBlockCmp {
operator ()maple::MemPoolCtrler::MemBlockCmp115         bool operator()(const MemBlock *l, const MemBlock *r) const
116         {
117             return l->memSize > r->memSize;
118         }
119     };
120 
121     void FreeMem();
122     void FreeMemBlocks(const MemPool &pool, MemBlock *fixedMemHead, MemBlock *bigMemHead);
123 
124     std::mutex ctrlerMutex;  // this mutex is used to protect memPools
125     MemBlock *fixedFreeMemBlocks = nullptr;
126     std::unique_ptr<SysMemoryManager> sysMemoryMgr;
127 };
128 
129 #ifdef MP_DEUG
130 class MemPoolStat {
131 public:
132     ~MemPoolStat() = default;
133 
134 protected:
SetName(const std::string &name)135     void SetName(const std::string &name)
136     {
137         this->name = name;
138     }
SetName(const char *name)139     void SetName(const char *name)
140     {
141         this->name = name;
142     }
143     std::string name;
144 };
145 #else
146 class MemPoolStat {
147 public:
148     virtual ~MemPoolStat() = default;
149 
150 protected:
SetName(const std::string & ) const151     void SetName(const std::string & /* name */) const {}
SetName(const char ) const152     void SetName(const char /* name */) const {}
153 };
154 #endif
155 
156 // memory front end
157 class MemPool : private MemPoolStat {
158     friend MemPoolCtrler;
159 
160 public:
MemPool(MemPoolCtrler &ctl, const std::string &name)161     MemPool(MemPoolCtrler &ctl, const std::string &name) : ctrler(ctl)
162     {
163         SetName(name);
164     }
MemPool(MemPoolCtrler &ctl, const char *name)165     MemPool(MemPoolCtrler &ctl, const char *name) : ctrler(ctl)
166     {
167         SetName(name);
168     }
169 
170     ~MemPool();
171 
172     virtual void *Malloc(size_t size);
173     void *Calloc(size_t size);
174     void *Realloc(const void *ptr, size_t oldSize, size_t newSize);
175     virtual void ReleaseContainingMem();
176 
GetCtrler()177     MemPoolCtrler &GetCtrler()
178     {
179         return ctrler;
180     }
181 
GetCtrler() const182     const MemPoolCtrler &GetCtrler() const
183     {
184         return ctrler;
185     }
186 
187     template <class T>
Clone(const T &t)188     T *Clone(const T &t)
189     {
190         void *p = Malloc(sizeof(T));
191         DEBUG_ASSERT(p != nullptr, "ERROR: New error");
192         p = new (p) T(t);
193         return static_cast<T *>(p);
194     }
195 
196     template <class T, typename... Arguments>
New(Arguments &&.... args)197     T *New(Arguments &&... args)
198     {
199         void *p = Malloc(sizeof(T));
200         DEBUG_ASSERT(p != nullptr, "ERROR: New error");
201         p = new (p) T(std::forward<Arguments>(args)...);
202         return static_cast<T *>(p);
203     }
204 
205     template <class T>
NewArray(size_t num)206     T *NewArray(size_t num)
207     {
208         void *p = Malloc(sizeof(T) * num);
209         DEBUG_ASSERT(p != nullptr, "ERROR: NewArray error");
210         p = new (p) T[num];
211         return static_cast<T *>(p);
212     }
213 
214 protected:
215     MemPoolCtrler &ctrler;  // Hookup controller object
216     uint8_t *endPtr = nullptr;
217     uint8_t *curPtr = nullptr;
218     MemBlock *fixedMemHead = nullptr;
219     MemBlock *bigMemHead = nullptr;
220 
221     uint8_t *AllocNewMemBlock(size_t bytes);
222 };
223 
224 using ThreadLocalMemPool = MemPool;
225 
226 class ThreadShareMemPool : public MemPool {
227 public:
228     using MemPool::MemPool;
229     virtual ~ThreadShareMemPool() = default;
230     void *Malloc(size_t size) override
231     {
232         return MemPool::Malloc(size);
233     }
234     void ReleaseContainingMem() override
235     {
236         MemPool::ReleaseContainingMem();
237     }
238 };
239 
240 class LocalMapleAllocator;
241 #ifdef MP_DEBUG
242 class StackMemPoolDebug {
243 protected:
PushAllocator(const LocalMapleAllocator *alloc)244     void PushAllocator(const LocalMapleAllocator *alloc)
245     {
246         allocators.push(alloc);
247     }
CheckTopAllocator(const LocalMapleAllocator *alloc) const248     void CheckTopAllocator(const LocalMapleAllocator *alloc) const
249     {
250         CHECK_FATAL(alloc == allocators.top(), "only top allocator allowed");
251     }
PopAllocator()252     void PopAllocator()
253     {
254         allocators.pop();
255     }
256     std::stack<const LocalMapleAllocator *> allocators;
257 };
258 #else
259 class StackMemPoolDebug {
260 protected:
PushAllocator(const LocalMapleAllocator * ) const261     void PushAllocator(const LocalMapleAllocator * /* alloc */) const {}
PopAllocator() const262     void PopAllocator() const {}
CheckTopAllocator(const LocalMapleAllocator * ) const263     void CheckTopAllocator(const LocalMapleAllocator * /* alloc */) const {}
264 };
265 #endif
266 
267 class StackMemPool : public MemPool, private StackMemPoolDebug {
268 public:
269     using MemPool::MemPool;
270     friend LocalMapleAllocator;
271 
272 private:
273     // all malloc requested from LocalMapleAllocator
274     void *Malloc(size_t size) override;
275     uint8_t *AllocTailMemBlock(size_t size);
276 
277     // these methods should be called from LocalMapleAllocator
278     template <class T>
279     T *Clone(const T &t) = delete;
280 
281     template <class T, typename... Arguments>
282     T *New(Arguments &&... args) = delete;
283 
284     template <class T>
285     T *NewArray(size_t num) = delete;
286 
287     // reuse mempool fixedMemHead, bigMemHead, (curPtr, endPtr for fixed memory)
288     MemBlock *fixedMemStackTop = nullptr;
289     MemBlock *bigMemStackTop = nullptr;
290     uint8_t *bigCurPtr = nullptr;
291     uint8_t *bigEndPtr = nullptr;
292     MemBlock *AllocMemBlockBySize(size_t size);
293     void ResetStackTop(const LocalMapleAllocator *alloc, uint8_t *fixedCurPtrMark, MemBlock *fixedStackTopMark,
294                        uint8_t *bigCurPtrMark, MemBlock *bigStackTopMark) noexcept;
295 };
296 }  // namespace maple
297 #endif  // MEMPOOL_INCLUDE_MEMPOOL_H
298