* 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 ECMASCRIPT_MEM_RSET_WORKLIST_HANDLER_INL_H
#define ECMASCRIPT_MEM_RSET_WORKLIST_HANDLER_INL_H
#include "ecmascript/mem/rset_worklist_handler.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/mem/heap.h"
namespace panda::ecmascript {
inline RSetWorkListHandler::RSetWorkListHandler(Heap *heap, JSThread *thread) : heap_(heap), ownerThread_(thread)
{
CollectRSetItemsInHeap(heap);
}
template<class Visitor>
inline void RSetItem::Process([[maybe_unused]] const Visitor &visitor)
{
rSet_->IterateAllMarkedBits(ToUintPtr(region_), visitor);
}
inline void RSetItem::MergeBack()
{
region_->MergeLocalToShareRSetForCM(rSet_);
}
inline void RSetWorkListHandler::EnumerateRegions(const Heap *heap)
{
heap->EnumerateRegions([this](Region *region) {
RememberedSet *rset = region->CollectLocalToShareRSet();
if (rset != nullptr) {
items_.emplace_back(region, rset);
}
});
ASSERT(!heap->GetJSThread()->IsConcurrentCopying());
}
inline void RSetWorkListHandler::CollectRSetItemsInHeap(const Heap *heap)
{
ASSERT(items_.empty());
ASSERT(items_.capacity() == 0);
items_.reserve(heap->GetRegionCount());
EnumerateRegions(heap);
ASSERT(items_.size() <= heap->GetRegionCount());
Initialize();
}
ARK_INLINE void RSetWorkListHandler::Initialize()
{
ASSERT(!initialized_);
ASSERT(nextItemIndex_ < 0);
ASSERT(remainItems_ == 0);
initialized_ = true;
int num = static_cast<int>(items_.size());
nextItemIndex_.store(num - 1, std::memory_order_relaxed);
remainItems_ = num;
}
template<class Visitor>
ARK_INLINE bool RSetWorkListHandler::ProcessNext(const Visitor &visitor)
{
int idx = nextItemIndex_.fetch_sub(1, std::memory_order_relaxed);
if (idx < 0) {
return false;
}
items_[idx].Process(visitor);
return true;
}
template<class Visitor>
inline void RSetWorkListHandler::ProcessAllVisitor(const Visitor &visitor, int done)
{
while (ProcessNext(visitor)) {
++done;
}
if (done > 0) {
LockHolder lock(mutex_);
remainItems_ -= done;
if (remainItems_ == 0) {
cv_.SignalAll();
}
}
}
template<class Visitor>
inline void RSetWorkListHandler::ProcessAll(const Visitor &visitor)
{
int done = 0;
ProcessAllVisitor(visitor, done);
}
inline void RSetWorkListHandler::WaitFinishedThenMergeBack()
{
ASSERT(JSThread::GetCurrent() == heap_->GetEcmaVM()->GetJSThread());
ASSERT(JSThread::GetCurrent()->IsInRunningState());
ASSERT(initialized_);
LockHolder lock(mutex_);
while (remainItems_ != 0) {
cv_.Wait(&mutex_);
}
ASSERT(remainItems_ == 0);
[[maybe_unused]] bool res = MergeBack();
ASSERT(res);
}
inline bool RSetWorkListHandler::TryMergeBack()
{
return reinterpret_cast<std::atomic<bool>*>(&initialized_)->exchange(false, std::memory_order_relaxed) == true;
}
inline void RSetWorkListHandler::MergeBackForAllItem()
{
ASSERT(nextItemIndex_ < 0);
ASSERT(remainItems_ == 0);
for (RSetItem &item : items_) {
item.MergeBack();
}
}
inline bool RSetWorkListHandler::MergeBack()
{
if (!TryMergeBack()) {
ASSERT(JSThread::GetCurrent()->IsDaemonThread());
return false;
}
MergeBackForAllItem();
heap_->SetRSetWorkListHandler(nullptr);
return true;
}
inline Heap *RSetWorkListHandler::GetHeap()
{
return heap_;
}
}
#endif