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 
29 namespace panda::ecmascript {
30 class Heap;
31 class SharedHeap;
32 class ConcurrentMarker;
33 class MemController;
34 class SharedMemController;
35 class EcmaVM;
36 
37 class IdleGCTrigger {
38 using TRIGGER_IDLE_GC_TYPE = panda::JSNApi::TRIGGER_IDLE_GC_TYPE;
39 using Clock = std::chrono::high_resolution_clock;
40 public:
IdleGCTrigger(Heap *heap, SharedHeap *sHeap, JSThread *thread, bool logEnable = false)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 
IsIdleState() const48     bool IsIdleState() const
49     {
50         return idleState_.load();
51     }
52 
GetGCTypeName(TRIGGER_IDLE_GC_TYPE gcType) const53     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 
IsPossiblePostGCTask(TRIGGER_IDLE_GC_TYPE gcType) const71     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 
SetPostGCTask(TRIGGER_IDLE_GC_TYPE gcType)77     void SetPostGCTask(TRIGGER_IDLE_GC_TYPE gcType)
78     {
79         uint8_t bit = static_cast<uint8_t>(gcType);
80         gcTaskPostedState_ = (gcTaskPostedState_ | bit);
81     }
82 
ClearPostGCTask(TRIGGER_IDLE_GC_TYPE gcType)83     void ClearPostGCTask(TRIGGER_IDLE_GC_TYPE gcType)
84     {
85         uint8_t bit = static_cast<uint8_t>(gcType);
86         gcTaskPostedState_ = (gcTaskPostedState_ & ~bit);
87     }
88 
SetTriggerGCTaskCallback(const TriggerGCTaskCallback& callback)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>
CheckIdleOrHintOldGC(const T *baseHeap) const107     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>
CheckIdleOrHintFullGC(const T *baseHeap) const122     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>
HintGCInLowDegree(const T *baseHeap) const146     bool HintGCInLowDegree(const T *baseHeap) const
147     {
148         return CheckIdleOrHintOldGC<T>(baseHeap);
149     }
150 
151     template<class T>
HintGCInMiddleDegree(const T *baseHeap) const152     bool HintGCInMiddleDegree(const T *baseHeap) const
153     {
154         return CheckIdleOrHintOldGC<T>(baseHeap);
155     }
156 
157     template<class T>
HintGCInHighDegree(const T *baseHeap) const158     bool HintGCInHighDegree(const T *baseHeap) const
159     {
160         return CheckIdleOrHintFullGC<T>(baseHeap);
161     }
162 
163 private:
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