1 /*
2  * Copyright (c) 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 <memory>
17 
18 #include "avsession_trace.h"
19 #include "napi_utils.h"
20 #include "napi_async_callback.h"
21 
22 namespace OHOS::AVSession {
NapiAsyncCallback(napi_env env)23 NapiAsyncCallback::NapiAsyncCallback(napi_env env) : env_(env)
24 {
25     if (env != nullptr) {
26         napi_get_uv_event_loop(env, &loop_);
27         napi_get_uv_event_loop(env, &loopOrder_);
28         int res = sem_init(&semaphore_, 0, 1);
29         SLOGD("loop to set sem with res %{public}d", res);
30     }
31 }
32 
~NapiAsyncCallback()33 NapiAsyncCallback::~NapiAsyncCallback()
34 {
35     SLOGI("no memory leak for queue-callback");
36     env_ = nullptr;
37     sem_destroy(&semaphore_);
38 }
39 
GetEnv() const40 napi_env NapiAsyncCallback::GetEnv() const
41 {
42     return env_;
43 }
44 
AfterWorkCallback(uv_work_t * work,int aStatus)45 void NapiAsyncCallback::AfterWorkCallback(uv_work_t* work, int aStatus)
46 {
47     AVSESSION_TRACE_SYNC_START("NapiAsyncCallback::AfterWorkCallback");
48     std::shared_ptr<DataContext> context(static_cast<DataContext*>(work->data), [work](DataContext* ptr) {
49         delete ptr;
50         delete work;
51     });
52 
53     napi_handle_scope scope = nullptr;
54     napi_open_handle_scope(context->env, &scope);
55 
56     int argc = 0;
57     napi_value argv[ARGC_MAX] = { nullptr };
58     if (context->getter) {
59         argc = ARGC_MAX;
60         context->getter(context->env, argc, argv);
61     }
62 
63     SLOGD("queue uv_after_work_cb");
64     napi_value global {};
65     napi_get_global(context->env, &global);
66     napi_value function {};
67     napi_get_reference_value(context->env, context->method, &function);
68     napi_value result;
69     napi_status status = napi_call_function(context->env, global, function, argc, argv, &result);
70     if (status != napi_ok) {
71         SLOGE("call function failed status=%{public}d.", status);
72     }
73     napi_close_handle_scope(context->env, scope);
74 }
75 
AfterWorkCallbackWithFlag(uv_work_t * work,int aStatus)76 void NapiAsyncCallback::AfterWorkCallbackWithFlag(uv_work_t* work, int aStatus)
77 {
78     AVSESSION_TRACE_SYNC_START("NapiAsyncCallback::AfterWorkCallbackWithFlag");
79     std::shared_ptr<DataContextWithFlag> context(static_cast<DataContextWithFlag*>(work->data),
80         [work](DataContextWithFlag* ptr) {
81         delete ptr;
82         delete work;
83     });
84 
85     napi_handle_scope scope = nullptr;
86     napi_open_handle_scope(context->env, &scope);
87 
88     int argc = 0;
89     napi_value argv[ARGC_MAX] = { nullptr };
90     if (context->getter) {
91         argc = ARGC_MAX;
92         context->getter(context->env, argc, argv);
93     }
94 
95     SLOGD("queue uv_after_work_cb");
96     napi_value global {};
97     napi_get_global(context->env, &global);
98     napi_value function {};
99     SLOGD("callback with flag");
100     if (!*context->isValid) {
101         SLOGE("AfterWorkCallbackWithFlag callback when callback is invalid");
102         napi_close_handle_scope(context->env, scope);
103         return;
104     }
105     SLOGI("callback with flag ref %{public}p, %{public}p", &(context->method), *(&(context->method)));
106     napi_get_reference_value(context->env, context->method, &function);
107     napi_value result;
108     napi_status status = napi_call_function(context->env, global, function, argc, argv, &result);
109     if (status != napi_ok) {
110         SLOGE("call function failed status=%{public}d.", status);
111     }
112     napi_close_handle_scope(context->env, scope);
113 }
114 
AfterWorkCallbackWithFunc(uv_work_t * work,int aStatus)115 void NapiAsyncCallback::AfterWorkCallbackWithFunc(uv_work_t* work, int aStatus)
116 {
117     AVSESSION_TRACE_SYNC_START("NapiAsyncCallback::AfterWorkCallbackWithFunc");
118     std::shared_ptr<DataContextWithFunc> context(static_cast<DataContextWithFunc*>(work->data),
119         [work](DataContextWithFunc* ptr) {
120         delete ptr;
121         delete work;
122     });
123 
124     napi_handle_scope scope = nullptr;
125     napi_open_handle_scope(context->env, &scope);
126 
127     int argc = 0;
128     napi_value argv[ARGC_MAX] = { nullptr };
129     if (context->getter) {
130         argc = ARGC_MAX;
131         context->getter(context->env, argc, argv);
132     }
133 
134     SLOGD("queue uv_after_work_cb");
135     if (!*context->isValid) {
136         SLOGE("AfterWorkCallbackWithFunc failed for context is invalid.");
137         napi_close_handle_scope(context->env, scope);
138         return;
139     }
140     napi_value global {};
141     napi_get_global(context->env, &global);
142     napi_value function {};
143     if (!context->checkCallbackValid()) {
144         SLOGE("Get func reference failed for func has been deleted.");
145         napi_close_handle_scope(context->env, scope);
146         return;
147     }
148     napi_get_reference_value(context->env, context->method, &function);
149     napi_value result;
150     if (!context->checkCallbackValid()) {
151         SLOGE("Call func failed for func has been deleted.");
152         napi_close_handle_scope(context->env, scope);
153         return;
154     }
155     napi_status status = napi_call_function(context->env, global, function, argc, argv, &result);
156     if (status != napi_ok) {
157         SLOGE("call function failed status=%{public}d.", status);
158     }
159     napi_close_handle_scope(context->env, scope);
160 }
161 
Call(napi_ref & method,NapiArgsGetter getter)162 void NapiAsyncCallback::Call(napi_ref& method, NapiArgsGetter getter)
163 {
164     CHECK_RETURN_VOID(loop_ != nullptr, "loop_ is nullptr");
165     CHECK_RETURN_VOID(method != nullptr, "method is nullptr");
166 
167     auto* work = new (std::nothrow) uv_work_t;
168     CHECK_RETURN_VOID(work != nullptr, "no memory for uv_work_t");
169 
170     work->data = new DataContext{env_, method, std::move(getter)};
171     int res = uv_queue_work_with_qos(loop_, work, [](uv_work_t* work) {}, AfterWorkCallback, uv_qos_user_initiated);
172     CHECK_RETURN_VOID(res == 0, "uv queue work failed");
173 }
174 
CallWithFlag(napi_ref & method,std::shared_ptr<bool> isValid,NapiArgsGetter getter)175 void NapiAsyncCallback::CallWithFlag(napi_ref& method, std::shared_ptr<bool> isValid, NapiArgsGetter getter)
176 {
177     CHECK_RETURN_VOID(loop_ != nullptr, "loop_ is nullptr");
178     CHECK_RETURN_VOID(method != nullptr, "method is nullptr");
179 
180     auto* work = new (std::nothrow) uv_work_t;
181     CHECK_RETURN_VOID(work != nullptr, "no memory for uv_work_t");
182 
183     work->data = new DataContextWithFlag { env_, method, isValid, std::move(getter) };
184     int res = uv_queue_work_with_qos(loop_, work, [](uv_work_t* work) {}, AfterWorkCallbackWithFlag,
185         uv_qos_user_initiated);
186     CHECK_RETURN_VOID(res == 0, "uv queue work failed");
187 }
188 
CallWithFunc(napi_ref & method,std::shared_ptr<bool> isValid,const std::function<bool ()> & checkCallbackValid,NapiArgsGetter getter)189 void NapiAsyncCallback::CallWithFunc(napi_ref& method, std::shared_ptr<bool> isValid,
190     const std::function<bool()>& checkCallbackValid, NapiArgsGetter getter)
191 {
192     CHECK_RETURN_VOID(loop_ != nullptr, "loop_ is nullptr");
193     CHECK_RETURN_VOID(method != nullptr, "method is nullptr");
194 
195     auto* work = new (std::nothrow) uv_work_t;
196     CHECK_RETURN_VOID(work != nullptr, "no memory for uv_work_t");
197 
198     work->data = new DataContextWithFunc { env_, method, isValid, std::move(getter), checkCallbackValid };
199     int res = uv_queue_work_with_qos(loop_, work, [](uv_work_t* work) {}, AfterWorkCallbackWithFunc,
200         uv_qos_user_initiated);
201     CHECK_RETURN_VOID(res == 0, "uv queue work failed");
202 }
203 }