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#ifndef ECMASCRIPT_MEM_GC_TRIGGER_H
16#define ECMASCRIPT_MEM_GC_TRIGGER_H
17
18#include <atomic>
19#include <ctime>
20#include <chrono>
21
22#include "libpandabase/macros.h"
23#include "ecmascript/common.h"
24#include "ecmascript/mem/mem_common.h"
25#include "ecmascript/base/gc_ring_buffer.h"
26#include "ecmascript/mem/heap.h"
27#include "ecmascript/napi/include/jsnapi_expo.h"
28
29namespace panda::ecmascript {
30class Heap;
31class SharedHeap;
32class ConcurrentMarker;
33class MemController;
34class SharedMemController;
35class EcmaVM;
36
37class IdleGCTrigger {
38using TRIGGER_IDLE_GC_TYPE = panda::JSNApi::TRIGGER_IDLE_GC_TYPE;
39using Clock = std::chrono::high_resolution_clock;
40public:
41    explicit IdleGCTrigger(Heap *heap, SharedHeap *sHeap, JSThread *thread, bool logEnable = false)
42        : heap_(heap),
43        sHeap_(sHeap),
44        thread_(thread),
45        optionalLogEnabled_(logEnable) {};
46    virtual ~IdleGCTrigger() = default;
47
48    bool IsIdleState() const
49    {
50        return idleState_.load();
51    }
52
53    const char *GetGCTypeName(TRIGGER_IDLE_GC_TYPE gcType) const
54    {
55        switch (gcType) {
56            case TRIGGER_IDLE_GC_TYPE::FULL_GC:
57                return "full gc";
58            case TRIGGER_IDLE_GC_TYPE::SHARED_CONCURRENT_MARK:
59                return "shared concurrent mark";
60            case TRIGGER_IDLE_GC_TYPE::SHARED_FULL_GC:
61                return "shared full gc";
62            case TRIGGER_IDLE_GC_TYPE::LOCAL_CONCURRENT_MARK:
63                return "local concurrent mark";
64            case TRIGGER_IDLE_GC_TYPE::LOCAL_REMARK:
65                return "local remark";
66            default:
67                return "UnknownType";
68        }
69    }
70
71    bool IsPossiblePostGCTask(TRIGGER_IDLE_GC_TYPE gcType) const
72    {
73        uint8_t bit = static_cast<uint8_t>(gcType);
74        return (bit & gcTaskPostedState_) != bit;
75    }
76
77    void SetPostGCTask(TRIGGER_IDLE_GC_TYPE gcType)
78    {
79        uint8_t bit = static_cast<uint8_t>(gcType);
80        gcTaskPostedState_ = (gcTaskPostedState_ | bit);
81    }
82
83    void ClearPostGCTask(TRIGGER_IDLE_GC_TYPE gcType)
84    {
85        uint8_t bit = static_cast<uint8_t>(gcType);
86        gcTaskPostedState_ = (gcTaskPostedState_ & ~bit);
87    }
88
89    void SetTriggerGCTaskCallback(const TriggerGCTaskCallback& callback)
90    {
91        triggerGCTaskCallback_ = callback;
92    }
93
94    void NotifyVsyncIdleStart();
95    void NotifyLooperIdleStart(int64_t timestamp, int idleTime);
96    void NotifyLooperIdleEnd(int64_t timestamp);
97    void TryTriggerHandleMarkFinished();
98    void TryTriggerLocalConcurrentMark();
99    bool TryTriggerIdleLocalOldGC();
100    bool TryTriggerIdleSharedOldGC();
101    bool ReachIdleLocalOldGCThresholds();
102    bool ReachIdleSharedGCThresholds();
103    void TryPostHandleMarkFinished();
104    void TryTriggerIdleGC(TRIGGER_IDLE_GC_TYPE gcType);
105    bool CheckIdleYoungGC() const;
106    template<class T>
107    bool CheckIdleOrHintOldGC(const T *baseHeap) const
108    {
109        size_t heapAliveSizeAfterGC = baseHeap->GetHeapAliveSizeAfterGC();
110        if (heapAliveSizeAfterGC == 0) {
111            return false;
112        }
113        size_t expectHeapSize = std::max(static_cast<size_t>(heapAliveSizeAfterGC * IDLE_SPACE_SIZE_MIN_INC_RATIO),
114            heapAliveSizeAfterGC + IDLE_SPACE_SIZE_MIN_INC_STEP);
115        LOG_ECMA_IF(optionalLogEnabled_, DEBUG) << "IdleGCTrigger: check old GC heapAliveSizeAfterGC:"
116            << heapAliveSizeAfterGC << ";expectHeapSize" << expectHeapSize
117            << "heapObjectSize" << baseHeap->GetHeapObjectSize();
118        return baseHeap->GetHeapObjectSize() >= expectHeapSize;
119    }
120
121    template<class T>
122    bool CheckIdleOrHintFullGC(const T *baseHeap) const
123    {
124        size_t heapAliveSizeAfterGC = baseHeap->GetHeapAliveSizeAfterGC();
125        size_t expectHeapSize = std::max(static_cast<size_t>(heapAliveSizeAfterGC * IDLE_SPACE_SIZE_MIN_INC_RATIO),
126            heapAliveSizeAfterGC + IDLE_SPACE_SIZE_MIN_INC_STEP_FULL);
127        LOG_GC(DEBUG) << "IdleGCTrigger: check full GC heapAliveSizeAfterGC:" << heapAliveSizeAfterGC
128            << ";expectHeapSize:" << expectHeapSize << ";heapObjectSize:" << baseHeap->GetHeapObjectSize();
129        if (baseHeap->GetHeapObjectSize() >= expectHeapSize) {
130            return true;
131        }
132        size_t fragmentSizeAfterGC = baseHeap->GetFragmentSizeAfterGC();
133        size_t heapBasicLoss = baseHeap->GetHeapBasicLoss();
134        if (fragmentSizeAfterGC <= heapBasicLoss) {
135            return false;
136        }
137        size_t fragmentSize = fragmentSizeAfterGC - heapBasicLoss;
138        size_t expectFragmentSize = std::max(static_cast<size_t>((baseHeap->GetCommittedSize() - heapBasicLoss) *
139            IDLE_FRAGMENT_SIZE_RATIO), IDLE_MIN_EXPECT_RECLAIM_SIZE);
140        LOG_GC(DEBUG) << "IdleGCTrigger: check full GC fragmentSizeAfterGC:" << fragmentSizeAfterGC
141            << ";heapBasicLoss:" << heapBasicLoss << ";expectFragmentSize" << expectFragmentSize;
142        return fragmentSize >= expectFragmentSize;
143    }
144
145    template<class T>
146    bool HintGCInLowDegree(const T *baseHeap) const
147    {
148        return CheckIdleOrHintOldGC<T>(baseHeap);
149    }
150
151    template<class T>
152    bool HintGCInMiddleDegree(const T *baseHeap) const
153    {
154        return CheckIdleOrHintOldGC<T>(baseHeap);
155    }
156
157    template<class T>
158    bool HintGCInHighDegree(const T *baseHeap) const
159    {
160        return CheckIdleOrHintFullGC<T>(baseHeap);
161    }
162
163private:
164    void PostIdleGCTask(TRIGGER_IDLE_GC_TYPE gcType);
165
166    Heap *heap_ {nullptr};
167    SharedHeap *sHeap_ {nullptr};
168    JSThread *thread_ {nullptr};
169    bool optionalLogEnabled_ {false};
170
171    std::atomic<bool> idleState_ {false};
172    uint8_t gcTaskPostedState_ {0};
173    TriggerGCTaskCallback triggerGCTaskCallback_ {nullptr};
174};
175
176}
177
178#endif  // ECMASCRIPT_MEM_GC_TRIGGER_H