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