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