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