1/* 2 * Copyright (c) 2021 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 "async_call.h" 17 18#include <algorithm> 19 20#include "global.h" 21#include "js_utils.h" 22 23namespace OHOS { 24namespace MiscServices { 25using namespace std::chrono; 26constexpr size_t ARGC_MAX = 6; 27constexpr int32_t MAX_WAIT_TIME = 500; // ms 28static inline uint64_t GetTimeStamp() 29{ 30 return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); 31} 32AsyncCall::AsyncCall(napi_env env, napi_callback_info info, std::shared_ptr<Context> context, size_t maxParamCount) 33 : env_(env) 34{ 35 context_ = new AsyncContext(); 36 NAPI_ASSERT_RETURN_VOID(env, context_ != nullptr, "context_ != nullptr"); 37 size_t argc = ARGC_MAX; 38 napi_value self = nullptr; 39 napi_value argv[ARGC_MAX] = { nullptr }; 40 NAPI_CALL_RETURN_VOID(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr)); 41 napi_valuetype valueType = napi_undefined; 42 argc = std::min(argc, maxParamCount); 43 if (argc > 0) { 44 napi_typeof(env, argv[argc - 1], &valueType); 45 if (valueType == napi_function) { 46 napi_create_reference(env, argv[argc - 1], 1, &context_->callback); 47 argc = argc - 1; 48 } 49 } 50 NAPI_CALL_RETURN_VOID(env, (*context)(env, argc, argv, self)); 51 context_->ctx = std::move(context); 52 napi_create_reference(env, self, 1, &context_->self); 53} 54 55AsyncCall::~AsyncCall() 56{ 57 if (context_ == nullptr) { 58 return; 59 } 60 61 DeleteContext(env_, context_); 62} 63 64napi_value AsyncCall::Call(napi_env env, Context::ExecAction exec, const std::string &resourceName) 65{ 66 if (context_ == nullptr) { 67 IMSA_HILOGE("context_ is nullptr!"); 68 return nullptr; 69 } 70 if (context_->ctx == nullptr) { 71 IMSA_HILOGE("context_->ctx is nullptr!"); 72 return nullptr; 73 } 74 context_->ctx->exec_ = std::move(exec); 75 napi_value promise = nullptr; 76 if (context_->callback == nullptr) { 77 napi_create_promise(env, &context_->defer, &promise); 78 } else { 79 napi_get_undefined(env, &promise); 80 } 81 napi_async_work work = context_->work; 82 napi_value resource = nullptr; 83 std::string name = "IMF_" + resourceName; 84 napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &resource); 85 napi_create_async_work(env, nullptr, resource, AsyncCall::OnExecute, AsyncCall::OnComplete, context_, &work); 86 context_->work = work; 87 context_ = nullptr; 88 napi_queue_async_work_with_qos(env, work, napi_qos_user_initiated); 89 return promise; 90} 91 92napi_value AsyncCall::Post(napi_env env, Context::ExecAction exec, std::shared_ptr<TaskQueue> queue, const char *func) 93{ 94 if (context_ == nullptr || context_->ctx == nullptr || queue == nullptr) { 95 IMSA_HILOGE("context is nullptr!"); 96 return nullptr; 97 } 98 context_->ctx->exec_ = std::move(exec); 99 napi_value promise = nullptr; 100 if (context_->callback == nullptr) { 101 napi_create_promise(env, &context_->defer, &promise); 102 } else { 103 napi_get_undefined(env, &promise); 104 } 105 napi_async_work work = context_->work; 106 napi_value resource = nullptr; 107 napi_create_string_utf8(env, func, NAPI_AUTO_LENGTH, &resource); 108 napi_create_async_work(env, nullptr, resource, AsyncCall::OnExecuteSeq, AsyncCall::OnComplete, context_, &work); 109 context_->work = work; 110 context_->queue = queue; 111 std::unique_lock<ffrt::mutex> lock(queue->queuesMutex_); 112 queue->taskQueue_.emplace(env, work, func); 113 if (!queue->isRunning) { 114 auto status = napi_queue_async_work_with_qos(env, work, napi_qos_user_initiated); 115 queue->isRunning = status == napi_ok; 116 if (status != napi_ok) { 117 IMSA_HILOGE("async work failed.status:%{public}d, func:%{public}s!", status, func); 118 } 119 } 120 context_ = nullptr; 121 return promise; 122} 123 124napi_value AsyncCall::SyncCall(napi_env env, AsyncCall::Context::ExecAction exec) 125{ 126 if ((context_ == nullptr) || (context_->ctx == nullptr)) { 127 IMSA_HILOGE("context_ or context_->ctx is nullptr!"); 128 return nullptr; 129 } 130 context_->ctx->exec_ = std::move(exec); 131 napi_value promise = nullptr; 132 if (context_->callback == nullptr) { 133 napi_create_promise(env, &context_->defer, &promise); 134 } else { 135 napi_get_undefined(env, &promise); 136 } 137 AsyncCall::OnExecute(env, context_); 138 AsyncCall::OnComplete(env, context_->ctx->status_, context_); 139 return promise; 140} 141 142void AsyncCall::OnExecute(napi_env env, void *data) 143{ 144 AsyncContext *context = reinterpret_cast<AsyncContext *>(data); 145 if (context == nullptr || context->ctx == nullptr) { 146 IMSA_HILOGE("context or context->ctx is nullptr!"); 147 return; 148 } 149 context->ctx->Exec(); 150} 151 152void AsyncCall::OnExecuteSeq(napi_env env, void *data) 153{ 154 OnExecute(env, data); 155 AsyncContext *context = reinterpret_cast<AsyncContext *>(data); 156 if (context == nullptr || context->queue == nullptr) { 157 IMSA_HILOGE("context or context->queue is nullptr!"); 158 return; 159 } 160 auto queue = context->queue; 161 std::unique_lock<ffrt::mutex> lock(queue->queuesMutex_); 162 if (!queue->taskQueue_.empty()) { 163 queue->taskQueue_.pop(); 164 } 165 queue->isRunning = !queue->taskQueue_.empty() && 166 napi_queue_async_work_with_qos(queue->taskQueue_.front().env, 167 queue->taskQueue_.front().work, napi_qos_user_initiated) == napi_ok; 168} 169 170void AsyncCall::OnComplete(napi_env env, napi_status status, void *data) 171{ 172 AsyncContext *context = reinterpret_cast<AsyncContext *>(data); 173 napi_value output = nullptr; 174 if (context == nullptr || context->ctx == nullptr) { 175 IMSA_HILOGE("context or context->ctx is nullptr!"); 176 return; 177 } 178 napi_status runStatus = (*context->ctx)(env, &output); 179 napi_value result[ARG_BUTT] = { 0 }; 180 if (status == napi_ok && runStatus == napi_ok) { 181 napi_get_undefined(env, &result[ARG_ERROR]); 182 if (output != nullptr) { 183 IMSA_HILOGD("output != nullptr!"); 184 result[ARG_DATA] = output; 185 } else { 186 IMSA_HILOGD("output is nullptr!"); 187 napi_get_undefined(env, &result[ARG_DATA]); 188 } 189 } else { 190 IMSA_HILOGE("failed, [status:%{public}d, runStatus:%{public}d, errorCode:%{public}d, errMessage:%{public}s].", 191 status, runStatus, context->ctx->errorCode_, context->ctx->errMessage_.c_str()); 192 result[ARG_ERROR] = JsUtils::ToError(env, context->ctx->errorCode_, context->ctx->errMessage_); 193 napi_get_undefined(env, &result[ARG_DATA]); 194 } 195 if (context->defer != nullptr) { 196 if (status == napi_ok && runStatus == napi_ok) { 197 napi_resolve_deferred(env, context->defer, result[ARG_DATA]); 198 } else { 199 napi_reject_deferred(env, context->defer, result[ARG_ERROR]); 200 } 201 } else { 202 napi_value callback = nullptr; 203 napi_get_reference_value(env, context->callback, &callback); 204 napi_value returnValue; 205 napi_call_function(env, nullptr, callback, ARG_BUTT, result, &returnValue); 206 } 207 DeleteContext(env, context); 208} 209 210void AsyncCall::DeleteContext(napi_env env, AsyncContext *context) 211{ 212 if (env != nullptr) { 213 napi_delete_reference(env, context->callback); 214 napi_delete_reference(env, context->self); 215 napi_delete_async_work(env, context->work); 216 } 217 delete context; 218} 219 220AsyncCall::InnerTask::InnerTask(napi_env env, napi_async_work work, const char *name) 221 : env(env), work(work), name(name), startTime(GetTimeStamp()) 222{ 223} 224 225AsyncCall::InnerTask::~InnerTask() 226{ 227 auto endTime = GetTimeStamp(); 228 if (endTime - startTime > MAX_WAIT_TIME) { 229 IMSA_HILOGW("async work timeout! func:%{public}s, startTime:%{public}" PRIu64 ", endTime:%{public}" PRIu64 230 ", cost:%{public}" PRIu64 "ms", 231 name, startTime, endTime, endTime - startTime); 232 } else { 233 IMSA_HILOGD("async work finished! func:%{public}s, startTime:%{public}" PRIu64 ", endTime:%{public}" PRIu64 234 ", cost:%{public}" PRIu64 "ms", 235 name, startTime, endTime, endTime - startTime); 236 } 237} 238} // namespace MiscServices 239} // namespace OHOS 240