• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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