1484543d1Sopenharmony_ci/*
2484543d1Sopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd.
3484543d1Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4484543d1Sopenharmony_ci * you may not use this file except in compliance with the License.
5484543d1Sopenharmony_ci * You may obtain a copy of the License at
6484543d1Sopenharmony_ci *
7484543d1Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8484543d1Sopenharmony_ci *
9484543d1Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10484543d1Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11484543d1Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12484543d1Sopenharmony_ci * See the License for the specific language governing permissions and
13484543d1Sopenharmony_ci * limitations under the License.
14484543d1Sopenharmony_ci */
15484543d1Sopenharmony_ci
16484543d1Sopenharmony_ci#ifndef UTIL_SLAB_HPP
17484543d1Sopenharmony_ci#define UTIL_SLAB_HPP
18484543d1Sopenharmony_ci
19484543d1Sopenharmony_ci#include <new>
20484543d1Sopenharmony_ci#include <vector>
21484543d1Sopenharmony_ci#include <mutex>
22484543d1Sopenharmony_ci#ifdef FFRT_BBOX_ENABLE
23484543d1Sopenharmony_ci#include <unordered_set>
24484543d1Sopenharmony_ci#endif
25484543d1Sopenharmony_ci#include <sys/mman.h>
26484543d1Sopenharmony_ci#include "dfx/log/ffrt_log_api.h"
27484543d1Sopenharmony_ci#include "spmc_queue.h"
28484543d1Sopenharmony_ci#include "sync/sync.h"
29484543d1Sopenharmony_ci
30484543d1Sopenharmony_cinamespace ffrt {
31484543d1Sopenharmony_ciconst std::size_t BatchAllocSize = 32 * 1024;
32484543d1Sopenharmony_ci#ifdef FFRT_BBOX_ENABLE
33484543d1Sopenharmony_ciconstexpr uint32_t ALLOCATOR_DESTRUCT_TIMESOUT = 1000;
34484543d1Sopenharmony_ci#endif
35484543d1Sopenharmony_ci
36484543d1Sopenharmony_ci#ifndef FFRT_ALLOCATOR_MMAP_SIZE
37484543d1Sopenharmony_ci#define FFRT_ALLOCATOR_MMAP_SIZE (8 * 1024 * 1024)
38484543d1Sopenharmony_ci#endif
39484543d1Sopenharmony_ci
40484543d1Sopenharmony_citemplate <typename T, size_t MmapSz = BatchAllocSize>
41484543d1Sopenharmony_ciclass SimpleAllocator {
42484543d1Sopenharmony_cipublic:
43484543d1Sopenharmony_ci    SimpleAllocator(const SimpleAllocator&) = delete;
44484543d1Sopenharmony_ci    SimpleAllocator(SimpleAllocator&&) = delete;
45484543d1Sopenharmony_ci    SimpleAllocator& operator=(const SimpleAllocator&) = delete;
46484543d1Sopenharmony_ci    SimpleAllocator& operator=(SimpleAllocator&&) = delete;
47484543d1Sopenharmony_ci    fast_mutex lock;
48484543d1Sopenharmony_ci
49484543d1Sopenharmony_ci    static SimpleAllocator<T>* Instance(std::size_t size = sizeof(T))
50484543d1Sopenharmony_ci    {
51484543d1Sopenharmony_ci        static SimpleAllocator<T> ins(size);
52484543d1Sopenharmony_ci        return &ins;
53484543d1Sopenharmony_ci    }
54484543d1Sopenharmony_ci
55484543d1Sopenharmony_ci    // NOTE: call constructor after AllocMem
56484543d1Sopenharmony_ci    static T* AllocMem()
57484543d1Sopenharmony_ci    {
58484543d1Sopenharmony_ci        return Instance()->Alloc();
59484543d1Sopenharmony_ci    }
60484543d1Sopenharmony_ci
61484543d1Sopenharmony_ci    // NOTE: call destructor before FreeMem
62484543d1Sopenharmony_ci    static void FreeMem(T* t)
63484543d1Sopenharmony_ci    {
64484543d1Sopenharmony_ci        t->~T();
65484543d1Sopenharmony_ci        // unlock()内部lck记录锁的状态为非持有状态,析构时访问状态变量为非持有状态,则不访问实际持有的mutex
66484543d1Sopenharmony_ci        // return之前的lck析构不产生UAF问题,因为return之前随着root析构,锁的内存被释放
67484543d1Sopenharmony_ci        Instance()->free(t);
68484543d1Sopenharmony_ci    }
69484543d1Sopenharmony_ci
70484543d1Sopenharmony_ci    // only used for BBOX
71484543d1Sopenharmony_ci    static std::vector<void *> getUnfreedMem()
72484543d1Sopenharmony_ci    {
73484543d1Sopenharmony_ci        return Instance()->getUnfreed();
74484543d1Sopenharmony_ci    }
75484543d1Sopenharmony_ci
76484543d1Sopenharmony_ci    static void LockMem()
77484543d1Sopenharmony_ci    {
78484543d1Sopenharmony_ci        return Instance()->SimpleAllocatorLock();
79484543d1Sopenharmony_ci    }
80484543d1Sopenharmony_ci
81484543d1Sopenharmony_ci    static void UnlockMem()
82484543d1Sopenharmony_ci    {
83484543d1Sopenharmony_ci        return Instance()->SimpleAllocatorUnLock();
84484543d1Sopenharmony_ci    }
85484543d1Sopenharmony_ciprivate:
86484543d1Sopenharmony_ci    SpmcQueue primaryCache;
87484543d1Sopenharmony_ci#ifdef FFRT_BBOX_ENABLE
88484543d1Sopenharmony_ci    std::unordered_set<T*> secondaryCache;
89484543d1Sopenharmony_ci#endif
90484543d1Sopenharmony_ci    std::size_t TSize;
91484543d1Sopenharmony_ci    T* basePtr = nullptr;
92484543d1Sopenharmony_ci    std::size_t count = 0;
93484543d1Sopenharmony_ci
94484543d1Sopenharmony_ci    std::vector<void *> getUnfreed()
95484543d1Sopenharmony_ci    {
96484543d1Sopenharmony_ci        std::vector<void *> ret;
97484543d1Sopenharmony_ci#ifdef FFRT_BBOX_ENABLE
98484543d1Sopenharmony_ci        ret.reserve(MmapSz / TSize + secondaryCache.size());
99484543d1Sopenharmony_ci        char* p = reinterpret_cast<char*>(basePtr);
100484543d1Sopenharmony_ci        for (std::size_t i = 0; i + TSize <= MmapSz; i += TSize) {
101484543d1Sopenharmony_ci            if (basePtr != nullptr &&
102484543d1Sopenharmony_ci                primaryCache.FindElement(reinterpret_cast<void *>(p + i)) == false) {
103484543d1Sopenharmony_ci                ret.push_back(reinterpret_cast<void *>(p + i));
104484543d1Sopenharmony_ci            }
105484543d1Sopenharmony_ci        }
106484543d1Sopenharmony_ci        for (auto ite = secondaryCache.cbegin(); ite != secondaryCache.cend(); ite++) {
107484543d1Sopenharmony_ci            ret.push_back(reinterpret_cast<void *>(*ite));
108484543d1Sopenharmony_ci        }
109484543d1Sopenharmony_ci#endif
110484543d1Sopenharmony_ci        return ret;
111484543d1Sopenharmony_ci    }
112484543d1Sopenharmony_ci
113484543d1Sopenharmony_ci    void SimpleAllocatorLock()
114484543d1Sopenharmony_ci    {
115484543d1Sopenharmony_ci        lock.lock();
116484543d1Sopenharmony_ci    }
117484543d1Sopenharmony_ci
118484543d1Sopenharmony_ci    void SimpleAllocatorUnLock()
119484543d1Sopenharmony_ci    {
120484543d1Sopenharmony_ci        lock.unlock();
121484543d1Sopenharmony_ci    }
122484543d1Sopenharmony_ci
123484543d1Sopenharmony_ci    void init()
124484543d1Sopenharmony_ci    {
125484543d1Sopenharmony_ci        lock.lock();
126484543d1Sopenharmony_ci        if (basePtr) {
127484543d1Sopenharmony_ci            lock.unlock();
128484543d1Sopenharmony_ci            return;
129484543d1Sopenharmony_ci        }
130484543d1Sopenharmony_ci
131484543d1Sopenharmony_ci        char* p = reinterpret_cast<char*>(operator new(MmapSz));
132484543d1Sopenharmony_ci        count = MmapSz / TSize;
133484543d1Sopenharmony_ci        primaryCache.Init(count);
134484543d1Sopenharmony_ci        for (std::size_t i = 0; i + TSize <= MmapSz; i += TSize) {
135484543d1Sopenharmony_ci            primaryCache.PushTail(p + i);
136484543d1Sopenharmony_ci        }
137484543d1Sopenharmony_ci        basePtr = reinterpret_cast<T*>(p);
138484543d1Sopenharmony_ci        lock.unlock();
139484543d1Sopenharmony_ci    }
140484543d1Sopenharmony_ci
141484543d1Sopenharmony_ci    T* Alloc()
142484543d1Sopenharmony_ci    {
143484543d1Sopenharmony_ci        // 未初始化
144484543d1Sopenharmony_ci        if (primaryCache.GetCapacity() == 0) {
145484543d1Sopenharmony_ci            init();
146484543d1Sopenharmony_ci        }
147484543d1Sopenharmony_ci        // 一级cache已耗尽
148484543d1Sopenharmony_ci        if (primaryCache.GetLength()) {
149484543d1Sopenharmony_ci            T* t = reinterpret_cast<T*>(::operator new(TSize));
150484543d1Sopenharmony_ci#ifdef FFRT_BBOX_ENABLE
151484543d1Sopenharmony_ci            lock.lock();
152484543d1Sopenharmony_ci            secondaryCache.insert(t);
153484543d1Sopenharmony_ci            lock.unlock();
154484543d1Sopenharmony_ci#endif
155484543d1Sopenharmony_ci            return t;
156484543d1Sopenharmony_ci        }
157484543d1Sopenharmony_ci        return static_cast<T*>(primaryCache.PopHead());
158484543d1Sopenharmony_ci    }
159484543d1Sopenharmony_ci
160484543d1Sopenharmony_ci    void free(T* t)
161484543d1Sopenharmony_ci    {
162484543d1Sopenharmony_ci        if (basePtr && basePtr <= t &&
163484543d1Sopenharmony_ci            static_cast<size_t>(reinterpret_cast<uintptr_t>(t)) <
164484543d1Sopenharmony_ci            static_cast<size_t>(reinterpret_cast<uintptr_t>(basePtr)) + MmapSz) {
165484543d1Sopenharmony_ci            primaryCache.PushTail(t);
166484543d1Sopenharmony_ci            return;
167484543d1Sopenharmony_ci        }
168484543d1Sopenharmony_ci
169484543d1Sopenharmony_ci#ifdef FFRT_BBOX_ENABLE
170484543d1Sopenharmony_ci        lock.lock();
171484543d1Sopenharmony_ci        secondaryCache.erase(t);
172484543d1Sopenharmony_ci        lock.unlock();
173484543d1Sopenharmony_ci#endif
174484543d1Sopenharmony_ci        ::operator delete(t);
175484543d1Sopenharmony_ci    }
176484543d1Sopenharmony_ci
177484543d1Sopenharmony_ci    SimpleAllocator(std::size_t size = sizeof(T)) : TSize(size)
178484543d1Sopenharmony_ci    {
179484543d1Sopenharmony_ci    }
180484543d1Sopenharmony_ci    ~SimpleAllocator()
181484543d1Sopenharmony_ci    {
182484543d1Sopenharmony_ci        std::unique_lock<decltype(lock)> lck(lock);
183484543d1Sopenharmony_ci        if (basePtr == nullptr) {
184484543d1Sopenharmony_ci            return;
185484543d1Sopenharmony_ci        }
186484543d1Sopenharmony_ci#ifdef FFRT_BBOX_ENABLE
187484543d1Sopenharmony_ci        uint32_t try_cnt = ALLOCATOR_DESTRUCT_TIMESOUT;
188484543d1Sopenharmony_ci        std::size_t reserved = MmapSz / TSize;
189484543d1Sopenharmony_ci        while (try_cnt > 0) {
190484543d1Sopenharmony_ci            if (primaryCache.GetLength() == reserved && secondaryCache.size() == 0) {
191484543d1Sopenharmony_ci                break;
192484543d1Sopenharmony_ci            }
193484543d1Sopenharmony_ci            lck.unlock();
194484543d1Sopenharmony_ci            usleep(1000);
195484543d1Sopenharmony_ci            try_cnt--;
196484543d1Sopenharmony_ci            lck.lock();
197484543d1Sopenharmony_ci        }
198484543d1Sopenharmony_ci        if (try_cnt == 0) {
199484543d1Sopenharmony_ci            FFRT_LOGE("clear allocator failed");
200484543d1Sopenharmony_ci        }
201484543d1Sopenharmony_ci        for (auto ite = secondaryCache.cbegin(); ite != secondaryCache.cend(); ite++) {
202484543d1Sopenharmony_ci            ::operator delete(*ite);
203484543d1Sopenharmony_ci        }
204484543d1Sopenharmony_ci#endif
205484543d1Sopenharmony_ci        ::operator delete(basePtr);
206484543d1Sopenharmony_ci        FFRT_LOGI("destruct SimpleAllocator");
207484543d1Sopenharmony_ci    }
208484543d1Sopenharmony_ci};
209484543d1Sopenharmony_ci
210484543d1Sopenharmony_citemplate <typename T, std::size_t MmapSz = FFRT_ALLOCATOR_MMAP_SIZE>
211484543d1Sopenharmony_ciclass QSimpleAllocator {
212484543d1Sopenharmony_ci    std::size_t TSize;
213484543d1Sopenharmony_ci    std::size_t curAllocated;
214484543d1Sopenharmony_ci    std::size_t maxAllocated;
215484543d1Sopenharmony_ci    std::mutex lock;
216484543d1Sopenharmony_ci    std::vector<T*> cache;
217484543d1Sopenharmony_ci    uint32_t flags = MAP_ANONYMOUS | MAP_PRIVATE;
218484543d1Sopenharmony_ci
219484543d1Sopenharmony_ci    bool expand()
220484543d1Sopenharmony_ci    {
221484543d1Sopenharmony_ci        const int prot = PROT_READ | PROT_WRITE;
222484543d1Sopenharmony_ci        char* p = reinterpret_cast<char*>(mmap(nullptr, MmapSz, prot, flags, -1, 0));
223484543d1Sopenharmony_ci        if (p == (char*)MAP_FAILED) {
224484543d1Sopenharmony_ci            if ((flags & MAP_HUGETLB) != 0) {
225484543d1Sopenharmony_ci                flags = MAP_ANONYMOUS | MAP_PRIVATE;
226484543d1Sopenharmony_ci                p = reinterpret_cast<char*>(mmap(nullptr, MmapSz, prot, flags, -1, 0));
227484543d1Sopenharmony_ci            }
228484543d1Sopenharmony_ci            if (p == (char*)MAP_FAILED) {
229484543d1Sopenharmony_ci                perror("mmap");
230484543d1Sopenharmony_ci                return false;
231484543d1Sopenharmony_ci            }
232484543d1Sopenharmony_ci        }
233484543d1Sopenharmony_ci        for (std::size_t i = 0; i + TSize <= MmapSz; i += TSize) {
234484543d1Sopenharmony_ci            cache.push_back(reinterpret_cast<T*>(p + i));
235484543d1Sopenharmony_ci        }
236484543d1Sopenharmony_ci        return true;
237484543d1Sopenharmony_ci    }
238484543d1Sopenharmony_ci
239484543d1Sopenharmony_ci    T* Alloc()
240484543d1Sopenharmony_ci    {
241484543d1Sopenharmony_ci        T* p = nullptr;
242484543d1Sopenharmony_ci        lock.lock();
243484543d1Sopenharmony_ci        if (cache.empty()) {
244484543d1Sopenharmony_ci            if (!expand()) {
245484543d1Sopenharmony_ci                lock.unlock();
246484543d1Sopenharmony_ci                return nullptr;
247484543d1Sopenharmony_ci            }
248484543d1Sopenharmony_ci        }
249484543d1Sopenharmony_ci        p = cache.back();
250484543d1Sopenharmony_ci        ++curAllocated;
251484543d1Sopenharmony_ci        maxAllocated = std::max(curAllocated, maxAllocated);
252484543d1Sopenharmony_ci        cache.pop_back();
253484543d1Sopenharmony_ci        lock.unlock();
254484543d1Sopenharmony_ci        return p;
255484543d1Sopenharmony_ci    }
256484543d1Sopenharmony_ci
257484543d1Sopenharmony_ci    void free(T* p)
258484543d1Sopenharmony_ci    {
259484543d1Sopenharmony_ci        lock.lock();
260484543d1Sopenharmony_ci        --curAllocated;
261484543d1Sopenharmony_ci        cache.push_back(p);
262484543d1Sopenharmony_ci        lock.unlock();
263484543d1Sopenharmony_ci    }
264484543d1Sopenharmony_ci
265484543d1Sopenharmony_ci    void release()
266484543d1Sopenharmony_ci    {
267484543d1Sopenharmony_ci        T* p = nullptr;
268484543d1Sopenharmony_ci        lock.lock();
269484543d1Sopenharmony_ci        FFRT_LOGD("coroutine release with waterline %d, cur occupied %d, cached size %d",
270484543d1Sopenharmony_ci            maxAllocated, curAllocated, cache.size());
271484543d1Sopenharmony_ci        size_t reservedCnt = maxAllocated - curAllocated + 1; // reserve additional one for robustness
272484543d1Sopenharmony_ci        maxAllocated = curAllocated;
273484543d1Sopenharmony_ci        while (cache.size() > reservedCnt) {
274484543d1Sopenharmony_ci            p = cache.back();
275484543d1Sopenharmony_ci            cache.pop_back();
276484543d1Sopenharmony_ci            int ret = munmap(p, TSize);
277484543d1Sopenharmony_ci            if (ret != 0) {
278484543d1Sopenharmony_ci                FFRT_LOGE("munmap failed with errno: %d", errno);
279484543d1Sopenharmony_ci            }
280484543d1Sopenharmony_ci        }
281484543d1Sopenharmony_ci        lock.unlock();
282484543d1Sopenharmony_ci    }
283484543d1Sopenharmony_ci
284484543d1Sopenharmony_ci    QSimpleAllocator()
285484543d1Sopenharmony_ci    {
286484543d1Sopenharmony_ci    }
287484543d1Sopenharmony_ci
288484543d1Sopenharmony_cipublic:
289484543d1Sopenharmony_ci    explicit QSimpleAllocator(std::size_t size = sizeof(T)) : curAllocated(0), maxAllocated(0)
290484543d1Sopenharmony_ci    {
291484543d1Sopenharmony_ci        std::size_t p_size = static_cast<std::size_t>(getpagesize());
292484543d1Sopenharmony_ci        // manually align the size to the page size
293484543d1Sopenharmony_ci        TSize = (size - 1 + p_size) & -p_size;
294484543d1Sopenharmony_ci        if (MmapSz % TSize != 0) {
295484543d1Sopenharmony_ci            FFRT_LOGE("MmapSz is not divisible by TSize which may cause memory leak!");
296484543d1Sopenharmony_ci        }
297484543d1Sopenharmony_ci    }
298484543d1Sopenharmony_ci    QSimpleAllocator(QSimpleAllocator const&) = delete;
299484543d1Sopenharmony_ci    void operator=(QSimpleAllocator const&) = delete;
300484543d1Sopenharmony_ci
301484543d1Sopenharmony_ci    static QSimpleAllocator<T, MmapSz>* Instance(std::size_t size)
302484543d1Sopenharmony_ci    {
303484543d1Sopenharmony_ci        static QSimpleAllocator<T, MmapSz> ins(size);
304484543d1Sopenharmony_ci        return &ins;
305484543d1Sopenharmony_ci    }
306484543d1Sopenharmony_ci
307484543d1Sopenharmony_ci    static T* AllocMem(std::size_t size = sizeof(T))
308484543d1Sopenharmony_ci    {
309484543d1Sopenharmony_ci        return Instance(size)->Alloc();
310484543d1Sopenharmony_ci    }
311484543d1Sopenharmony_ci
312484543d1Sopenharmony_ci    static void FreeMem(T* p, std::size_t size = sizeof(T))
313484543d1Sopenharmony_ci    {
314484543d1Sopenharmony_ci        Instance(size)->free(p);
315484543d1Sopenharmony_ci    }
316484543d1Sopenharmony_ci
317484543d1Sopenharmony_ci    static void releaseMem(std::size_t size = sizeof(T))
318484543d1Sopenharmony_ci    {
319484543d1Sopenharmony_ci        Instance(size)->release();
320484543d1Sopenharmony_ci    }
321484543d1Sopenharmony_ci};
322484543d1Sopenharmony_ci} // namespace ffrt
323484543d1Sopenharmony_ci#endif /* UTIL_SLAB_H */
324