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 "worker.h"
17
18 #include "commonlibrary/ets_utils/js_sys_module/timer/timer.h"
19 #include "helper/error_helper.h"
20 #include "helper/hitrace_helper.h"
21 #if defined(OHOS_PLATFORM)
22 #include "parameters.h"
23 #endif
24
25 namespace Commonlibrary::Concurrent::WorkerModule {
26 using namespace OHOS::JsSysModule;
27 static constexpr int8_t NUM_WORKER_ARGS = 2;
28 static std::list<Worker*> g_workers;
29 static constexpr int MAX_WORKERS = 8;
30 static std::mutex g_workersMutex;
31
Worker(napi_env env,napi_ref thisVar)32 Worker::Worker(napi_env env, napi_ref thisVar)
33 : hostEnv_(env), workerRef_(thisVar)
34 {}
35
GetContainerScopeId(napi_env env)36 void Worker::GetContainerScopeId(napi_env env)
37 {
38 NativeEngine* hostEngine = reinterpret_cast<NativeEngine*>(env);
39 scopeId_ = hostEngine->GetContainerScopeIdFunc();
40 }
41
StartExecuteInThread(napi_env env,const char * script)42 void Worker::StartExecuteInThread(napi_env env, const char* script)
43 {
44 // 1. init hostOnMessageSignal_ in host loop
45 uv_loop_t* loop = NapiHelper::GetLibUV(env);
46 if (loop == nullptr) {
47 napi_throw_error(env, nullptr, "worker::engine loop is null");
48 CloseHelp::DeletePointer(script, true);
49 return;
50 }
51 GetContainerScopeId(env);
52 hostOnMessageSignal_ = new uv_async_t;
53 uv_async_init(loop, hostOnMessageSignal_, reinterpret_cast<uv_async_cb>(Worker::HostOnMessage));
54 hostOnMessageSignal_->data = this;
55 hostOnErrorSignal_ = new uv_async_t;
56 uv_async_init(loop, hostOnErrorSignal_, reinterpret_cast<uv_async_cb>(Worker::HostOnError));
57 hostOnErrorSignal_->data = this;
58
59 // 2. copy the script
60 script_ = std::string(script);
61 CloseHelp::DeletePointer(script, true);
62
63 // 3. create WorkerRunner to Execute
64 if (!runner_) {
65 runner_ = std::make_unique<WorkerRunner>(WorkerStartCallback(ExecuteInThread, this));
66 }
67 if (runner_) {
68 runner_->Execute(); // start a new thread
69 } else {
70 HILOG_ERROR("runner_ is nullptr");
71 }
72 }
73
CloseInner()74 void Worker::CloseInner()
75 {
76 UpdateWorkerState(TERMINATEING);
77 TerminateWorker();
78 }
79
CloseWorker(napi_env env,napi_callback_info cbinfo)80 napi_value Worker::CloseWorker(napi_env env, napi_callback_info cbinfo)
81 {
82 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
83 Worker* worker = nullptr;
84 napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, (void**)&worker);
85 if (worker != nullptr) {
86 worker->CloseInner();
87 }
88 return NapiHelper::GetUndefinedValue(env);
89 }
90
CallWorkCallback(napi_env env,napi_value recv,size_t argc,const napi_value * argv,const char * type)91 void CallWorkCallback(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type)
92 {
93 napi_value callback = nullptr;
94 napi_get_named_property(env, recv, type, &callback);
95 if (NapiHelper::IsCallable(env, callback)) {
96 napi_value callbackResult = nullptr;
97 napi_call_function(env, recv, callback, argc, argv, &callbackResult);
98 }
99 }
100
PrepareForWorkerInstance()101 bool Worker::PrepareForWorkerInstance()
102 {
103 std::vector<uint8_t> scriptContent;
104 std::string workerAmi;
105 {
106 std::lock_guard<std::recursive_mutex> lock(liveStatusLock_);
107 if (HostIsStop()) {
108 return false;
109 }
110 // 1. init worker async func
111 auto workerEngine = reinterpret_cast<NativeEngine*>(workerEnv_);
112
113 auto hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_);
114 // 2. init worker environment
115 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(LINUX_PLATFORM)
116 workerEngine->SetDebuggerPostTaskFunc(
117 std::bind(&Worker::DebuggerOnPostTask, this, std::placeholders::_1));
118 #endif
119 if (!hostEngine->CallInitWorkerFunc(workerEngine)) {
120 HILOG_ERROR("worker:: CallInitWorkerFunc error");
121 return false;
122 }
123 // 3. get uril content
124 if (!hostEngine->CallGetAssetFunc(script_, scriptContent, workerAmi)) {
125 HILOG_ERROR("worker:: CallGetAssetFunc error");
126 return false;
127 }
128 }
129 // add timer interface
130 Timer::RegisterTime(workerEnv_);
131 HILOG_DEBUG("worker:: stringContent size is %{public}zu", scriptContent.size());
132 napi_value execScriptResult = nullptr;
133 napi_run_actor(workerEnv_, scriptContent, workerAmi.c_str(), &execScriptResult);
134 if (execScriptResult == nullptr) {
135 // An exception occurred when running the script.
136 HILOG_ERROR("worker:: run script exception occurs, will handle exception");
137 HandleException();
138 return false;
139 }
140
141 // 4. register worker name in DedicatedWorkerGlobalScope
142 if (!name_.empty()) {
143 napi_value nameValue = nullptr;
144 napi_create_string_utf8(workerEnv_, name_.c_str(), name_.length(), &nameValue);
145 NapiHelper::SetNamePropertyInGlobal(workerEnv_, "name", nameValue);
146 }
147 return true;
148 }
149
UpdateWorkerState(RunnerState state)150 bool Worker::UpdateWorkerState(RunnerState state)
151 {
152 bool done = false;
153 do {
154 RunnerState oldState = runnerState_.load(std::memory_order_acquire);
155 if (oldState >= state) {
156 // make sure state sequence is start, running, terminating, terminated
157 return false;
158 }
159 done = runnerState_.compare_exchange_strong(oldState, state);
160 } while (!done);
161 return true;
162 }
163
UpdateHostState(HostState state)164 bool Worker::UpdateHostState(HostState state)
165 {
166 bool done = false;
167 do {
168 HostState oldState = hostState_.load(std::memory_order_acquire);
169 if (oldState >= state) {
170 // make sure state sequence is ACTIVE, INACTIVE
171 return false;
172 }
173 done = hostState_.compare_exchange_strong(oldState, state);
174 } while (!done);
175 return true;
176 }
177
PublishWorkerOverSignal()178 void Worker::PublishWorkerOverSignal()
179 {
180 // post NULL tell host worker is not running
181 if (!HostIsStop()) {
182 hostMessageQueue_.EnQueue(NULL);
183 uv_async_send(hostOnMessageSignal_);
184 }
185 }
186
ExecuteInThread(const void * data)187 void Worker::ExecuteInThread(const void* data)
188 {
189 HITRACE_HELPER_START_TRACE(__PRETTY_FUNCTION__);
190 auto worker = reinterpret_cast<Worker*>(const_cast<void*>(data));
191 // 1. create a runtime, nativeengine
192 napi_env workerEnv = nullptr;
193 {
194 std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_);
195 if (worker->HostIsStop()) {
196 CloseHelp::DeletePointer(worker, false);
197 return;
198 }
199 napi_env env = worker->GetHostEnv();
200 napi_create_runtime(env, &workerEnv);
201 if (workerEnv == nullptr) {
202 napi_throw_error(env, nullptr, "Worker create runtime error");
203 return;
204 }
205 // mark worker env is workerThread
206 reinterpret_cast<NativeEngine*>(workerEnv)->MarkWorkerThread();
207 worker->SetWorkerEnv(workerEnv);
208 }
209
210 uv_loop_t* loop = worker->GetWorkerLoop();
211 if (loop == nullptr) {
212 HILOG_ERROR("worker:: Worker loop is nullptr");
213 return;
214 }
215
216 // 2. add some preparation for the worker
217 if (worker->PrepareForWorkerInstance()) {
218 worker->workerOnMessageSignal_ = new uv_async_t;
219 uv_async_init(loop, worker->workerOnMessageSignal_, reinterpret_cast<uv_async_cb>(Worker::WorkerOnMessage));
220 worker->workerOnMessageSignal_->data = worker;
221 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
222 uv_async_init(loop, &worker->debuggerOnPostTaskSignal_, reinterpret_cast<uv_async_cb>(
223 Worker::HandleDebuggerTask));
224 #endif
225 worker->UpdateWorkerState(RUNNING);
226 // in order to invoke worker send before subThread start
227 uv_async_send(worker->workerOnMessageSignal_);
228 HITRACE_HELPER_FINISH_TRACE;
229 // 3. start worker loop
230 worker->Loop();
231 } else {
232 HILOG_ERROR("worker:: worker PrepareForWorkerInstance fail");
233 worker->UpdateWorkerState(TERMINATED);
234 HITRACE_HELPER_FINISH_TRACE;
235 }
236 worker->ReleaseWorkerThreadContent();
237 std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_);
238 if (worker->HostIsStop()) {
239 CloseHelp::DeletePointer(worker, false);
240 } else {
241 worker->PublishWorkerOverSignal();
242 }
243 }
244
HostOnMessage(const uv_async_t * req)245 void Worker::HostOnMessage(const uv_async_t* req)
246 {
247 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
248 Worker* worker = static_cast<Worker*>(req->data);
249 if (worker == nullptr) {
250 HILOG_ERROR("worker:: worker is null");
251 return;
252 }
253 worker->HostOnMessageInner();
254 }
255
HostOnErrorInner()256 void Worker::HostOnErrorInner()
257 {
258 if (hostEnv_ == nullptr || HostIsStop()) {
259 HILOG_ERROR("worker:: host thread maybe is over");
260 return;
261 }
262 napi_status status = napi_ok;
263 HandleScope scope(hostEnv_, status);
264 NAPI_CALL_RETURN_VOID(hostEnv_, status);
265 NativeEngine* hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_);
266 if (!hostEngine->InitContainerScopeFunc(scopeId_)) {
267 HILOG_WARN("worker:: InitContainerScopeFunc error when onerror begin(only stage model)");
268 }
269
270 napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
271 napi_value callback = NapiHelper::GetNameProperty(hostEnv_, obj, "onerror");
272 bool isCallable = NapiHelper::IsCallable(hostEnv_, callback);
273
274 MessageDataType data;
275 while (errorQueue_.DeQueue(&data)) {
276 napi_value result = nullptr;
277 napi_deserialize(hostEnv_, data, &result);
278 napi_value argv[1] = { result };
279 if (isCallable) {
280 napi_value callbackResult = nullptr;
281 napi_call_function(hostEnv_, obj, callback, 1, argv, &callbackResult);
282 }
283 // handle listeners
284 HandleEventListeners(hostEnv_, obj, 1, argv, "error");
285 HandleHostException();
286 }
287 if (!hostEngine->FinishContainerScopeFunc(scopeId_)) {
288 HILOG_WARN("worker:: FinishContainerScopeFunc error when onerror end(only stage model)");
289 }
290 }
291
HostOnError(const uv_async_t * req)292 void Worker::HostOnError(const uv_async_t* req)
293 {
294 Worker* worker = static_cast<Worker*>(req->data);
295 if (worker == nullptr) {
296 HILOG_ERROR("worker::worker is null");
297 return;
298 }
299 worker->HostOnErrorInner();
300 worker->TerminateInner();
301 }
302
303 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
HandleDebuggerTask(const uv_async_t * req)304 void Worker::HandleDebuggerTask(const uv_async_t* req)
305 {
306 Worker* worker = DereferenceHelp::DereferenceOf(&Worker::debuggerOnPostTaskSignal_, req);
307 if (worker == nullptr) {
308 HILOG_ERROR("worker::worker is null");
309 return;
310 }
311
312 worker->debuggerTask_();
313 }
314
DebuggerOnPostTask(std::function<void ()> && task)315 void Worker::DebuggerOnPostTask(std::function<void()>&& task)
316 {
317 if (IsTerminated()) {
318 HILOG_ERROR("worker:: worker has been terminated.");
319 return;
320 }
321 if (uv_is_active((uv_handle_t*)&debuggerOnPostTaskSignal_)) {
322 debuggerTask_ = std::move(task);
323 uv_async_send(&debuggerOnPostTaskSignal_);
324 }
325 }
326 #endif
327
WorkerOnMessage(const uv_async_t * req)328 void Worker::WorkerOnMessage(const uv_async_t* req)
329 {
330 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
331 Worker* worker = static_cast<Worker*>(req->data);
332 if (worker == nullptr) {
333 HILOG_ERROR("worker::worker is null");
334 return;
335 }
336 worker->WorkerOnMessageInner();
337 }
338
CloseHostCallback()339 void Worker::CloseHostCallback()
340 {
341 {
342 napi_status status = napi_ok;
343 HandleScope scope(hostEnv_, status);
344 NAPI_CALL_RETURN_VOID(hostEnv_, status);
345 napi_value exitValue = nullptr;
346 if (isErrorExit_) {
347 napi_create_int32(hostEnv_, 1, &exitValue); // 1 : exit because of error
348 } else {
349 napi_create_int32(hostEnv_, 0, &exitValue); // 0 : exit normally
350 }
351 napi_value argv[1] = { exitValue };
352 CallHostFunction(1, argv, "onexit");
353 napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
354 // handle listeners
355 HandleEventListeners(hostEnv_, obj, 1, argv, "exit");
356 }
357 CloseHelp::DeletePointer(this, false);
358 }
359
HandleEventListeners(napi_env env,napi_value recv,size_t argc,const napi_value * argv,const char * type)360 void Worker::HandleEventListeners(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type)
361 {
362 std::string listener(type);
363 auto iter = eventListeners_.find(listener);
364 if (iter == eventListeners_.end()) {
365 HILOG_DEBUG("worker:: there is no listener for type %{public}s in host thread", type);
366 return;
367 }
368
369 std::list<WorkerListener*>& listeners = iter->second;
370 std::list<WorkerListener*>::iterator it = listeners.begin();
371 while (it != listeners.end()) {
372 WorkerListener* data = *it++;
373 napi_value callbackObj = NapiHelper::GetReferenceValue(env, data->callback_);
374 if (!NapiHelper::IsCallable(env, callbackObj)) {
375 HILOG_DEBUG("worker:: host thread listener %{public}s is not callable", type);
376 return;
377 }
378 napi_value callbackResult = nullptr;
379 napi_call_function(env, recv, callbackObj, argc, argv, &callbackResult);
380 if (!data->NextIsAvailable()) {
381 listeners.remove(data);
382 CloseHelp::DeletePointer(data, false);
383 }
384 }
385 }
386
HostOnMessageInner()387 void Worker::HostOnMessageInner()
388 {
389 if (hostEnv_ == nullptr || HostIsStop()) {
390 HILOG_ERROR("worker:: host thread maybe is over");
391 return;
392 }
393
394 NativeEngine* engine = reinterpret_cast<NativeEngine*>(hostEnv_);
395 if (!engine->InitContainerScopeFunc(scopeId_)) {
396 HILOG_WARN("worker:: InitContainerScopeFunc error when HostOnMessageInner begin(only stage model)");
397 }
398
399 napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
400 napi_value callback = NapiHelper::GetNameProperty(hostEnv_, obj, "onmessage");
401 bool isCallable = NapiHelper::IsCallable(hostEnv_, callback);
402
403 MessageDataType data = nullptr;
404 while (hostMessageQueue_.DeQueue(&data)) {
405 // receive close signal.
406 if (data == nullptr) {
407 HILOG_DEBUG("worker:: worker received close signal");
408 uv_close(reinterpret_cast<uv_handle_t*>(hostOnMessageSignal_), [](uv_handle_t* handle) {
409 if (handle != nullptr) {
410 delete reinterpret_cast<uv_async_t*>(handle);
411 handle = nullptr;
412 }
413 });
414 uv_close(reinterpret_cast<uv_handle_t*>(hostOnErrorSignal_), [](uv_handle_t* handle) {
415 if (handle != nullptr) {
416 delete reinterpret_cast<uv_async_t*>(handle);
417 handle = nullptr;
418 }
419 });
420 CloseHostCallback();
421 return;
422 }
423 // handle data, call worker onMessage function to handle.
424 napi_status status = napi_ok;
425 HandleScope scope(hostEnv_, status);
426 NAPI_CALL_RETURN_VOID(hostEnv_, status);
427 napi_value result = nullptr;
428 status = napi_deserialize(hostEnv_, data, &result);
429 if (status != napi_ok || result == nullptr) {
430 HostOnMessageErrorInner();
431 continue;
432 }
433
434 napi_value event = nullptr;
435 napi_create_object(hostEnv_, &event);
436 napi_set_named_property(hostEnv_, event, "data", result);
437 napi_value argv[1] = { event };
438 if (isCallable) {
439 napi_value callbackResult = nullptr;
440 napi_call_function(hostEnv_, obj, callback, 1, argv, &callbackResult);
441 }
442 // handle listeners.
443 HandleEventListeners(hostEnv_, obj, 1, argv, "message");
444 HandleHostException();
445 }
446 if (!engine->FinishContainerScopeFunc(scopeId_)) {
447 HILOG_WARN("worker:: FinishContainerScopeFunc error when HostOnMessageInner end(only stage model)");
448 }
449 }
450
TerminateWorker()451 void Worker::TerminateWorker()
452 {
453 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
454 // when there is no active handle, worker loop will stop automatic.
455 if (workerOnMessageSignal_ != nullptr) {
456 uv_close(reinterpret_cast<uv_handle_t*>(workerOnMessageSignal_), [](uv_handle_t* handle) {
457 if (handle != nullptr) {
458 delete reinterpret_cast<uv_async_t*>(handle);
459 handle = nullptr;
460 }
461 });
462 }
463 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
464 uv_close(reinterpret_cast<uv_handle_t*>(&debuggerOnPostTaskSignal_), nullptr);
465 #endif
466 CloseWorkerCallback();
467 uv_loop_t* loop = GetWorkerLoop();
468 if (loop != nullptr) {
469 if (g_workers.size() <= 1) {
470 Timer::ClearEnvironmentTimer(workerEnv_);
471 }
472 uv_stop(loop);
473 }
474 UpdateWorkerState(TERMINATED);
475 }
476
HandleHostException() const477 void Worker::HandleHostException() const
478 {
479 if (!NapiHelper::IsExceptionPending(hostEnv_)) {
480 return;
481 }
482 auto hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_);
483 hostEngine->HandleUncaughtException();
484 }
485
HandleException()486 void Worker::HandleException()
487 {
488 if (!NapiHelper::IsExceptionPending(workerEnv_)) {
489 return;
490 }
491
492 napi_status status = napi_ok;
493 HandleScope scope(workerEnv_, status);
494 NAPI_CALL_RETURN_VOID(workerEnv_, status);
495 napi_value exception;
496 napi_get_and_clear_last_exception(workerEnv_, &exception);
497 if (exception == nullptr) {
498 return;
499 }
500
501 napi_value obj = ErrorHelper::TranslateErrorEvent(workerEnv_, exception);
502
503 // add filename
504 napi_value filenameValue = nullptr;
505 napi_create_string_utf8(workerEnv_, script_.c_str(), script_.length(), &filenameValue);
506 napi_set_named_property(workerEnv_, obj, "filename", filenameValue);
507
508 // WorkerGlobalScope onerror
509 WorkerOnErrorInner(obj);
510
511 if (hostEnv_ != nullptr) {
512 napi_value data = nullptr;
513 napi_serialize(workerEnv_, obj, NapiHelper::GetUndefinedValue(workerEnv_), &data);
514 {
515 std::lock_guard<std::recursive_mutex> lock(liveStatusLock_);
516 if (!HostIsStop()) {
517 errorQueue_.EnQueue(data);
518 uv_async_send(hostOnErrorSignal_);
519 }
520 }
521 } else {
522 HILOG_ERROR("worker:: host engine is nullptr.");
523 }
524 }
525
WorkerOnMessageInner()526 void Worker::WorkerOnMessageInner()
527 {
528 if (IsTerminated()) {
529 return;
530 }
531 napi_status status;
532 napi_handle_scope scope = nullptr;
533 status = napi_open_handle_scope(workerEnv_, &scope);
534 if (status != napi_ok || scope == nullptr) {
535 HILOG_ERROR("worker:: WorkerOnMessage open handle scope failed.");
536 return;
537 }
538 MessageDataType data = nullptr;
539 while (!IsTerminated() && workerMessageQueue_.DeQueue(&data)) {
540 if (data == nullptr) {
541 HILOG_DEBUG("worker:: worker reveive terminate signal");
542 // Close handlescope need before TerminateWorker
543 napi_close_handle_scope(workerEnv_, scope);
544 TerminateWorker();
545 return;
546 }
547 napi_value result = nullptr;
548 status = napi_deserialize(workerEnv_, data, &result);
549 if (status != napi_ok || result == nullptr) {
550 WorkerOnMessageErrorInner();
551 continue;
552 }
553
554 napi_value event = nullptr;
555 napi_create_object(workerEnv_, &event);
556 napi_set_named_property(workerEnv_, event, "data", result);
557 napi_value argv[1] = { event };
558 CallWorkerFunction(1, argv, "onmessage", true);
559 napi_value obj = NapiHelper::GetReferenceValue(workerEnv_, this->parentPort_);
560 ParentPortHandleEventListeners(workerEnv_, obj, 1, argv, "message", true);
561 }
562 napi_close_handle_scope(workerEnv_, scope);
563 }
564
HostOnMessageErrorInner()565 void Worker::HostOnMessageErrorInner()
566 {
567 if (hostEnv_ == nullptr || HostIsStop()) {
568 HILOG_ERROR("worker:: host thread maybe is over");
569 return;
570 }
571 napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
572 CallHostFunction(0, nullptr, "onmessageerror");
573 // handle listeners
574 HandleEventListeners(hostEnv_, obj, 0, nullptr, "messageerror");
575 }
576
WorkerOnMessageErrorInner()577 void Worker::WorkerOnMessageErrorInner()
578 {
579 isErrorExit_ = true;
580 CallWorkerFunction(0, nullptr, "onmessageerror", true);
581 napi_value obj = NapiHelper::GetReferenceValue(workerEnv_, this->parentPort_);
582 ParentPortHandleEventListeners(workerEnv_, obj, 0, nullptr, "messageerror", true);
583 }
584
PostMessage(napi_env env,napi_callback_info cbinfo)585 napi_value Worker::PostMessage(napi_env env, napi_callback_info cbinfo)
586 {
587 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
588 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
589 if (argc < 1) {
590 napi_throw_error(env, nullptr, "Worker param count must be more than 1 with postMessage");
591 return nullptr;
592 }
593 napi_value* argv = new napi_value[argc];
594 ObjectScope<napi_value> scope(argv, true);
595 napi_value thisVar = nullptr;
596 napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, nullptr);
597 Worker* worker = nullptr;
598 napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
599
600 if (worker == nullptr) {
601 HILOG_ERROR("worker:: worker is nullptr when PostMessage, maybe worker is terminated");
602 return nullptr;
603 }
604
605 if (worker->IsTerminated() || worker->IsTerminating()) {
606 HILOG_INFO("worker:: worker not in running state");
607 return nullptr;
608 }
609
610 napi_value data = nullptr;
611 napi_status serializeStatus = napi_ok;
612 if (argc >= NUM_WORKER_ARGS) {
613 if (!NapiHelper::IsArray(argv[1])) {
614 napi_throw_error(env, nullptr, "Transfer list must be an Array");
615 return nullptr;
616 }
617 serializeStatus = napi_serialize(env, argv[0], argv[1], &data);
618 } else {
619 serializeStatus = napi_serialize(env, argv[0], NapiHelper::GetUndefinedValue(env), &data);
620 }
621 if (serializeStatus != napi_ok || data == nullptr) {
622 worker->HostOnMessageErrorInner();
623 return nullptr;
624 }
625 worker->PostMessageInner(data);
626 return NapiHelper::GetUndefinedValue(env);
627 }
628
PostMessageToHost(napi_env env,napi_callback_info cbinfo)629 napi_value Worker::PostMessageToHost(napi_env env, napi_callback_info cbinfo)
630 {
631 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
632 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
633 if (argc < 1) {
634 napi_throw_error(env, nullptr, "Worker param count must be more than 1 with new");
635 return nullptr;
636 }
637 napi_value* argv = new napi_value[argc];
638 ObjectScope<napi_value> scope(argv, true);
639 Worker* worker = nullptr;
640 napi_get_cb_info(env, cbinfo, &argc, argv, nullptr, reinterpret_cast<void**>(&worker));
641
642 if (worker == nullptr) {
643 HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
644 return nullptr;
645 }
646
647 if (!worker->IsRunning()) {
648 // if worker is not running, don't send any message to host thread
649 HILOG_DEBUG("worker:: when post message to host occur worker is not in running.");
650 return nullptr;
651 }
652
653 napi_value data = nullptr;
654 napi_status serializeStatus = napi_ok;
655 if (argc >= NUM_WORKER_ARGS) {
656 if (!NapiHelper::IsArray(argv[1])) {
657 napi_throw_error(env, nullptr, "Transfer list must be an Array");
658 return nullptr;
659 }
660 serializeStatus = napi_serialize(env, argv[0], argv[1], &data);
661 } else {
662 serializeStatus = napi_serialize(env, argv[0], NapiHelper::GetUndefinedValue(env), &data);
663 }
664
665 if (serializeStatus != napi_ok || data == nullptr) {
666 worker->WorkerOnMessageErrorInner();
667 return nullptr;
668 }
669 worker->PostMessageToHostInner(data);
670 return NapiHelper::GetUndefinedValue(env);
671 }
672
PostMessageToHostInner(MessageDataType data)673 void Worker::PostMessageToHostInner(MessageDataType data)
674 {
675 std::lock_guard<std::recursive_mutex> lock(liveStatusLock_);
676 if (hostEnv_ != nullptr && !HostIsStop()) {
677 hostMessageQueue_.EnQueue(data);
678 uv_async_send(hostOnMessageSignal_);
679 } else {
680 HILOG_ERROR("worker:: worker host engine is nullptr.");
681 }
682 }
683
PostMessageInner(MessageDataType data)684 void Worker::PostMessageInner(MessageDataType data)
685 {
686 if (IsTerminated()) {
687 HILOG_DEBUG("worker:: worker has been terminated.");
688 return;
689 }
690 workerMessageQueue_.EnQueue(data);
691 if (workerOnMessageSignal_ != nullptr && uv_is_active((uv_handle_t*)workerOnMessageSignal_)) {
692 uv_async_send(workerOnMessageSignal_);
693 }
694 }
695
Terminate(napi_env env,napi_callback_info cbinfo)696 napi_value Worker::Terminate(napi_env env, napi_callback_info cbinfo)
697 {
698 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
699 napi_value thisVar = nullptr;
700 napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr);
701 Worker* worker = nullptr;
702 napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
703 if (worker == nullptr) {
704 HILOG_ERROR("worker:: worker is nullptr when Terminate, maybe worker is terminated");
705 return nullptr;
706 }
707 if (worker->IsTerminated() || worker->IsTerminating()) {
708 HILOG_DEBUG("worker:: worker is not in running");
709 return nullptr;
710 }
711 worker->TerminateInner();
712 return NapiHelper::GetUndefinedValue(env);
713 }
714
TerminateInner()715 void Worker::TerminateInner()
716 {
717 if (IsTerminated() || IsTerminating()) {
718 HILOG_INFO("worker:: worker is not in running");
719 return;
720 }
721 // 1. Update State
722 UpdateWorkerState(TERMINATEING);
723 // 2. send null signal
724 PostMessageInner(NULL);
725 }
726
~Worker()727 Worker::~Worker()
728 {
729 if (!HostIsStop()) {
730 ReleaseHostThreadContent();
731 }
732 RemoveAllListenerInner();
733 }
734
CancelTask(napi_env env,napi_callback_info cbinfo)735 napi_value Worker::CancelTask(napi_env env, napi_callback_info cbinfo)
736 {
737 napi_value thisVar = nullptr;
738 napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr);
739 Worker* worker = nullptr;
740 napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
741 if (worker == nullptr) {
742 HILOG_ERROR("worker:: worker is nullptr when CancelTask, maybe worker is terminated");
743 return nullptr;
744 }
745
746 if (worker->IsTerminated() || worker->IsTerminating()) {
747 HILOG_INFO("worker:: worker is not in running");
748 return nullptr;
749 }
750
751 if (!worker->ClearWorkerTasks()) {
752 HILOG_ERROR("worker:: clear worker task error");
753 }
754 return NapiHelper::GetUndefinedValue(env);
755 }
756
ParentPortCancelTask(napi_env env,napi_callback_info cbinfo)757 napi_value Worker::ParentPortCancelTask(napi_env env, napi_callback_info cbinfo)
758 {
759 Worker* worker = nullptr;
760 napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, reinterpret_cast<void**>(&worker));
761 if (worker == nullptr) {
762 HILOG_ERROR("worker:: worker is nullptr when CancelTask, maybe worker is terminated");
763 return nullptr;
764 }
765
766 if (worker->IsTerminated() || worker->IsTerminating()) {
767 HILOG_INFO("worker:: worker is not in running");
768 return nullptr;
769 }
770
771 if (!worker->ClearWorkerTasks()) {
772 HILOG_ERROR("worker:: clear worker task error");
773 }
774 return NapiHelper::GetUndefinedValue(env);
775 }
776
WorkerConstructor(napi_env env,napi_callback_info cbinfo)777 napi_value Worker::WorkerConstructor(napi_env env, napi_callback_info cbinfo)
778 {
779 // check argv count
780 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
781 if (argc < 1) {
782 napi_throw_error(env, nullptr, "Worker param count must be more than 1 with new");
783 return nullptr;
784 }
785
786 // check 1st param is string
787 napi_value thisVar = nullptr;
788 void* data = nullptr;
789 napi_value* args = new napi_value[argc];
790 ObjectScope<napi_value> scope(args, true);
791 napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data);
792 if (!NapiHelper::IsString(args[0])) {
793 napi_throw_error(env, nullptr, "Worker 1st param must be string with new");
794 return nullptr;
795 }
796 Worker* worker = nullptr;
797 {
798 int maxWorkers = MAX_WORKERS;
799 #if defined(OHOS_PLATFORM)
800 maxWorkers = OHOS::system::GetIntParameter<int>("persist.commonlibrary.maxworkers", MAX_WORKERS);
801 #endif
802 std::lock_guard<std::mutex> lock(g_workersMutex);
803 if (static_cast<int>(g_workers.size()) >= maxWorkers) {
804 napi_throw_error(env, nullptr, "Too many workers, the number of workers exceeds the maximum.");
805 return nullptr;
806 }
807
808 // 2. new worker instance
809 worker = new Worker(env, nullptr);
810 if (worker == nullptr) {
811 napi_throw_error(env, nullptr, "create worker error");
812 return nullptr;
813 }
814 g_workers.push_back(worker);
815 }
816
817 if (argc > 1 && NapiHelper::IsObject(args[1])) {
818 napi_value nameValue = NapiHelper::GetNameProperty(env, args[1], "name");
819 if (NapiHelper::IsString(nameValue)) {
820 char* nameStr = NapiHelper::GetString(env, nameValue);
821 if (nameStr == nullptr) {
822 napi_throw_error(env, nullptr, "worker name create error, please check.");
823 return nullptr;
824 }
825 worker->name_ = std::string(nameStr);
826 CloseHelp::DeletePointer(nameStr, true);
827 }
828
829 napi_value typeValue = NapiHelper::GetNameProperty(env, args[1], "type");
830 if (NapiHelper::IsString(typeValue)) {
831 char* typeStr = NapiHelper::GetString(env, typeValue);
832 if (typeStr == nullptr) {
833 napi_throw_error(env, nullptr, "worker type create error, please check.");
834 return nullptr;
835 }
836 if (strcmp("classic", typeStr) == 0) {
837 worker->SetScriptMode(CLASSIC);
838 CloseHelp::DeletePointer(typeStr, true);
839 } else {
840 napi_throw_error(env, nullptr, "unsupport module");
841 CloseHelp::DeletePointer(typeStr, true);
842 CloseHelp::DeletePointer(worker, false);
843 return nullptr;
844 }
845 }
846 }
847
848 // 3. execute in thread
849 char* script = NapiHelper::GetString(env, args[0]);
850 if (script == nullptr) {
851 napi_throw_error(env, nullptr, "worker script create error, please check.");
852 return nullptr;
853 }
854 napi_wrap(
855 env, thisVar, worker,
856 [](napi_env env, void* data, void* hint) {
857 Worker* worker = reinterpret_cast<Worker*>(data);
858 {
859 std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_);
860 if (worker->UpdateHostState(INACTIVE)) {
861 if (worker->hostOnMessageSignal_ != nullptr &&
862 !uv_is_closing(reinterpret_cast<uv_handle_t*>(worker->hostOnMessageSignal_))) {
863 uv_close(reinterpret_cast<uv_handle_t*>(worker->hostOnMessageSignal_), [](uv_handle_t* handle) {
864 if (handle != nullptr) {
865 delete reinterpret_cast<uv_async_t*>(handle);
866 handle = nullptr;
867 }
868 });
869 }
870 if (worker->hostOnErrorSignal_ != nullptr &&
871 !uv_is_closing(reinterpret_cast<uv_handle_t*>(worker->hostOnErrorSignal_))) {
872 uv_close(reinterpret_cast<uv_handle_t*>(worker->hostOnErrorSignal_), [](uv_handle_t* handle) {
873 if (handle != nullptr) {
874 delete reinterpret_cast<uv_async_t*>(handle);
875 handle = nullptr;
876 }
877 });
878 }
879 worker->ReleaseHostThreadContent();
880 }
881 if (!worker->IsRunning()) {
882 HILOG_DEBUG("worker:: worker is not in running");
883 return;
884 }
885 worker->TerminateInner();
886 }
887 },
888 nullptr, &worker->workerRef_);
889 worker->StartExecuteInThread(env, script);
890 return thisVar;
891 }
892
AddListener(napi_env env,napi_callback_info cbinfo,ListenerMode mode)893 napi_value Worker::AddListener(napi_env env, napi_callback_info cbinfo, ListenerMode mode)
894 {
895 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
896 if (argc < NUM_WORKER_ARGS) {
897 napi_throw_error(env, nullptr, "Worker param count must be more than WORKPARAMNUM with on");
898 return nullptr;
899 }
900 // check 1st param is string
901 napi_value thisVar = nullptr;
902 void* data = nullptr;
903 napi_value* args = new napi_value[argc];
904 ObjectScope<napi_value> scope(args, true);
905 napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data);
906 if (!NapiHelper::IsString(args[0])) {
907 napi_throw_error(env, nullptr, "Worker 1st param must be string with on");
908 return nullptr;
909 }
910 if (!NapiHelper::IsCallable(env, args[1])) {
911 napi_throw_error(env, nullptr, "Worker 2st param must be callable with on");
912 return nullptr;
913 }
914 Worker* worker = nullptr;
915 napi_unwrap(env, thisVar, (void**)&worker);
916 if (worker == nullptr) {
917 HILOG_ERROR("worker:: worker is nullptr when addListener, maybe worker is terminated");
918 return nullptr;
919 }
920
921 napi_ref callback = NapiHelper::CreateReference(env, args[1], 1);
922 auto listener = new WorkerListener(env, callback, mode);
923 if (mode == ONCE && argc > NUM_WORKER_ARGS) {
924 if (NapiHelper::IsObject(args[NUM_WORKER_ARGS])) {
925 napi_value onceValue = NapiHelper::GetNameProperty(env, args[NUM_WORKER_ARGS], "once");
926 bool isOnce = NapiHelper::GetBooleanValue(env, onceValue);
927 if (!isOnce) {
928 listener->SetMode(PERMANENT);
929 }
930 }
931 }
932 char* typeStr = NapiHelper::GetString(env, args[0]);
933 worker->AddListenerInner(env, typeStr, listener);
934 CloseHelp::DeletePointer(typeStr, true);
935 return NapiHelper::GetUndefinedValue(env);
936 }
937
operator ==(const WorkerListener & listener) const938 bool Worker::WorkerListener::operator==(const WorkerListener& listener) const
939 {
940 napi_value obj = NapiHelper::GetReferenceValue(listener.env_, listener.callback_);
941 napi_value compareObj = NapiHelper::GetReferenceValue(env_, callback_);
942 // the env of listener and cmp listener must be same env because of Synchronization method
943 return NapiHelper::StrictEqual(env_, compareObj, obj);
944 }
945
AddListenerInner(napi_env env,const char * type,const WorkerListener * listener)946 void Worker::AddListenerInner(napi_env env, const char* type, const WorkerListener* listener)
947 {
948 std::string typestr(type);
949 auto iter = eventListeners_.find(typestr);
950 if (iter == eventListeners_.end()) {
951 std::list<WorkerListener*> listeners;
952 listeners.emplace_back(const_cast<WorkerListener*>(listener));
953 eventListeners_[typestr] = listeners;
954 } else {
955 std::list<WorkerListener*>& listenerList = iter->second;
956 std::list<WorkerListener*>::iterator it = std::find_if(
957 listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, listener->callback_));
958 if (it != listenerList.end()) {
959 return;
960 }
961 listenerList.emplace_back(const_cast<WorkerListener*>(listener));
962 }
963 }
964
RemoveListenerInner(napi_env env,const char * type,napi_ref callback)965 void Worker::RemoveListenerInner(napi_env env, const char* type, napi_ref callback)
966 {
967 std::string typestr(type);
968 auto iter = eventListeners_.find(typestr);
969 if (iter == eventListeners_.end()) {
970 return;
971 }
972 std::list<WorkerListener*>& listenerList = iter->second;
973 if (callback != nullptr) {
974 std::list<WorkerListener*>::iterator it =
975 std::find_if(listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, callback));
976 if (it != listenerList.end()) {
977 CloseHelp::DeletePointer(*it, false);
978 listenerList.erase(it);
979 }
980 } else {
981 for (auto it = listenerList.begin(); it != listenerList.end(); it++) {
982 CloseHelp::DeletePointer(*it, false);
983 }
984 eventListeners_.erase(typestr);
985 }
986 }
987
On(napi_env env,napi_callback_info cbinfo)988 napi_value Worker::On(napi_env env, napi_callback_info cbinfo)
989 {
990 return AddListener(env, cbinfo, PERMANENT);
991 }
992
Once(napi_env env,napi_callback_info cbinfo)993 napi_value Worker::Once(napi_env env, napi_callback_info cbinfo)
994 {
995 return AddListener(env, cbinfo, ONCE);
996 }
997
RemoveListener(napi_env env,napi_callback_info cbinfo)998 napi_value Worker::RemoveListener(napi_env env, napi_callback_info cbinfo)
999 {
1000 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
1001 if (argc < 1) {
1002 napi_throw_error(env, nullptr, "Worker param count must be more than 2 with on");
1003 return nullptr;
1004 }
1005 // check 1st param is string
1006 napi_value thisVar = nullptr;
1007 void* data = nullptr;
1008 napi_value* args = new napi_value[argc];
1009 ObjectScope<napi_value> scope(args, true);
1010 napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data);
1011 if (!NapiHelper::IsString(args[0])) {
1012 napi_throw_error(env, nullptr, "Worker 1st param must be string with on");
1013 return nullptr;
1014 }
1015
1016 Worker* worker = nullptr;
1017 napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
1018 if (worker == nullptr) {
1019 HILOG_ERROR("worker:: worker is nullptr when RemoveListener, maybe worker is terminated");
1020 return nullptr;
1021 }
1022
1023 if (argc > 1 && !NapiHelper::IsCallable(env, args[1])) {
1024 napi_throw_error(env, nullptr, "Worker 2st param must be callable with on");
1025 return nullptr;
1026 }
1027
1028 char* typeStr = NapiHelper::GetString(env, args[0]);
1029 if (typeStr == nullptr) {
1030 napi_throw_error(env, nullptr, "worker listener type create error, please check.");
1031 return nullptr;
1032 }
1033
1034 napi_ref callback = nullptr;
1035 if (argc > 1 && NapiHelper::IsCallable(env, args[1])) {
1036 napi_create_reference(env, args[1], 1, &callback);
1037 }
1038 worker->RemoveListenerInner(env, typeStr, callback);
1039 CloseHelp::DeletePointer(typeStr, true);
1040 NapiHelper::DeleteReference(env, callback);
1041 return NapiHelper::GetUndefinedValue(env);
1042 }
1043
Off(napi_env env,napi_callback_info cbinfo)1044 napi_value Worker::Off(napi_env env, napi_callback_info cbinfo)
1045 {
1046 return RemoveListener(env, cbinfo);
1047 }
1048
AddEventListener(napi_env env,napi_callback_info cbinfo)1049 napi_value Worker::AddEventListener(napi_env env, napi_callback_info cbinfo)
1050 {
1051 return AddListener(env, cbinfo, PERMANENT);
1052 }
1053
DispatchEvent(napi_env env,napi_callback_info cbinfo)1054 napi_value Worker::DispatchEvent(napi_env env, napi_callback_info cbinfo)
1055 {
1056 size_t argc = 1;
1057 napi_value args[1];
1058 napi_value thisVar = nullptr;
1059 void* data = nullptr;
1060 napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data);
1061 if (argc < 1) {
1062 napi_throw_error(env, nullptr, "worker:: DispatchEvent param count must be more than 1");
1063 return NapiHelper::CreateBooleanValue(env, false);
1064 }
1065
1066 // check 1st param is event
1067 if (!NapiHelper::IsObject(args[0])) {
1068 napi_throw_error(env, nullptr, "worker DispatchEvent 1st param must be Event");
1069 return NapiHelper::CreateBooleanValue(env, false);
1070 }
1071
1072 Worker* worker = nullptr;
1073 napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
1074 if (worker == nullptr) {
1075 HILOG_ERROR("worker:: worker is nullptr when DispatchEvent, maybe worker is terminated");
1076 return NapiHelper::CreateBooleanValue(env, false);
1077 }
1078
1079 napi_value typeValue = NapiHelper::GetNameProperty(env, args[0], "type");
1080 if (!NapiHelper::IsString(typeValue)) {
1081 napi_throw_error(env, nullptr, "worker event type must be string");
1082 return NapiHelper::CreateBooleanValue(env, false);
1083 }
1084
1085 napi_value obj = NapiHelper::GetReferenceValue(env, worker->workerRef_);
1086
1087 char* typeStr = NapiHelper::GetString(env, typeValue);
1088 if (typeStr == nullptr) {
1089 napi_throw_error(env, nullptr, "worker listener type create error, please check.");
1090 return NapiHelper::CreateBooleanValue(env, false);
1091 }
1092 if (strcmp(typeStr, "error") == 0) {
1093 CallWorkCallback(env, obj, 1, args, "onerror");
1094 } else if (strcmp(typeStr, "messageerror") == 0) {
1095 CallWorkCallback(env, obj, 1, args, "onmessageerror");
1096 } else if (strcmp(typeStr, "message") == 0) {
1097 CallWorkCallback(env, obj, 1, args, "onmessage");
1098 }
1099
1100 worker->HandleEventListeners(env, obj, 1, args, typeStr);
1101
1102 CloseHelp::DeletePointer(typeStr, true);
1103 return NapiHelper::CreateBooleanValue(env, true);
1104 }
1105
RemoveEventListener(napi_env env,napi_callback_info cbinfo)1106 napi_value Worker::RemoveEventListener(napi_env env, napi_callback_info cbinfo)
1107 {
1108 return RemoveListener(env, cbinfo);
1109 }
1110
RemoveAllListenerInner()1111 void Worker::RemoveAllListenerInner()
1112 {
1113 for (auto iter = eventListeners_.begin(); iter != eventListeners_.end(); iter++) {
1114 std::list<WorkerListener*>& listeners = iter->second;
1115 for (auto item = listeners.begin(); item != listeners.end(); item++) {
1116 WorkerListener* listener = *item;
1117 CloseHelp::DeletePointer(listener, false);
1118 }
1119 }
1120 eventListeners_.clear();
1121 }
1122
RemoveAllListener(napi_env env,napi_callback_info cbinfo)1123 napi_value Worker::RemoveAllListener(napi_env env, napi_callback_info cbinfo)
1124 {
1125 napi_value thisVar = nullptr;
1126 napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr);
1127 Worker* worker = nullptr;
1128 napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
1129 if (worker == nullptr) {
1130 HILOG_ERROR("worker:: worker is nullptr when RemoveAllListener, maybe worker is terminated");
1131 return nullptr;
1132 }
1133
1134 worker->RemoveAllListenerInner();
1135 return NapiHelper::GetUndefinedValue(env);
1136 }
1137
InitWorker(napi_env env,napi_value exports)1138 napi_value Worker::InitWorker(napi_env env, napi_value exports)
1139 {
1140 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
1141 NativeEngine* engine = reinterpret_cast<NativeEngine*>(env);
1142 const char className[] = "Worker";
1143 napi_property_descriptor properties[] = {
1144 DECLARE_NAPI_FUNCTION("postMessage", PostMessage),
1145 DECLARE_NAPI_FUNCTION("terminate", Terminate),
1146 DECLARE_NAPI_FUNCTION("on", On),
1147 DECLARE_NAPI_FUNCTION("once", Once),
1148 DECLARE_NAPI_FUNCTION("off", Off),
1149 DECLARE_NAPI_FUNCTION("addEventListener", AddEventListener),
1150 DECLARE_NAPI_FUNCTION("dispatchEvent", DispatchEvent),
1151 DECLARE_NAPI_FUNCTION("removeEventListener", RemoveEventListener),
1152 DECLARE_NAPI_FUNCTION("removeAllListener", RemoveAllListener),
1153 DECLARE_NAPI_FUNCTION("cancelTasks", CancelTask),
1154 };
1155 napi_value workerClazz = nullptr;
1156 napi_define_class(env, className, sizeof(className), Worker::WorkerConstructor, nullptr,
1157 sizeof(properties) / sizeof(properties[0]), properties, &workerClazz);
1158 napi_set_named_property(env, exports, "Worker", workerClazz);
1159
1160 if (engine->IsWorkerThread()) {
1161 if (g_workers.size() == 0) {
1162 HILOG_DEBUG("worker:: The old worker is not used.");
1163 return exports;
1164 }
1165 Worker* worker = nullptr;
1166 for (auto item = g_workers.begin(); item != g_workers.end(); item++) {
1167 if ((*item)->IsSameWorkerEnv(env)) {
1168 worker = *item;
1169 }
1170 }
1171 if (worker == nullptr) {
1172 napi_throw_error(env, nullptr, "worker:: worker is null");
1173 return exports;
1174 }
1175
1176 napi_property_descriptor properties[] = {
1177 DECLARE_NAPI_FUNCTION_WITH_DATA("postMessage", PostMessageToHost, worker),
1178 DECLARE_NAPI_FUNCTION_WITH_DATA("close", CloseWorker, worker),
1179 DECLARE_NAPI_FUNCTION_WITH_DATA("cancelTasks", ParentPortCancelTask, worker),
1180 DECLARE_NAPI_FUNCTION_WITH_DATA("addEventListener", ParentPortAddEventListener, worker),
1181 DECLARE_NAPI_FUNCTION_WITH_DATA("dispatchEvent", ParentPortDispatchEvent, worker),
1182 DECLARE_NAPI_FUNCTION_WITH_DATA("removeEventListener", ParentPortRemoveEventListener, worker),
1183 DECLARE_NAPI_FUNCTION_WITH_DATA("removeAllListener", ParentPortRemoveAllListener, worker),
1184 };
1185 napi_value parentPortObj = nullptr;
1186 napi_create_object(env, &parentPortObj);
1187 napi_define_properties(env, parentPortObj, sizeof(properties) / sizeof(properties[0]), properties);
1188
1189 // 5. register worker name in DedicatedWorkerGlobalScope
1190 std::string workerName = worker->GetName();
1191 if (!workerName.empty()) {
1192 napi_value nameValue = nullptr;
1193 napi_create_string_utf8(env, workerName.c_str(), workerName.length(), &nameValue);
1194 napi_set_named_property(env, parentPortObj, "name", nameValue);
1195 }
1196 napi_set_named_property(env, exports, "parentPort", parentPortObj);
1197
1198 // register worker parentPort.
1199 napi_create_reference(env, parentPortObj, 1, &worker->parentPort_);
1200 }
1201 return exports;
1202 }
1203
WorkerOnErrorInner(napi_value error)1204 void Worker::WorkerOnErrorInner(napi_value error)
1205 {
1206 isErrorExit_ = true;
1207 napi_value argv[1] = { error };
1208 CallWorkerFunction(1, argv, "onerror", false);
1209 napi_value obj = NapiHelper::GetReferenceValue(workerEnv_, this->parentPort_);
1210 ParentPortHandleEventListeners(workerEnv_, obj, 1, argv, "error", false);
1211 }
1212
CallWorkerFunction(size_t argc,const napi_value * argv,const char * methodName,bool tryCatch)1213 bool Worker::CallWorkerFunction(size_t argc, const napi_value* argv, const char* methodName, bool tryCatch)
1214 {
1215 if (workerEnv_ == nullptr) {
1216 return false;
1217 }
1218 napi_value callback = NapiHelper::GetNamePropertyInParentPort(workerEnv_, parentPort_, methodName);
1219 bool isCallable = NapiHelper::IsCallable(workerEnv_, callback);
1220 if (!isCallable) {
1221 HILOG_DEBUG("worker:: parentPort %{public}s is not Callable", methodName);
1222 return false;
1223 }
1224 napi_value undefinedValue = NapiHelper::GetUndefinedValue(workerEnv_);
1225 napi_value callbackResult = nullptr;
1226 napi_call_function(workerEnv_, undefinedValue, callback, argc, argv, &callbackResult);
1227 if (tryCatch && callbackResult == nullptr) {
1228 HILOG_DEBUG("worker:: parentPort %{public}s handle exception", methodName);
1229 HandleException();
1230 return false;
1231 }
1232 return true;
1233 }
1234
CloseWorkerCallback()1235 void Worker::CloseWorkerCallback()
1236 {
1237 CallWorkerFunction(0, nullptr, "onclose", true);
1238 // off worker inited environment
1239 {
1240 std::lock_guard<std::recursive_mutex> lock(liveStatusLock_);
1241 if (HostIsStop()) {
1242 return;
1243 }
1244 auto hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_);
1245 if (!hostEngine->CallOffWorkerFunc(reinterpret_cast<NativeEngine*>(workerEnv_))) {
1246 HILOG_ERROR("worker:: CallOffWorkerFunc error");
1247 }
1248 }
1249 }
1250
CallHostFunction(size_t argc,const napi_value * argv,const char * methodName) const1251 void Worker::CallHostFunction(size_t argc, const napi_value* argv, const char* methodName) const
1252 {
1253 if (hostEnv_ == nullptr || HostIsStop()) {
1254 HILOG_ERROR("worker:: host thread maybe is over");
1255 return;
1256 }
1257 napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
1258 napi_value callback = NapiHelper::GetNameProperty(hostEnv_, obj, methodName);
1259 bool isCallable = NapiHelper::IsCallable(hostEnv_, callback);
1260 if (!isCallable) {
1261 HILOG_DEBUG("worker:: host thread %{public}s is not Callable", methodName);
1262 return;
1263 }
1264 napi_value callbackResult = nullptr;
1265 napi_call_function(hostEnv_, obj, callback, argc, argv, &callbackResult);
1266 HandleHostException();
1267 }
1268
ReleaseWorkerThreadContent()1269 void Worker::ReleaseWorkerThreadContent()
1270 {
1271 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
1272 auto hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_);
1273 auto workerEngine = reinterpret_cast<NativeEngine*>(workerEnv_);
1274 if (hostEngine != nullptr && workerEngine != nullptr) {
1275 if (!hostEngine->DeleteWorker(workerEngine)) {
1276 HILOG_ERROR("worker:: DeleteWorker error");
1277 }
1278 }
1279 // 1. remove worker instance count
1280 {
1281 std::lock_guard<std::mutex> lock(g_workersMutex);
1282 std::list<Worker*>::iterator it = std::find(g_workers.begin(), g_workers.end(), this);
1283 if (it != g_workers.end()) {
1284 g_workers.erase(it);
1285 }
1286 }
1287
1288 ParentPortRemoveAllListenerInner();
1289
1290 // 2. delete worker's parentPort
1291 NapiHelper::DeleteReference(workerEnv_, parentPort_);
1292 parentPort_ = nullptr;
1293
1294 // 3. clear message send to worker thread
1295 workerMessageQueue_.Clear(workerEnv_);
1296 CloseHelp::DeletePointer(reinterpret_cast<NativeEngine*>(workerEnv_), false);
1297 workerEnv_ = nullptr;
1298 }
1299
ReleaseHostThreadContent()1300 void Worker::ReleaseHostThreadContent()
1301 {
1302 // 1. clear message send to host thread
1303 hostMessageQueue_.Clear(hostEnv_);
1304 // 2. clear error queue send to host thread
1305 errorQueue_.Clear(hostEnv_);
1306 if (!HostIsStop()) {
1307 napi_status status = napi_ok;
1308 HandleScope scope(hostEnv_, status);
1309 NAPI_CALL_RETURN_VOID(hostEnv_, status);
1310 // 3. set thisVar's nativepointer be null
1311 napi_value thisVar = NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
1312 Worker* worker = nullptr;
1313 napi_remove_wrap(hostEnv_, thisVar, reinterpret_cast<void**>(&worker));
1314 hostEnv_ = nullptr;
1315 // 4. set workerRef_ be null
1316 workerRef_ = nullptr;
1317 }
1318 }
1319
ParentPortAddEventListener(napi_env env,napi_callback_info cbinfo)1320 napi_value Worker::ParentPortAddEventListener(napi_env env, napi_callback_info cbinfo)
1321 {
1322 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
1323 if (argc < NUM_WORKER_ARGS) {
1324 napi_throw_error(env, nullptr, "Worker param count must be more than NUM_WORKER_ARGS with on");
1325 return nullptr;
1326 }
1327
1328 napi_value* args = new napi_value[argc];
1329 ObjectScope<napi_value> scope(args, true);
1330 Worker* worker = nullptr;
1331 napi_get_cb_info(env, cbinfo, &argc, args, nullptr, reinterpret_cast<void**>(&worker));
1332
1333 if (!NapiHelper::IsString(args[0])) {
1334 napi_throw_error(env, nullptr, "Worker 1st param must be string with on");
1335 return nullptr;
1336 }
1337
1338 if (!NapiHelper::IsCallable(env, args[1])) {
1339 napi_throw_error(env, nullptr, "Worker 2st param must be callable with on");
1340 return nullptr;
1341 }
1342
1343 if (worker == nullptr) {
1344 HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
1345 return nullptr;
1346 }
1347
1348 if (!worker->IsNotTerminate()) {
1349 // if worker is not running, don't send any message to host thread
1350 HILOG_DEBUG("worker:: when post message to host occur worker is not in running.");
1351 return nullptr;
1352 }
1353
1354 napi_ref callback = NapiHelper::CreateReference(env, args[1], 1);
1355 auto listener = new WorkerListener(env, callback, PERMANENT);
1356 if (argc > NUM_WORKER_ARGS && NapiHelper::IsObject(args[NUM_WORKER_ARGS])) {
1357 napi_value onceValue = NapiHelper::GetNameProperty(env, args[NUM_WORKER_ARGS], "once");
1358 bool isOnce = NapiHelper::GetBooleanValue(env, onceValue);
1359 if (isOnce) {
1360 listener->SetMode(ONCE);
1361 }
1362 }
1363 char* typeStr = NapiHelper::GetString(env, args[0]);
1364 worker->ParentPortAddListenerInner(env, typeStr, listener);
1365 CloseHelp::DeletePointer(typeStr, true);
1366 return NapiHelper::GetUndefinedValue(env);
1367 }
1368
ParentPortRemoveAllListener(napi_env env,napi_callback_info cbinfo)1369 napi_value Worker::ParentPortRemoveAllListener(napi_env env, napi_callback_info cbinfo)
1370 {
1371 Worker* worker = nullptr;
1372 napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, reinterpret_cast<void**>(&worker));
1373
1374 if (worker == nullptr) {
1375 HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
1376 return nullptr;
1377 }
1378
1379 if (!worker->IsNotTerminate()) {
1380 // if worker is not running, don't send any message to host thread
1381 HILOG_DEBUG("worker:: when post message to host occur worker is not in running.");
1382 return nullptr;
1383 }
1384
1385 worker->ParentPortRemoveAllListenerInner();
1386 return NapiHelper::GetUndefinedValue(env);
1387 }
1388
ParentPortDispatchEvent(napi_env env,napi_callback_info cbinfo)1389 napi_value Worker::ParentPortDispatchEvent(napi_env env, napi_callback_info cbinfo)
1390 {
1391 size_t argc = 1;
1392 napi_value args[1];
1393 Worker* worker = nullptr;
1394 napi_get_cb_info(env, cbinfo, &argc, args, nullptr, reinterpret_cast<void**>(&worker));
1395 if (argc < 1) {
1396 napi_throw_error(env, nullptr, "worker:: DispatchEvent param count must be more than 1");
1397 return NapiHelper::CreateBooleanValue(env, false);
1398 }
1399
1400 if (!NapiHelper::IsObject(args[0])) {
1401 napi_throw_error(env, nullptr, "worker DispatchEvent 1st param must be Event");
1402 return NapiHelper::CreateBooleanValue(env, false);
1403 }
1404
1405 napi_value typeValue = NapiHelper::GetNameProperty(env, args[0], "type");
1406 if (!NapiHelper::IsString(typeValue)) {
1407 napi_throw_error(env, nullptr, "worker event type must be string");
1408 return NapiHelper::CreateBooleanValue(env, false);
1409 }
1410
1411 if (worker == nullptr) {
1412 HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
1413 return NapiHelper::CreateBooleanValue(env, false);
1414 }
1415
1416 if (!worker->IsNotTerminate()) {
1417 // if worker is not running, don't send any message to host thread
1418 HILOG_DEBUG("worker:: when post message to host occur worker is not in running.");
1419 return NapiHelper::CreateBooleanValue(env, false);
1420 }
1421
1422 char* typeStr = NapiHelper::GetString(env, typeValue);
1423 if (typeStr == nullptr) {
1424 napi_throw_error(env, nullptr, "worker listener type create error, please check.");
1425 return NapiHelper::CreateBooleanValue(env, false);
1426 }
1427
1428 napi_value obj = NapiHelper::GetReferenceValue(env, worker->parentPort_);
1429
1430 if (strcmp(typeStr, "error") == 0) {
1431 CallWorkCallback(env, obj, 1, args, "onerror");
1432 } else if (strcmp(typeStr, "messageerror") == 0) {
1433 CallWorkCallback(env, obj, 1, args, "onmessageerror");
1434 } else if (strcmp(typeStr, "message") == 0) {
1435 CallWorkCallback(env, obj, 1, args, "onmessage");
1436 }
1437
1438 worker->ParentPortHandleEventListeners(env, obj, 1, args, typeStr, true);
1439
1440 CloseHelp::DeletePointer(typeStr, true);
1441 return NapiHelper::CreateBooleanValue(env, true);
1442 }
1443
ParentPortRemoveEventListener(napi_env env,napi_callback_info cbinfo)1444 napi_value Worker::ParentPortRemoveEventListener(napi_env env, napi_callback_info cbinfo)
1445 {
1446 size_t argc = 2; // 2: max args number is 2
1447 napi_value args[argc];
1448 Worker* worker = nullptr;
1449 napi_get_cb_info(env, cbinfo, &argc, args, nullptr, reinterpret_cast<void**>(&worker));
1450 if (argc < 1) {
1451 napi_throw_error(env, nullptr, "Worker param count must be more than 2 with on");
1452 return nullptr;
1453 }
1454
1455 if (!NapiHelper::IsString(args[0])) {
1456 napi_throw_error(env, nullptr, "Worker 1st param must be string with on");
1457 return nullptr;
1458 }
1459
1460 if (argc > 1 && !NapiHelper::IsCallable(env, args[1])) {
1461 napi_throw_error(env, nullptr, "Worker 2st param must be callable with on");
1462 return nullptr;
1463 }
1464
1465 if (worker == nullptr) {
1466 HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
1467 return nullptr;
1468 }
1469
1470 if (!worker->IsNotTerminate()) {
1471 // if worker is not running, don't send any message to host thread
1472 HILOG_DEBUG("worker:: when post message to host occur worker is not in running.");
1473 return nullptr;
1474 }
1475
1476 napi_ref callback = nullptr;
1477 if (argc > 1 && NapiHelper::IsCallable(env, args[1])) {
1478 napi_create_reference(env, args[1], 1, &callback);
1479 }
1480
1481 char* typeStr = NapiHelper::GetString(env, args[0]);
1482 if (typeStr == nullptr) {
1483 napi_throw_error(env, nullptr, "worker listener type create error, please check.");
1484 return nullptr;
1485 }
1486 worker->ParentPortRemoveListenerInner(env, typeStr, callback);
1487 CloseHelp::DeletePointer(typeStr, true);
1488 NapiHelper::DeleteReference(env, callback);
1489 return NapiHelper::GetUndefinedValue(env);
1490 }
1491
ParentPortAddListenerInner(napi_env env,const char * type,const WorkerListener * listener)1492 void Worker::ParentPortAddListenerInner(napi_env env, const char* type, const WorkerListener* listener)
1493 {
1494 std::string typestr(type);
1495 auto iter = parentPortEventListeners_.find(typestr);
1496 if (iter == parentPortEventListeners_.end()) {
1497 std::list<WorkerListener*> listeners;
1498 listeners.emplace_back(const_cast<WorkerListener*>(listener));
1499 parentPortEventListeners_[typestr] = listeners;
1500 } else {
1501 std::list<WorkerListener*>& listenerList = iter->second;
1502 std::list<WorkerListener*>::iterator it = std::find_if(
1503 listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, listener->callback_));
1504 if (it != listenerList.end()) {
1505 return;
1506 }
1507 listenerList.emplace_back(const_cast<WorkerListener*>(listener));
1508 }
1509 }
1510
ParentPortRemoveAllListenerInner()1511 void Worker::ParentPortRemoveAllListenerInner()
1512 {
1513 for (auto iter = parentPortEventListeners_.begin(); iter != parentPortEventListeners_.end(); iter++) {
1514 std::list<WorkerListener*>& listeners = iter->second;
1515 for (auto item = listeners.begin(); item != listeners.end(); item++) {
1516 WorkerListener* listener = *item;
1517 CloseHelp::DeletePointer(listener, false);
1518 }
1519 }
1520 parentPortEventListeners_.clear();
1521 }
1522
ParentPortRemoveListenerInner(napi_env env,const char * type,napi_ref callback)1523 void Worker::ParentPortRemoveListenerInner(napi_env env, const char* type, napi_ref callback)
1524 {
1525 std::string typestr(type);
1526 auto iter = parentPortEventListeners_.find(typestr);
1527 if (iter == parentPortEventListeners_.end()) {
1528 return;
1529 }
1530 std::list<WorkerListener*>& listenerList = iter->second;
1531 if (callback != nullptr) {
1532 std::list<WorkerListener*>::iterator it =
1533 std::find_if(listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, callback));
1534 if (it != listenerList.end()) {
1535 CloseHelp::DeletePointer(*it, false);
1536 listenerList.erase(it);
1537 }
1538 } else {
1539 for (auto it = listenerList.begin(); it != listenerList.end(); it++) {
1540 CloseHelp::DeletePointer(*it, false);
1541 }
1542 parentPortEventListeners_.erase(typestr);
1543 }
1544 }
1545
ParentPortHandleEventListeners(napi_env env,napi_value recv,size_t argc,const napi_value * argv,const char * type,bool tryCatch)1546 void Worker::ParentPortHandleEventListeners(napi_env env, napi_value recv, size_t argc,
1547 const napi_value* argv, const char* type, bool tryCatch)
1548 {
1549 std::string listener(type);
1550 auto iter = parentPortEventListeners_.find(listener);
1551 if (iter == parentPortEventListeners_.end()) {
1552 HILOG_DEBUG("worker:: there is no listener for type %{public}s", type);
1553 return;
1554 }
1555
1556 std::list<WorkerListener*>& listeners = iter->second;
1557 std::list<WorkerListener*>::iterator it = listeners.begin();
1558 while (it != listeners.end()) {
1559 WorkerListener* data = *it++;
1560 napi_value callbackObj = NapiHelper::GetReferenceValue(env, data->callback_);
1561 if (!NapiHelper::IsCallable(env, callbackObj)) {
1562 HILOG_DEBUG("worker:: parentPort.addEventListener %{public}s is not callable", type);
1563 return;
1564 }
1565 napi_value callbackResult = nullptr;
1566 napi_call_function(env, recv, callbackObj, argc, argv, &callbackResult);
1567 if (!data->NextIsAvailable()) {
1568 listeners.remove(data);
1569 CloseHelp::DeletePointer(data, false);
1570 }
1571 if (tryCatch && callbackResult == nullptr) {
1572 HandleException();
1573 return;
1574 }
1575 }
1576 }
1577 } // namespace Commonlibrary::Concurrent::WorkerModule
1578