1 /* 2 * Copyright (c) 2024 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 17 #ifndef MEMORY_MANAGER_H 18 #define MEMORY_MANAGER_H 19 #include <algorithm> 20 21 #include "jsvm_util.h" 22 23 template<size_t elementSize, size_t sizePerChunk = 8, size_t threshold = 10> 24 class MemoryChunkList { 25 private: 26 union ElementMemory { ElementMemory()27 constexpr ElementMemory() : next(nullptr) {} 28 ~ElementMemory()29 ~ElementMemory() {} 30 31 ElementMemory* next; 32 char ele[elementSize]; 33 }; 34 35 struct MemoryChunk; 36 37 struct ElementContainer { 38 uintptr_t header; 39 ElementMemory memory; 40 GetMemoryChunkElementContainer41 static MemoryChunk* GetMemoryChunk(const ElementMemory* mem) 42 { 43 uintptr_t headerPtr = reinterpret_cast<uintptr_t>(mem) - sizeof(header); 44 return reinterpret_cast<MemoryChunk*>(*reinterpret_cast<uintptr_t*>(headerPtr)); 45 } 46 }; 47 48 struct MemoryChunk { 49 MemoryChunk* prev; 50 MemoryChunk* next; 51 size_t freeCount; 52 ElementContainer elements[sizePerChunk]; 53 MemoryChunkMemoryChunk54 MemoryChunk() : prev(nullptr), next(nullptr), freeCount(sizePerChunk) 55 { 56 for (size_t i = 0; i < sizePerChunk - 1; ++i) { 57 elements[i].header = reinterpret_cast<uintptr_t>(this); 58 elements[i].memory.next = &(elements[i + 1].memory); 59 } 60 elements[sizePerChunk - 1].header = reinterpret_cast<uintptr_t>(this); 61 elements[sizePerChunk - 1].memory.next = nullptr; 62 } 63 IncMemoryChunk64 void Inc() 65 { 66 ++freeCount; 67 } 68 DecMemoryChunk69 void Dec() 70 { 71 --freeCount; 72 } 73 CanBeFreeMemoryChunk74 bool CanBeFree() 75 { 76 return freeCount == sizePerChunk; 77 } 78 }; 79 80 public: MemoryChunkList()81 MemoryChunkList() : head(nullptr), freeList(nullptr) 82 { 83 AllocateChunk(); 84 } 85 ~MemoryChunkList()86 ~MemoryChunkList() 87 { 88 auto* ptr = head; 89 while (ptr) { 90 auto* next = ptr->next; 91 if (UNLIKELY(!ptr->CanBeFree())) { 92 LOG(Error) << "Memory is in use when free " << std::hex << reinterpret_cast<uintptr_t>(ptr); 93 DCHECK(false && "MemoryChunk can not free"); 94 } 95 96 delete ptr; 97 ptr = next; 98 } 99 } 100 101 template<typename Element, typename... Args> New(Args &&...args)102 Element* New(Args&&... args) 103 { 104 static_assert(elementSize >= sizeof(Element)); 105 return new (GetMemory()) Element(std::forward<Args>(args)...); 106 } 107 108 template<typename Element> Delete(Element * element)109 void Delete(Element* element) 110 { 111 element->~Element(); 112 auto* memory = reinterpret_cast<ElementMemory*>(element); 113 auto* chunk = ElementContainer::GetMemoryChunk(memory); 114 chunk->Inc(); 115 116 if (UNLIKELY(chunkNumber > threshold && chunk->CanBeFree())) { 117 FreeChunk(chunk); 118 return; 119 } 120 121 memory->next = freeList; 122 freeList = memory; 123 } 124 125 private: GetMemory()126 void* GetMemory() 127 { 128 if (UNLIKELY(!freeList)) { 129 AllocateChunk(); 130 } 131 132 auto* memory = freeList; 133 ElementContainer::GetMemoryChunk(memory)->Dec(); 134 freeList = memory->next; 135 136 return reinterpret_cast<void*>(memory); 137 } 138 AllocateChunk()139 void AllocateChunk() 140 { 141 DCHECK(freeList == nullptr); 142 auto* newChunk = new MemoryChunk(); 143 if (head) { 144 newChunk->next = head; 145 head->prev = newChunk; 146 } 147 head = newChunk; 148 ++chunkNumber; 149 150 freeList = &(newChunk->elements[0].memory); 151 } 152 FreeChunk(MemoryChunk * chunk)153 void FreeChunk(MemoryChunk* chunk) 154 { 155 // Rebuild freeList 156 size_t count = 0; 157 auto* newFreeList = freeList; 158 ElementMemory* prev = nullptr; 159 for (auto* current = freeList; count < sizePerChunk - 1 && current; current = current->next) { 160 if (ElementContainer::GetMemoryChunk(current) == chunk) { 161 if (prev) { 162 prev->next = current->next; 163 } else { 164 newFreeList = current->next; 165 } 166 ++count; 167 } else { 168 prev = current; 169 } 170 } 171 DCHECK(count == sizePerChunk - 1); 172 freeList = newFreeList; 173 174 // Rebuild chunk list 175 if (chunk->next) { 176 chunk->next->prev = chunk->prev; 177 } 178 if (chunk->prev) { 179 chunk->prev->next = chunk->next; 180 } else { 181 // chunk is head 182 DCHECK(chunk == head); 183 head = chunk->next; 184 } 185 --chunkNumber; 186 187 // Delete chunk 188 delete chunk; 189 } 190 191 private: 192 MemoryChunk* head; 193 size_t chunkNumber; 194 ElementMemory* freeList; 195 }; 196 197 #endif