1 /*
2 * Copyright (C) 2023-2023 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 * Description: supply napi async work realization for interfaces.
15 * Author: zhangjingnan
16 * Create: 2023-4-11
17 */
18
19 #include "napi_async_work.h"
20 #include "napi_castengine_utils.h"
21
22 namespace OHOS {
23 namespace CastEngine {
24 namespace CastEngineClient {
25 DEFINE_CAST_ENGINE_LABEL("Cast-Napi-AsyncWork");
26
~NapiAsyncTask()27 NapiAsyncTask::~NapiAsyncTask()
28 {
29 CLOGD("no memory leak after callback or promise[resolved/rejected]");
30 if (env != nullptr) {
31 if (work != nullptr) {
32 NAPI_CALL_RETURN_VOID(env, napi_delete_async_work(env, work));
33 }
34 if (callbackRef != nullptr) {
35 NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, callbackRef));
36 }
37 env = nullptr;
38 }
39 }
40
GetJSInfo(napi_env envi,napi_callback_info info,NapiCbInfoParser parser,bool sync)41 void NapiAsyncTask::GetJSInfo(napi_env envi, napi_callback_info info, NapiCbInfoParser parser, bool sync)
42 {
43 env = envi;
44 size_t argc = ARGC_MAX;
45 napi_value argv[ARGC_MAX] = {nullptr};
46 napi_value thisVar = nullptr;
47 status = napi_get_cb_info(envi, info, &argc, argv, &thisVar, nullptr);
48 if (status != napi_ok || argc > ARGC_MAX) {
49 CLOGE("napi_get_cb_info failed");
50 return;
51 }
52 status = napi_unwrap(envi, thisVar, &native);
53 if (status != napi_ok) {
54 CLOGE("napi_unwrap failed");
55 return;
56 }
57 if (native == nullptr) {
58 CLOGD("thisVar is null");
59 }
60
61 if (!sync && argc > 0) {
62 // get the last arguments :: <callback>
63 size_t index = argc - 1;
64 napi_valuetype type = napi_undefined;
65 status = napi_typeof(envi, argv[index], &type);
66 if ((status == napi_ok) && (type == napi_function)) {
67 status = napi_create_reference(envi, argv[index], 1, &callbackRef);
68 if (status != napi_ok) {
69 CLOGE("napi_get_cb_info failed");
70 return;
71 }
72 argc = index;
73 CLOGD("async callback, no promise");
74 } else {
75 CLOGD("no callback, async promise");
76 }
77 }
78
79 if (parser) {
80 CLOGD("input parser exists");
81 parser(argc, argv);
82 }
83 }
84
Enqueue(napi_env env,std::shared_ptr<NapiAsyncTask> napiAsyncTask,const std::string & name,NapiAsyncExecute execute,NapiAsyncComplete complete)85 napi_value NapiAsyncWork::Enqueue(napi_env env, std::shared_ptr<NapiAsyncTask> napiAsyncTask, const std::string &name,
86 NapiAsyncExecute execute, NapiAsyncComplete complete)
87 {
88 CLOGI("name=%{public}s", name.c_str());
89 napiAsyncTask->execute = std::move(execute);
90 napiAsyncTask->complete = std::move(complete);
91 napiAsyncTask->taskName = name;
92 napi_value promise = nullptr;
93 if (napiAsyncTask->callbackRef == nullptr) {
94 NAPI_CALL(napiAsyncTask->env, napi_create_promise(napiAsyncTask->env, &napiAsyncTask->deferred, &promise));
95 CLOGD("create deferred promise");
96 } else {
97 NAPI_CALL(napiAsyncTask->env, napi_get_undefined(napiAsyncTask->env, &promise));
98 }
99
100 napi_value resourceName = nullptr;
101 NAPI_CALL(napiAsyncTask->env,
102 napi_create_string_utf8(napiAsyncTask->env, name.c_str(), NAPI_AUTO_LENGTH, &resourceName));
103 napi_create_async_work(
104 napiAsyncTask->env, nullptr, resourceName,
105 [](napi_env env, void *data) {
106 if (data == nullptr) {
107 CLOGE("napi_async_execute_callback nullptr");
108 return;
109 }
110 auto task = reinterpret_cast<NapiAsyncTask *>(data);
111 CLOGD("napi_async_execute_callback status=%{public}d", task->status);
112 if (task->execute && task->status == napi_ok) {
113 task->execute();
114 task->execute = nullptr;
115 }
116 },
117 [](napi_env env, napi_status status, void *data) {
118 if (data == nullptr) {
119 CLOGE("napi_async_complete_callback nullptr");
120 return;
121 }
122 auto task = reinterpret_cast<NapiAsyncTask *>(data);
123 CLOGD("napi_async_complete_callback status=%{public}d, status=%{public}d", status, task->status);
124 if ((status != napi_ok) && (task->status == napi_ok)) {
125 task->status = status;
126 }
127 if ((task->complete) && (status == napi_ok) && (task->status == napi_ok)) {
128 task->complete(task->output);
129 task->complete = nullptr;
130 }
131 GenerateOutput(task);
132 },
133 reinterpret_cast<void *>(napiAsyncTask.get()), &napiAsyncTask->work);
134 NAPI_CALL(napiAsyncTask->env, napi_queue_async_work(napiAsyncTask->env, napiAsyncTask->work));
135 napiAsyncTask->hold = napiAsyncTask; // save crossing-thread ctxt.
136 return promise;
137 }
138
GenerateOutput(NapiAsyncTask * napiAsyncTask)139 void NapiAsyncWork::GenerateOutput(NapiAsyncTask *napiAsyncTask)
140 {
141 napi_value result[RESULT_ALL] = {nullptr};
142 if (napiAsyncTask->status == napi_ok) {
143 NAPI_CALL_RETURN_VOID(napiAsyncTask->env, napi_get_undefined(napiAsyncTask->env, &result[RESULT_ERROR]));
144 if (napiAsyncTask->output == nullptr) {
145 NAPI_CALL_RETURN_VOID(napiAsyncTask->env, napi_get_undefined(napiAsyncTask->env, &napiAsyncTask->output));
146 }
147 result[RESULT_DATA] = napiAsyncTask->output;
148 } else {
149 napi_value message = nullptr;
150 napi_value code = nullptr;
151 NAPI_CALL_RETURN_VOID(napiAsyncTask->env,
152 napi_create_string_utf8(napiAsyncTask->env, napiAsyncTask->errMessage.c_str(), NAPI_AUTO_LENGTH, &message));
153 NAPI_CALL_RETURN_VOID(napiAsyncTask->env,
154 napi_create_error(napiAsyncTask->env, nullptr, message, &result[RESULT_ERROR]));
155 NAPI_CALL_RETURN_VOID(napiAsyncTask->env, napi_create_int32(napiAsyncTask->env, napiAsyncTask->errCode, &code));
156 NAPI_CALL_RETURN_VOID(napiAsyncTask->env,
157 napi_set_named_property(napiAsyncTask->env, result[RESULT_ERROR], "code", code));
158 NAPI_CALL_RETURN_VOID(napiAsyncTask->env, napi_get_undefined(napiAsyncTask->env, &result[RESULT_DATA]));
159 }
160
161 if (napiAsyncTask->deferred != nullptr) {
162 if (napiAsyncTask->status == napi_ok) {
163 CLOGD("deferred promise resolved");
164 NAPI_CALL_RETURN_VOID(napiAsyncTask->env,
165 napi_resolve_deferred(napiAsyncTask->env, napiAsyncTask->deferred, result[RESULT_DATA]));
166 } else {
167 CLOGD("deferred promise rejected");
168 NAPI_CALL_RETURN_VOID(napiAsyncTask->env,
169 napi_reject_deferred(napiAsyncTask->env, napiAsyncTask->deferred, result[RESULT_ERROR]));
170 }
171 } else {
172 CallJSFunc(napiAsyncTask->env, napiAsyncTask->callbackRef, RESULT_ALL, result);
173 }
174 napiAsyncTask->hold.reset(); // release napiAsyncTask.
175 }
176 } // namespace CastEngineClient
177 } // namespace CastEngine
178 } // namespace OHOS