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 36using OHOS::Ace::ContainerScope; 37#endif 38 39#ifdef ENABLE_HITRACE 40bool g_napiTraceIdEnabled = false; 41bool g_ParamUpdated = false; 42constexpr size_t TRACE_BUFFER_SIZE = 120; 43constexpr size_t TRACEID_PARAM_SIZE = 10; 44const std::string TRACE_POINT_QUEUE = "napi::NativeAsyncWork::Queue"; 45const std::string TRACE_POINT_QUEUE_WITH_QOS = "napi::NativeAsyncWork::QueueWithQos"; 46const std::string TRACE_POINT_ASYNCWORKCALLBACK = "napi::NativeAsyncWork::AsyncWorkCallback"; 47using namespace OHOS::HiviewDFX; 48#endif 49 50NativeAsyncWork::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 94NativeAsyncWork::~NativeAsyncWork() = default; 95 96bool 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 130bool 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 164bool 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 181void 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 209void 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 263std::string NativeAsyncWork::GetTraceDescription() 264{ 265 return traceDescription_; 266} 267