• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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