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