• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 UTIL_SLAB_HPP
17 #define UTIL_SLAB_HPP
18 
19 #include <new>
20 #include <vector>
21 #include <mutex>
22 #ifdef FFRT_BBOX_ENABLE
23 #include <unordered_set>
24 #endif
25 #include <sys/mman.h>
26 #include "sync/sync.h"
27 #include "dfx/log/ffrt_log_api.h"
28 
29 namespace ffrt {
30 const std::size_t BatchAllocSize = 128 * 1024;
31 #ifdef FFRT_BBOX_ENABLE
32 constexpr uint32_t ALLOCATOR_DESTRUCT_TIMESOUT = 1000;
33 #endif
34 
35 template <typename T, size_t MmapSz = BatchAllocSize>
36 class SimpleAllocator {
37 public:
38     SimpleAllocator(const SimpleAllocator&) = delete;
39     SimpleAllocator(SimpleAllocator&&) = delete;
40     SimpleAllocator& operator=(const SimpleAllocator&) = delete;
41     SimpleAllocator& operator=(SimpleAllocator&&) = delete;
42 
instance()43     static SimpleAllocator<T>* instance()
44     {
45         static SimpleAllocator<T> ins;
46         return &ins;
47     }
48 
49     // NOTE: call constructor after allocMem
allocMem()50     static T* allocMem()
51     {
52         return instance()->alloc();
53     }
54 
55     // NOTE: call destructor before freeMem
freeMem(T * t)56     static void freeMem(T* t)
57     {
58         t->~T();
59         // unlock()内部lck记录锁的状态为非持有状态,析构时访问状态变量为非持有状态,则不访问实际持有的mutex
60         // return之前的lck析构不产生UAF问题,因为return之前随着root析构,锁的内存被释放
61         instance()->free(t);
62     }
63 
64     // only used for BBOX
getUnfreedMem()65     static std::vector<T*> getUnfreedMem()
66     {
67         return instance()->getUnfreed();
68     }
69 private:
70     fast_mutex lock;
71     std::vector<T*> primaryCache;
72 #ifdef FFRT_BBOX_ENABLE
73     std::unordered_set<T*> secondaryCache;
74 #endif
75     T* basePtr = nullptr;
76     std::size_t count = 0;
77 
getUnfreed()78     std::vector<T*> getUnfreed()
79     {
80         lock.lock();
81         std::vector<T*> ret;
82 #ifdef FFRT_BBOX_ENABLE
83         ret.reserve(MmapSz / sizeof(T) + secondaryCache.size());
84         for (std::size_t i = 0; i < MmapSz / sizeof(T); ++i) {
85             if (basePtr != nullptr &&
86                 std::find(primaryCache.begin(), primaryCache.end(), &basePtr[i]) == primaryCache.end()) {
87                 ret.push_back(&basePtr[i]);
88             }
89         }
90         for (auto ite = secondaryCache.cbegin(); ite != secondaryCache.cend(); ite++) {
91             ret.push_back(*ite);
92         }
93 #endif
94         lock.unlock();
95         return ret;
96     }
97 
init()98     void init()
99     {
100         basePtr = reinterpret_cast<T*>(::operator new(MmapSz));
101         count = MmapSz / sizeof(T);
102         primaryCache.reserve(count);
103         for (std::size_t i = 0; i < count; ++i) {
104             primaryCache.push_back(&basePtr[i]);
105         }
106     }
107 
alloc()108     T* alloc()
109     {
110         lock.lock();
111         T* t = nullptr;
112         if (count == 0) {
113             if (basePtr != nullptr) {
114                 t = reinterpret_cast<T*>(::operator new(sizeof(T)));
115 #ifdef FFRT_BBOX_ENABLE
116                 secondaryCache.insert(t);
117 #endif
118                 lock.unlock();
119                 return t;
120             }
121             init();
122         }
123         t = primaryCache.back();
124         primaryCache.pop_back();
125         count--;
126         lock.unlock();
127         return t;
128     }
129 
free(T * t)130     void free(T* t)
131     {
132         lock.lock();
133         if (basePtr != nullptr &&
134             basePtr <= t &&
135             static_cast<size_t>(reinterpret_cast<uintptr_t>(t)) <
136             static_cast<size_t>(reinterpret_cast<uintptr_t>(basePtr)) + MmapSz) {
137             primaryCache.push_back(t);
138             count++;
139         } else {
140             ::operator delete(t);
141 #ifdef FFRT_BBOX_ENABLE
142             secondaryCache.erase(t);
143 #endif
144         }
145         lock.unlock();
146     }
147 
SimpleAllocator()148     SimpleAllocator()
149     {
150     }
~SimpleAllocator()151     ~SimpleAllocator()
152     {
153         std::unique_lock<decltype(lock)> lck(lock);
154         if (basePtr == nullptr) {
155             return;
156         }
157 #ifdef FFRT_BBOX_ENABLE
158         uint32_t try_cnt = ALLOCATOR_DESTRUCT_TIMESOUT;
159         std::size_t reserved = MmapSz / sizeof(T);
160         while (try_cnt > 0) {
161             if (primaryCache.size() == reserved && secondaryCache.size() == 0) {
162                 break;
163             }
164             lck.unlock();
165             usleep(1000);
166             try_cnt--;
167             lck.lock();
168         }
169         if (try_cnt == 0) {
170             FFRT_LOGE("clear allocator failed");
171         }
172         for (auto ite = secondaryCache.cbegin(); ite != secondaryCache.cend(); ite++) {
173             ::operator delete(*ite);
174         }
175 #endif
176         ::operator delete(basePtr);
177     }
178 };
179 
180 constexpr uint32_t RESERVED_COROUTINE_COUNT = 0;
181 template <typename T, std::size_t MmapSz = 16 * 1024 * 1024>
182 class QSimpleAllocator {
183     std::size_t TSize;
184     std::mutex lock;
185     std::vector<T*> cache;
186     uint32_t flags = MAP_ANONYMOUS | MAP_PRIVATE;
187 
expand()188     bool expand()
189     {
190         const int prot = PROT_READ | PROT_WRITE;
191         char* p = reinterpret_cast<char*>(mmap(nullptr, MmapSz, prot, flags, -1, 0));
192         if (p == (char*)MAP_FAILED) {
193             if ((flags & MAP_HUGETLB) != 0) {
194                 flags = MAP_ANONYMOUS | MAP_PRIVATE;
195                 p = reinterpret_cast<char*>(mmap(nullptr, MmapSz, prot, flags, -1, 0));
196             }
197             if (p == (char*)MAP_FAILED) {
198                 perror("mmap");
199                 return false;
200             }
201         }
202         for (std::size_t i = 0; i + TSize <= MmapSz; i += TSize) {
203             cache.push_back(reinterpret_cast<T*>(p + i));
204         }
205         return true;
206     }
207 
alloc()208     T* alloc()
209     {
210         T* p = nullptr;
211         lock.lock();
212         if (cache.empty()) {
213             if (!expand()) {
214                 lock.unlock();
215                 return nullptr;
216             }
217         }
218         p = cache.back();
219         cache.pop_back();
220         lock.unlock();
221         return p;
222     }
223 
free(T * p)224     void free(T* p)
225     {
226         lock.lock();
227         cache.push_back(p);
228         lock.unlock();
229     }
230 
release()231     void release()
232     {
233         T* p = nullptr;
234         lock.lock();
235         while (cache.size() > RESERVED_COROUTINE_COUNT) {
236             p = cache.back();
237             cache.pop_back();
238             int ret = munmap(p, TSize);
239             if (ret != 0) {
240                 FFRT_LOGE("munmap failed with errno: %d", errno);
241             }
242         }
243         lock.unlock();
244     }
245 
QSimpleAllocator()246     QSimpleAllocator()
247     {
248     }
249 
250 public:
251     explicit QSimpleAllocator(std::size_t size = sizeof(T))
252     {
253         std::size_t p_size = static_cast<std::size_t>(getpagesize());
254         // manually align the size to the page size
255         TSize = (size - 1 + p_size) & -p_size;
256         if (MmapSz % TSize != 0) {
257             FFRT_LOGE("MmapSz is not divisible by TSize which may cause memory leak!");
258         }
259     }
260     QSimpleAllocator(QSimpleAllocator const&) = delete;
261     void operator=(QSimpleAllocator const&) = delete;
262 
instance(std::size_t size)263     static QSimpleAllocator<T, MmapSz>* instance(std::size_t size)
264     {
265         static QSimpleAllocator<T, MmapSz> ins(size);
266         return &ins;
267     }
268 
269     static T* allocMem(std::size_t size = sizeof(T))
270     {
271         return instance(size)->alloc();
272     }
273 
274     static void freeMem(T* p, std::size_t size = sizeof(T))
275     {
276         instance(size)->free(p);
277     }
278 
279     static void releaseMem(std::size_t size = sizeof(T))
280     {
281         instance(size)->release();
282     }
283 };
284 } // namespace ffrt
285 #endif /* UTIL_SLAB_H */
286