• 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 "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