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_engine_interface.h"
17
18 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(IOS_PLATFORM) && !defined(LINUX_PLATFORM)
19 #include <sys/epoll.h>
20 #endif
21
22 #ifdef IOS_PLATFORM
23 #include <sys/event.h>
24 #endif
25
26 #include <uv.h>
27
28 #include "utils/log.h"
29
30 constexpr size_t NAME_BUFFER_SIZE = 64;
31
32 namespace {
33 const char* g_errorMessages[] = {
34 nullptr,
35 "Invalid parameter",
36 "Need object",
37 "Need string",
38 "Need string or symbol",
39 "Need function",
40 "Need number",
41 "Need boolean",
42 "Need array",
43 "Generic failure",
44 "An exception is blocking",
45 "Asynchronous work cancelled",
46 "Escape called twice",
47 "Handle scope mismatch",
48 "Callback scope mismatch",
49 "Asynchronous work queue is full",
50 "Asynchronous work handle is closing",
51 "Need bigint",
52 "Need date",
53 "Need arraybuffer",
54 "Need detachable arraybuffer",
55 };
56 } // namespace
57
NativeEngineInterface(NativeEngine * engine,void * jsEngineInterface)58 NativeEngineInterface::NativeEngineInterface(NativeEngine* engine, void* jsEngineInterface)
59 : jsEngineInterface_(jsEngineInterface), rootNativeEngine__(engine)
60 {
61 }
62
Init()63 void NativeEngineInterface::Init()
64 {
65 HILOG_INFO("NativeEngineInterface::Init");
66 moduleManager_ = NativeModuleManager::GetInstance();
67 referenceManager_ = new NativeReferenceManager();
68 scopeManager_ = new NativeScopeManager();
69 callbackScopeManager_ = new NativeCallbackScopeManager();
70 loop_ = uv_loop_new();
71 if (loop_ == nullptr) {
72 return;
73 }
74 tid_ = pthread_self();
75 uv_async_init(loop_, &uvAsync_, nullptr);
76 uv_sem_init(&uvSem_, 0);
77 }
78
~NativeEngineInterface()79 NativeEngineInterface::~NativeEngineInterface()
80 {
81 HILOG_INFO("NativeEngineInterface::~NativeEngineInterface");
82 if (cleanEnv_ != nullptr) {
83 cleanEnv_();
84 }
85 }
86
Deinit()87 void NativeEngineInterface::Deinit()
88 {
89 HILOG_INFO("NativeEngineInterface::Deinit");
90 uv_sem_destroy(&uvSem_);
91 uv_close((uv_handle_t*)&uvAsync_, nullptr);
92 uv_run(loop_, UV_RUN_ONCE);
93 if (referenceManager_ != nullptr) {
94 delete referenceManager_;
95 referenceManager_ = nullptr;
96 }
97 if (scopeManager_ != nullptr) {
98 delete scopeManager_;
99 scopeManager_ = nullptr;
100 }
101
102 SetStopping(true);
103 uv_loop_delete(loop_);
104 loop_ = nullptr;
105 }
106
GetScopeManager()107 NativeScopeManager* NativeEngineInterface::GetScopeManager()
108 {
109 return scopeManager_;
110 }
111
GetReferenceManager()112 NativeReferenceManager* NativeEngineInterface::GetReferenceManager()
113 {
114 return referenceManager_;
115 }
116
GetModuleManager()117 NativeModuleManager* NativeEngineInterface::GetModuleManager()
118 {
119 return moduleManager_;
120 }
121
GetCallbackScopeManager()122 NativeCallbackScopeManager* NativeEngineInterface::GetCallbackScopeManager()
123 {
124 return callbackScopeManager_;
125 }
126
GetUVLoop() const127 uv_loop_t* NativeEngineInterface::GetUVLoop() const
128 {
129 return loop_;
130 }
131
GetTid() const132 pthread_t NativeEngineInterface::GetTid() const
133 {
134 return tid_;
135 }
136
ReinitUVLoop()137 bool NativeEngineInterface::ReinitUVLoop()
138 {
139 if (loop_ != nullptr) {
140 uv_sem_destroy(&uvSem_);
141 uv_close((uv_handle_t*)&uvAsync_, nullptr);
142 uv_run(loop_, UV_RUN_ONCE);
143 uv_loop_delete(loop_);
144 }
145
146 loop_ = uv_loop_new();
147 if (loop_ == nullptr) {
148 return false;
149 }
150 tid_ = pthread_self();
151 uv_async_init(loop_, &uvAsync_, nullptr);
152 uv_sem_init(&uvSem_, 0);
153 return true;
154 }
155
Loop(LoopMode mode,bool needSync)156 void NativeEngineInterface::Loop(LoopMode mode, bool needSync)
157 {
158 bool more = true;
159 switch (mode) {
160 case LoopMode::LOOP_DEFAULT:
161 more = uv_run(loop_, UV_RUN_DEFAULT);
162 break;
163 case LoopMode::LOOP_ONCE:
164 more = uv_run(loop_, UV_RUN_ONCE);
165 break;
166 case LoopMode::LOOP_NOWAIT:
167 more = uv_run(loop_, UV_RUN_NOWAIT);
168 break;
169 default:
170 return;
171 }
172 if (more == false) {
173 uv_loop_alive(loop_);
174 }
175
176 if (needSync) {
177 uv_sem_post(&uvSem_);
178 }
179 }
180
CreateAsyncWork(NativeEngine * engine,NativeValue * asyncResource,NativeValue * asyncResourceName,NativeAsyncExecuteCallback execute,NativeAsyncCompleteCallback complete,void * data)181 NativeAsyncWork* NativeEngineInterface::CreateAsyncWork(NativeEngine* engine, NativeValue* asyncResource,
182 NativeValue* asyncResourceName, NativeAsyncExecuteCallback execute, NativeAsyncCompleteCallback complete,
183 void* data)
184 {
185 (void)asyncResource;
186 (void)asyncResourceName;
187 char name[NAME_BUFFER_SIZE] = {0};
188 if (asyncResourceName != nullptr) {
189 auto nativeString = reinterpret_cast<NativeString*>(
190 asyncResourceName->GetInterface(NativeString::INTERFACE_ID));
191 size_t strLength = 0;
192 nativeString->GetCString(name, NAME_BUFFER_SIZE, &strLength);
193 }
194 return new NativeAsyncWork(engine, execute, complete, name, data);
195 }
196
CreateAsyncWork(NativeEngine * engine,const std::string & asyncResourceName,NativeAsyncExecuteCallback execute,NativeAsyncCompleteCallback complete,void * data)197 NativeAsyncWork* NativeEngineInterface::CreateAsyncWork(NativeEngine* engine, const std::string &asyncResourceName,
198 NativeAsyncExecuteCallback execute, NativeAsyncCompleteCallback complete, void* data)
199 {
200 return new NativeAsyncWork(engine, execute, complete, asyncResourceName, data);
201 }
202
CreateSafeAsyncWork(NativeEngine * engine,NativeValue * func,NativeValue * asyncResource,NativeValue * asyncResourceName,size_t maxQueueSize,size_t threadCount,void * finalizeData,NativeFinalize finalizeCallback,void * context,NativeThreadSafeFunctionCallJs callJsCallback)203 NativeSafeAsyncWork* NativeEngineInterface::CreateSafeAsyncWork(NativeEngine* engine, NativeValue* func,
204 NativeValue* asyncResource, NativeValue* asyncResourceName, size_t maxQueueSize, size_t threadCount,
205 void* finalizeData, NativeFinalize finalizeCallback, void* context, NativeThreadSafeFunctionCallJs callJsCallback)
206 {
207 return new NativeSafeAsyncWork(engine, func, asyncResource, asyncResourceName, maxQueueSize, threadCount,
208 finalizeData, finalizeCallback, context, callJsCallback);
209 }
210
GetLastError()211 NativeErrorExtendedInfo* NativeEngineInterface::GetLastError()
212 {
213 return &lastError_;
214 }
215
SetLastError(int errorCode,uint32_t engineErrorCode,void * engineReserved)216 void NativeEngineInterface::SetLastError(int errorCode, uint32_t engineErrorCode, void* engineReserved)
217 {
218 lastError_.errorCode = errorCode;
219 lastError_.engineErrorCode = engineErrorCode;
220 lastError_.message = g_errorMessages[lastError_.errorCode];
221 lastError_.reserved = engineReserved;
222 }
223
ClearLastError()224 void NativeEngineInterface::ClearLastError()
225 {
226 lastError_.errorCode = 0;
227 lastError_.engineErrorCode = 0;
228 lastError_.message = nullptr;
229 lastError_.reserved = nullptr;
230 }
231
EncodeToUtf8(NativeValue * nativeValue,char * buffer,int32_t * written,size_t bufferSize,int32_t * nchars)232 void NativeEngineInterface::EncodeToUtf8(
233 NativeValue* nativeValue, char* buffer, int32_t* written, size_t bufferSize, int32_t* nchars)
234 {
235 if (nativeValue == nullptr || nchars == nullptr || written == nullptr) {
236 HILOG_ERROR("NativeEngine EncodeToUtf8 args is nullptr");
237 return;
238 }
239
240 auto nativeString = reinterpret_cast<NativeString*>(nativeValue->GetInterface(NativeString::INTERFACE_ID));
241 if (nativeString == nullptr) {
242 HILOG_ERROR("nativeValue GetInterface is nullptr");
243 return;
244 }
245 *written = nativeString->EncodeWriteUtf8(buffer, bufferSize, nchars);
246 }
247
EncodeToChinese(NativeValue * nativeValue,std::string & buffer,const std::string & encoding)248 void NativeEngineInterface::EncodeToChinese(NativeValue* nativeValue, std::string& buffer, const std::string& encoding)
249 {
250 if (nativeValue == nullptr) {
251 HILOG_ERROR("NativeEngine is nullptr");
252 return;
253 }
254
255 auto nativeString = reinterpret_cast<NativeString*>(nativeValue->GetInterface(NativeString::INTERFACE_ID));
256 if (nativeString == nullptr) {
257 HILOG_ERROR("nativeValue GetInterface is nullptr");
258 return;
259 }
260 nativeString->EncodeWriteChinese(buffer, encoding.c_str());
261 }
262
263 #if !defined(PREVIEW)
CheckUVLoop()264 void NativeEngineInterface::CheckUVLoop()
265 {
266 checkUVLoop_ = true;
267 uv_thread_create(&uvThread_, NativeEngineInterface::UVThreadRunner, this);
268 }
269
CancelCheckUVLoop()270 void NativeEngineInterface::CancelCheckUVLoop()
271 {
272 checkUVLoop_ = false;
273 RunCleanup();
274 uv_async_send(&uvAsync_);
275 uv_sem_post(&uvSem_);
276 uv_thread_join(&uvThread_);
277 }
278
PostLoopTask()279 void NativeEngineInterface::PostLoopTask()
280 {
281 postTask_(true);
282 uv_sem_wait(&uvSem_);
283 }
284
UVThreadRunner(void * nativeEngineImpl)285 void NativeEngineInterface::UVThreadRunner(void* nativeEngineImpl)
286 {
287 std::string name("UVLoop");
288 #ifdef IOS_PLATFORM
289 pthread_setname_np(name.c_str());
290 #else
291 pthread_setname_np(pthread_self(), name.c_str());
292 #endif
293 auto engineImpl = static_cast<NativeEngineInterface*>(nativeEngineImpl);
294 engineImpl->PostLoopTask();
295 while (engineImpl->checkUVLoop_) {
296 int32_t fd = uv_backend_fd(engineImpl->loop_);
297 int32_t timeout = uv_backend_timeout(engineImpl->loop_);
298 int32_t result = -1;
299 #ifdef IOS_PLATFORM
300 struct kevent events[1];
301 struct timespec spec;
302 static const int32_t mSec = 1000;
303 static const int32_t uSec = 1000000;
304 if (timeout != -1) {
305 spec.tv_sec = timeout / mSec;
306 spec.tv_nsec = (timeout % mSec) * uSec;
307 }
308 result = kevent(fd, NULL, 0, events, 1, timeout == -1 ? NULL : &spec);
309
310 #else
311 struct epoll_event ev;
312 result = epoll_wait(fd, &ev, 1, timeout);
313 #endif
314
315 if (!engineImpl->checkUVLoop_) {
316 HILOG_INFO("break thread after epoll wait");
317 break;
318 }
319 if (result >= 0) {
320 engineImpl->PostLoopTask();
321 } else {
322 HILOG_ERROR("epoll wait fail: result: %{public}d, errno: %{public}d", result, errno);
323 }
324 if (!engineImpl->checkUVLoop_) {
325 HILOG_INFO("break thread after post loop task");
326 break;
327 }
328 }
329 }
330 #endif
331
SetPostTask(PostTask postTask)332 void NativeEngineInterface::SetPostTask(PostTask postTask)
333 {
334 postTask_ = postTask;
335 }
336
TriggerPostTask()337 void NativeEngineInterface::TriggerPostTask()
338 {
339 if (postTask_ == nullptr) {
340 HILOG_ERROR("postTask_ is nullptr");
341 return;
342 }
343 postTask_(false);
344 }
345
GetJsEngine()346 void* NativeEngineInterface::GetJsEngine()
347 {
348 return jsEngineInterface_;
349 }
350
AddCleanupHook(CleanupCallback fun,void * arg)351 void NativeEngineInterface::AddCleanupHook(CleanupCallback fun, void* arg)
352 {
353 HILOG_INFO("%{public}s, start.", __func__);
354 auto insertion_info = cleanup_hooks_.emplace(CleanupHookCallback { fun, arg, cleanup_hook_counter_++ });
355 if (insertion_info.second != true) {
356 HILOG_ERROR("AddCleanupHook Failed.");
357 }
358 HILOG_INFO("%{public}s, end.", __func__);
359 }
360
RemoveCleanupHook(CleanupCallback fun,void * arg)361 void NativeEngineInterface::RemoveCleanupHook(CleanupCallback fun, void* arg)
362 {
363 HILOG_INFO("%{public}s, start.", __func__);
364 CleanupHookCallback hook { fun, arg, 0 };
365 cleanup_hooks_.erase(hook);
366 HILOG_INFO("%{public}s, end.", __func__);
367 }
368
RunCleanup()369 void NativeEngineInterface::RunCleanup()
370 {
371 HILOG_INFO("%{public}s, start.", __func__);
372 CleanupHandles();
373 // sync clean up
374 while (!cleanup_hooks_.empty()) {
375 HILOG_INFO("NativeEngineInterface::RunCleanup cleanup_hooks is not empty");
376 // Copy into a vector, since we can't sort an unordered_set in-place.
377 std::vector<CleanupHookCallback> callbacks(cleanup_hooks_.begin(), cleanup_hooks_.end());
378 // We can't erase the copied elements from `cleanup_hooks_` yet, because we
379 // need to be able to check whether they were un-scheduled by another hook.
380
381 std::sort(callbacks.begin(), callbacks.end(), [](const CleanupHookCallback& a, const CleanupHookCallback& b) {
382 // Sort in descending order so that the most recently inserted callbacks are run first.
383 return a.insertion_order_counter_ > b.insertion_order_counter_;
384 });
385 HILOG_INFO(
386 "NativeEngineInterface::RunCleanup cleanup_hooks callbacks size:%{public}d", (int32_t)callbacks.size());
387 for (const CleanupHookCallback& cb : callbacks) {
388 if (cleanup_hooks_.count(cb) == 0) {
389 // This hook was removed from the `cleanup_hooks_` set during another
390 // hook that was run earlier. Nothing to do here.
391 continue;
392 }
393 cb.fn_(cb.arg_);
394 cleanup_hooks_.erase(cb);
395 }
396 CleanupHandles();
397 }
398 HILOG_INFO("%{public}s, end.", __func__);
399 }
400
CleanupHandles()401 void NativeEngineInterface::CleanupHandles()
402 {
403 HILOG_INFO("%{public}s, start.", __func__);
404 while (request_waiting_ > 0) {
405 HILOG_INFO("%{public}s, request waiting:%{public}d.", __func__, request_waiting_);
406 uv_run(loop_, UV_RUN_ONCE);
407 }
408 HILOG_INFO("%{public}s, end.", __func__);
409 }
410
IncreaseWaitingRequestCounter()411 void NativeEngineInterface::IncreaseWaitingRequestCounter()
412 {
413 request_waiting_++;
414 HILOG_INFO("%{public}s, request waiting:%{public}d.", __func__, request_waiting_);
415 }
416
DecreaseWaitingRequestCounter()417 void NativeEngineInterface::DecreaseWaitingRequestCounter()
418 {
419 request_waiting_--;
420 HILOG_INFO("%{public}s, request waiting:%{public}d.", __func__, request_waiting_);
421 }
422
GetRootNativeEngine(void)423 NativeEngine* NativeEngineInterface::GetRootNativeEngine(void)
424 {
425 return rootNativeEngine__;
426 }
427