1 /*
2 * Copyright (c) 2021-2022 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 "native_async_work.h"
17
18 #ifdef ENABLE_HITRACE
19 #include "hitrace_meter.h"
20 #include "parameter.h"
21 #endif
22 #ifdef ENABLE_CONTAINER_SCOPE
23 #include "core/common/container_scope.h"
24 #endif
25
26 #include <cinttypes>
27 #include "native_api_internal.h"
28
29 #ifdef ENABLE_CONTAINER_SCOPE
30 using OHOS::Ace::ContainerScope;
31 #endif
32
33 #ifdef ENABLE_HITRACE
34 std::atomic<bool> g_napiTraceIdEnabled(false);
35 std::atomic<bool> g_ParamUpdated(false);
36 constexpr size_t TRACE_BUFFER_SIZE = 120;
37 constexpr size_t TRACEID_PARAM_SIZE = 10;
38 const std::string TRACE_POINT_QUEUE = "napi::NativeAsyncWork::Queue";
39 const std::string TRACE_POINT_QUEUE_WITH_QOS = "napi::NativeAsyncWork::QueueWithQos";
40 const std::string TRACE_POINT_ASYNCWORKCALLBACK = "napi::NativeAsyncWork::AsyncWorkCallback";
41 using namespace OHOS::HiviewDFX;
42 #endif
43
NativeAsyncWork(NativeEngine * engine,NativeAsyncExecuteCallback execute,NativeAsyncCompleteCallback complete,const std::string & asyncResourceName,void * data)44 NativeAsyncWork::NativeAsyncWork(NativeEngine* engine,
45 NativeAsyncExecuteCallback execute,
46 NativeAsyncCompleteCallback complete,
47 const std::string &asyncResourceName,
48 void* data)
49 : work_({ 0 }), engine_(engine), engineId_(engine->GetId()), execute_(execute), complete_(complete), data_(data)
50 {
51 work_.data = this;
52 (void)asyncResourceName;
53 #ifdef ENABLE_HITRACE
54 if (!g_ParamUpdated.load()) {
55 char napiTraceIdEnabled[TRACEID_PARAM_SIZE] = {0};
56 int ret = GetParameter("persist.hiviewdfx.napitraceid.enabled", "false",
57 napiTraceIdEnabled, sizeof(napiTraceIdEnabled));
58 if (ret > 0 && strcmp(napiTraceIdEnabled, "true") == 0) {
59 g_napiTraceIdEnabled.store(true);
60 }
61 g_ParamUpdated.store(true);
62 }
63 bool createdTraceId = false;
64
65 HiTraceId thisId = HiTraceChain::GetId();
66 if (g_napiTraceIdEnabled.load() && (!thisId.IsValid())) {
67 thisId = HiTraceChain::Begin("New NativeAsyncWork", 0);
68 createdTraceId = true;
69 }
70 if (thisId.IsValid()) {
71 taskTraceId_ = HiTraceChain::CreateSpan();
72 }
73 char traceStr[TRACE_BUFFER_SIZE] = {0};
74 if (sprintf_s(traceStr, sizeof(traceStr),
75 "name:%s#%" PRIuPTR ", traceid:0x%x",
76 asyncResourceName.c_str(),
77 reinterpret_cast<uintptr_t>(this),
78 taskTraceId_.GetChainId()) < 0) {
79 HILOG_ERROR("Get traceStr fail");
80 }
81 traceDescription_ = traceStr;
82 if (createdTraceId) {
83 OHOS::HiviewDFX::HiTraceChain::ClearId();
84 }
85 #endif
86 #ifdef ENABLE_CONTAINER_SCOPE
87 containerScopeId_ = ContainerScope::CurrentId();
88 #endif
89 }
90
91 NativeAsyncWork::~NativeAsyncWork() = default;
92
Queue(NativeEngine * engine)93 bool NativeAsyncWork::Queue(NativeEngine* engine)
94 {
95 VALID_ENGINE_CHECK(engine, engine_, engineId_);
96
97 uv_loop_t* loop = engine_->GetUVLoop();
98 if (loop == nullptr) {
99 HILOG_ERROR("Get loop failed");
100 return false;
101 }
102 engine_->IncreaseWaitingRequestCounter();
103 #ifdef ENABLE_HITRACE
104 StartTrace(HITRACE_TAG_ACE, "Native async work queue, " + this->GetTraceDescription());
105 HiTraceId taskId = taskTraceId_;
106 HiTraceChain::Tracepoint(HITRACE_TP_CS, taskId, "%s", TRACE_POINT_QUEUE.c_str());
107 #endif
108 int status = uv_queue_work(loop, &work_, AsyncWorkCallback, AsyncAfterWorkCallback);
109 #ifdef ENABLE_HITRACE
110 HiTraceChain::Tracepoint(HITRACE_TP_CR, taskId, "%s", TRACE_POINT_QUEUE.c_str());
111 FinishTrace(HITRACE_TAG_ACE);
112 #endif
113 if (status != 0) {
114 HILOG_ERROR("uv_queue_work failed");
115 engine_->DecreaseWaitingRequestCounter();
116 return false;
117 }
118 HILOG_DEBUG("uv_queue_work succeed");
119 return true;
120 }
121
QueueWithQos(NativeEngine * engine,napi_qos_t qos)122 bool NativeAsyncWork::QueueWithQos(NativeEngine* engine, napi_qos_t qos)
123 {
124 VALID_ENGINE_CHECK(engine, engine_, engineId_);
125
126 uv_loop_t* loop = engine_->GetUVLoop();
127 if (loop == nullptr) {
128 HILOG_ERROR("Get loop failed");
129 return false;
130 }
131 engine_->IncreaseWaitingRequestCounter();
132 #ifdef ENABLE_HITRACE
133 StartTrace(HITRACE_TAG_ACE, "Native async work queueWithQos, " + this->GetTraceDescription());
134 HiTraceId taskId = taskTraceId_;
135 HiTraceChain::Tracepoint(HITRACE_TP_CS, taskId, "%s", TRACE_POINT_QUEUE_WITH_QOS.c_str());
136 #endif
137 int status = uv_queue_work_with_qos(loop, &work_, AsyncWorkCallback, AsyncAfterWorkCallback, uv_qos_t(qos));
138 #ifdef ENABLE_HITRACE
139 HiTraceChain::Tracepoint(HITRACE_TP_CR, taskId, "%s", TRACE_POINT_QUEUE_WITH_QOS.c_str());
140 FinishTrace(HITRACE_TAG_ACE);
141 #endif
142 if (status != 0) {
143 HILOG_ERROR("uv_queue_work_with_qos failed");
144 engine_->DecreaseWaitingRequestCounter();
145 return false;
146 }
147 HILOG_DEBUG("uv_queue_work_with_qos succeed");
148 return true;
149 }
150
Cancel(NativeEngine * engine)151 bool NativeAsyncWork::Cancel(NativeEngine* engine)
152 {
153 VALID_ENGINE_CHECK(engine, engine_, engineId_);
154
155 int status = uv_cancel((uv_req_t*)&work_);
156 if (status != 0) {
157 HILOG_ERROR("uv_cancel failed");
158 return false;
159 }
160 return true;
161 }
162
AsyncWorkCallback(uv_work_t * req)163 void NativeAsyncWork::AsyncWorkCallback(uv_work_t* req)
164 {
165 if (req == nullptr) {
166 HILOG_ERROR("req is nullptr");
167 return;
168 }
169
170 auto that = reinterpret_cast<NativeAsyncWork*>(req->data);
171 HILOG_DEBUG("NativeAsyncWork::AsyncWorkCallback start to execute.");
172
173 #ifdef ENABLE_HITRACE
174 StartTrace(HITRACE_TAG_ACE, "Native async work execute callback, " + that->GetTraceDescription());
175 if (that->taskTraceId_.IsValid()) {
176 HiTraceId currentId = HiTraceChain::SaveAndSet(that->taskTraceId_);
177 HiTraceChain::Tracepoint(HITRACE_TP_SR, that->taskTraceId_, "%s", TRACE_POINT_ASYNCWORKCALLBACK.c_str());
178 that->execute_(that->engine_, that->data_);
179 FinishTrace(HITRACE_TAG_ACE);
180 HiTraceChain::Tracepoint(HITRACE_TP_SS, that->taskTraceId_, "%s", TRACE_POINT_ASYNCWORKCALLBACK.c_str());
181 HiTraceChain::Restore(currentId);
182 return;
183 }
184 #endif
185 that->execute_(that->engine_, that->data_);
186 #ifdef ENABLE_HITRACE
187 FinishTrace(HITRACE_TAG_ACE);
188 #endif
189 }
190
AsyncAfterWorkCallback(uv_work_t * req,int status)191 void NativeAsyncWork::AsyncAfterWorkCallback(uv_work_t* req, int status)
192 {
193 if (req == nullptr) {
194 HILOG_ERROR("req is nullptr");
195 return;
196 }
197
198 auto that = reinterpret_cast<NativeAsyncWork*>(req->data);
199 auto engine = that->engine_;
200 engine->DecreaseWaitingRequestCounter();
201 auto vm = engine->GetEcmaVm();
202 panda::LocalScope scope(vm);
203 napi_status nstatus = napi_generic_failure;
204 switch (status) {
205 case 0:
206 nstatus = napi_ok;
207 break;
208 case (int)UV_EINVAL:
209 nstatus = napi_invalid_arg;
210 break;
211 case (int)UV_ECANCELED:
212 nstatus = napi_cancelled;
213 break;
214 default:
215 nstatus = napi_generic_failure;
216 }
217 #ifdef ENABLE_CONTAINER_SCOPE
218 ContainerScope containerScope(that->containerScopeId_);
219 #endif
220
221 TryCatch tryCatch(reinterpret_cast<napi_env>(engine));
222 HILOG_DEBUG("NativeAsyncWork::AsyncAfterWorkCallback start to execute.");
223 #ifdef ENABLE_HITRACE
224 StartTrace(HITRACE_TAG_ACE, "Native async work complete callback, " + that->GetTraceDescription());
225 bool isValidTraceId = that->taskTraceId_.IsValid();
226 if (isValidTraceId) {
227 OHOS::HiviewDFX::HiTraceChain::SaveAndSet(that->taskTraceId_);
228 }
229 #endif
230
231 // Don't use that after complete
232 that->complete_(engine, nstatus, that->data_);
233 if (tryCatch.HasCaught()) {
234 engine->HandleUncaughtException();
235 }
236
237 #ifdef ENABLE_HITRACE
238 FinishTrace(HITRACE_TAG_ACE);
239 if (isValidTraceId) {
240 OHOS::HiviewDFX::HiTraceChain::ClearId();
241 }
242 #endif
243 }
244
GetTraceDescription()245 std::string NativeAsyncWork::GetTraceDescription()
246 {
247 return traceDescription_;
248 }
249