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 #ifndef JS_CONCURRENT_MODULE_WORKER_WORKER_NEW_H 17 #define JS_CONCURRENT_MODULE_WORKER_WORKER_NEW_H 18 19 #include <list> 20 #include <map> 21 #include <mutex> 22 23 #include "helper/napi_helper.h" 24 #include "helper/object_helper.h" 25 #include "message_queue.h" 26 #include "napi/native_api.h" 27 #include "napi/native_node_api.h" 28 #include "native_engine/native_engine.h" 29 #include "utils/log.h" 30 #include "worker_runner.h" 31 32 namespace Commonlibrary::Concurrent::WorkerModule { 33 using namespace Commonlibrary::Concurrent::Common::Helper; 34 35 class NewWorker { 36 public: 37 enum RunnerState { STARTING, RUNNING, TERMINATEING, TERMINATED }; 38 enum HostState { ACTIVE, INACTIVE }; 39 enum ListenerMode { ONCE, PERMANENT }; 40 41 enum ScriptMode { CLASSIC, MODULE }; 42 43 struct WorkerListener { WorkerListenerWorkerListener44 WorkerListener(napi_env env, napi_ref callback, ListenerMode mode) 45 : env_(env), callback_(callback), mode_(mode) 46 {} 47 ~WorkerListenerWorkerListener48 ~WorkerListener() 49 { 50 NapiHelper::DeleteReference(env_, callback_); 51 callback_ = nullptr; 52 } 53 NextIsAvailableWorkerListener54 bool NextIsAvailable() const 55 { 56 return mode_ != ONCE; 57 } 58 SetModeWorkerListener59 void SetMode(ListenerMode mode) 60 { 61 mode_ = mode; 62 } 63 64 bool operator==(const WorkerListener& listener) const; 65 66 napi_env env_ {NULL}; 67 napi_ref callback_ {NULL}; 68 ListenerMode mode_ {PERMANENT}; 69 }; 70 71 struct FindWorkerListener { FindWorkerListenerFindWorkerListener72 FindWorkerListener(napi_env env, napi_ref ref) : env_(env), ref_(ref) {} 73 operatorFindWorkerListener74 bool operator()(const WorkerListener* listener) const 75 { 76 napi_value compareObj = NapiHelper::GetReferenceValue(env_, listener->callback_); 77 napi_value obj = NapiHelper::GetReferenceValue(env_, ref_); 78 // the env of listener and cmp listener must be same env because of Synchronization method 79 return NapiHelper::StrictEqual(env_, compareObj, obj); 80 } 81 82 napi_env env_ {nullptr}; 83 napi_ref ref_ {nullptr}; 84 }; 85 86 /** 87 * Creates a worker instance. 88 * 89 * @param env NAPI environment parameters. 90 * @param thisVar URL of the script to be executed by the worker. 91 */ 92 NewWorker(napi_env env, napi_ref thisVar); 93 94 /** 95 * The destructor of the Worker. 96 */ 97 ~NewWorker(); 98 99 /** 100 * The host thread receives the information. 101 * 102 * @param req The value of the object passed in by the js layer. 103 */ 104 static void HostOnMessage(const uv_async_t* req); 105 106 /** 107 * The host thread receives the information. 108 * 109 * @param req The value of the object passed in by the js layer. 110 */ 111 static void HostOnError(const uv_async_t* req); 112 113 /** 114 * The worker thread receives the information. 115 * 116 * @param req The value of the object passed in by the js layer. 117 */ 118 static void WorkerOnMessage(const uv_async_t* req); 119 120 /** 121 * ExecuteIn in thread. 122 * 123 * @param data The worker pointer. 124 */ 125 static void ExecuteInThread(const void* data); 126 127 /** 128 * Post a message. 129 * 130 * @param env NAPI environment parameters. 131 * @param thisVar The callback information of the js layer. 132 */ 133 static napi_value PostMessage(napi_env env, napi_callback_info cbinfo); 134 135 /** 136 * Add event listeners to host. 137 * 138 * @param env NAPI environment parameters. 139 * @param cbinfo The callback information of the js layer. 140 */ 141 static napi_value PostMessageToHost(napi_env env, napi_callback_info cbinfo); 142 143 /** 144 * Terminates the worker thread to stop the worker from receiving messages. 145 * 146 * @param env NAPI environment parameters. 147 * @param cbinfo The callback information of the js layer. 148 */ 149 static napi_value Terminate(napi_env env, napi_callback_info cbinfo); 150 151 /** 152 * Close the worker. 153 * 154 * @param env NAPI environment parameters. 155 * @param cbinfo The callback information of the js layer. 156 */ 157 static napi_value CloseWorker(napi_env env, napi_callback_info cbinfo); 158 159 /** 160 * Adds an event listener to the worker. 161 * 162 * @param env NAPI environment parameters. 163 * @param cbinfo The callback information of the js layer. 164 */ 165 static napi_value On(napi_env env, napi_callback_info cbinfo); 166 167 /** 168 * Adds an event listener to the worker and removes the event listener automatically after it is invoked once. 169 * 170 * @param env NAPI environment parameters. 171 * @param cbinfo The callback information of the js layer. 172 */ 173 static napi_value Once(napi_env env, napi_callback_info cbinfo); 174 175 /** 176 * Removes an event listener to the worker. 177 * 178 * @param env NAPI environment parameters. 179 * @param cbinfo The callback information of the js layer. 180 */ 181 static napi_value Off(napi_env env, napi_callback_info cbinfo); 182 183 /** 184 * Add event listeners. 185 * 186 * @param env NAPI environment parameters. 187 * @param cbinfo The callback information of the js layer. 188 */ 189 static napi_value AddEventListener(napi_env env, napi_callback_info cbinfo); 190 191 /** 192 * Dispatch the event. 193 * 194 * @param env NAPI environment parameters. 195 * @param cbinfo The callback information of the js layer. 196 */ 197 static napi_value DispatchEvent(napi_env env, napi_callback_info cbinfo); 198 199 /** 200 * Remove the event listener. 201 * 202 * @param env NAPI environment parameters. 203 * @param cbinfo The callback information of the js layer. 204 */ 205 static napi_value RemoveEventListener(napi_env env, napi_callback_info cbinfo); 206 207 /** 208 * Remove all listener. 209 * 210 * @param env NAPI environment parameters. 211 * @param cbinfo The callback information of the js layer. 212 */ 213 static napi_value RemoveAllListener(napi_env env, napi_callback_info cbinfo); 214 215 /** 216 * Add the listener. 217 * 218 * @param env NAPI environment parameters. 219 * @param cbinfo The callback information of the js layer. 220 */ 221 static napi_value AddListener(napi_env env, napi_callback_info cbinfo, ListenerMode mode); 222 223 /** 224 * Remove the listener. 225 * 226 * @param env NAPI environment parameters. 227 * @param cbinfo The callback information of the js layer. 228 */ 229 static napi_value RemoveListener(napi_env env, napi_callback_info cbinfo); 230 231 /** 232 * The constructor of worker. 233 * 234 * @param env NAPI environment parameters. 235 * @param cbinfo The callback information of the js layer. 236 */ 237 static napi_value WorkerConstructor(napi_env env, napi_callback_info cbinfo); 238 239 /** 240 * Initialize the worker. 241 * 242 * @param env NAPI environment parameters. 243 * @param cbinfo The callback information of the js layer. 244 */ 245 static napi_value InitWorker(napi_env env, napi_value exports); 246 247 /** 248 * Cancel the task. 249 * 250 * @param env NAPI environment parameters. 251 * @param cbinfo The callback information of the js layer. 252 */ 253 static napi_value CancelTask(napi_env env, napi_callback_info cbinfo); 254 255 /** 256 * The parent port cancels the task. 257 * 258 * @param env NAPI environment parameters. 259 * @param cbinfo The callback information of the js layer. 260 */ 261 static napi_value WorkerPortCancelTask(napi_env env, napi_callback_info cbinfo); 262 263 /** 264 * The parent port adds an event listener. 265 * 266 * @param env NAPI environment parameters. 267 * @param cbinfo The callback information of the js layer. 268 */ 269 static napi_value WorkerPortAddEventListener(napi_env env, napi_callback_info cbinfo); 270 271 /** 272 * The parent port removes all event listener. 273 * 274 * @param env NAPI environment parameters. 275 * @param cbinfo The callback information of the js layer. 276 */ 277 static napi_value WorkerPortRemoveAllListener(napi_env env, napi_callback_info cbinfo); 278 279 /** 280 * The parent port dispatch the event listener. 281 * 282 * @param env NAPI environment parameters. 283 * @param cbinfo The callback information of the js layer. 284 */ 285 static napi_value WorkerPortDispatchEvent(napi_env env, napi_callback_info cbinfo); 286 287 /** 288 * The parent port removes the event listener. 289 * 290 * @param env NAPI environment parameters. 291 * @param cbinfo The callback information of the js layer. 292 */ 293 static napi_value WorkerPortRemoveEventListener(napi_env env, napi_callback_info cbinfo); 294 295 void StartExecuteInThread(napi_env env, const char* script); 296 297 bool UpdateWorkerState(RunnerState state); 298 bool UpdateHostState(HostState state); 299 IsNotTerminate()300 bool IsNotTerminate() const 301 { 302 return runnerState_.load(std::memory_order_acquire) <= RUNNING; 303 } 304 IsRunning()305 bool IsRunning() const 306 { 307 return runnerState_.load(std::memory_order_acquire) == RUNNING; 308 } 309 IsTerminated()310 bool IsTerminated() const 311 { 312 return runnerState_.load(std::memory_order_acquire) >= TERMINATED; 313 } 314 IsTerminating()315 bool IsTerminating() const 316 { 317 return runnerState_.load(std::memory_order_acquire) == TERMINATEING; 318 } 319 SetScriptMode(ScriptMode mode)320 void SetScriptMode(ScriptMode mode) 321 { 322 scriptMode_ = mode; 323 } 324 325 void AddListenerInner(napi_env env, const char* type, const WorkerListener* listener); 326 void RemoveListenerInner(napi_env env, const char* type, napi_ref callback); 327 void RemoveAllListenerInner(); 328 GetWorkerLoop()329 uv_loop_t* GetWorkerLoop() const 330 { 331 if (workerEnv_ != nullptr) { 332 return NapiHelper::GetLibUV(workerEnv_); 333 } 334 return nullptr; 335 } 336 SetWorkerEnv(napi_env workerEnv)337 void SetWorkerEnv(napi_env workerEnv) 338 { 339 workerEnv_ = workerEnv; 340 } 341 GetScript()342 std::string GetScript() const 343 { 344 return script_; 345 } 346 GetName()347 std::string GetName() const 348 { 349 return name_; 350 } 351 ClearWorkerTasks()352 bool ClearWorkerTasks() 353 { 354 if (hostEnv_ != nullptr) { 355 workerMessageQueue_.Clear(hostEnv_); 356 return true; 357 } 358 return false; 359 } 360 HostIsStop()361 bool HostIsStop() const 362 { 363 return hostState_.load(std::memory_order_acquire) == INACTIVE; 364 } 365 IsSameWorkerEnv(napi_env env)366 bool IsSameWorkerEnv(napi_env env) const 367 { 368 return workerEnv_ == env; 369 } 370 Loop()371 void Loop() const 372 { 373 uv_loop_t* loop = GetWorkerLoop(); 374 if (loop != nullptr) { 375 uv_run(loop, UV_RUN_DEFAULT); 376 } else { 377 HILOG_ERROR("worker:: Worker loop is nullptr when start worker loop"); 378 return; 379 } 380 } 381 382 private: 383 void WorkerOnMessageInner(); 384 void HostOnMessageInner(); 385 void HostOnErrorInner(); 386 void HostOnMessageErrorInner(); 387 void WorkerOnMessageErrorInner(); 388 void WorkerOnErrorInner(napi_value error); 389 390 void HandleHostException() const; 391 void HandleException(); 392 bool CallWorkerFunction(size_t argc, const napi_value* argv, const char* methodName, bool tryCatch); 393 void CallHostFunction(size_t argc, const napi_value* argv, const char* methodName) const; 394 395 void HandleEventListeners(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type); 396 void ParentPortHandleEventListeners(napi_env env, napi_value recv, size_t argc, 397 const napi_value* argv, const char* type, bool tryCatch); 398 void TerminateInner(); 399 400 void PostMessageInner(MessageDataType data); 401 void PostMessageToHostInner(MessageDataType data); 402 403 void TerminateWorker(); 404 void CloseInner(); 405 406 void PublishWorkerOverSignal(); 407 void CloseWorkerCallback(); 408 void CloseHostCallback(); 409 410 void ReleaseWorkerThreadContent(); 411 void ReleaseHostThreadContent(); 412 bool PrepareForWorkerInstance(); 413 void ParentPortAddListenerInner(napi_env env, const char* type, const WorkerListener* listener); 414 void ParentPortRemoveAllListenerInner(); 415 void ParentPortRemoveListenerInner(napi_env env, const char* type, napi_ref callback); 416 void GetContainerScopeId(napi_env env); 417 418 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) 419 static void HandleDebuggerTask(const uv_async_t* req); 420 void DebuggerOnPostTask(std::function<void()>&& task); 421 #endif 422 GetHostEnv()423 napi_env GetHostEnv() const 424 { 425 return hostEnv_; 426 } 427 GetWorkerEnv()428 napi_env GetWorkerEnv() const 429 { 430 return workerEnv_; 431 } 432 433 std::string script_ {}; 434 std::string name_ {}; 435 ScriptMode scriptMode_ {CLASSIC}; 436 int32_t scopeId_ {-1}; 437 438 MessageQueue workerMessageQueue_ {}; 439 MessageQueue hostMessageQueue_ {}; 440 MessageQueue errorQueue_ {}; 441 442 uv_async_t* workerOnMessageSignal_ = nullptr; 443 uv_async_t* hostOnMessageSignal_ = nullptr; 444 uv_async_t* hostOnErrorSignal_ = nullptr; 445 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) 446 uv_async_t ddebuggerOnPostTaskSignal_ {}; 447 std::function<void()> debuggerTask_; 448 #endif 449 450 std::atomic<RunnerState> runnerState_ {STARTING}; 451 std::atomic<HostState> hostState_ {ACTIVE}; 452 std::unique_ptr<WorkerRunner> runner_ {}; 453 454 std::atomic<bool> isErrorExit_ = false; 455 456 napi_env hostEnv_ {nullptr}; 457 napi_env workerEnv_ {nullptr}; 458 459 napi_ref workerRef_ {nullptr}; 460 napi_ref workerPort_ {nullptr}; 461 462 std::map<std::string, std::list<WorkerListener*>> eventListeners_ {}; 463 std::map<std::string, std::list<WorkerListener*>> parentPortEventListeners_ {}; 464 465 std::recursive_mutex liveStatusLock_ {}; 466 }; 467 } // namespace Commonlibrary::Concurrent::WorkerModule 468 #endif // JS_CONCURRENT_MODULE_WORKER_WORKER_NEW_H 469