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
30namespace maple {
31#define BITS_ALIGN(size) (((size) + 7) & (0xFFFFFFF8))
32
33constexpr size_t kMemBlockSizeMin = 2 * 1024;
34constexpr size_t kMemBlockMalloc = 1024 * 1024;
35static_assert((kMemBlockMalloc > kMemBlockSizeMin) && ((kMemBlockMalloc % kMemBlockSizeMin) == 0), "mempool error");
36
37struct MemBlock {
38    MemBlock(uint8_t *startPtr, size_t size) : startPtr(startPtr), memSize(size) {}
39    ~MemBlock() = default;
40
41    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
52class MemPool;
53class StackMemPool;
54class MemPoolCtrler;
55extern MemPoolCtrler memPoolCtrler;
56
57// memory backend
58class SysMemoryManager {
59public:
60    virtual ~SysMemoryManager() = default;
61    virtual uint8_t *RealAllocMemory(size_t size) = 0;
62    virtual void ReleaseMemory() = 0;
63};
64
65class MallocSysMemoryManager : public SysMemoryManager {
66public:
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
96class MemPoolCtrler {
97    friend MemPool;
98
99public:
100    static bool freeMemInTime;
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
113private:
114    struct MemBlockCmp {
115        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
130class MemPoolStat {
131public:
132    ~MemPoolStat() = default;
133
134protected:
135    void SetName(const std::string &name)
136    {
137        this->name = name;
138    }
139    void SetName(const char *name)
140    {
141        this->name = name;
142    }
143    std::string name;
144};
145#else
146class MemPoolStat {
147public:
148    virtual ~MemPoolStat() = default;
149
150protected:
151    void SetName(const std::string & /* name */) const {}
152    void SetName(const char /* name */) const {}
153};
154#endif
155
156// memory front end
157class MemPool : private MemPoolStat {
158    friend MemPoolCtrler;
159
160public:
161    MemPool(MemPoolCtrler &ctl, const std::string &name) : ctrler(ctl)
162    {
163        SetName(name);
164    }
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
177    MemPoolCtrler &GetCtrler()
178    {
179        return ctrler;
180    }
181
182    const MemPoolCtrler &GetCtrler() const
183    {
184        return ctrler;
185    }
186
187    template <class 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>
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>
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
214protected:
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
224using ThreadLocalMemPool = MemPool;
225
226class ThreadShareMemPool : public MemPool {
227public:
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
240class LocalMapleAllocator;
241#ifdef MP_DEBUG
242class StackMemPoolDebug {
243protected:
244    void PushAllocator(const LocalMapleAllocator *alloc)
245    {
246        allocators.push(alloc);
247    }
248    void CheckTopAllocator(const LocalMapleAllocator *alloc) const
249    {
250        CHECK_FATAL(alloc == allocators.top(), "only top allocator allowed");
251    }
252    void PopAllocator()
253    {
254        allocators.pop();
255    }
256    std::stack<const LocalMapleAllocator *> allocators;
257};
258#else
259class StackMemPoolDebug {
260protected:
261    void PushAllocator(const LocalMapleAllocator * /* alloc */) const {}
262    void PopAllocator() const {}
263    void CheckTopAllocator(const LocalMapleAllocator * /* alloc */) const {}
264};
265#endif
266
267class StackMemPool : public MemPool, private StackMemPoolDebug {
268public:
269    using MemPool::MemPool;
270    friend LocalMapleAllocator;
271
272private:
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