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