Add concurrent native delter

Signed-off-by: xiongluo <xiongluo@huawei.com>
Change-Id: I133a594bd52ffa58427cbfd467fca383e3d66921
This commit is contained in:
xiongluo 2024-03-25 22:12:47 +08:00
parent 04754e39fc
commit f7a5a4034f
14 changed files with 210 additions and 15 deletions

View File

@ -472,6 +472,21 @@ void EcmaVM::ProcessNativeDelete(const WeakRootVisitor &visitor)
++iter;
}
}
auto newIter = concurrentNativePointerList_.begin();
while (newIter != concurrentNativePointerList_.end()) {
JSNativePointer *object = *newIter;
auto fwd = visitor(reinterpret_cast<TaggedObject *>(object));
if (fwd == nullptr) {
nativeAreaAllocator_->DecreaseNativeSizeStats(object->GetBindingSize(), object->GetNativeFlag());
nativePointerCallbacks_.emplace_back(std::make_pair(object->GetDeleter(),
std::make_pair(object->GetExternalPointer(),
object->GetData())));
newIter = concurrentNativePointerList_.erase(newIter);
} else {
++newIter;
}
}
}
}
@ -500,14 +515,37 @@ void EcmaVM::ProcessReferences(const WeakRootVisitor &visitor)
}
++iter;
}
auto newIter = concurrentNativePointerList_.begin();
while (newIter != concurrentNativePointerList_.end()) {
JSNativePointer *object = *newIter;
auto fwd = visitor(reinterpret_cast<TaggedObject *>(object));
if (fwd == nullptr) {
nativeAreaAllocator_->DecreaseNativeSizeStats(object->GetBindingSize(), object->GetNativeFlag());
nativePointerCallbacks_.emplace_back(std::make_pair(object->GetDeleter(),
std::make_pair(object->GetExternalPointer(),
object->GetData())));
newIter = concurrentNativePointerList_.erase(newIter);
continue;
}
heap_->IncreaseNativeBindingSize(JSNativePointer::Cast(fwd));
if (fwd != reinterpret_cast<TaggedObject *>(object)) {
*newIter = JSNativePointer::Cast(fwd);
}
++newIter;
}
}
GetPGOProfiler()->ProcessReferences(visitor);
}
void EcmaVM::PushToNativePointerList(JSNativePointer *pointer)
void EcmaVM::PushToNativePointerList(JSNativePointer *pointer, Concurrent isConcurrent)
{
ASSERT(!JSTaggedValue(pointer).IsInSharedHeap());
nativePointerList_.emplace_back(pointer);
if (isConcurrent == Concurrent::YES) {
concurrentNativePointerList_.emplace_back(pointer);
} else {
nativePointerList_.emplace_back(pointer);
}
}
void EcmaVM::RemoveFromNativePointerList(JSNativePointer *pointer)
@ -519,6 +557,13 @@ void EcmaVM::RemoveFromNativePointerList(JSNativePointer *pointer)
object->Destroy();
nativePointerList_.erase(iter);
}
auto newIter = std::find(concurrentNativePointerList_.begin(), concurrentNativePointerList_.end(), pointer);
if (newIter != concurrentNativePointerList_.end()) {
JSNativePointer *object = *iter;
nativeAreaAllocator_->DecreaseNativeSizeStats(object->GetBindingSize(), object->GetNativeFlag());
object->Destroy();
concurrentNativePointerList_.erase(iter);
}
}
void EcmaVM::PushToDeregisterModuleList(CString module)
@ -545,6 +590,9 @@ void EcmaVM::ClearBufferData()
for (auto iter : nativePointerList_) {
iter->Destroy();
}
for (auto iter : concurrentNativePointerList_) {
iter->Destroy();
}
nativePointerList_.clear();
thread_->GetCurrentEcmaContext()->ClearBufferData();
internalNativeMethods_.clear();

View File

@ -53,6 +53,7 @@ class Tracing;
class RegExpExecResultCache;
class JSPromise;
enum class PromiseRejectionEvent : uint8_t;
enum class Concurrent { YES, NO };
class JSPandaFileManager;
class JSPandaFile;
class EcmaStringTable;
@ -100,6 +101,7 @@ using RequestAotCallback =
using SearchHapPathCallBack = std::function<bool(const std::string moduleName, std::string &hapPath)>;
using DeviceDisconnectCallback = std::function<bool()>;
using UncatchableErrorHandler = std::function<void(panda::TryCatch&)>;
using DeleteEntryPoint = void (*)(void *, void *);
class EcmaVM {
public:
static EcmaVM *Create(const JSRuntimeOptions &options);
@ -207,7 +209,7 @@ public:
return icEnabled_;
}
void PushToNativePointerList(JSNativePointer *pointer);
void PushToNativePointerList(JSNativePointer *pointer, Concurrent isConcurrent = Concurrent::NO);
void RemoveFromNativePointerList(JSNativePointer *pointer);
void PushToDeregisterModuleList(CString module);
void RemoveFromDeregisterModuleList(CString module);
@ -319,6 +321,11 @@ public:
return nativePointerList_;
}
size_t GetConcurrentNativePointerListSize() const
{
return concurrentNativePointerList_.size();
}
void SetResolveBufferCallback(ResolveBufferCallback cb)
{
resolveBufferCallback_ = cb;
@ -600,6 +607,11 @@ public:
return thread_->GetThreadId();
}
std::vector<std::pair<DeleteEntryPoint, std::pair<void *, void *>>> &GetNativePointerCallbacks()
{
return nativePointerCallbacks_;
}
static void InitializeIcuData(const JSRuntimeOptions &options);
protected:
@ -629,6 +641,8 @@ private:
Heap *heap_ {nullptr};
ObjectFactory *factory_ {nullptr};
CList<JSNativePointer *> nativePointerList_;
CList<JSNativePointer *> concurrentNativePointerList_;
std::vector<std::pair<DeleteEntryPoint, std::pair<void *, void *>>> nativePointerCallbacks_ {};
// VM execution states.
JSThread *thread_ {nullptr};

View File

@ -894,15 +894,15 @@ bool JSFunction::NameSetter(JSThread *thread, const JSHandle<JSObject> &self, co
return true;
}
void JSFunction::SetFunctionExtraInfo(JSThread *thread, void *nativeFunc,
const DeleteEntryPoint &deleter, void *data, size_t nativeBindingsize)
void JSFunction::SetFunctionExtraInfo(JSThread *thread, void *nativeFunc, const DeleteEntryPoint &deleter,
void *data, size_t nativeBindingsize, Concurrent isConcurrent)
{
JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
EcmaVM *vm = thread->GetEcmaVM();
JSHandle<JSTaggedValue> value(thread, JSTaggedValue(hashField));
JSHandle<ECMAObject> obj(thread, this);
JSHandle<JSNativePointer> pointer = vm->GetFactory()->NewJSNativePointer(nativeFunc, deleter, data,
false, nativeBindingsize);
false, nativeBindingsize, isConcurrent);
if (!obj->HasHash()) {
Barriers::SetObject<true>(thread, *obj, HASH_OFFSET, pointer.GetTaggedValue().GetRawData());
return;

View File

@ -221,7 +221,7 @@ public:
}
void SetFunctionExtraInfo(JSThread *thread, void *nativeFunc, const DeleteEntryPoint &deleter,
void *data, size_t nativeBindingsize = 0);
void *data, size_t nativeBindingsize = 0, Concurrent isConcurrent = Concurrent::NO);
void SetSFunctionExtraInfo(
JSThread *thread, void *nativeFunc, const DeleteEntryPoint &deleter, void *data, size_t nativeBindingsize = 0);

View File

@ -2797,7 +2797,7 @@ void *ECMAObject::GetNativePointerField(int32_t index) const
}
void ECMAObject::SetNativePointerField(const JSThread *thread, int32_t index, void *nativePointer,
const DeleteEntryPoint &callBack, void *data, size_t nativeBindingsize)
const DeleteEntryPoint &callBack, void *data, size_t nativeBindingsize, Concurrent isConcurrent)
{
JSTaggedType hashField = Barriers::GetValue<JSTaggedType>(this, HASH_OFFSET);
JSTaggedValue value(hashField);
@ -2812,7 +2812,7 @@ void ECMAObject::SetNativePointerField(const JSThread *thread, int32_t index, vo
array->Set(thread, index, JSTaggedValue::Hole());
} else {
JSHandle<JSNativePointer> pointer = vm->GetFactory()->NewJSNativePointer(
nativePointer, callBack, data, false, nativeBindingsize);
nativePointer, callBack, data, false, nativeBindingsize, isConcurrent);
array->Set(thread, index, pointer.GetTaggedValue());
}
}

View File

@ -18,6 +18,7 @@
#include "ecmascript/ecma_macros.h"
#include "ecmascript/ecma_string.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/filter_helper.h"
#include "ecmascript/ic/property_box.h"
#include "ecmascript/js_handle.h"
@ -383,7 +384,8 @@ public:
void* GetNativePointerField(int32_t index) const;
void SetNativePointerField(const JSThread *thread, int32_t index, void *nativePointer,
const DeleteEntryPoint &callBack, void *data, size_t nativeBindingsize = 0);
const DeleteEntryPoint &callBack, void *data, size_t nativeBindingsize = 0,
Concurrent isConcurrent = Concurrent::NO);
int32_t GetNativePointerFieldCount() const;
void SetNativePointerFieldCount(const JSThread *thread, int32_t count);

View File

@ -713,6 +713,8 @@ void Heap::CollectGarbage(TriggerGCType gcType, GCReason reason)
// Weak node nativeFinalizeCallback may execute JS and change the weakNodeList status,
// even lead to another GC, so this have to invoke after this GC process.
thread_->InvokeWeakNodeNativeFinalizeCallback();
// PostTask for ProcessNativeDelete
CleanCallBack();
if (UNLIKELY(ShouldVerifyHeap())) {
// verify post gc heap verify
@ -1636,6 +1638,27 @@ bool Heap::FinishColdStartTask::Run([[maybe_unused]] uint32_t threadIndex)
return true;
}
void Heap::CleanCallBack()
{
auto &callbacks = this->GetEcmaVM()->GetNativePointerCallbacks();
if (!callbacks.empty()) {
Taskpool::GetCurrentTaskpool()->PostTask(
std::make_unique<DeleteCallbackTask>(this->GetJSThread()->GetThreadId(), callbacks)
);
}
ASSERT(callbacks.empty());
}
bool Heap::DeleteCallbackTask::Run([[maybe_unused]] uint32_t threadIndex)
{
for (auto iter : nativePointerCallbacks_) {
if (iter.first != nullptr) {
iter.first(iter.second.first, iter.second.second);
}
}
return true;
}
size_t Heap::GetArrayBufferSize() const
{
size_t result = 0;

View File

@ -17,6 +17,7 @@
#define ECMASCRIPT_MEM_HEAP_H
#include "ecmascript/base/config.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/frames.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/mem/linear_space.h"
@ -1030,6 +1031,7 @@ private:
inline void ReclaimRegions(TriggerGCType gcType);
inline size_t CalculateCommittedCacheSize();
void ProcessGCListeners();
void CleanCallBack();
class ParallelGCTask : public Task {
public:
ParallelGCTask(int32_t id, Heap *heap, ParallelGCTaskPhase taskPhase)
@ -1072,6 +1074,23 @@ private:
Heap *heap_;
};
class DeleteCallbackTask : public Task {
public:
DeleteCallbackTask(int32_t id, std::vector<std::pair<DeleteEntryPoint, std::pair<void *, void *>>> &callbacks)
: Task(id)
{
std::swap(callbacks, nativePointerCallbacks_);
};
~DeleteCallbackTask() override = default;
bool Run(uint32_t threadIndex) override;
NO_COPY_SEMANTIC(DeleteCallbackTask);
NO_MOVE_SEMANTIC(DeleteCallbackTask);
private:
std::vector<std::pair<DeleteEntryPoint, std::pair<void *, void *>>> nativePointerCallbacks_ {};
};
class RecursionScope {
public:
explicit RecursionScope(Heap* heap) : heap_(heap)

View File

@ -624,6 +624,9 @@ public:
static Local<NativePointerRef> New(const EcmaVM *vm, void *nativePointer, size_t nativeBindingsize = 0);
static Local<NativePointerRef> New(const EcmaVM *vm, void *nativePointer, NativePointerCallback callBack,
void *data, size_t nativeBindingsize = 0);
static Local<NativePointerRef> NewConcurrent(const EcmaVM *vm, void *nativePointer,
NativePointerCallback callBack,
void *data, size_t nativeBindingsize = 0);
void *Value();
};
@ -677,6 +680,11 @@ public:
void *nativePointer = nullptr,
NativePointerCallback callBack = nullptr,
void *data = nullptr, size_t nativeBindingsize = 0);
void SetConcurrentNativePointerField(const EcmaVM *vm,
int32_t index,
void *nativePointer = nullptr,
NativePointerCallback callBack = nullptr,
void *data = nullptr, size_t nativeBindingsize = 0);
};
using FunctionCallback = Local<JSValueRef>(*)(JsiRuntimeCallInfo*);
@ -695,8 +703,12 @@ public:
};
static Local<FunctionRef> New(EcmaVM *vm, FunctionCallback nativeFunc, Deleter deleter = nullptr,
void *data = nullptr, bool callNapi = false, size_t nativeBindingsize = 0);
static Local<FunctionRef> NewConcurrent(EcmaVM *vm, FunctionCallback nativeFunc, Deleter deleter = nullptr,
void *data = nullptr, bool callNapi = false, size_t nativeBindingsize = 0);
static Local<FunctionRef> New(EcmaVM *vm, InternalFunctionCallback nativeFunc, Deleter deleter,
void *data = nullptr, bool callNapi = false, size_t nativeBindingsize = 0);
static Local<FunctionRef> NewConcurrent(EcmaVM *vm, InternalFunctionCallback nativeFunc, Deleter deleter,
void *data = nullptr, bool callNapi = false, size_t nativeBindingsize = 0);
static Local<FunctionRef> NewSendable(EcmaVM *vm,
InternalFunctionCallback nativeFunc,
Deleter deleter,

View File

@ -194,6 +194,7 @@ using PathHelper = ecmascript::base::PathHelper;
using ModulePathHelper = ecmascript::ModulePathHelper;
using JsDebuggerManager = ecmascript::tooling::JsDebuggerManager;
using FrameIterator = ecmascript::FrameIterator;
using Concurrent = ecmascript::Concurrent;
namespace {
// NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
@ -2012,6 +2013,17 @@ void ObjectRef::SetNativePointerField(const EcmaVM *vm, int32_t index, void *nat
object->SetNativePointerField(thread, index, nativePointer, callBack, data, nativeBindingsize);
}
void ObjectRef::SetConcurrentNativePointerField(const EcmaVM *vm, int32_t index, void *nativePointer,
NativePointerCallback callBack, void *data, size_t nativeBindingsize)
{
CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
// ObjectRef::New may return special value if exception occurs.
// So we need do special value check before use it.
DCHECK_SPECIAL_VALUE(this);
JSHandle<JSObject> object(JSNApiHelper::ToJSHandle(this));
object->SetNativePointerField(thread, index, nativePointer, callBack, data, nativeBindingsize, Concurrent::YES);
}
// -------------------------------- NativePointerRef ------------------------------------
Local<NativePointerRef> NativePointerRef::New(const EcmaVM *vm, void *nativePointer, size_t nativeBindingsize)
{
@ -2034,6 +2046,16 @@ Local<NativePointerRef> NativePointerRef::New(
return JSNApiHelper::ToLocal<NativePointerRef>(JSHandle<JSTaggedValue>(obj));
}
Local<NativePointerRef> NativePointerRef::NewConcurrent(
const EcmaVM *vm, void *nativePointer, NativePointerCallback callBack, void *data, size_t nativeBindingsize)
{
CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
ObjectFactory *factory = vm->GetFactory();
JSHandle<JSNativePointer> obj = factory->NewJSNativePointer(nativePointer, callBack, data,
false, nativeBindingsize, Concurrent::YES);
return JSNApiHelper::ToLocal<NativePointerRef>(JSHandle<JSTaggedValue>(obj));
}
void *NativePointerRef::Value()
{
DCHECK_SPECIAL_VALUE_WITH_RETURN(this, nullptr);
@ -2184,6 +2206,19 @@ Local<FunctionRef> FunctionRef::New(EcmaVM *vm, FunctionCallback nativeFunc,
return JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(current));
}
Local<FunctionRef> FunctionRef::NewConcurrent(EcmaVM *vm, FunctionCallback nativeFunc,
Deleter deleter, void *data, bool callNapi, size_t nativeBindingsize)
{
CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
ObjectFactory *factory = vm->GetFactory();
JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
JSHandle<JSFunction> current(factory->NewJSFunction(env, reinterpret_cast<void *>(Callback::RegisterCallback)));
current->SetFunctionExtraInfo(thread, reinterpret_cast<void *>(nativeFunc), deleter,
data, nativeBindingsize, Concurrent::YES);
current->SetCallNapi(callNapi);
return JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(current));
}
Local<FunctionRef> FunctionRef::New(EcmaVM *vm, InternalFunctionCallback nativeFunc,
Deleter deleter, void *data, bool callNapi, size_t nativeBindingsize)
{
@ -2214,6 +2249,18 @@ Local<FunctionRef> FunctionRef::NewSendable(EcmaVM *vm,
return JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(current));
}
Local<FunctionRef> FunctionRef::NewConcurrent(EcmaVM *vm, InternalFunctionCallback nativeFunc,
Deleter deleter, void *data, bool callNapi, size_t nativeBindingsize)
{
CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
ObjectFactory *factory = vm->GetFactory();
JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
JSHandle<JSFunction> current(factory->NewJSFunction(env, reinterpret_cast<void *>(nativeFunc)));
current->SetFunctionExtraInfo(thread, nullptr, deleter, data, nativeBindingsize, Concurrent::YES);
current->SetCallNapi(callNapi);
return JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(current));
}
static void InitClassFunction(EcmaVM *vm, JSHandle<JSFunction> &func, bool callNapi)
{
CROSS_THREAD_CHECK(vm);

View File

@ -90,6 +90,7 @@ JSHandle<JSNativePointer> ObjectFactory::NewJSNativePointer(void *externalPointe
void *data,
bool nonMovable,
size_t nativeBindingsize,
Concurrent isConcurrent,
NativeFlag flag)
{
NewObjectHook();
@ -109,7 +110,7 @@ JSHandle<JSNativePointer> ObjectFactory::NewJSNativePointer(void *externalPointe
if (callBack != nullptr) {
heap_->IncreaseNativeBindingSize(nativeBindingsize);
vm_->PushToNativePointerList(static_cast<JSNativePointer *>(header));
vm_->PushToNativePointerList(static_cast<JSNativePointer *>(header), isConcurrent);
// In some cases, the size of JS/TS object is too small and the native binding size is too large.
// Check and try trigger concurrent mark here.
heap_->TryTriggerFullMarkByNativeSize();

View File

@ -271,7 +271,7 @@ void ObjectFactory::NewJSArrayBufferData(const JSHandle<JSArrayBuffer> &array, i
UNREACHABLE();
}
JSHandle<JSNativePointer> pointer = NewJSNativePointer(newData, NativeAreaAllocator::FreeBufferFunc,
vm_->GetNativeAreaAllocator(), false, size,
vm_->GetNativeAreaAllocator(), false, size, Concurrent::NO,
NativeFlag::ARRAY_BUFFER);
array->SetArrayBufferData(thread_, pointer);
array->SetWithNativeAreaAllocator(true);
@ -311,7 +311,7 @@ JSHandle<JSArrayBuffer> ObjectFactory::NewJSArrayBuffer(int32_t length)
}
JSHandle<JSNativePointer> pointer = NewJSNativePointer(newData, NativeAreaAllocator::FreeBufferFunc,
vm_->GetNativeAreaAllocator(), false, length,
NativeFlag::ARRAY_BUFFER);
Concurrent::NO, NativeFlag::ARRAY_BUFFER);
arrayBuffer->SetArrayBufferData(thread_, pointer.GetTaggedValue());
arrayBuffer->SetWithNativeAreaAllocator(true);
vm_->GetNativeAreaAllocator()->IncreaseNativeSizeStats(length, NativeFlag::ARRAY_BUFFER);
@ -406,7 +406,7 @@ void ObjectFactory::NewJSRegExpByteCodeData(const JSHandle<JSRegExp> &regexp, vo
return;
}
JSHandle<JSNativePointer> pointer = NewJSNativePointer(newBuffer, NativeAreaAllocator::FreeBufferFunc,
vm_->GetNativeAreaAllocator(), false, size,
vm_->GetNativeAreaAllocator(), false, size, Concurrent::NO,
NativeFlag::REGEXP_BTYECODE);
regexp->SetByteCodeBuffer(thread_, pointer.GetTaggedValue());
regexp->SetLength(static_cast<uint32_t>(size));

View File

@ -194,7 +194,6 @@ enum class MethodIndex : uint8_t;
using ErrorType = base::ErrorType;
using base::ErrorType;
using DeleteEntryPoint = void (*)(void *, void *);
enum class RemoveSlots { YES, NO };
enum class GrowMode { KEEP, GROW };
@ -522,6 +521,7 @@ public:
void *data = nullptr,
bool nonMovable = false,
size_t nativeBindingsize = 0,
Concurrent isConcurrent = Concurrent::NO,
NativeFlag flag = NativeFlag::NO_DIV);
JSHandle<JSObject> NewOldSpaceObjLiteralByHClass(const JSHandle<JSHClass> &hclass);

View File

@ -189,4 +189,33 @@ HWTEST_F_L0(GCTest, ArkToolsHintGC)
heap->ChangeGCParams(false);
}
}
HWTEST_F_L0(GCTest, CallbackTask)
{
auto vm = thread->GetEcmaVM();
Heap *heap = const_cast<Heap *>(vm->GetHeap());
auto factory = vm->GetFactory();
{
[[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
for (int i = 0; i < 10; i++) {
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
void *externalPointer = malloc(10);
[[maybe_unused]] JSHandle<JSNativePointer> nativePointer
= factory->NewJSNativePointer(externalPointer,
[](void* pointer, [[maybe_unused]] void* data) {
if (pointer != nullptr) {
free(pointer);
}
},
nullptr, false, 10, Concurrent::YES);
}
}
size_t number = vm->GetConcurrentNativePointerListSize();
EXPECT_TRUE(number > 0);
heap->CollectGarbage(TriggerGCType::OLD_GC);
size_t newNumber = vm->GetConcurrentNativePointerListSize();
EXPECT_TRUE(number > newNumber);
}
} // namespace panda::test