* Copyright (c) 2021 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_PARALLEL_EVACUATOR_H
#define ECMASCRIPT_MEM_PARALLEL_EVACUATOR_H
#include <atomic>
#include <memory>
#include "common_components/taskpool/task.h"
#include "ecmascript/js_hclass.h"
#include "ecmascript/mem/heap.h"
#include "ecmascript/mem/object_xray.h"
#include "ecmascript/mem/region.h"
#include "ecmascript/mem/space.h"
#include "ecmascript/mem/tagged_object.h"
#include "ecmascript/mem/tlab_allocator.h"
#include "ecmascript/platform/mutex.h"
namespace panda::ecmascript {
enum RegionEvacuateType {
REGION_NEW_TO_NEW,
REGION_NEW_TO_OLD,
OBJECT_EVACUATE,
};
class ParallelEvacuator {
public:
explicit ParallelEvacuator(Heap *heap);
~ParallelEvacuator() = default;
void Initialize();
void Finalize();
void Evacuate();
void SweepNewToOldRegions();
size_t GetPromotedSize() const
{
return promotedSize_;
}
private:
class UpdateRootVisitor final : public RootVisitor {
public:
explicit UpdateRootVisitor(ParallelEvacuator *evacuator);
~UpdateRootVisitor() = default;
void VisitRoot([[maybe_unused]] Root type, ObjectSlot slot) override;
void VisitRangeRoot([[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) override;
void VisitBaseAndDerivedRoot([[maybe_unused]] Root type, ObjectSlot base, ObjectSlot derived,
uintptr_t baseOldObject) override;
private:
ParallelEvacuator *evacuator_ {nullptr};
};
class SetObjectFieldRSetVisitor final : public BaseObjectVisitor<SetObjectFieldRSetVisitor> {
public:
explicit SetObjectFieldRSetVisitor(ParallelEvacuator *evacuator);
~SetObjectFieldRSetVisitor() = default;
void VisitObjectRangeImpl(BaseObject *root, uintptr_t startAddr, uintptr_t endAddr,
VisitObjectArea area) override;
private:
ParallelEvacuator *evacuator_ {nullptr};
};
template <TriggerGCType gcType, bool needUpdateLocalToShare>
class UpdateNewObjectFieldVisitor final :
public BaseObjectVisitor<UpdateNewObjectFieldVisitor<gcType, needUpdateLocalToShare>> {
public:
explicit UpdateNewObjectFieldVisitor(ParallelEvacuator *evacuator);
~UpdateNewObjectFieldVisitor() = default;
void VisitObjectRangeImpl(BaseObject *root, uintptr_t start, uintptr_t end, VisitObjectArea area) override;
private:
ParallelEvacuator *evacuator_ {nullptr};
};
class EvacuationTask : public common::Task {
public:
EvacuationTask(int32_t id, uint32_t idOrder, ParallelEvacuator *evacuator);
~EvacuationTask() override;
bool Run(uint32_t threadIndex) override;
NO_COPY_SEMANTIC(EvacuationTask);
NO_MOVE_SEMANTIC(EvacuationTask);
private:
uint32_t idOrder_;
ParallelEvacuator *evacuator_;
TlabAllocator *allocator_ {nullptr};
};
class UpdateReferenceTask : public common::Task {
public:
UpdateReferenceTask(int32_t id, ParallelEvacuator *evacuator) : common::Task(id), evacuator_(evacuator) {}
~UpdateReferenceTask() override = default;
bool Run(uint32_t threadIndex) override;
NO_COPY_SEMANTIC(UpdateReferenceTask);
NO_MOVE_SEMANTIC(UpdateReferenceTask);
private:
ParallelEvacuator *evacuator_;
};
class Workload {
public:
Workload(ParallelEvacuator *evacuator, Region *region) : evacuator_(evacuator), region_(region) {};
virtual ~Workload() = default;
virtual bool Process(bool isMain, uint32_t threadIndex) = 0;
inline Region *GetRegion() const
{
return region_;
}
inline ParallelEvacuator *GetEvacuator() const
{
return evacuator_;
}
protected:
ParallelEvacuator *evacuator_;
Region *region_;
};
class AcquireItem {
public:
AcquireItem() = default;
AcquireItem(AcquireItem&& other) noexcept {(void)other;};
bool TryAcquire();
private:
std::atomic<bool> acquire_{false};
};
class WorkloadSet {
public:
inline void Add(std::unique_ptr<Workload> workload);
inline size_t GetWorkloadCount() const;
inline bool HasRemaningWorkload() const;
inline bool FetchSubAndCheckWorkloadCount(size_t finishedCount);
void PrepareWorkloads();
std::optional<size_t> GetNextIndex();
std::unique_ptr<ParallelEvacuator::Workload> TryGetWorkload(size_t index);
void Clear();
private:
using WorkItem = std::pair<AcquireItem, std::unique_ptr<Workload>>;
std::vector<WorkItem> workloads_;
std::vector<size_t> indexList_;
std::atomic<size_t> indexCursor_ = 0;
std::atomic<size_t> remainingWorkloadNum_ = 0;
};
class EvacuateWorkload : public Workload {
public:
EvacuateWorkload(ParallelEvacuator *evacuator, Region *region) : Workload(evacuator, region) {}
~EvacuateWorkload() override = default;
bool Process(bool isMain, uint32_t threadIndex) override;
};
template <bool cmsGC>
class UpdateRSetWorkload : public Workload {
public:
UpdateRSetWorkload(ParallelEvacuator *evacuator, Region *region)
: Workload(evacuator, region) {}
~UpdateRSetWorkload() override = default;
bool Process(bool isMain, uint32_t threadIndex) override;
};
class UpdateNewRegionWorkload : public Workload {
public:
UpdateNewRegionWorkload(ParallelEvacuator *evacuator, Region *region, bool isYoungGC)
: Workload(evacuator, region), isYoungGC_(isYoungGC) {}
~UpdateNewRegionWorkload() override = default;
bool Process(bool isMain, uint32_t threadIndex) override;
private:
bool isYoungGC_;
};
class UpdateAndSweepNewRegionWorkload : public Workload {
public:
UpdateAndSweepNewRegionWorkload(ParallelEvacuator *evacuator, Region *region, bool isYoungGC)
: Workload(evacuator, region), isYoungGC_(isYoungGC) {}
~UpdateAndSweepNewRegionWorkload() override = default;
bool Process(bool isMain, uint32_t threadIndex) override;
private:
bool isYoungGC_;
};
class UpdateNewToOldEvacuationWorkload : public Workload {
public:
UpdateNewToOldEvacuationWorkload(ParallelEvacuator *evacuator, Region *region, bool isYoungGC)
: Workload(evacuator, region), isYoungGC_(isYoungGC) {}
~UpdateNewToOldEvacuationWorkload() override = default;
bool Process(bool isMain, uint32_t threadIndex) override;
private:
bool isYoungGC_;
};
template <typename WorkloadCallback>
void DrainWorkloads(WorkloadSet &workloadSet, WorkloadCallback callback);
std::unordered_set<JSTaggedType> &ArrayTrackInfoSet(uint32_t threadIndex)
{
return arrayTrackInfoSets_[threadIndex];
}
TaggedObject* UpdateAddressAfterEvacation(TaggedObject *oldTrackInfo);
void UpdateTrackInfo();
bool ProcessWorkloads(bool isMain = false, uint32_t threadIndex = 0);
void UpdateWeakReferenceInCmsGC();
void UpdateOldToNewRSetInCmsGC();
void EvacuateSpace();
bool EvacuateSpace(TlabAllocator *allocation, uint32_t threadIndex, uint32_t idOrder, bool isMain = false);
void UpdateRecordWeakReferenceInParallel(uint32_t idOrder);
void UpdateRecordWeakReference(uint32_t threadId);
template <TriggerGCType gcType>
void UpdateRecordWeakLinkedHashMap(uint32_t threadId);
void EvacuateRegion(TlabAllocator *allocator, Region *region, std::unordered_set<JSTaggedType> &trackSet);
inline void SetObjectFieldRSet(TaggedObject *object, JSHClass *cls);
inline void SetObjectRSet(ObjectSlot slot, Region *region);
void ProcessFromSpaceEvacuation();
inline RegionEvacuateType SelectRegionEvacuateType(Region *region);
inline bool TryWholeRegionEvacuate(Region *region, RegionEvacuateType type);
inline void CompensateOvershootSizeIfHighAliveRate(Region* region);
void VerifyValue(TaggedObject *object, ObjectSlot slot);
void VerifyHeapObject(TaggedObject *object);
void UpdateReference();
void UpdateRoot();
template<TriggerGCType gcType, bool cmsGC>
auto GetUpdateWeakReferenceOptVisitor();
template<TriggerGCType gcType, bool cmsgC>
void UpdateWeakReferenceOpt();
template <bool cmsGC>
void UpdateRSet(Region *region);
template<TriggerGCType gcType, bool needUpdateLocalToShare>
void UpdateNewRegionReference(Region *region);
template<TriggerGCType gcType, bool needUpdateLocalToShare>
void UpdateAndSweepNewRegionReference(Region *region);
template<TriggerGCType gcType, bool needUpdateLocalToShare>
void UpdateNewObjectField(TaggedObject *object, JSHClass *cls,
UpdateNewObjectFieldVisitor<gcType, needUpdateLocalToShare> &updateFieldVisito);
template<TriggerGCType gcType>
void UpdateNewToOldEvacuationReference(Region *region, uint32_t threadIndex);
inline bool UpdateForwardedOldToNewObjectSlot(TaggedObject *object, ObjectSlot &slot, bool isWeak);
template <bool cmsGC>
inline bool UpdateOldToNewObjectSlot(ObjectSlot &slot);
inline void UpdateObjectSlot(ObjectSlot &slot);
inline void UpdateWeakObjectSlot(TaggedObject *object, ObjectSlot &slot);
inline void UpdateCrossRegionObjectSlot(ObjectSlot &slot);
template<TriggerGCType gcType, bool needUpdateLocalToShare>
inline void UpdateNewObjectSlot(ObjectSlot &slot);
inline void UpdateObjectSlotValue(JSTaggedValue value, ObjectSlot &slot);
inline int CalculateEvacuationThreadNum();
inline int CalculateUpdateThreadNum();
void WaitFinished();
Heap *heap_;
UpdateRootVisitor updateRootVisitor_;
SetObjectFieldRSetVisitor setObjectFieldRSetVisitor_;
TlabAllocator *allocator_ {nullptr};
uintptr_t waterLine_ = 0;
std::unordered_set<JSTaggedType> arrayTrackInfoSets_[common::MAX_TASKPOOL_THREAD_NUM + 1];
bool hasNewToOldRegions_ {false};
uint32_t evacuateTaskNum_ = 0;
std::atomic_int parallel_ = 0;
Mutex mutex_;
ConditionVariable condition_;
std::atomic<size_t> promotedSize_ = 0;
WorkloadSet evacuateWorkloadSet_;
WorkloadSet updateWorkloadSet_;
template<TriggerGCType gcType>
friend class SlotUpdateRangeVisitor;
};
}
#endif