1 /*
2 * Copyright (c) 2025 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
16 #include "core/components_ng/base/ui_node_gc.h"
17
18 #include "base/utils/time_util.h"
19 #include "core/components_ng/base/ui_node.h"
20 #include "core/pipeline/pipeline_base.h"
21
22 namespace OHOS::Ace::NG {
23 namespace {
24 enum {
25 PRIORITYTYPE_WATERLINE_LOW = 50,
26 PRIORITYTYPE_WATERLINE_HIGH = 100,
27 PRIORITYTYPE_WATERLINE_IMMEDIATE = 500,
28 PRIORITYTYPE_WATERLINE_VIP,
29 };
30 constexpr size_t PER_BUCKET_MAX_SIZE = 10;
31 constexpr size_t BUCKET_MAX_SIZE = 1000;
32 thread_local int64_t deadline_ = -1;
33 thread_local std::queue<std::vector<OHOS::Ace::NG::UINode*>> nodeRawBucket_;
34 } // namespace
35
IsTooLate(int64_t deadline)36 bool UiNodeGc::IsTooLate(int64_t deadline)
37 {
38 if (deadline == -1) {
39 return false;
40 }
41
42 return GetSysTimestamp() > deadline;
43 }
44
PostTask(const RefPtr<TaskExecutor> & taskExecutor,const TaskExecutor::Task & task,const std::string & name,PriorityType priorityType)45 bool UiNodeGc::PostTask(const RefPtr<TaskExecutor>& taskExecutor, const TaskExecutor::Task& task,
46 const std::string& name, PriorityType priorityType)
47 {
48 if (taskExecutor) {
49 taskExecutor->PostTask(task, TaskExecutor::TaskType::UI, name, priorityType);
50 return true;
51 }
52 LOGW("UiNodeGc::PostTask failed");
53 task();
54 return false;
55 }
56
ReleaseInner(OHOS::Ace::NG::UINode * rawPtr)57 void UiNodeGc::ReleaseInner(OHOS::Ace::NG::UINode* rawPtr)
58 {
59 if (nodeRawBucket_.empty()) {
60 nodeRawBucket_.push({ rawPtr });
61 return;
62 }
63 auto& bucket = nodeRawBucket_.back();
64 if (bucket.size() >= PER_BUCKET_MAX_SIZE) {
65 nodeRawBucket_.push({ rawPtr });
66 return;
67 }
68 bucket.push_back(rawPtr);
69 }
70
ReleaseNodeRawBucket()71 void UiNodeGc::ReleaseNodeRawBucket()
72 {
73 if (nodeRawBucket_.empty()) {
74 return;
75 }
76 std::vector<OHOS::Ace::NG::UINode*> toDele = std::move(nodeRawBucket_.front());
77 nodeRawBucket_.pop();
78 ACE_SCOPED_TRACE_COMMERCIAL(
79 "ReleaseNodeRawBucket delete %zu, remain node buckets %zu", toDele.size(), nodeRawBucket_.size());
80 for (auto ptr : toDele) {
81 if (ptr) {
82 delete ptr;
83 }
84 }
85 }
86
JudgeGCLevel(uint32_t remainBucketSize,int64_t deadline)87 PriorityType UiNodeGc::JudgeGCLevel(uint32_t remainBucketSize, int64_t deadline)
88 {
89 if (deadline == -1) {
90 // we don't know next frame idle
91 return PriorityType::IDLE;
92 }
93
94 if (remainBucketSize < PRIORITYTYPE_WATERLINE_LOW) {
95 return PriorityType::LOW;
96 } else if (remainBucketSize < PRIORITYTYPE_WATERLINE_HIGH) {
97 return PriorityType::HIGH;
98 } else if (remainBucketSize < PRIORITYTYPE_WATERLINE_IMMEDIATE) {
99 return PriorityType::IMMEDIATE;
100 }
101 return PriorityType::VIP;
102 }
103
PostReleaseNodeRawMemoryTask(const RefPtr<TaskExecutor> & taskExecutor)104 void UiNodeGc::PostReleaseNodeRawMemoryTask(const RefPtr<TaskExecutor>& taskExecutor)
105 {
106 if (!taskExecutor) {
107 LOGE("UiNodeGc::PostReleaseNodeRawMemoryTask taskExecutor is nullptr");
108 return;
109 }
110 ReleaseNodeRawMemory(-1, taskExecutor);
111 }
112
ReleaseNodeRawMemoryInner(const RefPtr<TaskExecutor> & taskExecutor)113 void UiNodeGc::ReleaseNodeRawMemoryInner(const RefPtr<TaskExecutor>& taskExecutor)
114 {
115 if (nodeRawBucket_.empty()) {
116 return;
117 }
118 auto remainBucketSize = nodeRawBucket_.size();
119 auto nodeGCLevel = JudgeGCLevel(remainBucketSize, deadline_);
120 auto task = [nodeGCLevel, remainBucketSize, taskExecutor = taskExecutor]() {
121 if (IsTooLate(deadline_) && nodeGCLevel != PriorityType::VIP) {
122 return;
123 }
124 ReleaseNodeRawBucket();
125 ReleaseNodeRawMemoryInner(taskExecutor);
126 };
127 PostTask(taskExecutor, task, "ReleaseNodeRawMemoryTask", nodeGCLevel);
128 }
129
ReleaseNodeRawMemory(int64_t deadline,const RefPtr<TaskExecutor> & taskExecutor)130 void UiNodeGc::ReleaseNodeRawMemory(int64_t deadline, const RefPtr<TaskExecutor>& taskExecutor)
131 {
132 deadline_ = deadline;
133 UiNodeGc::ReleaseNodeRawMemoryInner(taskExecutor);
134 }
135
OnReleaseFunc(OHOS::Ace::NG::UINode * rawPtr)136 void UiNodeGc::OnReleaseFunc(OHOS::Ace::NG::UINode* rawPtr)
137 {
138 if (!rawPtr) {
139 LOGE("UiNodeGc::OnReleaseFunc rawPtr is nullptr");
140 return;
141 }
142
143 if (nodeRawBucket_.size() > BUCKET_MAX_SIZE) {
144 LOGW("UiNodeGc::OnReleaseFunc nodeRawBucket size is %{public}zu", nodeRawBucket_.size());
145 delete rawPtr;
146 return;
147 }
148
149 rawPtr->OnDelete();
150 ReleaseInner(rawPtr);
151 }
152
MockGetNodeRawBucket()153 std::queue<std::vector<OHOS::Ace::NG::UINode*>>& UiNodeGc::MockGetNodeRawBucket()
154 {
155 return nodeRawBucket_;
156 }
157 } // namespace OHOS::Ace::NG
158