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