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