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