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