• 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/concurrent_helper.h"
20 #include "helper/error_helper.h"
21 #include "helper/hitrace_helper.h"
22 #include "helper/path_helper.h"
23 #include "tools/log.h"
24 #if defined(OHOS_PLATFORM)
25 #include "parameters.h"
26 #endif
27 
28 namespace Commonlibrary::Concurrent::WorkerModule {
29 using namespace OHOS::JsSysModule;
30 static constexpr int8_t NUM_WORKER_ARGS = 2;
31 static constexpr uint8_t NUM_GLOBAL_CALL_ARGS = 3;
32 static std::list<Worker *> g_workers;
33 static constexpr int MAX_WORKERS = 8;
34 static constexpr int MAX_THREAD_WORKERS = 64;
35 static std::mutex g_workersMutex;
36 static std::list<Worker *> g_limitedworkers;
37 static constexpr int MAX_LIMITEDWORKERS = 16;
38 static std::mutex g_limitedworkersMutex;
39 static constexpr uint8_t BEGIN_INDEX_OF_ARGUMENTS = 2;
40 static constexpr uint32_t DEFAULT_TIMEOUT = 5000;
41 static constexpr uint32_t GLOBAL_CALL_ID_MAX = 4294967295;
42 static constexpr size_t GLOBAL_CALL_MAX_COUNT = 65535;
43 
44 #if defined(ENABLE_WORKER_EVENTHANDLER)
GetMainThreadHandler()45 std::shared_ptr<OHOS::AppExecFwk::EventHandler> Worker::GetMainThreadHandler()
46 {
47     static std::shared_ptr<OHOS::AppExecFwk::EventHandler> mainThreadHandler;
48     static std::mutex mainThreadHandlerMutex;
49     if (mainThreadHandler == nullptr) {
50         std::lock_guard<std::mutex> lock(mainThreadHandlerMutex);
51         if (mainThreadHandler == nullptr) {
52             mainThreadHandler = std::make_shared<OHOS::AppExecFwk::EventHandler>(
53                 OHOS::AppExecFwk::EventRunner::GetMainEventRunner());
54         }
55     }
56     return mainThreadHandler;
57 }
58 #endif
59 
Worker(napi_env env,napi_ref thisVar)60 Worker::Worker(napi_env env, napi_ref thisVar)
61     : hostEnv_(env), workerRef_(thisVar)
62 {
63     workerWrapper_ = std::make_shared<WorkerWrapper>(this);
64 }
65 
InitWorker(napi_env env,napi_value exports)66 napi_value Worker::InitWorker(napi_env env, napi_value exports)
67 {
68     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
69     napi_property_descriptor properties[] = {
70         DECLARE_NAPI_FUNCTION("postMessage", PostMessage),
71         DECLARE_NAPI_FUNCTION("postMessageWithSharedSendable", PostMessageWithSharedSendable),
72         DECLARE_NAPI_FUNCTION("terminate", Terminate),
73         DECLARE_NAPI_FUNCTION("on", On),
74         DECLARE_NAPI_FUNCTION("registerGlobalCallObject", RegisterGlobalCallObject),
75         DECLARE_NAPI_FUNCTION("unregisterGlobalCallObject", UnregisterGlobalCallObject),
76         DECLARE_NAPI_FUNCTION("once", Once),
77         DECLARE_NAPI_FUNCTION("off", Off),
78         DECLARE_NAPI_FUNCTION("addEventListener", AddEventListener),
79         DECLARE_NAPI_FUNCTION("dispatchEvent", DispatchEvent),
80         DECLARE_NAPI_FUNCTION("removeEventListener", RemoveEventListener),
81         DECLARE_NAPI_FUNCTION("removeAllListener", RemoveAllListener),
82         DECLARE_NAPI_FUNCTION("cancelTasks", CancelTask),
83     };
84     // for worker.ThreadWorker
85     const char threadWorkerName[] = "ThreadWorker";
86     napi_value threadWorkerClazz = nullptr;
87     napi_define_class(env, threadWorkerName, sizeof(threadWorkerName), Worker::ThreadWorkerConstructor, nullptr,
88         sizeof(properties) / sizeof(properties[0]), properties, &threadWorkerClazz);
89     napi_set_named_property(env, exports, "ThreadWorker", threadWorkerClazz);
90 
91     // for worker.Worker
92     const char workerName[] = "Worker";
93     napi_value workerClazz = nullptr;
94     napi_define_class(env, workerName, sizeof(workerName), Worker::WorkerConstructor, nullptr,
95         sizeof(properties) / sizeof(properties[0]), properties, &workerClazz);
96     napi_set_named_property(env, exports, "Worker", workerClazz);
97 
98     // for worker.LimitedWorker
99     const char limitedWorkerName[] = "RestrictedWorker";
100     napi_value limitedWorkerClazz = nullptr;
101     napi_define_class(env, limitedWorkerName, sizeof(limitedWorkerName), Worker::LimitedWorkerConstructor, nullptr,
102         sizeof(properties) / sizeof(properties[0]), properties, &limitedWorkerClazz);
103     napi_set_named_property(env, exports, "RestrictedWorker", limitedWorkerClazz);
104     return InitPort(env, exports);
105 }
106 
InitPort(napi_env env,napi_value exports)107 napi_value Worker::InitPort(napi_env env, napi_value exports)
108 {
109     NativeEngine* engine = reinterpret_cast<NativeEngine*>(env);
110     Worker* worker = nullptr;
111     if (engine->IsRestrictedWorkerThread()) {
112         std::lock_guard<std::mutex> lock(g_limitedworkersMutex);
113         for (auto item = g_limitedworkers.begin(); item != g_limitedworkers.end(); item++) {
114             if ((*item)->IsSameWorkerEnv(env)) {
115                 worker = *item;
116             }
117         }
118     } else if (engine->IsWorkerThread()) {
119         std::lock_guard<std::mutex> lock(g_workersMutex);
120         for (auto item = g_workers.begin(); item != g_workers.end(); item++) {
121             if ((*item)->IsSameWorkerEnv(env)) {
122                 worker = *item;
123             }
124         }
125     } else {
126         return exports;
127     }
128 
129     if (worker == nullptr) {
130         ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is null when InitWorker");
131         return exports;
132     }
133 
134     napi_property_descriptor properties[] = {
135         DECLARE_NAPI_FUNCTION_WITH_DATA("postMessage", PostMessageToHost, worker),
136         DECLARE_NAPI_FUNCTION_WITH_DATA("postMessageWithSharedSendable", PostMessageWithSharedSendableToHost, worker),
137         DECLARE_NAPI_FUNCTION_WITH_DATA("callGlobalCallObjectMethod", GlobalCall, worker),
138         DECLARE_NAPI_FUNCTION_WITH_DATA("close", CloseWorker, worker),
139         DECLARE_NAPI_FUNCTION_WITH_DATA("cancelTasks", ParentPortCancelTask, worker),
140         DECLARE_NAPI_FUNCTION_WITH_DATA("addEventListener", ParentPortAddEventListener, worker),
141         DECLARE_NAPI_FUNCTION_WITH_DATA("dispatchEvent", ParentPortDispatchEvent, worker),
142         DECLARE_NAPI_FUNCTION_WITH_DATA("removeEventListener", ParentPortRemoveEventListener, worker),
143         DECLARE_NAPI_FUNCTION_WITH_DATA("removeAllListener", ParentPortRemoveAllListener, worker),
144     };
145     napi_value workerPortObj = nullptr;
146     napi_create_object(env, &workerPortObj);
147     napi_define_properties(env, workerPortObj, sizeof(properties) / sizeof(properties[0]), properties);
148 
149     // 5. register worker name in DedicatedWorkerGlobalScope
150     std::string name = worker->GetName();
151     if (!name.empty()) {
152         napi_value nameValue = nullptr;
153         napi_create_string_utf8(env, name.c_str(), name.length(), &nameValue);
154         napi_set_named_property(env, workerPortObj, "name", nameValue);
155     }
156 
157     napi_set_named_property(env, workerPortObj, "self", workerPortObj);
158 
159     if (worker->isNewVersion_) {
160         napi_set_named_property(env, exports, "workerPort", workerPortObj);
161     } else {
162         napi_set_named_property(env, exports, "parentPort", workerPortObj);
163     }
164     // register worker Port.
165     napi_create_reference(env, workerPortObj, 1, &worker->workerPort_);
166 #if defined(ENABLE_WORKER_EVENTHANDLER)
167     GetMainThreadHandler();
168 #endif
169     return exports;
170 }
171 
LimitedWorkerConstructor(napi_env env,napi_callback_info cbinfo)172 napi_value Worker::LimitedWorkerConstructor(napi_env env, napi_callback_info cbinfo)
173 {
174     if (CanCreateWorker(env, WorkerVersion::NEW)) {
175         return Constructor(env, cbinfo, true);
176     }
177     HILOG_ERROR("worker:: using both Worker and LimitedWorker is not supported");
178     ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INITIALIZATION,
179         "Using both Worker and LimitedWorker is not supported.");
180     return nullptr;
181 }
182 
ThreadWorkerConstructor(napi_env env,napi_callback_info cbinfo)183 napi_value Worker::ThreadWorkerConstructor(napi_env env, napi_callback_info cbinfo)
184 {
185     HITRACE_HELPER_METER_NAME("ThreadWorkerConstructor: [Add Thread]");
186     if (CanCreateWorker(env, WorkerVersion::NEW)) {
187         return Constructor(env, cbinfo, false, WorkerVersion::NEW);
188     }
189     HILOG_ERROR("worker:: ThreadWorker construct failed");
190     ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INITIALIZATION,
191         "Using both Worker and ThreadWorker is not supported.");
192     return nullptr;
193 }
194 
WorkerConstructor(napi_env env,napi_callback_info cbinfo)195 napi_value Worker::WorkerConstructor(napi_env env, napi_callback_info cbinfo)
196 {
197     HITRACE_HELPER_METER_NAME("WorkerConstructor: [Add Thread]");
198     if (CanCreateWorker(env, WorkerVersion::OLD)) {
199         return Constructor(env, cbinfo, false, WorkerVersion::OLD);
200     }
201     HILOG_ERROR("worker:: using both Worker and other Workers is not supported");
202     ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INITIALIZATION,
203         "Using both Worker and other Workers is not supported.");
204     return nullptr;
205 }
206 
Constructor(napi_env env,napi_callback_info cbinfo,bool limitSign,WorkerVersion version)207 napi_value Worker::Constructor(napi_env env, napi_callback_info cbinfo, bool limitSign, WorkerVersion version)
208 {
209     napi_value thisVar = nullptr;
210     void* data = nullptr;
211     size_t argc = 2;  // 2: max args number is 2
212     napi_value args[argc];
213     napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data);
214     // check argv count
215     if (argc < 1) {
216         HILOG_ERROR("worker:: the number of create worker param must be more than 1 with new");
217         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "the number of parameters must be more than 1.");
218         return nullptr;
219     }
220     // check 1st param is string
221     if (!NapiHelper::IsString(env, args[0])) {
222         HILOG_ERROR("worker:: the type of Worker 1st param must be string");
223         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
224             "the type of the first param must be string.");
225         return nullptr;
226     }
227     WorkerParams* workerParams = nullptr;
228     if (argc == 2) {  // 2: max args number is 2
229         workerParams = CheckWorkerArgs(env, args[1]);
230         if (workerParams == nullptr) {
231             HILOG_ERROR("Worker:: arguments check failed.");
232             return nullptr;
233         }
234     }
235 
236     Worker* worker = nullptr;
237     if (limitSign) {
238         std::lock_guard<std::mutex> lock(g_limitedworkersMutex);
239         if (static_cast<int>(g_limitedworkers.size()) >= MAX_LIMITEDWORKERS) {
240             HILOG_ERROR("worker:: the number of limiteworkers exceeds the maximum");
241             ErrorHelper::ThrowError(env,
242                 ErrorHelper::ERR_WORKER_INITIALIZATION, "the number of limiteworkers exceeds the maximum.");
243             return nullptr;
244         }
245 
246         // 2. new worker instance
247         worker = new Worker(env, nullptr);
248         if (worker == nullptr) {
249             HILOG_ERROR("worker:: creat worker error");
250             ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INITIALIZATION, "creat worker error");
251             return nullptr;
252         }
253         g_limitedworkers.push_back(worker);
254         HILOG_INFO("worker:: limited workers num %{public}zu", g_limitedworkers.size());
255     } else {
256         int maxWorkers = (version == WorkerVersion::NEW) ? MAX_THREAD_WORKERS : MAX_WORKERS;
257     #if defined(OHOS_PLATFORM)
258         maxWorkers = OHOS::system::GetIntParameter<int>("persist.commonlibrary.maxworkers", maxWorkers);
259     #endif
260         std::lock_guard<std::mutex> lock(g_workersMutex);
261         if (static_cast<int>(g_workers.size()) >= maxWorkers) {
262             HILOG_ERROR("worker:: the number of workers exceeds the maximum");
263             ErrorHelper::ThrowError(env,
264                 ErrorHelper::ERR_WORKER_INITIALIZATION, "the number of workers exceeds the maximum.");
265             return nullptr;
266         }
267 
268         // 2. new worker instance
269         worker = new Worker(env, nullptr);
270         if (worker == nullptr) {
271             HILOG_ERROR("worker:: creat worker error");
272             ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INITIALIZATION, "creat worker error");
273             return nullptr;
274         }
275         g_workers.push_back(worker);
276         HILOG_INFO("worker:: workers num %{public}zu", g_workers.size());
277     }
278 
279     if (workerParams != nullptr) {
280         if (!workerParams->name_.empty()) {
281             worker->name_ = workerParams->name_;
282         }
283         // default classic
284         worker->SetScriptMode(workerParams->type_);
285         CloseHelp::DeletePointer(workerParams, false);
286         workerParams = nullptr;
287     }
288     worker->isLimitedWorker_ = limitSign;
289     worker->isNewVersion_ = (version != WorkerVersion::OLD) ? true : false;
290 
291     // 3. execute in thread
292     char* script = NapiHelper::GetChars(env, args[0]);
293     if (script == nullptr) {
294         HILOG_ERROR("worker:: the file path is invaild, maybe path is null");
295         ErrorHelper::ThrowError(env,
296             ErrorHelper::ERR_WORKER_INVALID_FILEPATH, "the file path is invaild, maybe path is null.");
297         return nullptr;
298     }
299     napi_add_env_cleanup_hook(env, HostEnvCleanCallback, worker);
300     napi_status status = napi_wrap(env, thisVar, worker, WorkerDestructor, nullptr, &worker->workerRef_);
301     if (status != napi_ok) {
302         HILOG_ERROR("worker::Constructor napi_wrap return value is %{public}d", status);
303         WorkerDestructor(env, worker, nullptr);
304         return nullptr;
305     }
306     worker->StartExecuteInThread(env, script);
307     return thisVar;
308 }
309 
WorkerDestructor(napi_env env,void * data,void * hint)310 void Worker::WorkerDestructor(napi_env env, void *data, void *hint)
311 {
312     Worker* worker = static_cast<Worker*>(data);
313     if (worker == nullptr) {
314         HILOG_WARN("worker:: worker is null.");
315         return;
316     }
317     napi_remove_env_cleanup_hook(env, HostEnvCleanCallback, worker);
318     std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_);
319     if (worker->isHostEnvExited_) {
320         HILOG_INFO("worker:: host env exit.");
321         return;
322     }
323     if (worker->UpdateHostState(INACTIVE)) {
324 #if defined(ENABLE_WORKER_EVENTHANDLER)
325         if (!worker->isMainThreadWorker_ || worker->isLimitedWorker_) {
326             worker->CloseHostHandle();
327         }
328 #else
329         worker->CloseHostHandle();
330 #endif
331         worker->ReleaseHostThreadContent();
332     }
333     if (!worker->IsRunning()) {
334         HILOG_DEBUG("worker:: worker is not running");
335         return;
336     }
337     worker->TerminateInner();
338 }
339 
HostEnvCleanCallback(void * data)340 void Worker::HostEnvCleanCallback(void *data)
341 {
342     Worker* worker = static_cast<Worker*>(data);
343     if (worker == nullptr) {
344         HILOG_INFO("worker:: worker is nullptr when host env exit.");
345         return;
346     }
347     if (!IsValidWorker(worker)) {
348         HILOG_INFO("worker:: worker is terminated when host env exit.");
349         return;
350     }
351     std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_);
352     worker->isHostEnvExited_ = true;
353 #if defined(ENABLE_WORKER_EVENTHANDLER)
354     if (!worker->isMainThreadWorker_ || worker->isLimitedWorker_) {
355         worker->CloseHostHandle();
356     }
357 #else
358     worker->CloseHostHandle();
359 #endif
360     worker->ReleaseHostThreadContent();
361     worker->RemoveAllListenerInner();
362     worker->ClearGlobalCallObject();
363 }
364 
CheckWorkerArgs(napi_env env,napi_value argsValue)365 Worker::WorkerParams* Worker::CheckWorkerArgs(napi_env env, napi_value argsValue)
366 {
367     WorkerParams* workerParams = nullptr;
368     if (NapiHelper::IsObject(env, argsValue)) {
369         workerParams = new WorkerParams();
370         napi_value nameValue = NapiHelper::GetNameProperty(env, argsValue, "name");
371         if (NapiHelper::IsNotUndefined(env, nameValue)) {
372             if (!NapiHelper::IsString(env, nameValue)) {
373                 CloseHelp::DeletePointer(workerParams, false);
374                 WorkerThrowError(env, ErrorHelper::TYPE_ERROR, "the type of name must be string.");
375                 return nullptr;
376             }
377             char* nameStr = NapiHelper::GetChars(env, nameValue);
378             if (nameStr == nullptr) {
379                 CloseHelp::DeletePointer(workerParams, false);
380                 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INITIALIZATION, "the name of worker is null.");
381                 return nullptr;
382             }
383             workerParams->name_ = std::string(nameStr);
384             CloseHelp::DeletePointer(nameStr, true);
385         }
386         napi_value typeValue = NapiHelper::GetNameProperty(env, argsValue, "type");
387         if (NapiHelper::IsNotUndefined(env, typeValue)) {
388             if (!NapiHelper::IsString(env, typeValue)) {
389                 CloseHelp::DeletePointer(workerParams, false);
390                 WorkerThrowError(env, ErrorHelper::TYPE_ERROR,
391                     "the type of type's value must be string.");
392                 return nullptr;
393             }
394             char* typeStr = NapiHelper::GetChars(env, typeValue);
395             if (typeStr == nullptr) {
396                 CloseHelp::DeletePointer(workerParams, false);
397                 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INITIALIZATION, "the type of worker is null.");
398                 return nullptr;
399             }
400             if (strcmp("classic", typeStr) == 0) {
401                 workerParams->type_ = CLASSIC;
402                 CloseHelp::DeletePointer(typeStr, true);
403             } else {
404                 CloseHelp::DeletePointer(workerParams, false);
405                 CloseHelp::DeletePointer(typeStr, true);
406                 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
407                     "the type must be classic, unsupport others now.");
408                 return nullptr;
409             }
410         }
411     }
412     return workerParams;
413 }
414 
PostMessage(napi_env env,napi_callback_info cbinfo)415 napi_value Worker::PostMessage(napi_env env, napi_callback_info cbinfo)
416 {
417     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
418     return CommonPostMessage(env, cbinfo, true);
419 }
420 
PostMessageWithSharedSendable(napi_env env,napi_callback_info cbinfo)421 napi_value Worker::PostMessageWithSharedSendable(napi_env env, napi_callback_info cbinfo)
422 {
423     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
424     return CommonPostMessage(env, cbinfo, false);
425 }
426 
CommonPostMessage(napi_env env,napi_callback_info cbinfo,bool cloneSendable)427 napi_value Worker::CommonPostMessage(napi_env env, napi_callback_info cbinfo, bool cloneSendable)
428 {
429     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
430     size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
431     if (argc < 1) {
432         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
433             "Worker messageObject must be not null with postMessage");
434         return nullptr;
435     }
436     napi_value* argv = new napi_value[argc];
437     ObjectScope<napi_value> scope(argv, true);
438     napi_value thisVar = nullptr;
439     napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, nullptr);
440     Worker* worker = nullptr;
441     napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
442 
443     if (worker == nullptr || worker->IsTerminated() || worker->IsTerminating()) {
444         HILOG_ERROR("worker:: worker is nullptr when PostMessage, maybe worker is terminated");
445         WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "maybe worker is terminated when PostMessage");
446         return nullptr;
447     }
448 
449     MessageDataType data = nullptr;
450     napi_status serializeStatus = napi_ok;
451     bool defaultClone = cloneSendable ? true : false;
452     napi_value undefined = NapiHelper::GetUndefinedValue(env);
453     if (argc >= NUM_WORKER_ARGS) {
454         if (!NapiHelper::IsArray(env, argv[1])) {
455             ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
456                 "the type of the transfer list must be an array.");
457             return nullptr;
458         }
459         serializeStatus = napi_serialize_inner(env, argv[0], argv[1], undefined, false, defaultClone, &data);
460     } else {
461         serializeStatus = napi_serialize_inner(env, argv[0], undefined, undefined, false, defaultClone, &data);
462     }
463     if (serializeStatus != napi_ok || data == nullptr) {
464         worker->HostOnMessageErrorInner();
465         WorkerThrowError(env, ErrorHelper::ERR_WORKER_SERIALIZATION, "failed to serialize message.");
466         return nullptr;
467     }
468     worker->PostMessageInner(data);
469     return NapiHelper::GetUndefinedValue(env);
470 }
471 
Terminate(napi_env env,napi_callback_info cbinfo)472 napi_value Worker::Terminate(napi_env env, napi_callback_info cbinfo)
473 {
474     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
475     napi_value thisVar = nullptr;
476     napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr);
477     Worker* worker = nullptr;
478     napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
479     if (worker == nullptr) {
480         HILOG_ERROR("worker:: worker is nullptr when Terminate, maybe worker is terminated");
481         WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is nullptr when Terminate");
482         return nullptr;
483     }
484     bool expected = false;
485     if (worker->isTerminated_.compare_exchange_weak(expected, true)) {
486         HILOG_INFO("worker:: Terminate worker");
487     } else {
488         HILOG_DEBUG("worker:: worker is terminated when Terminate");
489         return nullptr;
490     }
491     if (worker->IsTerminated() || worker->IsTerminating()) {
492         HILOG_DEBUG("worker:: worker is not in running when Terminate");
493         return nullptr;
494     }
495     worker->TerminateInner();
496     return NapiHelper::GetUndefinedValue(env);
497 }
498 
On(napi_env env,napi_callback_info cbinfo)499 napi_value Worker::On(napi_env env, napi_callback_info cbinfo)
500 {
501     return AddListener(env, cbinfo, PERMANENT);
502 }
503 
Once(napi_env env,napi_callback_info cbinfo)504 napi_value Worker::Once(napi_env env, napi_callback_info cbinfo)
505 {
506     return AddListener(env, cbinfo, ONCE);
507 }
508 
RegisterGlobalCallObject(napi_env env,napi_callback_info cbinfo)509 napi_value Worker::RegisterGlobalCallObject(napi_env env, napi_callback_info cbinfo)
510 {
511     size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
512     if (argc != NUM_WORKER_ARGS) {
513         ErrorHelper::ThrowError(env,
514             ErrorHelper::TYPE_ERROR, "the number of parameters must be 2.");
515         return nullptr;
516     }
517     // check 1st param is string
518     napi_value thisVar = nullptr;
519     void* data = nullptr;
520     napi_value* args = new napi_value[argc];
521     ObjectScope<napi_value> scope(args, true);
522     napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data);
523     if (!NapiHelper::IsString(env, args[0])) {
524         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
525             "the type of instanceName must be string.");
526         return nullptr;
527     }
528     std::string instanceName = NapiHelper::GetString(env, args[0]);
529 
530     Worker* worker = nullptr;
531     napi_unwrap(env, thisVar, (void**)&worker);
532     if (worker == nullptr) {
533         ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "maybe worker is terminated");
534         return nullptr;
535     }
536     napi_ref obj = NapiHelper::CreateReference(env, args[1], 1);
537     worker->AddGlobalCallObject(instanceName, obj);
538     return nullptr;
539 }
540 
UnregisterGlobalCallObject(napi_env env,napi_callback_info cbinfo)541 napi_value Worker::UnregisterGlobalCallObject(napi_env env, napi_callback_info cbinfo)
542 {
543     size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
544     if (argc > 1) {
545         ErrorHelper::ThrowError(env,
546             ErrorHelper::TYPE_ERROR, "the number of the parameters must be 1 or 0.");
547         return nullptr;
548     }
549     napi_value thisVar = nullptr;
550     void* data = nullptr;
551     napi_value* args = new napi_value[argc];
552     ObjectScope<napi_value> scope(args, true);
553     napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data);
554     Worker* worker = nullptr;
555     napi_unwrap(env, thisVar, (void**)&worker);
556     if (worker == nullptr) {
557         ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "maybe worker is terminated");
558         return nullptr;
559     }
560     if (argc == 0) {
561         worker->ClearGlobalCallObject();
562         HILOG_DEBUG("worker:: clear all registered globalCallObject");
563         return nullptr;
564     }
565     // check 1st param is string
566     if (!NapiHelper::IsString(env, args[0])) {
567         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
568             "the type of instanceName must be string.");
569         return nullptr;
570     }
571     std::string instanceName = NapiHelper::GetString(env, args[0]);
572     if (!worker->RemoveGlobalCallObject(instanceName)) {
573         HILOG_ERROR("worker:: unregister unexist globalCallObject");
574     }
575     return nullptr;
576 }
577 
Off(napi_env env,napi_callback_info cbinfo)578 napi_value Worker::Off(napi_env env, napi_callback_info cbinfo)
579 {
580     return RemoveListener(env, cbinfo);
581 }
582 
RemoveEventListener(napi_env env,napi_callback_info cbinfo)583 napi_value Worker::RemoveEventListener(napi_env env, napi_callback_info cbinfo)
584 {
585     return RemoveListener(env, cbinfo);
586 }
587 
AddEventListener(napi_env env,napi_callback_info cbinfo)588 napi_value Worker::AddEventListener(napi_env env, napi_callback_info cbinfo)
589 {
590     return AddListener(env, cbinfo, PERMANENT);
591 }
592 
AddListener(napi_env env,napi_callback_info cbinfo,ListenerMode mode)593 napi_value Worker::AddListener(napi_env env, napi_callback_info cbinfo, ListenerMode mode)
594 {
595     size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
596     if (argc < NUM_WORKER_ARGS) {
597         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
598             "the number of listener parameters is not less than 2.");
599         return nullptr;
600     }
601     // check 1st param is string
602     napi_value thisVar = nullptr;
603     void* data = nullptr;
604     napi_value* args = new napi_value[argc];
605     ObjectScope<napi_value> scope(args, true);
606     napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data);
607     if (!NapiHelper::IsString(env, args[0])) {
608         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
609             "the type of listener first param must be string.");
610         return nullptr;
611     }
612     if (!NapiHelper::IsCallable(env, args[1])) {
613         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
614             "the type of listener the second param must be callable.");
615         return nullptr;
616     }
617     Worker* worker = nullptr;
618     napi_unwrap(env, thisVar, (void**)&worker);
619     if (worker == nullptr) {
620         HILOG_ERROR("worker:: worker is nullptr when addListener, maybe worker is terminated");
621         WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "maybe worker is terminated");
622         return nullptr;
623     }
624 
625     napi_ref callback = NapiHelper::CreateReference(env, args[1], 1);
626     auto listener = new WorkerListener(env, callback, mode);
627     if (mode == ONCE && argc > NUM_WORKER_ARGS) {
628         if (NapiHelper::IsObject(env, args[NUM_WORKER_ARGS])) {
629             napi_value onceValue = NapiHelper::GetNameProperty(env, args[NUM_WORKER_ARGS], "once");
630             bool isOnce = NapiHelper::GetBooleanValue(env, onceValue);
631             if (!isOnce) {
632                 listener->SetMode(PERMANENT);
633             }
634         }
635     }
636     char* typeStr = NapiHelper::GetChars(env, args[0]);
637     worker->AddListenerInner(env, typeStr, listener);
638     CloseHelp::DeletePointer(typeStr, true);
639     return NapiHelper::GetUndefinedValue(env);
640 }
641 
RemoveListener(napi_env env,napi_callback_info cbinfo)642 napi_value Worker::RemoveListener(napi_env env, napi_callback_info cbinfo)
643 {
644     size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
645     if (argc < 1) {
646         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
647             "the number of parameters is not less than 1.");
648         return nullptr;
649     }
650     // check 1st param is string
651     napi_value thisVar = nullptr;
652     void* data = nullptr;
653     napi_value* args = new napi_value[argc];
654     ObjectScope<napi_value> scope(args, true);
655     napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data);
656     if (!NapiHelper::IsString(env, args[0])) {
657         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
658             "the type of removelistener the first param must be string.");
659         return nullptr;
660     }
661 
662     Worker* worker = nullptr;
663     napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
664     if (worker == nullptr) {
665         HILOG_ERROR("worker:: worker is nullptr when RemoveListener, maybe worker is terminated");
666         WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "maybe worker is terminated");
667         return nullptr;
668     }
669 
670     if (argc > 1 && !NapiHelper::IsCallable(env, args[1])) {
671         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
672             "the type of removelistener the second param must be callable.");
673         return nullptr;
674     }
675 
676     char* typeStr = NapiHelper::GetChars(env, args[0]);
677     if (typeStr == nullptr) {
678         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "the type of remove listener type must be not null");
679         return nullptr;
680     }
681 
682     napi_ref callback = nullptr;
683     if (argc > 1 && NapiHelper::IsCallable(env, args[1])) {
684         napi_create_reference(env, args[1], 1, &callback);
685     }
686     worker->RemoveListenerInner(env, typeStr, callback);
687     CloseHelp::DeletePointer(typeStr, true);
688     NapiHelper::DeleteReference(env, callback);
689     return NapiHelper::GetUndefinedValue(env);
690 }
691 
CallWorkCallback(napi_env env,napi_value recv,size_t argc,const napi_value * argv,const char * type)692 void CallWorkCallback(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type)
693 {
694     napi_value callback = nullptr;
695     napi_get_named_property(env, recv, type, &callback);
696     if (NapiHelper::IsCallable(env, callback)) {
697         napi_value callbackResult = nullptr;
698         napi_call_function(env, recv, callback, argc, argv, &callbackResult);
699     }
700 }
701 
DispatchEvent(napi_env env,napi_callback_info cbinfo)702 napi_value Worker::DispatchEvent(napi_env env, napi_callback_info cbinfo)
703 {
704     size_t argc = 1;
705     napi_value args[1];
706     napi_value thisVar = nullptr;
707     void* data = nullptr;
708     napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data);
709     if (argc < 1) {
710         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "the number of the parameters must be more than 1.");
711         return NapiHelper::CreateBooleanValue(env, false);
712     }
713 
714     // check 1st param is event
715     if (!NapiHelper::IsObject(env, args[0])) {
716         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
717             "the type of DispatchEvent first param must be event object.");
718         return NapiHelper::CreateBooleanValue(env, false);
719     }
720 
721     Worker* worker = nullptr;
722     napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
723     if (worker == nullptr) {
724         HILOG_ERROR("worker:: worker is nullptr when DispatchEvent, maybe worker is terminated");
725         WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "maybe worker has been terminated");
726         return NapiHelper::CreateBooleanValue(env, false);
727     }
728 
729     napi_value typeValue = NapiHelper::GetNameProperty(env, args[0], "type");
730     if (!NapiHelper::IsString(env, typeValue)) {
731         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
732             "the type of event type must be string.");
733         return NapiHelper::CreateBooleanValue(env, false);
734     }
735 
736     napi_value obj = NapiHelper::GetReferenceValue(env, worker->workerRef_);
737 
738     char* typeStr = NapiHelper::GetChars(env, typeValue);
739     if (typeStr == nullptr) {
740         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "dispatchEvent event type must be not null");
741         return NapiHelper::CreateBooleanValue(env, false);
742     }
743     if (strcmp(typeStr, "error") == 0) {
744         CallWorkCallback(env, obj, 1, args, "onerror");
745     } else if (strcmp(typeStr, "messageerror") == 0) {
746         CallWorkCallback(env, obj, 1, args, "onmessageerror");
747     } else if (strcmp(typeStr, "message") == 0) {
748         CallWorkCallback(env, obj, 1, args, "onmessage");
749     }
750 
751     worker->HandleEventListeners(env, obj, 1, args, typeStr);
752 
753     CloseHelp::DeletePointer(typeStr, true);
754     return NapiHelper::CreateBooleanValue(env, true);
755 }
756 
RemoveAllListener(napi_env env,napi_callback_info cbinfo)757 napi_value Worker::RemoveAllListener(napi_env env, napi_callback_info cbinfo)
758 {
759     napi_value thisVar = nullptr;
760     napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr);
761     Worker* worker = nullptr;
762     napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
763     if (worker == nullptr) {
764         HILOG_ERROR("worker:: worker is nullptr when RemoveAllListener, maybe worker is terminated");
765         WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "maybe worker is terminated");
766         return nullptr;
767     }
768 
769     worker->RemoveAllListenerInner();
770     return NapiHelper::GetUndefinedValue(env);
771 }
772 
CancelTask(napi_env env,napi_callback_info cbinfo)773 napi_value Worker::CancelTask(napi_env env, napi_callback_info cbinfo)
774 {
775     napi_value thisVar = nullptr;
776     napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr);
777     Worker* worker = nullptr;
778     napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
779     if (worker == nullptr) {
780         HILOG_ERROR("worker:: worker is nullptr when CancelTask, maybe worker is terminated");
781         return nullptr;
782     }
783 
784     if (worker->IsTerminated() || worker->IsTerminating()) {
785         HILOG_INFO("worker:: worker is not in running");
786         return nullptr;
787     }
788 
789     if (!worker->ClearWorkerTasks()) {
790         HILOG_ERROR("worker:: clear worker task error");
791     }
792     return NapiHelper::GetUndefinedValue(env);
793 }
794 
PostMessageToHost(napi_env env,napi_callback_info cbinfo)795 napi_value Worker::PostMessageToHost(napi_env env, napi_callback_info cbinfo)
796 {
797     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
798     return CommonPostMessageToHost(env, cbinfo, true);
799 }
800 
PostMessageWithSharedSendableToHost(napi_env env,napi_callback_info cbinfo)801 napi_value Worker::PostMessageWithSharedSendableToHost(napi_env env, napi_callback_info cbinfo)
802 {
803     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
804     return CommonPostMessageToHost(env, cbinfo, false);
805 }
806 
CommonPostMessageToHost(napi_env env,napi_callback_info cbinfo,bool cloneSendable)807 napi_value Worker::CommonPostMessageToHost(napi_env env, napi_callback_info cbinfo, bool cloneSendable)
808 {
809     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
810     size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
811     if (argc < 1) {
812         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "the number of parameters must be more than 1.");
813         return nullptr;
814     }
815     napi_value* argv = new napi_value[argc];
816     ObjectScope<napi_value> scope(argv, true);
817     Worker* worker = nullptr;
818     napi_get_cb_info(env, cbinfo, &argc, argv, nullptr, reinterpret_cast<void**>(&worker));
819 
820     if (worker == nullptr) {
821         HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
822         WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is nullptr when post message to host");
823         return nullptr;
824     }
825 
826     if (!worker->IsRunning()) {
827         // if worker is not running, don't send any message to host thread
828         HILOG_DEBUG("worker:: when post message to host occur worker is not in running.");
829         return nullptr;
830     }
831 
832     MessageDataType data = nullptr;
833     napi_status serializeStatus = napi_ok;
834     bool defaultClone = cloneSendable ? true : false;
835     napi_value undefined = NapiHelper::GetUndefinedValue(env);
836     if (argc >= NUM_WORKER_ARGS) {
837         if (!NapiHelper::IsArray(env, argv[1])) {
838             ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "Transfer list must be an Array");
839             return nullptr;
840         }
841         serializeStatus = napi_serialize_inner(env, argv[0], argv[1], undefined, false, defaultClone, &data);
842     } else {
843         napi_value undefined = NapiHelper::GetUndefinedValue(env);
844         serializeStatus = napi_serialize_inner(env, argv[0], undefined, undefined, false, defaultClone, &data);
845     }
846 
847     if (serializeStatus != napi_ok || data == nullptr) {
848         worker->WorkerOnMessageErrorInner();
849         WorkerThrowError(env, ErrorHelper::ERR_WORKER_SERIALIZATION, "failed to serialize message.");
850         return nullptr;
851     }
852     worker->PostMessageToHostInner(data);
853     return NapiHelper::GetUndefinedValue(env);
854 }
855 
GlobalCall(napi_env env,napi_callback_info cbinfo)856 napi_value Worker::GlobalCall(napi_env env, napi_callback_info cbinfo)
857 {
858     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
859     size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
860     if (argc < NUM_GLOBAL_CALL_ARGS) {
861         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
862             "the number of parameters must be equal or more than 3.");
863         return nullptr;
864     }
865     napi_value* args = new napi_value[argc];
866     ObjectScope<napi_value> scope(args, true);
867     Worker* worker = nullptr;
868     napi_get_cb_info(env, cbinfo, &argc, args, nullptr, reinterpret_cast<void**>(&worker));
869     if (worker == nullptr) {
870         HILOG_ERROR("worker:: worker is null when callGlobalCallObjectMethod to host");
871         ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING,
872             "worker is null when callGlobalCallObjectMethod to host");
873         return nullptr;
874     }
875 
876     if (!worker->IsRunning()) {
877         // if worker is not running, don't send any message to host thread
878         HILOG_DEBUG("worker:: when post message to host occur worker is not in running.");
879         return nullptr;
880     }
881 
882     if (!NapiHelper::IsString(env, args[0])) {
883         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
884             "the type of instanceName must be string.");
885         return nullptr;
886     }
887     if (!NapiHelper::IsString(env, args[1])) {
888         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
889             "the type of methodname must be string.");
890         return nullptr;
891     }
892     if (!NapiHelper::IsNumber(env, args[2])) { // 2: the index of argument "timeout"
893         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
894             "the type of timeout must be number.");
895         return nullptr;
896     }
897 
898     napi_status serializeStatus = napi_ok;
899     MessageDataType data = nullptr;
900     napi_value argsArray;
901     napi_create_array_with_length(env, argc - 1, &argsArray);
902     size_t index = 0;
903     uint32_t timeout = 0;
904     for (size_t i = 0; i < argc; i++) {
905         if (i == 2) { // 2: index of time limitation arg
906             timeout = NapiHelper::GetUint32Value(env, args[i]);
907             continue;
908         }
909         napi_set_element(env, argsArray, index, args[i]);
910         index++;
911     }
912     if (timeout <= 0 || timeout > DEFAULT_TIMEOUT) {
913         timeout = DEFAULT_TIMEOUT;
914     }
915 
916     // defautly not transfer
917     napi_value undefined = NapiHelper::GetUndefinedValue(env);
918     // meaningless to copy sendable object when call globalObject
919     bool defaultClone = true;
920     bool defaultTransfer = false;
921     serializeStatus = napi_serialize_inner(env, argsArray, undefined, undefined, defaultTransfer, defaultClone, &data);
922     if (serializeStatus != napi_ok || data == nullptr) {
923         ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_SERIALIZATION, "failed to serialize message.");
924         return nullptr;
925     }
926     worker->hostGlobalCallQueue_.Push(worker->globalCallId_, data);
927 
928     std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_);
929     if (env != nullptr && !worker->HostIsStop() && !worker->isHostEnvExited_) {
930         worker->InitGlobalCallStatus(env);
931 #if defined(ENABLE_WORKER_EVENTHANDLER)
932         if (worker->isMainThreadWorker_ && !worker->isLimitedWorker_) {
933             worker->PostWorkerGlobalCallTask();
934         } else {
935             uv_async_send(worker->hostOnGlobalCallSignal_);
936         }
937 #else
938         uv_async_send(worker->hostOnGlobalCallSignal_);
939 #endif
940     } else {
941         HILOG_ERROR("worker:: worker host engine is nullptr when callGloballCallObjectMethod.");
942         ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is null");
943         return nullptr;
944     }
945 
946     {
947         std::unique_lock lock(worker->globalCallMutex_);
948         if (!worker->cv_.wait_for(lock, std::chrono::milliseconds(timeout), [worker]() {
949             return !worker->workerGlobalCallQueue_.IsEmpty() || !worker->globalCallSuccess_;
950         })) {
951             worker->IncreaseGlobalCallId();
952             HILOG_ERROR("worker:: callGlobalCallObjectMethod has exceeded the waiting time limitation, skip this turn");
953             ErrorHelper::ThrowError(env, ErrorHelper::ERR_GLOBAL_CALL_TIMEOUT);
954             return nullptr;
955         }
956     }
957     worker->IncreaseGlobalCallId();
958     if (!worker->globalCallSuccess_) {
959         worker->HandleGlobalCallError(env);
960         return nullptr;
961     }
962     if (!worker->workerGlobalCallQueue_.DeQueue(&data)) {
963         HILOG_ERROR("worker:: message returned from host is empty when callGloballCallObjectMethod");
964         return nullptr;
965     }
966     napi_value res = nullptr;
967     serializeStatus = napi_deserialize(env, data, &res);
968     napi_delete_serialization_data(env, data);
969     if (serializeStatus != napi_ok || res == nullptr) {
970         ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_SERIALIZATION, "failed to serialize message.");
971         return nullptr;
972     }
973     return res;
974 }
975 
InitGlobalCallStatus(napi_env env)976 void Worker::InitGlobalCallStatus(napi_env env)
977 {
978     // worker side event data queue shall be empty before uv_async_send
979     workerGlobalCallQueue_.Clear(env);
980     ClearGlobalCallError(env);
981     globalCallSuccess_ = true;
982 }
983 
IncreaseGlobalCallId()984 void Worker::IncreaseGlobalCallId()
985 {
986     if (UNLIKELY(globalCallId_ == GLOBAL_CALL_ID_MAX)) {
987         globalCallId_ = 1;
988     } else {
989         globalCallId_++;
990     }
991 }
992 
CloseWorker(napi_env env,napi_callback_info cbinfo)993 napi_value Worker::CloseWorker(napi_env env, napi_callback_info cbinfo)
994 {
995     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
996     Worker* worker = nullptr;
997     napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, (void**)&worker);
998     if (worker != nullptr) {
999         worker->CloseInner();
1000     } else {
1001         WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is null");
1002         return nullptr;
1003     }
1004     return NapiHelper::GetUndefinedValue(env);
1005 }
1006 
ParentPortCancelTask(napi_env env,napi_callback_info cbinfo)1007 napi_value Worker::ParentPortCancelTask(napi_env env, napi_callback_info cbinfo)
1008 {
1009     Worker* worker = nullptr;
1010     napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, reinterpret_cast<void**>(&worker));
1011     if (worker == nullptr) {
1012         HILOG_ERROR("worker:: worker is nullptr when CancelTask, maybe worker is terminated");
1013         return nullptr;
1014     }
1015 
1016     if (worker->IsTerminated() || worker->IsTerminating()) {
1017         HILOG_INFO("worker:: worker is not in running");
1018         return nullptr;
1019     }
1020 
1021     if (!worker->ClearWorkerTasks()) {
1022         HILOG_ERROR("worker:: clear worker task error");
1023     }
1024     return NapiHelper::GetUndefinedValue(env);
1025 }
1026 
ParentPortAddEventListener(napi_env env,napi_callback_info cbinfo)1027 napi_value Worker::ParentPortAddEventListener(napi_env env, napi_callback_info cbinfo)
1028 {
1029     size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
1030     if (argc < NUM_WORKER_ARGS) {
1031         ErrorHelper::ThrowError(env,
1032             ErrorHelper::TYPE_ERROR, "worker listener param count must be more than WORKPARAMNUM.");
1033         return nullptr;
1034     }
1035 
1036     napi_value* args = new napi_value[argc];
1037     ObjectScope<napi_value> scope(args, true);
1038     Worker* worker = nullptr;
1039     napi_get_cb_info(env, cbinfo, &argc, args, nullptr, reinterpret_cast<void**>(&worker));
1040 
1041     if (!NapiHelper::IsString(env, args[0])) {
1042         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
1043             "the type of worker listener first param must be string.");
1044         return nullptr;
1045     }
1046 
1047     if (!NapiHelper::IsCallable(env, args[1])) {
1048         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
1049             "the type of worker listener second param must be callable.");
1050         return nullptr;
1051     }
1052 
1053     if (worker == nullptr || !worker->IsNotTerminate()) {
1054         HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
1055         WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is not running.");
1056         return nullptr;
1057     }
1058 
1059     napi_ref callback = NapiHelper::CreateReference(env, args[1], 1);
1060     auto listener = new WorkerListener(env, callback, PERMANENT);
1061     if (argc > NUM_WORKER_ARGS && NapiHelper::IsObject(env, args[NUM_WORKER_ARGS])) {
1062         napi_value onceValue = NapiHelper::GetNameProperty(env, args[NUM_WORKER_ARGS], "once");
1063         bool isOnce = NapiHelper::GetBooleanValue(env, onceValue);
1064         if (isOnce) {
1065             listener->SetMode(ONCE);
1066         }
1067     }
1068     char* typeStr = NapiHelper::GetChars(env, args[0]);
1069     worker->ParentPortAddListenerInner(env, typeStr, listener);
1070     CloseHelp::DeletePointer(typeStr, true);
1071     return NapiHelper::GetUndefinedValue(env);
1072 }
1073 
ParentPortDispatchEvent(napi_env env,napi_callback_info cbinfo)1074 napi_value Worker::ParentPortDispatchEvent(napi_env env, napi_callback_info cbinfo)
1075 {
1076     size_t argc = 1;
1077     napi_value args[1];
1078     Worker* worker = nullptr;
1079     napi_get_cb_info(env, cbinfo, &argc, args, nullptr, reinterpret_cast<void**>(&worker));
1080     if (argc < 1) {
1081         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "DispatchEvent param count must be more than 1.");
1082         return NapiHelper::CreateBooleanValue(env, false);
1083     }
1084 
1085     if (!NapiHelper::IsObject(env, args[0])) {
1086         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
1087             "the type of worker DispatchEvent first param must be Event.");
1088         return NapiHelper::CreateBooleanValue(env, false);
1089     }
1090 
1091     napi_value typeValue = NapiHelper::GetNameProperty(env, args[0], "type");
1092     if (!NapiHelper::IsString(env, typeValue)) {
1093         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
1094             "the type of worker event must be string.");
1095         return NapiHelper::CreateBooleanValue(env, false);
1096     }
1097 
1098     if (worker == nullptr || !worker->IsNotTerminate()) {
1099         HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
1100         WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is nullptr.");
1101         return NapiHelper::CreateBooleanValue(env, false);
1102     }
1103 
1104     char* typeStr = NapiHelper::GetChars(env, typeValue);
1105     if (typeStr == nullptr) {
1106         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "worker listener type must be not null.");
1107         return NapiHelper::CreateBooleanValue(env, false);
1108     }
1109 
1110     napi_value obj = NapiHelper::GetReferenceValue(env, worker->workerPort_);
1111 
1112     if (strcmp(typeStr, "error") == 0) {
1113         CallWorkCallback(env, obj, 1, args, "onerror");
1114     } else if (strcmp(typeStr, "messageerror") == 0) {
1115         CallWorkCallback(env, obj, 1, args, "onmessageerror");
1116     } else if (strcmp(typeStr, "message") == 0) {
1117         CallWorkCallback(env, obj, 1, args, "onmessage");
1118     }
1119 
1120     worker->ParentPortHandleEventListeners(env, obj, 1, args, typeStr, true);
1121 
1122     CloseHelp::DeletePointer(typeStr, true);
1123     return NapiHelper::CreateBooleanValue(env, true);
1124 }
1125 
ParentPortRemoveEventListener(napi_env env,napi_callback_info cbinfo)1126 napi_value Worker::ParentPortRemoveEventListener(napi_env env, napi_callback_info cbinfo)
1127 {
1128     size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo);
1129     if (argc < 1) {
1130         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "the number of parameters must be more than 2.");
1131         return nullptr;
1132     }
1133 
1134     napi_value* args = new napi_value[argc];
1135     ObjectScope<napi_value> scope(args, true);
1136     Worker* worker = nullptr;
1137     napi_get_cb_info(env, cbinfo, &argc, args, nullptr, reinterpret_cast<void**>(&worker));
1138 
1139     if (!NapiHelper::IsString(env, args[0])) {
1140         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "the type of worker listener 1st param must be string.");
1141         return nullptr;
1142     }
1143 
1144     if (argc > 1 && !NapiHelper::IsCallable(env, args[1])) {
1145         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR,
1146             "the type of worker listener second param must be callable.");
1147         return nullptr;
1148     }
1149 
1150     if (worker == nullptr || !worker->IsNotTerminate()) {
1151         HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
1152         WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is not running.");
1153         return nullptr;
1154     }
1155 
1156     napi_ref callback = nullptr;
1157     if (argc > 1 && NapiHelper::IsCallable(env, args[1])) {
1158         napi_create_reference(env, args[1], 1, &callback);
1159     }
1160 
1161     char* typeStr = NapiHelper::GetChars(env, args[0]);
1162     if (typeStr == nullptr) {
1163         ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "worker listener type must be not null.");
1164         return nullptr;
1165     }
1166     worker->ParentPortRemoveListenerInner(env, typeStr, callback);
1167     CloseHelp::DeletePointer(typeStr, true);
1168     NapiHelper::DeleteReference(env, callback);
1169     return NapiHelper::GetUndefinedValue(env);
1170 }
1171 
ParentPortRemoveAllListener(napi_env env,napi_callback_info cbinfo)1172 napi_value Worker::ParentPortRemoveAllListener(napi_env env, napi_callback_info cbinfo)
1173 {
1174     Worker* worker = nullptr;
1175     napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, reinterpret_cast<void**>(&worker));
1176 
1177     if (worker == nullptr || !worker->IsNotTerminate()) {
1178         HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
1179         WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING,
1180             "worker is nullptr when ParentPortRemoveAllListener");
1181         return nullptr;
1182     }
1183 
1184     worker->ParentPortRemoveAllListenerInner();
1185     return NapiHelper::GetUndefinedValue(env);
1186 }
1187 
GetContainerScopeId(napi_env env)1188 void Worker::GetContainerScopeId(napi_env env)
1189 {
1190     NativeEngine* hostEngine = reinterpret_cast<NativeEngine*>(env);
1191     scopeId_ = hostEngine->GetContainerScopeIdFunc();
1192 }
1193 
AddGlobalCallObject(const std::string & instanceName,napi_ref obj)1194 void Worker::AddGlobalCallObject(const std::string &instanceName, napi_ref obj)
1195 {
1196     globalCallObjects_.insert_or_assign(instanceName, obj);
1197 }
1198 
RemoveGlobalCallObject(const std::string & instanceName)1199 bool Worker::RemoveGlobalCallObject(const std::string &instanceName)
1200 {
1201     for (auto iter = globalCallObjects_.begin(); iter != globalCallObjects_.end(); iter++) {
1202         if (iter->first == instanceName) {
1203             NapiHelper::DeleteReference(hostEnv_, iter->second);
1204             globalCallObjects_.erase(iter);
1205             return true;
1206         }
1207     }
1208     return false;
1209 }
1210 
ClearGlobalCallObject()1211 void Worker::ClearGlobalCallObject()
1212 {
1213     for (auto iter = globalCallObjects_.begin(); iter != globalCallObjects_.end(); iter++) {
1214         napi_ref objRef = iter->second;
1215         NapiHelper::DeleteReference(hostEnv_, objRef);
1216     }
1217     globalCallObjects_.clear();
1218 }
1219 
StartExecuteInThread(napi_env env,const char * script)1220 void Worker::StartExecuteInThread(napi_env env, const char* script)
1221 {
1222     HILOG_INFO("worker:: Start execute in the thread!");
1223     // 1. init hostHandle in host loop
1224     uv_loop_t* loop = NapiHelper::GetLibUV(env);
1225     if (loop == nullptr) {
1226         ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "engine loop is null");
1227         CloseHelp::DeletePointer(script, true);
1228         return;
1229     }
1230     GetContainerScopeId(env);
1231 #if defined(ENABLE_WORKER_EVENTHANDLER)
1232     if (!OHOS::AppExecFwk::EventRunner::IsAppMainThread()) {
1233         isMainThreadWorker_ = false;
1234         InitHostHandle(loop);
1235     } else if (isLimitedWorker_) {
1236         InitHostHandle(loop);
1237     }
1238 #else
1239     InitHostHandle(loop);
1240 #endif
1241 
1242     // 2. copy the script
1243     script_ = std::string(script);
1244     // isBundle : FA mode and BundlePack.
1245     bool isBundle = reinterpret_cast<NativeEngine*>(env)->GetIsBundle();
1246     // if worker file is packed in har, need find moduleName in hostVM, and concat new recordName.
1247     bool isHar = script_.find_first_of(PathHelper::NAME_SPACE_TAG) == 0;
1248     if ((isHar && script_.find(PathHelper::PREFIX_BUNDLE) == std::string::npos) ||
1249         (!isBundle && script_.find_first_of(PathHelper::POINT_TAG) == 0)) {
1250         PathHelper::ConcatFileNameForWorker(env, script_, fileName_, isRelativePath_);
1251         HILOG_INFO("worker:: Concated worker recordName: %{public}s, fileName: %{public}s",
1252                    script_.c_str(), fileName_.c_str());
1253     }
1254     // check the path is vaild.
1255     if (!isBundle) {
1256         if (!PathHelper::CheckWorkerPath(env, script_, fileName_, isRelativePath_)) {
1257             EraseWorker();
1258             HILOG_ERROR("worker:: the file path is invaild, can't find the file : %{public}s.", script);
1259             CloseHelp::DeletePointer(script, true);
1260             ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INVALID_FILEPATH,
1261                                     "the file path is invaild, can't find the file.");
1262             return;
1263         }
1264     }
1265 
1266     // 3. create WorkerRunner to Execute
1267     if (!runner_) {
1268         runner_ = std::make_unique<WorkerRunner>(WorkerStartCallback(ExecuteInThread, this));
1269     }
1270     if (runner_) {
1271         runner_->Execute(); // start a new thread
1272     } else {
1273         HILOG_ERROR("runner_ is nullptr");
1274     }
1275     CloseHelp::DeletePointer(script, true);
1276 }
1277 
ExecuteInThread(const void * data)1278 void Worker::ExecuteInThread(const void* data)
1279 {
1280     HITRACE_HELPER_START_TRACE(__PRETTY_FUNCTION__);
1281     auto worker = reinterpret_cast<Worker*>(const_cast<void*>(data));
1282     // 1. create a runtime, nativeengine
1283     napi_env workerEnv = nullptr;
1284     {
1285         std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_);
1286         if (worker->HostIsStop() || worker->isHostEnvExited_) {
1287             HILOG_ERROR("worker:: host thread is stop");
1288             worker->EraseWorker();
1289             CloseHelp::DeletePointer(worker, false);
1290             return;
1291         }
1292         napi_env env = worker->GetHostEnv();
1293         if (worker->isLimitedWorker_) {
1294             napi_create_limit_runtime(env, &workerEnv);
1295         } else {
1296             napi_create_runtime(env, &workerEnv);
1297         }
1298         if (workerEnv == nullptr) {
1299             HILOG_ERROR("worker:: Worker create runtime error");
1300             worker->EraseWorker();
1301             ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "Worker create runtime error");
1302             return;
1303         }
1304         if (worker->isLimitedWorker_) {
1305             reinterpret_cast<NativeEngine*>(workerEnv)->MarkRestrictedWorkerThread();
1306         } else {
1307             // mark worker env is workerThread
1308             reinterpret_cast<NativeEngine*>(workerEnv)->MarkWorkerThread();
1309         }
1310         // for load balance in taskpool
1311         reinterpret_cast<NativeEngine*>(env)->IncreaseSubEnvCounter();
1312 
1313         worker->SetWorkerEnv(workerEnv);
1314     }
1315 
1316     uv_loop_t* loop = worker->GetWorkerLoop();
1317     if (loop == nullptr) {
1318         HILOG_ERROR("worker:: Worker loop is nullptr");
1319         worker->EraseWorker();
1320         return;
1321     }
1322 
1323     // 2. add some preparation for the worker
1324     if (worker->PrepareForWorkerInstance()) {
1325         worker->workerOnMessageSignal_ = new uv_async_t;
1326         uv_async_init(loop, worker->workerOnMessageSignal_, reinterpret_cast<uv_async_cb>(Worker::WorkerOnMessage));
1327         worker->workerOnMessageSignal_->data = worker;
1328 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
1329         uv_async_init(loop, &worker->debuggerOnPostTaskSignal_, reinterpret_cast<uv_async_cb>(
1330             Worker::HandleDebuggerTask));
1331 #endif
1332         worker->UpdateWorkerState(RUNNING);
1333         // in order to invoke worker send before subThread start
1334         uv_async_send(worker->workerOnMessageSignal_);
1335         HITRACE_HELPER_FINISH_TRACE;
1336         // 3. start worker loop
1337         worker->Loop();
1338     } else {
1339         HILOG_ERROR("worker:: worker PrepareForWorkerInstance fail");
1340         worker->UpdateWorkerState(TERMINATED);
1341         HITRACE_HELPER_FINISH_TRACE;
1342     }
1343     worker->ReleaseWorkerThreadContent();
1344     std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_);
1345     if (worker->HostIsStop() || worker->isHostEnvExited_) {
1346         HILOG_INFO("worker:: host is stopped");
1347         CloseHelp::DeletePointer(worker, false);
1348     } else {
1349         worker->PublishWorkerOverSignal();
1350     }
1351     worker->EraseWorker();
1352 }
1353 
PrepareForWorkerInstance()1354 bool Worker::PrepareForWorkerInstance()
1355 {
1356     std::string rawFileName = script_;
1357     uint8_t* scriptContent = nullptr;
1358     size_t scriptContentSize = 0;
1359     std::vector<uint8_t> content;
1360     std::string workerAmi;
1361     {
1362         std::lock_guard<std::recursive_mutex> lock(liveStatusLock_);
1363         if (HostIsStop() || isHostEnvExited_) {
1364             HILOG_INFO("worker:: host is stopped");
1365             return false;
1366         }
1367         auto workerEngine = reinterpret_cast<NativeEngine*>(workerEnv_);
1368         auto hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_);
1369         // 1. init worker environment
1370 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
1371         workerEngine->SetDebuggerPostTaskFunc([this](std::function<void()>&& task) {
1372             this->DebuggerOnPostTask(std::move(task));
1373         });
1374 #endif
1375         if (!hostEngine->CallInitWorkerFunc(workerEngine)) {
1376             HILOG_ERROR("worker:: CallInitWorkerFunc error");
1377             return false;
1378         }
1379         // 2. get uril content
1380         if (isRelativePath_) {
1381             rawFileName = fileName_;
1382         }
1383         if (!hostEngine->GetAbcBuffer(rawFileName.c_str(), &scriptContent, &scriptContentSize, content, workerAmi)) {
1384             HILOG_ERROR("worker:: GetAbcBuffer error");
1385             return false;
1386         }
1387     }
1388     // add timer interface
1389     Timer::RegisterTime(workerEnv_);
1390     HILOG_DEBUG("worker:: stringContent size is %{public}zu", scriptContentSize);
1391     napi_value execScriptResult = nullptr;
1392     napi_status status = napi_run_actor(workerEnv_, scriptContent, scriptContentSize,
1393         workerAmi.c_str(), &execScriptResult, const_cast<char*>(script_.c_str()));
1394     if (status != napi_ok || execScriptResult == nullptr) {
1395         // An exception occurred when running the script.
1396         HILOG_ERROR("worker:: run script exception occurs, will handle exception");
1397         HandleException();
1398         return false;
1399     }
1400 
1401     // 4. register worker name in DedicatedWorkerGlobalScope
1402     if (!name_.empty()) {
1403         napi_value nameValue = nullptr;
1404         napi_create_string_utf8(workerEnv_, name_.c_str(), name_.length(), &nameValue);
1405         NapiHelper::SetNamePropertyInGlobal(workerEnv_, "name", nameValue);
1406     }
1407     return true;
1408 }
1409 
HostOnMessage(const uv_async_t * req)1410 void Worker::HostOnMessage(const uv_async_t* req)
1411 {
1412     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
1413     Worker* worker = static_cast<Worker*>(req->data);
1414     if (worker == nullptr) {
1415         HILOG_ERROR("worker:: worker is null when host onmessage.");
1416         return;
1417     }
1418     worker->HostOnMessageInner();
1419 }
1420 
HostOnMessageInner()1421 void Worker::HostOnMessageInner()
1422 {
1423     if (hostEnv_ == nullptr || HostIsStop()) {
1424         HILOG_ERROR("worker:: host thread maybe is over when host onmessage.");
1425         return;
1426     }
1427 
1428     NativeEngine* engine = reinterpret_cast<NativeEngine*>(hostEnv_);
1429     if (!engine->InitContainerScopeFunc(scopeId_)) {
1430         HILOG_WARN("worker:: InitContainerScopeFunc error when HostOnMessageInner begin(only stage model)");
1431     }
1432 
1433     napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
1434     napi_value callback = NapiHelper::GetNameProperty(hostEnv_, obj, "onmessage");
1435     bool isCallable = NapiHelper::IsCallable(hostEnv_, callback);
1436 
1437     MessageDataType data = nullptr;
1438     while (hostMessageQueue_.DeQueue(&data)) {
1439         // receive close signal.
1440         if (data == nullptr) {
1441             HILOG_DEBUG("worker:: worker received close signal");
1442 #if defined(ENABLE_WORKER_EVENTHANDLER)
1443             if ((!isMainThreadWorker_ || isLimitedWorker_) && !isHostEnvExited_) {
1444                 CloseHostHandle();
1445             }
1446 #else
1447             if (!isHostEnvExited_) {
1448                 CloseHostHandle();
1449             }
1450 #endif
1451             CloseHostCallback();
1452             return;
1453         }
1454         // handle data, call worker onMessage function to handle.
1455         napi_status status = napi_ok;
1456         HandleScope scope(hostEnv_, status);
1457         NAPI_CALL_RETURN_VOID(hostEnv_, status);
1458         napi_value result = nullptr;
1459         status = napi_deserialize(hostEnv_, data, &result);
1460         napi_delete_serialization_data(hostEnv_, data);
1461         if (status != napi_ok || result == nullptr) {
1462             HostOnMessageErrorInner();
1463             continue;
1464         }
1465         napi_value event = nullptr;
1466         napi_create_object(hostEnv_, &event);
1467         napi_set_named_property(hostEnv_, event, "data", result);
1468         napi_value argv[1] = { event };
1469         if (isCallable) {
1470             napi_value callbackResult = nullptr;
1471             napi_call_function(hostEnv_, obj, callback, 1, argv, &callbackResult);
1472         }
1473         // handle listeners.
1474         HandleEventListeners(hostEnv_, obj, 1, argv, "message");
1475         HandleHostException();
1476     }
1477     if (!engine->FinishContainerScopeFunc(scopeId_)) {
1478         HILOG_WARN("worker:: FinishContainerScopeFunc error when HostOnMessageInner end(only stage model)");
1479     }
1480 }
1481 
HostOnGlobalCall(const uv_async_t * req)1482 void Worker::HostOnGlobalCall(const uv_async_t* req)
1483 {
1484     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
1485     Worker* worker = static_cast<Worker*>(req->data);
1486     if (worker == nullptr) {
1487         HILOG_ERROR("worker:: worker is null");
1488         return;
1489     }
1490     worker->HostOnGlobalCallInner();
1491 }
1492 
HostOnGlobalCallInner()1493 void Worker::HostOnGlobalCallInner()
1494 {
1495     if (hostEnv_ == nullptr || HostIsStop()) {
1496         HILOG_ERROR("worker:: host thread maybe is over when host onmessage.");
1497         globalCallSuccess_ = false;
1498         cv_.notify_one();
1499         return;
1500     }
1501 
1502     NativeEngine* engine = reinterpret_cast<NativeEngine*>(hostEnv_);
1503     if (!engine->InitContainerScopeFunc(scopeId_)) {
1504         HILOG_WARN("worker:: InitContainerScopeFunc error when HostOnMessageInner begin(only stage model)");
1505     }
1506 
1507     if (hostGlobalCallQueue_.IsEmpty()) {
1508         HILOG_ERROR("worker:: message queue is empty when HostOnGlobalCallInner");
1509         globalCallSuccess_ = false;
1510         cv_.notify_one();
1511         return;
1512     }
1513     MessageDataType data = nullptr;
1514     uint32_t currentCallId = 0;
1515     size_t size = hostGlobalCallQueue_.GetSize();
1516     if (size < 0 || size > GLOBAL_CALL_MAX_COUNT) {
1517         HILOG_ERROR("worker:: hostGlobalCallQueue_ size error");
1518         globalCallSuccess_ = false;
1519         cv_.notify_one();
1520         return;
1521     }
1522     for (size_t i = 0; i < size; i++) {
1523         std::pair<uint32_t, MessageDataType> pair = hostGlobalCallQueue_.Front();
1524         hostGlobalCallQueue_.Pop();
1525         if (pair.first == globalCallId_) {
1526             currentCallId = pair.first;
1527             data = pair.second;
1528             break;
1529         }
1530         napi_delete_serialization_data(hostEnv_, pair.second);
1531     }
1532     napi_value argsArray = nullptr;
1533     napi_status status = napi_ok;
1534     status = napi_deserialize(hostEnv_, data, &argsArray);
1535     napi_delete_serialization_data(hostEnv_, data);
1536     if (status != napi_ok || argsArray == nullptr) {
1537         AddGlobalCallError(ErrorHelper::ERR_WORKER_SERIALIZATION);
1538         globalCallSuccess_ = false;
1539         cv_.notify_one();
1540         return;
1541     }
1542     napi_value instanceName = nullptr;
1543     napi_get_element(hostEnv_, argsArray, 0, &instanceName);
1544     napi_value methodName = nullptr;
1545     napi_get_element(hostEnv_, argsArray, 1, &methodName);
1546 
1547     std::string instanceNameStr = NapiHelper::GetString(hostEnv_, instanceName);
1548     auto iter = globalCallObjects_.find(instanceNameStr);
1549     if (iter == globalCallObjects_.end()) {
1550         HILOG_ERROR("worker:: there is no instance: %{public}s registered for global call", instanceNameStr.c_str());
1551         AddGlobalCallError(ErrorHelper::ERR_TRIGGER_NONEXIST_EVENT);
1552         globalCallSuccess_ = false;
1553         cv_.notify_one();
1554         return;
1555     }
1556     napi_ref objRef = iter->second;
1557     napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, objRef);
1558     bool hasProperty = false;
1559     napi_has_property(hostEnv_, obj, methodName, &hasProperty);
1560     if (!hasProperty) {
1561         std::string methodNameStr = NapiHelper::GetString(hostEnv_, methodName);
1562         HILOG_ERROR("worker:: registered obj for global call has no method: %{public}s", methodNameStr.c_str());
1563         AddGlobalCallError(ErrorHelper::ERR_CALL_METHOD_ON_BINDING_OBJ);
1564         globalCallSuccess_ = false;
1565         cv_.notify_one();
1566         return;
1567     }
1568     napi_value method = nullptr;
1569     napi_get_property(hostEnv_, obj, methodName, &method);
1570     // call method must not be generator function or async function
1571     bool validMethod = NapiHelper::IsCallable(hostEnv_, method) && !NapiHelper::IsAsyncFunction(hostEnv_, method) &&
1572         !NapiHelper::IsGeneratorFunction(hostEnv_, method);
1573     if (!validMethod) {
1574         std::string methodNameStr = NapiHelper::GetString(hostEnv_, methodName);
1575         HILOG_ERROR("worker:: method %{public}s shall be callable and not async or generator method",
1576             methodNameStr.c_str());
1577         AddGlobalCallError(ErrorHelper::ERR_CALL_METHOD_ON_BINDING_OBJ);
1578         globalCallSuccess_ = false;
1579         cv_.notify_one();
1580         return;
1581     }
1582     uint32_t argc = 0;
1583     napi_get_array_length(hostEnv_, argsArray, &argc);
1584     napi_value* args = nullptr;
1585     ObjectScope<napi_value> scope(args, true);
1586     if (argc > BEGIN_INDEX_OF_ARGUMENTS) {
1587         args = new napi_value[argc - BEGIN_INDEX_OF_ARGUMENTS];
1588         for (uint32_t index = 0; index < argc - BEGIN_INDEX_OF_ARGUMENTS; index++) {
1589             napi_get_element(hostEnv_, argsArray, index + BEGIN_INDEX_OF_ARGUMENTS, &args[index]);
1590         }
1591     }
1592 
1593     napi_value res = nullptr;
1594     napi_call_function(hostEnv_, obj, method, argc - BEGIN_INDEX_OF_ARGUMENTS, args, &res);
1595     bool hasPendingException = NapiHelper::IsExceptionPending(hostEnv_);
1596     if (hasPendingException) {
1597         napi_value exception = nullptr;
1598         napi_get_and_clear_last_exception(hostEnv_, &exception);
1599         napi_throw(hostEnv_, exception);
1600         globalCallSuccess_ = false;
1601         cv_.notify_one();
1602         return;
1603     }
1604     // defautly not transfer
1605     napi_value undefined = NapiHelper::GetUndefinedValue(hostEnv_);
1606     // meaningless to copy sendable object when call globalObject
1607     bool defaultClone = true;
1608     bool defaultTransfer = false;
1609     status = napi_serialize_inner(hostEnv_, res, undefined, undefined, defaultTransfer, defaultClone, &data);
1610     if (status != napi_ok || data == nullptr) {
1611         AddGlobalCallError(ErrorHelper::ERR_WORKER_SERIALIZATION);
1612         globalCallSuccess_ = false;
1613         cv_.notify_one();
1614         return;
1615     }
1616     // drop and destruct result if timeout
1617     if (currentCallId != globalCallId_ || currentCallId == 0) {
1618         napi_delete_serialization_data(hostEnv_, data);
1619         cv_.notify_one();
1620         return;
1621     }
1622     workerGlobalCallQueue_.EnQueue(data);
1623     globalCallSuccess_ = true;
1624     cv_.notify_one();
1625 }
1626 
AddGlobalCallError(int32_t errCode,napi_value errData)1627 void Worker::AddGlobalCallError(int32_t errCode, napi_value errData)
1628 {
1629     globalCallErrors_.push({errCode, errData});
1630 }
1631 
HandleGlobalCallError(napi_env env)1632 void Worker::HandleGlobalCallError(napi_env env)
1633 {
1634     while (!globalCallErrors_.empty()) {
1635         std::pair<int32_t, napi_value> pair = globalCallErrors_.front();
1636         globalCallErrors_.pop();
1637         int32_t errCode = pair.first;
1638         ErrorHelper::ThrowError(env, errCode);
1639     }
1640 }
1641 
ClearGlobalCallError(napi_env env)1642 void Worker::ClearGlobalCallError(napi_env env)
1643 {
1644     while (!globalCallErrors_.empty()) {
1645         std::pair<int32_t, napi_value> pair = globalCallErrors_.front();
1646         globalCallErrors_.pop();
1647         if (pair.second != nullptr) {
1648             napi_delete_serialization_data(env, pair.second);
1649         }
1650     }
1651 }
1652 
CallHostFunction(size_t argc,const napi_value * argv,const char * methodName) const1653 void Worker::CallHostFunction(size_t argc, const napi_value* argv, const char* methodName) const
1654 {
1655     if (hostEnv_ == nullptr) {
1656         HILOG_ERROR("worker:: host thread maybe is over");
1657         return;
1658     }
1659     if (HostIsStop()) {
1660         HILOG_ERROR("worker:: host thread maybe is over");
1661         WorkerThrowError(hostEnv_, ErrorHelper::ERR_WORKER_NOT_RUNNING,
1662             "host thread maybe is over when CallHostFunction");
1663         return;
1664     }
1665     napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
1666     napi_value callback = NapiHelper::GetNameProperty(hostEnv_, obj, methodName);
1667     bool isCallable = NapiHelper::IsCallable(hostEnv_, callback);
1668     if (!isCallable) {
1669         HILOG_DEBUG("worker:: host thread %{public}s is not Callable", methodName);
1670         return;
1671     }
1672     napi_value callbackResult = nullptr;
1673     napi_call_function(hostEnv_, obj, callback, argc, argv, &callbackResult);
1674     HandleHostException();
1675 }
1676 
CloseHostCallback()1677 void Worker::CloseHostCallback()
1678 {
1679     {
1680         napi_status status = napi_ok;
1681         HandleScope scope(hostEnv_, status);
1682         NAPI_CALL_RETURN_VOID(hostEnv_, status);
1683         napi_value exitValue = nullptr;
1684         if (isErrorExit_) {
1685             napi_create_int32(hostEnv_, 1, &exitValue); // 1 : exit because of error
1686         } else {
1687             napi_create_int32(hostEnv_, 0, &exitValue); // 0 : exit normally
1688         }
1689         napi_value argv[1] = { exitValue };
1690         CallHostFunction(1, argv, "onexit");
1691         napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
1692         // handle listeners
1693         HandleEventListeners(hostEnv_, obj, 1, argv, "exit");
1694     }
1695     CloseHelp::DeletePointer(this, false);
1696 }
1697 
HostOnError(const uv_async_t * req)1698 void Worker::HostOnError(const uv_async_t* req)
1699 {
1700     Worker* worker = static_cast<Worker*>(req->data);
1701     if (worker == nullptr) {
1702         HILOG_ERROR("worker:: worker is null");
1703         return;
1704     }
1705     worker->HostOnErrorInner();
1706     worker->TerminateInner();
1707 }
1708 
HostOnErrorInner()1709 void Worker::HostOnErrorInner()
1710 {
1711     if (hostEnv_ == nullptr || HostIsStop()) {
1712         HILOG_ERROR("worker:: host thread maybe is over when host onerror.");
1713         return;
1714     }
1715     napi_status status = napi_ok;
1716     HandleScope scope(hostEnv_, status);
1717     NAPI_CALL_RETURN_VOID(hostEnv_, status);
1718     NativeEngine* hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_);
1719     if (!hostEngine->InitContainerScopeFunc(scopeId_)) {
1720         HILOG_WARN("worker:: InitContainerScopeFunc error when onerror begin(only stage model)");
1721     }
1722 
1723     napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
1724     napi_value callback = NapiHelper::GetNameProperty(hostEnv_, obj, "onerror");
1725     bool isCallable = NapiHelper::IsCallable(hostEnv_, callback);
1726 
1727     MessageDataType data;
1728     while (errorQueue_.DeQueue(&data)) {
1729         napi_value result = nullptr;
1730         napi_deserialize(hostEnv_, data, &result);
1731         napi_delete_serialization_data(hostEnv_, data);
1732 
1733         napi_value argv[1] = { result };
1734         if (isCallable) {
1735             napi_value callbackResult = nullptr;
1736             napi_call_function(hostEnv_, obj, callback, 1, argv, &callbackResult);
1737         }
1738         // handle listeners
1739         bool isHandle = HandleEventListeners(hostEnv_, obj, 1, argv, "error");
1740         if (!isCallable && !isHandle) {
1741             napi_value businessError = ErrorHelper::ObjectToError(hostEnv_, result);
1742             napi_throw(hostEnv_, businessError);
1743             HandleHostException();
1744             return;
1745         }
1746         HandleHostException();
1747     }
1748     if (!hostEngine->FinishContainerScopeFunc(scopeId_)) {
1749         HILOG_WARN("worker:: FinishContainerScopeFunc error when onerror end(only stage model)");
1750     }
1751 }
1752 
PostMessageInner(MessageDataType data)1753 void Worker::PostMessageInner(MessageDataType data)
1754 {
1755     if (IsTerminated()) {
1756         HILOG_DEBUG("worker:: worker has been terminated when PostMessageInner.");
1757         return;
1758     }
1759     workerMessageQueue_.EnQueue(data);
1760     std::lock_guard<std::mutex> lock(workerOnmessageMutex_);
1761     if (workerOnMessageSignal_ != nullptr && !uv_is_closing((uv_handle_t*)workerOnMessageSignal_)) {
1762         uv_async_send(workerOnMessageSignal_);
1763     }
1764 }
1765 
HostOnMessageErrorInner()1766 void Worker::HostOnMessageErrorInner()
1767 {
1768     if (hostEnv_ == nullptr || HostIsStop()) {
1769         HILOG_ERROR("worker:: host thread maybe is over");
1770         return;
1771     }
1772     napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
1773     CallHostFunction(0, nullptr, "onmessageerror");
1774     // handle listeners
1775     HandleEventListeners(hostEnv_, obj, 0, nullptr, "messageerror");
1776 }
1777 
TerminateInner()1778 void Worker::TerminateInner()
1779 {
1780     if (IsTerminated() || IsTerminating()) {
1781         HILOG_INFO("worker:: worker is not in running when TerminateInner");
1782         return;
1783     }
1784     // 1. Update State
1785     UpdateWorkerState(TERMINATEING);
1786     // 2. send null signal
1787     PostMessageInner(nullptr);
1788 }
1789 
CloseInner()1790 void Worker::CloseInner()
1791 {
1792     bool expected = false;
1793     if (isTerminated_.compare_exchange_weak(expected, true)) {
1794         HILOG_INFO("worker:: Close worker");
1795     } else {
1796         HILOG_DEBUG("worker:: worker is terminated when Close");
1797         return;
1798     }
1799     UpdateWorkerState(TERMINATEING);
1800     TerminateWorker();
1801 }
1802 
UpdateWorkerState(RunnerState state)1803 bool Worker::UpdateWorkerState(RunnerState state)
1804 {
1805     bool done = false;
1806     do {
1807         RunnerState oldState = runnerState_.load(std::memory_order_acquire);
1808         if (oldState >= state) {
1809             // make sure state sequence is start, running, terminating, terminated
1810             return false;
1811         }
1812         done = runnerState_.compare_exchange_strong(oldState, state);
1813     } while (!done);
1814     return true;
1815 }
1816 
UpdateHostState(HostState state)1817 bool Worker::UpdateHostState(HostState state)
1818 {
1819     bool done = false;
1820     do {
1821         HostState oldState = hostState_.load(std::memory_order_acquire);
1822         if (oldState >= state) {
1823             // make sure state sequence is ACTIVE, INACTIVE
1824             return false;
1825         }
1826         done = hostState_.compare_exchange_strong(oldState, state);
1827     } while (!done);
1828     return true;
1829 }
1830 
TerminateWorker()1831 void Worker::TerminateWorker()
1832 {
1833     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
1834     // when there is no active handle, worker loop will stop automatic.
1835     {
1836         std::lock_guard<std::mutex> lock(workerOnmessageMutex_);
1837         uv_close(reinterpret_cast<uv_handle_t*>(workerOnMessageSignal_), [](uv_handle_t* handle) {
1838             delete reinterpret_cast<uv_async_t*>(handle);
1839             handle = nullptr;
1840         });
1841     }
1842 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
1843     uv_close(reinterpret_cast<uv_handle_t*>(&debuggerOnPostTaskSignal_), nullptr);
1844 #endif
1845     CloseWorkerCallback();
1846     uv_loop_t* loop = GetWorkerLoop();
1847     if (loop != nullptr) {
1848         Timer::ClearEnvironmentTimer(workerEnv_);
1849         uv_stop(loop);
1850     }
1851     UpdateWorkerState(TERMINATED);
1852 }
1853 
PublishWorkerOverSignal()1854 void Worker::PublishWorkerOverSignal()
1855 {
1856     if (HostIsStop()) {
1857         return;
1858     }
1859     // post nullptr tell host worker is not running
1860     hostMessageQueue_.EnQueue(nullptr);
1861 #if defined(ENABLE_WORKER_EVENTHANDLER)
1862     if (isMainThreadWorker_ && !isLimitedWorker_) {
1863         PostWorkerOverTask();
1864     } else {
1865         uv_async_send(hostOnMessageSignal_);
1866     }
1867 #else
1868     uv_async_send(hostOnMessageSignal_);
1869 #endif
1870 }
1871 
1872 #if defined(ENABLE_WORKER_EVENTHANDLER)
PostWorkerOverTask()1873 void Worker::PostWorkerOverTask()
1874 {
1875     std::weak_ptr<WorkerWrapper> weak = workerWrapper_;
1876     auto hostOnOverSignalTask = [weak]() {
1877         auto strong = weak.lock();
1878         if (strong) {
1879             HILOG_INFO("worker:: host receive terminate.");
1880             HITRACE_HELPER_METER_NAME("Worker:: HostOnTerminateSignal");
1881             strong->GetWorker()->HostOnMessageInner();
1882         } else {
1883             HILOG_INFO("worker:: worker is null.");
1884         }
1885     };
1886     GetMainThreadHandler()->PostTask(hostOnOverSignalTask, "WorkerHostOnOverSignalTask",
1887         0, OHOS::AppExecFwk::EventQueue::Priority::HIGH);
1888 }
1889 
PostWorkerErrorTask()1890 void Worker::PostWorkerErrorTask()
1891 {
1892     auto hostOnErrorTask = [this]() {
1893         if (IsValidWorker(this)) {
1894             HILOG_INFO("worker:: host receive error.");
1895             HITRACE_HELPER_METER_NAME("Worker:: HostOnErrorMessage");
1896             this->HostOnErrorInner();
1897             this->TerminateInner();
1898         }
1899     };
1900     GetMainThreadHandler()->PostTask(hostOnErrorTask, "WorkerHostOnErrorTask",
1901         0, OHOS::AppExecFwk::EventQueue::Priority::HIGH);
1902 }
1903 
PostWorkerMessageTask()1904 void Worker::PostWorkerMessageTask()
1905 {
1906     auto hostOnMessageTask = [this]() {
1907         if (IsValidWorker(this)) {
1908             HILOG_DEBUG("worker:: host thread receive message.");
1909             HITRACE_HELPER_METER_NAME("Worker:: HostOnMessage");
1910             this->HostOnMessageInner();
1911         }
1912     };
1913     GetMainThreadHandler()->PostTask(hostOnMessageTask, "WorkerHostOnMessageTask",
1914         0, OHOS::AppExecFwk::EventQueue::Priority::HIGH);
1915 }
1916 
PostWorkerGlobalCallTask()1917 void Worker::PostWorkerGlobalCallTask()
1918 {
1919     auto hostOnGlobalCallTask = [this]() {
1920         if (IsValidWorker(this)) {
1921             HILOG_DEBUG("worker:: host thread receive globalCall signal.");
1922             HITRACE_HELPER_METER_NAME("Worker:: HostOnGlobalCallSignal");
1923             this->HostOnGlobalCallInner();
1924         }
1925     };
1926     GetMainThreadHandler()->PostTask(hostOnGlobalCallTask, "WorkerHostOnGlobalCallTask",
1927         0, OHOS::AppExecFwk::EventQueue::Priority::HIGH);
1928 }
1929 #endif
1930 
IsValidWorker(Worker * worker)1931 bool Worker::IsValidWorker(Worker* worker)
1932 {
1933     std::lock_guard<std::mutex> lock(g_workersMutex);
1934     std::list<Worker*>::iterator it = std::find(g_workers.begin(), g_workers.end(), worker);
1935     if (it == g_workers.end()) {
1936         return false;
1937     }
1938     return true;
1939 }
1940 
WorkerOnMessage(const uv_async_t * req)1941 void Worker::WorkerOnMessage(const uv_async_t* req)
1942 {
1943     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
1944     Worker* worker = static_cast<Worker*>(req->data);
1945     if (worker == nullptr) {
1946         HILOG_ERROR("worker::worker is null");
1947         return;
1948     }
1949     worker->WorkerOnMessageInner();
1950 }
1951 
WorkerOnMessageInner()1952 void Worker::WorkerOnMessageInner()
1953 {
1954     if (IsTerminated()) {
1955         return;
1956     }
1957     napi_status status;
1958     napi_handle_scope scope = nullptr;
1959     status = napi_open_handle_scope(workerEnv_, &scope);
1960     if (status != napi_ok || scope == nullptr) {
1961         HILOG_ERROR("worker:: WorkerOnMessage open handle scope failed.");
1962         return;
1963     }
1964     MessageDataType data = nullptr;
1965     while (!IsTerminated() && workerMessageQueue_.DeQueue(&data)) {
1966         if (data == nullptr) {
1967             HILOG_DEBUG("worker:: worker reveive terminate signal");
1968             // Close handlescope need before TerminateWorker
1969             napi_close_handle_scope(workerEnv_, scope);
1970             TerminateWorker();
1971             return;
1972         }
1973         napi_value result = nullptr;
1974         status = napi_deserialize(workerEnv_, data, &result);
1975         napi_delete_serialization_data(workerEnv_, data);
1976         if (status != napi_ok || result == nullptr) {
1977             WorkerOnMessageErrorInner();
1978             continue;
1979         }
1980 
1981         napi_value event = nullptr;
1982         napi_create_object(workerEnv_, &event);
1983         napi_set_named_property(workerEnv_, event, "data", result);
1984         napi_value argv[1] = { event };
1985         CallWorkerFunction(1, argv, "onmessage", true);
1986 
1987         napi_value obj = NapiHelper::GetReferenceValue(workerEnv_, this->workerPort_);
1988         ParentPortHandleEventListeners(workerEnv_, obj, 1, argv, "message", true);
1989     }
1990     napi_close_handle_scope(workerEnv_, scope);
1991 }
1992 
HandleEventListeners(napi_env env,napi_value recv,size_t argc,const napi_value * argv,const char * type)1993 bool Worker::HandleEventListeners(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type)
1994 {
1995     std::string listener(type);
1996     auto iter = eventListeners_.find(listener);
1997     if (iter == eventListeners_.end()) {
1998         HILOG_DEBUG("worker:: there is no listener for type %{public}s in host thread", type);
1999         return false;
2000     }
2001 
2002     std::list<WorkerListener*>& listeners = iter->second;
2003     std::list<WorkerListener*>::iterator it = listeners.begin();
2004     while (it != listeners.end()) {
2005         WorkerListener* data = *it++;
2006         napi_value callbackObj = NapiHelper::GetReferenceValue(env, data->callback_);
2007         if (!NapiHelper::IsCallable(env, callbackObj)) {
2008             HILOG_WARN("worker:: host thread listener %{public}s is not callable", type);
2009             return false;
2010         }
2011         napi_value callbackResult = nullptr;
2012         napi_call_function(env, recv, callbackObj, argc, argv, &callbackResult);
2013         if (!data->NextIsAvailable()) {
2014             listeners.remove(data);
2015             CloseHelp::DeletePointer(data, false);
2016         }
2017     }
2018     return true;
2019 }
2020 
HandleHostException() const2021 void Worker::HandleHostException() const
2022 {
2023     if (!NapiHelper::IsExceptionPending(hostEnv_)) {
2024         return;
2025     }
2026     auto hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_);
2027     hostEngine->HandleUncaughtException();
2028 }
2029 
HandleException()2030 void Worker::HandleException()
2031 {
2032     if (!NapiHelper::IsExceptionPending(workerEnv_)) {
2033         return;
2034     }
2035 
2036     napi_status status = napi_ok;
2037     HandleScope scope(workerEnv_, status);
2038     NAPI_CALL_RETURN_VOID(workerEnv_, status);
2039     napi_value exception;
2040     napi_get_and_clear_last_exception(workerEnv_, &exception);
2041     if (exception == nullptr) {
2042         return;
2043     }
2044 
2045     HandleUncaughtException(exception);
2046 }
2047 
HandleUncaughtException(napi_value exception)2048 void Worker::HandleUncaughtException(napi_value exception)
2049 {
2050     napi_value obj = ErrorHelper::TranslateErrorEvent(workerEnv_, exception);
2051 
2052     // WorkerGlobalScope onerror
2053     WorkerOnErrorInner(obj);
2054 
2055     if (hostEnv_ == nullptr) {
2056         HILOG_ERROR("worker:: host engine is nullptr.");
2057         return;
2058     }
2059     MessageDataType data = nullptr;
2060     napi_value undefined = NapiHelper::GetUndefinedValue(workerEnv_);
2061     napi_serialize_inner(workerEnv_, obj, undefined, undefined, false, true, &data);
2062     {
2063         std::lock_guard<std::recursive_mutex> lock(liveStatusLock_);
2064         if (HostIsStop() || isHostEnvExited_) {
2065             return;
2066         }
2067         errorQueue_.EnQueue(data);
2068 #if defined(ENABLE_WORKER_EVENTHANDLER)
2069         if (isMainThreadWorker_ && !isLimitedWorker_) {
2070             PostWorkerErrorTask();
2071         } else {
2072             uv_async_send(hostOnErrorSignal_);
2073         }
2074 #else
2075         uv_async_send(hostOnErrorSignal_);
2076 #endif
2077     }
2078 }
2079 
WorkerOnMessageErrorInner()2080 void Worker::WorkerOnMessageErrorInner()
2081 {
2082     isErrorExit_ = true;
2083     CallWorkerFunction(0, nullptr, "onmessageerror", true);
2084     napi_value obj = NapiHelper::GetReferenceValue(workerEnv_, this->workerPort_);
2085     ParentPortHandleEventListeners(workerEnv_, obj, 0, nullptr, "messageerror", true);
2086 }
2087 
PostMessageToHostInner(MessageDataType data)2088 void Worker::PostMessageToHostInner(MessageDataType data)
2089 {
2090     std::lock_guard<std::recursive_mutex> lock(liveStatusLock_);
2091     if (hostEnv_ != nullptr && !HostIsStop() && !isHostEnvExited_) {
2092         hostMessageQueue_.EnQueue(data);
2093 #if defined(ENABLE_WORKER_EVENTHANDLER)
2094         if (isMainThreadWorker_ && !isLimitedWorker_) {
2095             PostWorkerMessageTask();
2096         } else {
2097             uv_async_send(hostOnMessageSignal_);
2098         }
2099 #else
2100         uv_async_send(hostOnMessageSignal_);
2101 #endif
2102     } else {
2103         HILOG_ERROR("worker:: worker host engine is nullptr when PostMessageToHostInner.");
2104     }
2105 }
2106 
operator ==(const WorkerListener & listener) const2107 bool Worker::WorkerListener::operator==(const WorkerListener& listener) const
2108 {
2109     napi_value obj = NapiHelper::GetReferenceValue(listener.env_, listener.callback_);
2110     napi_value compareObj = NapiHelper::GetReferenceValue(env_, callback_);
2111     // the env of listener and cmp listener must be same env because of Synchronization method
2112     return NapiHelper::StrictEqual(env_, compareObj, obj);
2113 }
2114 
AddListenerInner(napi_env env,const char * type,const WorkerListener * listener)2115 void Worker::AddListenerInner(napi_env env, const char* type, const WorkerListener* listener)
2116 {
2117     std::string typestr(type);
2118     auto iter = eventListeners_.find(typestr);
2119     if (iter == eventListeners_.end()) {
2120         std::list<WorkerListener*> listeners;
2121         listeners.emplace_back(const_cast<WorkerListener*>(listener));
2122         eventListeners_[typestr] = listeners;
2123     } else {
2124         std::list<WorkerListener*>& listenerList = iter->second;
2125         std::list<WorkerListener*>::iterator it = std::find_if(
2126             listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, listener->callback_));
2127         if (it != listenerList.end()) {
2128             return;
2129         }
2130         listenerList.emplace_back(const_cast<WorkerListener*>(listener));
2131     }
2132 }
2133 
RemoveListenerInner(napi_env env,const char * type,napi_ref callback)2134 void Worker::RemoveListenerInner(napi_env env, const char* type, napi_ref callback)
2135 {
2136     std::string typestr(type);
2137     auto iter = eventListeners_.find(typestr);
2138     if (iter == eventListeners_.end()) {
2139         return;
2140     }
2141     std::list<WorkerListener*>& listenerList = iter->second;
2142     if (callback != nullptr) {
2143         std::list<WorkerListener*>::iterator it =
2144             std::find_if(listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, callback));
2145         if (it != listenerList.end()) {
2146             CloseHelp::DeletePointer(*it, false);
2147             listenerList.erase(it);
2148         }
2149     } else {
2150         for (auto it = listenerList.begin(); it != listenerList.end(); it++) {
2151             CloseHelp::DeletePointer(*it, false);
2152         }
2153         eventListeners_.erase(typestr);
2154     }
2155 }
2156 
~Worker()2157 Worker::~Worker()
2158 {
2159     std::lock_guard<std::recursive_mutex> lock(liveStatusLock_);
2160     if (!HostIsStop() && !isHostEnvExited_) {
2161         ReleaseHostThreadContent();
2162         RemoveAllListenerInner();
2163         ClearGlobalCallObject();
2164     }
2165 }
2166 
RemoveAllListenerInner()2167 void Worker::RemoveAllListenerInner()
2168 {
2169     for (auto iter = eventListeners_.begin(); iter != eventListeners_.end(); iter++) {
2170         std::list<WorkerListener*>& listeners = iter->second;
2171         for (auto item = listeners.begin(); item != listeners.end(); item++) {
2172             WorkerListener* listener = *item;
2173             CloseHelp::DeletePointer(listener, false);
2174         }
2175     }
2176     eventListeners_.clear();
2177 }
2178 
ReleaseHostThreadContent()2179 void Worker::ReleaseHostThreadContent()
2180 {
2181     ClearHostMessage(hostEnv_);
2182     if (!HostIsStop()) {
2183         napi_status status = napi_ok;
2184         HandleScope scope(hostEnv_, status);
2185         NAPI_CALL_RETURN_VOID(hostEnv_, status);
2186         // 3. set thisVar's nativepointer be null
2187         napi_value thisVar = NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
2188         Worker* worker = nullptr;
2189         napi_remove_wrap(hostEnv_, thisVar, reinterpret_cast<void**>(&worker));
2190         hostEnv_ = nullptr;
2191         // 4. set workerRef_ be null
2192         workerRef_ = nullptr;
2193     }
2194 }
2195 
WorkerOnErrorInner(napi_value error)2196 void Worker::WorkerOnErrorInner(napi_value error)
2197 {
2198     isErrorExit_ = true;
2199     napi_value argv[1] = { error };
2200     CallWorkerFunction(1, argv, "onerror", false);
2201     napi_value obj = NapiHelper::GetReferenceValue(workerEnv_, this->workerPort_);
2202     ParentPortHandleEventListeners(workerEnv_, obj, 1, argv, "error", false);
2203 }
2204 
CallWorkerFunction(size_t argc,const napi_value * argv,const char * methodName,bool tryCatch)2205 bool Worker::CallWorkerFunction(size_t argc, const napi_value* argv, const char* methodName, bool tryCatch)
2206 {
2207     if (workerEnv_ == nullptr) {
2208         HILOG_ERROR("Worker:: worker is not running when call workerPort.%{public}s.", methodName);
2209         return false;
2210     }
2211     napi_value callback = NapiHelper::GetNamePropertyInParentPort(workerEnv_, workerPort_, methodName);
2212     bool isCallable = NapiHelper::IsCallable(workerEnv_, callback);
2213     if (!isCallable) {
2214         HILOG_WARN("worker:: workerPort.%{public}s is not Callable", methodName);
2215         return false;
2216     }
2217     napi_value workerPortObj = NapiHelper::GetReferenceValue(workerEnv_, workerPort_);
2218     napi_value callbackResult = nullptr;
2219     napi_call_function(workerEnv_, workerPortObj, callback, argc, argv, &callbackResult);
2220     if (tryCatch && callbackResult == nullptr) {
2221         HILOG_ERROR("worker:: workerPort.%{public}s handle exception", methodName);
2222         HandleException();
2223         return false;
2224     }
2225     return true;
2226 }
2227 
CloseWorkerCallback()2228 void Worker::CloseWorkerCallback()
2229 {
2230     CallWorkerFunction(0, nullptr, "onclose", true);
2231     // off worker inited environment
2232     {
2233         std::lock_guard<std::recursive_mutex> lock(liveStatusLock_);
2234         if (HostIsStop() || isHostEnvExited_) {
2235             return;
2236         }
2237         auto hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_);
2238         if (!hostEngine->CallOffWorkerFunc(reinterpret_cast<NativeEngine*>(workerEnv_))) {
2239             HILOG_ERROR("worker:: CallOffWorkerFunc error");
2240         }
2241     }
2242 }
2243 
ReleaseWorkerThreadContent()2244 void Worker::ReleaseWorkerThreadContent()
2245 {
2246     HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__);
2247     {
2248         std::lock_guard<std::recursive_mutex> lock(liveStatusLock_);
2249         if (!HostIsStop() && !isHostEnvExited_) {
2250             auto hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_);
2251             auto workerEngine = reinterpret_cast<NativeEngine*>(workerEnv_);
2252             if (hostEngine != nullptr && workerEngine != nullptr) {
2253                 if (!hostEngine->DeleteWorker(workerEngine)) {
2254                     HILOG_ERROR("worker:: DeleteWorker error");
2255                 }
2256                 hostEngine->DecreaseSubEnvCounter();
2257             }
2258         }
2259     }
2260     // 1. delete worker listener
2261     ParentPortRemoveAllListenerInner();
2262 
2263     // 2. delete worker's parentPort
2264     NapiHelper::DeleteReference(workerEnv_, workerPort_);
2265     workerPort_ = nullptr;
2266 
2267     // 3. clear message send to worker thread
2268     workerMessageQueue_.Clear(workerEnv_);
2269     workerGlobalCallQueue_.Clear(workerEnv_);
2270     CloseHelp::DeletePointer(reinterpret_cast<NativeEngine*>(workerEnv_), false);
2271     workerEnv_ = nullptr;
2272 }
2273 
ParentPortAddListenerInner(napi_env env,const char * type,const WorkerListener * listener)2274 void Worker::ParentPortAddListenerInner(napi_env env, const char* type, const WorkerListener* listener)
2275 {
2276     std::string typestr(type);
2277     auto iter = parentPortEventListeners_.find(typestr);
2278     if (iter == parentPortEventListeners_.end()) {
2279         std::list<WorkerListener*> listeners;
2280         listeners.emplace_back(const_cast<WorkerListener*>(listener));
2281         parentPortEventListeners_[typestr] = listeners;
2282     } else {
2283         std::list<WorkerListener*>& listenerList = iter->second;
2284         std::list<WorkerListener*>::iterator it = std::find_if(
2285             listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, listener->callback_));
2286         if (it != listenerList.end()) {
2287             return;
2288         }
2289         listenerList.emplace_back(const_cast<WorkerListener*>(listener));
2290     }
2291 }
2292 
ParentPortRemoveAllListenerInner()2293 void Worker::ParentPortRemoveAllListenerInner()
2294 {
2295     for (auto iter = parentPortEventListeners_.begin(); iter != parentPortEventListeners_.end(); iter++) {
2296         std::list<WorkerListener*>& listeners = iter->second;
2297         for (auto item = listeners.begin(); item != listeners.end(); item++) {
2298             WorkerListener* listener = *item;
2299             CloseHelp::DeletePointer(listener, false);
2300         }
2301     }
2302     parentPortEventListeners_.clear();
2303 }
2304 
ParentPortRemoveListenerInner(napi_env env,const char * type,napi_ref callback)2305 void Worker::ParentPortRemoveListenerInner(napi_env env, const char* type, napi_ref callback)
2306 {
2307     std::string typestr(type);
2308     auto iter = parentPortEventListeners_.find(typestr);
2309     if (iter == parentPortEventListeners_.end()) {
2310         return;
2311     }
2312     std::list<WorkerListener*>& listenerList = iter->second;
2313     if (callback != nullptr) {
2314         std::list<WorkerListener*>::iterator it =
2315             std::find_if(listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, callback));
2316         if (it != listenerList.end()) {
2317             CloseHelp::DeletePointer(*it, false);
2318             listenerList.erase(it);
2319         }
2320     } else {
2321         for (auto it = listenerList.begin(); it != listenerList.end(); it++) {
2322             CloseHelp::DeletePointer(*it, false);
2323         }
2324         parentPortEventListeners_.erase(typestr);
2325     }
2326 }
2327 
ParentPortHandleEventListeners(napi_env env,napi_value recv,size_t argc,const napi_value * argv,const char * type,bool tryCatch)2328 void Worker::ParentPortHandleEventListeners(napi_env env, napi_value recv, size_t argc,
2329                                             const napi_value* argv, const char* type, bool tryCatch)
2330 {
2331     std::string listener(type);
2332     auto iter = parentPortEventListeners_.find(listener);
2333     if (iter == parentPortEventListeners_.end()) {
2334         HILOG_DEBUG("worker:: there is no listener for type %{public}s in worker thread", type);
2335         return;
2336     }
2337 
2338     std::list<WorkerListener*>& listeners = iter->second;
2339     std::list<WorkerListener*>::iterator it = listeners.begin();
2340     while (it != listeners.end()) {
2341         WorkerListener* data = *it++;
2342         napi_value callbackObj = NapiHelper::GetReferenceValue(env, data->callback_);
2343         if (!NapiHelper::IsCallable(env, callbackObj)) {
2344             HILOG_WARN("worker:: workerPort.addEventListener %{public}s is not callable", type);
2345             return;
2346         }
2347         napi_value callbackResult = nullptr;
2348         napi_call_function(env, recv, callbackObj, argc, argv, &callbackResult);
2349         if (!data->NextIsAvailable()) {
2350             listeners.remove(data);
2351             CloseHelp::DeletePointer(data, false);
2352         }
2353         if (tryCatch && callbackResult == nullptr) {
2354             HandleException();
2355             return;
2356         }
2357     }
2358 }
2359 
WorkerThrowError(napi_env env,int32_t errCode,const char * errMessage)2360 void Worker::WorkerThrowError(napi_env env, int32_t errCode, const char* errMessage)
2361 {
2362     auto mainThreadEngine = NativeEngine::GetMainThreadEngine();
2363     if (mainThreadEngine == nullptr) {
2364         HILOG_ERROR("worker:: mainThreadEngine is nullptr");
2365         return;
2366     }
2367     if (mainThreadEngine->IsTargetWorkerVersion(WorkerVersion::NEW)) {
2368         ErrorHelper::ThrowError(env, errCode, errMessage);
2369     }
2370 }
2371 
CanCreateWorker(napi_env env,WorkerVersion target)2372 bool Worker::CanCreateWorker(napi_env env, WorkerVersion target)
2373 {
2374     auto mainThreadEngine = NativeEngine::GetMainThreadEngine();
2375     if (mainThreadEngine == nullptr) {
2376         HILOG_ERROR("worker:: mainThreadEngine is nullptr");
2377         return false;
2378     }
2379     if (mainThreadEngine->CheckAndSetWorkerVersion(WorkerVersion::NONE, target) ||
2380         mainThreadEngine->IsTargetWorkerVersion(target)) {
2381         return true;
2382     }
2383     return false;
2384 }
2385 
2386 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
HandleDebuggerTask(const uv_async_t * req)2387 void Worker::HandleDebuggerTask(const uv_async_t* req)
2388 {
2389     Worker* worker = DereferenceHelp::DereferenceOf(&Worker::debuggerOnPostTaskSignal_, req);
2390     if (worker == nullptr) {
2391         HILOG_ERROR("worker::worker is null");
2392         return;
2393     }
2394 
2395     worker->debuggerMutex_.lock();
2396     auto task = std::move(worker->debuggerQueue_.front());
2397     worker->debuggerQueue_.pop();
2398     worker->debuggerMutex_.unlock();
2399     task();
2400 }
2401 
DebuggerOnPostTask(std::function<void ()> && task)2402 void Worker::DebuggerOnPostTask(std::function<void()>&& task)
2403 {
2404     if (IsTerminated()) {
2405         HILOG_ERROR("worker:: worker has been terminated.");
2406         return;
2407     }
2408     if (!uv_is_closing((uv_handle_t*)&debuggerOnPostTaskSignal_)) {
2409         std::lock_guard<std::mutex> lock(debuggerMutex_);
2410         debuggerQueue_.push(std::move(task));
2411         uv_async_send(&debuggerOnPostTaskSignal_);
2412     }
2413 }
2414 #endif
2415 
InitHostHandle(uv_loop_t * loop)2416 void Worker::InitHostHandle(uv_loop_t* loop)
2417 {
2418     hostOnMessageSignal_ = new uv_async_t;
2419     uv_async_init(loop, hostOnMessageSignal_, reinterpret_cast<uv_async_cb>(Worker::HostOnMessage));
2420     hostOnMessageSignal_->data = this;
2421     hostOnErrorSignal_ = new uv_async_t;
2422     uv_async_init(loop, hostOnErrorSignal_, reinterpret_cast<uv_async_cb>(Worker::HostOnError));
2423     hostOnErrorSignal_->data = this;
2424     hostOnGlobalCallSignal_ = new uv_async_t;
2425     uv_async_init(loop, hostOnGlobalCallSignal_, reinterpret_cast<uv_async_cb>(Worker::HostOnGlobalCall));
2426     hostOnGlobalCallSignal_->data = this;
2427 }
2428 
CloseHostHandle()2429 void Worker::CloseHostHandle()
2430 {
2431     if (hostOnMessageSignal_ != nullptr && !uv_is_closing(reinterpret_cast<uv_handle_t*>(hostOnMessageSignal_))) {
2432         uv_close(reinterpret_cast<uv_handle_t*>(hostOnMessageSignal_), [](uv_handle_t* handle) {
2433             delete reinterpret_cast<uv_async_t*>(handle);
2434             handle = nullptr;
2435         });
2436     }
2437     if (hostOnErrorSignal_ != nullptr && !uv_is_closing(reinterpret_cast<uv_handle_t*>(hostOnErrorSignal_))) {
2438         uv_close(reinterpret_cast<uv_handle_t*>(hostOnErrorSignal_), [](uv_handle_t* handle) {
2439             delete reinterpret_cast<uv_async_t*>(handle);
2440             handle = nullptr;
2441         });
2442     }
2443     if (hostOnGlobalCallSignal_ != nullptr && !uv_is_closing(reinterpret_cast<uv_handle_t*>(hostOnGlobalCallSignal_))) {
2444         uv_close(reinterpret_cast<uv_handle_t*>(hostOnGlobalCallSignal_), [](uv_handle_t* handle) {
2445             delete reinterpret_cast<uv_async_t*>(handle);
2446             handle = nullptr;
2447         });
2448     }
2449 }
2450 
EraseWorker()2451 void Worker::EraseWorker()
2452 {
2453     if (!isLimitedWorker_) {
2454         std::lock_guard<std::mutex> lock(g_workersMutex);
2455         std::list<Worker*>::iterator it = std::find(g_workers.begin(), g_workers.end(), this);
2456         if (it != g_workers.end()) {
2457             g_workers.erase(it);
2458         }
2459     } else {
2460         std::lock_guard<std::mutex> lock(g_limitedworkersMutex);
2461         std::list<Worker*>::iterator it = std::find(g_limitedworkers.begin(), g_limitedworkers.end(), this);
2462         if (it != g_limitedworkers.end()) {
2463             g_limitedworkers.erase(it);
2464         }
2465     }
2466 }
2467 
ClearHostMessage(napi_env env)2468 void Worker::ClearHostMessage(napi_env env)
2469 {
2470     hostMessageQueue_.Clear(env);
2471     hostGlobalCallQueue_.Clear(env);
2472     errorQueue_.Clear(env);
2473 }
2474 } // namespace Commonlibrary::Concurrent::WorkerModule
2475