diff --git a/src/js_native_api_v8.cpp b/src/js_native_api_v8.cpp index d8188ece4adef7995ee24c005cb1f640c5b2189f..13768a832a73381787f52aa1e82a1e45313f7a5a 100644 --- a/src/js_native_api_v8.cpp +++ b/src/js_native_api_v8.cpp @@ -1199,7 +1199,7 @@ JSVM_Status OH_JSVM_DestroyEnv(JSVM_Env env) JSVM_Status OH_JSVM_OpenEnvScope(JSVM_Env env, JSVM_EnvScope* result) { - auto v8scope = new v8::Context::Scope(env->context()); + auto *v8scope = env->scopeMemoryManager.New(env->context()); *result = reinterpret_cast(v8scope); return ClearLastError(env); } @@ -1207,7 +1207,7 @@ JSVM_Status OH_JSVM_OpenEnvScope(JSVM_Env env, JSVM_EnvScope* result) JSVM_Status OH_JSVM_CloseEnvScope(JSVM_Env env, JSVM_EnvScope scope) { auto v8scope = reinterpret_cast(scope); - delete v8scope; + env->scopeMemoryManager.Delete(v8scope); return ClearLastError(env); } @@ -3574,7 +3574,9 @@ JSVM_Status OH_JSVM_OpenHandleScope(JSVM_Env env, JSVM_HandleScope* result) CHECK_ENV(env); CHECK_ARG(env, result); - *result = v8impl::JsHandleScopeFromV8HandleScope(new v8impl::HandleScopeWrapper(env->isolate)); + auto* scope = env->scopeMemoryManager.New(env->isolate); + + *result = v8impl::JsHandleScopeFromV8HandleScope(scope); env->openHandleScopes++; if (UNLIKELY(env->debugFlags)) { @@ -3598,7 +3600,8 @@ JSVM_Status OH_JSVM_CloseHandleScope(JSVM_Env env, JSVM_HandleScope scope) env->ReleaseJsvmData(); env->openHandleScopes--; - delete v8impl::V8HandleScopeFromJsHandleScope(scope); + + env->scopeMemoryManager.Delete(v8impl::V8HandleScopeFromJsHandleScope(scope)); if (UNLIKELY(env->debugFlags)) { if (UNLIKELY(env->debugFlags & (1 << JSVM_SCOPE_CHECK))) { @@ -3617,8 +3620,8 @@ JSVM_Status OH_JSVM_OpenEscapableHandleScope(JSVM_Env env, JSVM_EscapableHandleS CHECK_ENV(env); CHECK_ARG(env, result); - *result = - v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope(new v8impl::EscapableHandleScopeWrapper(env->isolate)); + auto* scope = env->scopeMemoryManager.New(env->isolate); + *result = v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope(scope); env->openHandleScopes++; return ClearLastError(env); } @@ -3633,7 +3636,7 @@ JSVM_Status OH_JSVM_CloseEscapableHandleScope(JSVM_Env env, JSVM_EscapableHandle return JSVM_HANDLE_SCOPE_MISMATCH; } - delete v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); + env->scopeMemoryManager.Delete(v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope)); env->openHandleScopes--; return ClearLastError(env); } diff --git a/src/jsvm_env.h b/src/jsvm_env.h index 9f7b248f0d1f4d76d2e9975af824d3a32382cc30..7f28702de8345b8a70d178f8ac3ac921d90af95a 100644 --- a/src/jsvm_env.h +++ b/src/jsvm_env.h @@ -24,6 +24,7 @@ #include "jsvm_reference.h" #include "jsvm_types.h" #include "jsvm_util.h" +#include "memory_manager.h" #include "type_conversion.h" #include "v8.h" @@ -190,6 +191,10 @@ public: // Store v8::Locker v8::Locker* locker = nullptr; + using ScopeMemoryManager = MemoryChunkList< + jsvm::MaxSize()>; + ScopeMemoryManager scopeMemoryManager; + int32_t apiVersion; int openHandleScopes = 0; diff --git a/src/jsvm_util.h b/src/jsvm_util.h index 598aaaa682c051cdacb20fd96f98ae99eeddbea8..88142dfdee642ef7858259aedbfc1fd3c451717d 100644 --- a/src/jsvm_util.h +++ b/src/jsvm_util.h @@ -47,7 +47,6 @@ #include "v8-profiler.h" #include "v8.h" - #ifdef __GNUC__ #define FORCE_INLINE __attribute__((always_inline)) #else @@ -60,6 +59,15 @@ constexpr size_t ArraySize(const T (&)[N]) { return N; } + +// Get maximum size for all given types +template +constexpr size_t MaxSize() +{ + size_t max = 0; + ((max = sizeof(Types) > max ? sizeof(Types) : max), ...); + return max; +} } // namespace jsvm namespace v8impl { diff --git a/src/memory_manager.h b/src/memory_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..aca69a3ba7127a29ea5038a2bbe4ceb93d3e3347 --- /dev/null +++ b/src/memory_manager.h @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef MEMORY_MANAGER_H +#define MEMORY_MANAGER_H +#include + +#include "jsvm_util.h" + +template +class MemoryChunkList { +private: + union ElementMemory { + constexpr ElementMemory() : next(nullptr) {} + + ~ElementMemory() {} + + ElementMemory* next; + char ele[elementSize]; + }; + + struct MemoryChunk; + + struct ElementContainer { + uintptr_t header; + ElementMemory memory; + + static MemoryChunk* GetMemoryChunk(const ElementMemory* mem) + { + uintptr_t headerPtr = reinterpret_cast(mem) - sizeof(header); + return reinterpret_cast(*reinterpret_cast(headerPtr)); + } + }; + + struct MemoryChunk { + MemoryChunk* prev; + MemoryChunk* next; + size_t freeCount; + ElementContainer elements[sizePerChunk]; + + MemoryChunk() : prev(nullptr), next(nullptr), freeCount(sizePerChunk) + { + for (size_t i = 0; i < sizePerChunk - 1; ++i) { + elements[i].header = reinterpret_cast(this); + elements[i].memory.next = &(elements[i + 1].memory); + } + elements[sizePerChunk - 1].header = reinterpret_cast(this); + elements[sizePerChunk - 1].memory.next = nullptr; + } + + void Inc() + { + ++freeCount; + } + + void Dec() + { + --freeCount; + } + + bool CanBeFree() + { + return freeCount == sizePerChunk; + } + }; + +public: + MemoryChunkList() : head(nullptr), freeList(nullptr) + { + AllocateChunk(); + } + + ~MemoryChunkList() + { + auto* ptr = head; + while (ptr) { + auto* next = ptr->next; + if (UNLIKELY(!ptr->CanBeFree())) { + LOG(Error) << "Memory is in use when free " << std::hex << reinterpret_cast(ptr); + DCHECK(false && "MemoryChunk can not free"); + } + + delete ptr; + ptr = next; + } + } + + template + Element* New(Args&&... args) + { + static_assert(elementSize >= sizeof(Element)); + return new (GetMemory()) Element(std::forward(args)...); + } + + template + void Delete(Element* element) + { + element->~Element(); + auto* memory = reinterpret_cast(element); + auto* chunk = ElementContainer::GetMemoryChunk(memory); + chunk->Inc(); + + if (UNLIKELY(chunkNumber > threshold && chunk->CanBeFree())) { + FreeChunk(chunk); + return; + } + + memory->next = freeList; + freeList = memory; + } + +private: + void* GetMemory() + { + if (UNLIKELY(!freeList)) { + AllocateChunk(); + } + + auto* memory = freeList; + ElementContainer::GetMemoryChunk(memory)->Dec(); + freeList = memory->next; + + return reinterpret_cast(memory); + } + + void AllocateChunk() + { + DCHECK(freeList == nullptr); + auto* newChunk = new MemoryChunk(); + if (head) { + newChunk->next = head; + head->prev = newChunk; + } + head = newChunk; + ++chunkNumber; + + freeList = &(newChunk->elements[0].memory); + } + + void FreeChunk(MemoryChunk* chunk) + { + // Rebuild freeList + size_t count = 0; + auto* newFreeList = freeList; + ElementMemory* prev = nullptr; + for (auto* current = freeList; count < sizePerChunk - 1 && current; current = current->next) { + if (ElementContainer::GetMemoryChunk(current) == chunk) { + if (prev) { + prev->next = current->next; + } else { + newFreeList = current->next; + } + ++count; + } else { + prev = current; + } + } + DCHECK(count == sizePerChunk - 1); + freeList = newFreeList; + + // Rebuild chunk list + if (chunk->next) { + chunk->next->prev = chunk->prev; + } + if (chunk->prev) { + chunk->prev->next = chunk->next; + } else { + // chunk is head + DCHECK(chunk == head); + head = chunk->next; + } + --chunkNumber; + + // Delete chunk + delete chunk; + } + +private: + MemoryChunk* head; + size_t chunkNumber; + ElementMemory* freeList; +}; + +#endif \ No newline at end of file