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