• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef JS_CONCURRENT_MODULE_WORKER_WORKER_H
17 #define JS_CONCURRENT_MODULE_WORKER_WORKER_H
18 
19 #include <condition_variable>
20 #include <list>
21 #include <map>
22 #include <mutex>
23 
24 #include "helper/napi_helper.h"
25 #include "helper/object_helper.h"
26 #include "message_queue.h"
27 #include "napi/native_api.h"
28 #include "napi/native_node_api.h"
29 #include "native_engine/native_engine.h"
30 #include "native_engine/worker_manager.h"
31 #include "worker_runner.h"
32 #if defined(ENABLE_WORKER_EVENTHANDLER)
33 #include "event_handler.h"
34 #endif
35 
36 namespace Commonlibrary::Concurrent::WorkerModule {
37 using namespace Commonlibrary::Concurrent::Common::Helper;
38 
39 class Worker {
40 public:
41     enum RunnerState { STARTING, RUNNING, TERMINATEING, TERMINATED };
42     enum HostState { ACTIVE, INACTIVE };
43     enum ListenerMode { ONCE, PERMANENT };
44     enum ScriptMode { CLASSIC, MODULE };
45 
46     using DebuggerPostTask = std::function<void()>;
47 
48     struct WorkerListener {
WorkerListenerWorkerListener49         WorkerListener(napi_env env, napi_ref callback, ListenerMode mode)
50             : env_(env), callback_(callback), mode_(mode)
51         {}
52 
~WorkerListenerWorkerListener53         ~WorkerListener()
54         {
55             NapiHelper::DeleteReference(env_, callback_);
56             callback_ = nullptr;
57         }
58 
NextIsAvailableWorkerListener59         bool NextIsAvailable() const
60         {
61             return mode_ != ONCE;
62         }
63 
SetModeWorkerListener64         void SetMode(ListenerMode mode)
65         {
66             mode_ = mode;
67         }
68 
69         bool operator==(const WorkerListener& listener) const;
70 
71         napi_env env_ {NULL};
72         napi_ref callback_ {NULL};
73         ListenerMode mode_ {PERMANENT};
74     };
75 
76     struct FindWorkerListener {
FindWorkerListenerFindWorkerListener77         FindWorkerListener(napi_env env, napi_ref ref) : env_(env), ref_(ref) {}
78 
operatorFindWorkerListener79         bool operator()(const WorkerListener* listener) const
80         {
81             napi_value compareObj = NapiHelper::GetReferenceValue(env_, listener->callback_);
82             napi_value obj = NapiHelper::GetReferenceValue(env_, ref_);
83             // the env of listener and cmp listener must be same env because of Synchronization method
84             return NapiHelper::StrictEqual(env_, compareObj, obj);
85         }
86 
87         napi_env env_ {nullptr};
88         napi_ref ref_ {nullptr};
89     };
90 
91     struct WorkerParams {
92         std::string name_ {};
93         ScriptMode type_ {CLASSIC};
94     };
95 
96     struct WorkerWrapper {
WorkerWrapperWorkerWrapper97         explicit WorkerWrapper(Worker* worker) : workerPtr_(worker) {}
98 
GetWorkerWorkerWrapper99         Worker* GetWorker() const
100         {
101             return workerPtr_;
102         }
103 
104         Worker* workerPtr_ {nullptr};
105     };
106 
107     /**
108     * Creates a worker instance.
109     *
110     * @param env NAPI environment parameters.
111     * @param thisVar URL of the script to be executed by the worker.
112     */
113     Worker(napi_env env, napi_ref thisVar);
114 
115     /**
116         * The destructor of the Worker.
117         */
118     ~Worker();
119 
120     /**
121      * The host thread receives the information.
122      *
123      * @param req The value of the object passed in by the js layer.
124      */
125     static void HostOnMessage(const uv_async_t* req);
126 
127     /**
128      * The host thread receives the information.
129      *
130      * @param req The value of the object passed in by the js layer.
131      */
132     static void HostOnError(const uv_async_t* req);
133 
134     /**
135      * The worker thread receives the information.
136      *
137      * @param req The value of the object passed in by the js layer.
138      */
139     static void WorkerOnMessage(const uv_async_t* req);
140 
141     /**
142      * ExecuteIn in thread.
143      *
144      * @param data The worker pointer.
145      */
146     static void ExecuteInThread(const void* data);
147 
148     /**
149     * Post a message.
150     *
151     * @param env NAPI environment parameters.
152     * @param thisVar The callback information of the js layer.
153     */
154     static napi_value PostMessage(napi_env env, napi_callback_info cbinfo);
155 
156     /**
157     * Post a message, if has sendable objects in it pass sendable objects' reference.
158     *
159     * @param env NAPI environment parameters.
160     * @param thisVar The callback information of the js layer.
161     */
162     static napi_value PostMessageWithSharedSendable(napi_env env, napi_callback_info cbinfo);
163 
164     /**
165     * postMessage implementation
166     *
167     * @param env NAPI environment parameters.
168     * @param thisVar The callback information of the js layer.
169     */
170     static napi_value CommonPostMessage(napi_env env, napi_callback_info cbinfo, bool cloneSendable);
171 
172     /**
173      * Add event listeners to host.
174      *
175      * @param env NAPI environment parameters.
176      * @param cbinfo The callback information of the js layer.
177      */
178     static napi_value PostMessageToHost(napi_env env, napi_callback_info cbinfo);
179 
180     /**
181     * Post a message, if has sendable objects in it pass sendable objects' reference.
182     *
183     * @param env NAPI environment parameters.
184     * @param thisVar The callback information of the js layer.
185     */
186     static napi_value PostMessageWithSharedSendableToHost(napi_env env, napi_callback_info cbinfo);
187 
188     /**
189     * postMessage implementation
190     *
191     * @param env NAPI environment parameters.
192     * @param thisVar The callback information of the js layer.
193     */
194     static napi_value CommonPostMessageToHost(napi_env env, napi_callback_info cbinfo, bool cloneSendable);
195 
196     /**
197      * Terminates the worker thread to stop the worker from receiving messages.
198      *
199      * @param env NAPI environment parameters.
200      * @param cbinfo The callback information of the js layer.
201      */
202     static napi_value Terminate(napi_env env, napi_callback_info cbinfo);
203 
204     /**
205      * Close the worker.
206      *
207      * @param env NAPI environment parameters.
208      * @param cbinfo The callback information of the js layer.
209      */
210     static napi_value CloseWorker(napi_env env, napi_callback_info cbinfo);
211 
212     /**
213      * Adds an event listener to the worker.
214      *
215      * @param env NAPI environment parameters.
216      * @param cbinfo The callback information of the js layer.
217      */
218     static napi_value On(napi_env env, napi_callback_info cbinfo);
219 
220     /**
221      * Adds an event listener to the worker and removes the event listener automatically after it is invoked once.
222      *
223      * @param env NAPI environment parameters.
224      * @param cbinfo The callback information of the js layer.
225      */
226     static napi_value Once(napi_env env, napi_callback_info cbinfo);
227 
228     /**
229      * Removes an event listener to the worker.
230      *
231      * @param env NAPI environment parameters.
232      * @param cbinfo The callback information of the js layer.
233      */
234     static napi_value Off(napi_env env, napi_callback_info cbinfo);
235 
236     /**
237      * Add event listeners.
238      *
239      * @param env NAPI environment parameters.
240      * @param cbinfo The callback information of the js layer.
241      */
242     static napi_value AddEventListener(napi_env env, napi_callback_info cbinfo);
243 
244     /**
245      * Dispatch the event.
246      *
247      * @param env NAPI environment parameters.
248      * @param cbinfo The callback information of the js layer.
249      */
250     static napi_value DispatchEvent(napi_env env, napi_callback_info cbinfo);
251 
252     /**
253      * Remove the event listener.
254      *
255      * @param env NAPI environment parameters.
256      * @param cbinfo The callback information of the js layer.
257      */
258     static napi_value RemoveEventListener(napi_env env, napi_callback_info cbinfo);
259 
260     /**
261      * Remove all listener.
262      *
263      * @param env NAPI environment parameters.
264      * @param cbinfo The callback information of the js layer.
265      */
266     static napi_value RemoveAllListener(napi_env env, napi_callback_info cbinfo);
267 
268     /**
269      * Add the listener.
270      *
271      * @param env NAPI environment parameters.
272      * @param cbinfo The callback information of the js layer.
273      */
274     static napi_value AddListener(napi_env env, napi_callback_info cbinfo, ListenerMode mode);
275 
276     /**
277      * Remove the listener.
278      *
279      * @param env NAPI environment parameters.
280      * @param cbinfo The callback information of the js layer.
281      */
282     static napi_value RemoveListener(napi_env env, napi_callback_info cbinfo);
283 
284     /**
285      * The constructor of worker.
286      *
287      * @param env NAPI environment parameters.
288      * @param cbinfo The callback information of the js layer.
289      */
290     static napi_value LimitedWorkerConstructor(napi_env env, napi_callback_info cbinfo);
291     static napi_value ThreadWorkerConstructor(napi_env env, napi_callback_info cbinfo);
292     static napi_value WorkerConstructor(napi_env env, napi_callback_info cbinfo);
293     static napi_value Constructor(napi_env env, napi_callback_info cbinfo, bool limitSign = false,
294                                   WorkerVersion version = WorkerVersion::NONE);
295 
296     /**
297      * Initialize the worker and port.
298      *
299      * @param env NAPI environment parameters.
300      * @param cbinfo The callback information of the js layer.
301      */
302     static napi_value InitWorker(napi_env env, napi_value exports);
303     static napi_value InitPort(napi_env env, napi_value exports);
304 
305     /**
306      * Cancel the task.
307      *
308      * @param env NAPI environment parameters.
309      * @param cbinfo The callback information of the js layer.
310      */
311     static napi_value CancelTask(napi_env env, napi_callback_info cbinfo);
312 
313     /**
314      * The parent port cancels the task.
315      *
316      * @param env NAPI environment parameters.
317      * @param cbinfo The callback information of the js layer.
318      */
319     static napi_value ParentPortCancelTask(napi_env env, napi_callback_info cbinfo);
320 
321     /**
322      * The parent port adds an event listener.
323      *
324      * @param env NAPI environment parameters.
325      * @param cbinfo The callback information of the js layer.
326      */
327     static napi_value ParentPortAddEventListener(napi_env env, napi_callback_info cbinfo);
328 
329     /**
330      * The parent port removes all event listener.
331      *
332      * @param env NAPI environment parameters.
333      * @param cbinfo The callback information of the js layer.
334      */
335     static napi_value ParentPortRemoveAllListener(napi_env env, napi_callback_info cbinfo);
336 
337     /**
338      * The parent port dispatch the event listener.
339      *
340      * @param env NAPI environment parameters.
341      * @param cbinfo The callback information of the js layer.
342      */
343     static napi_value ParentPortDispatchEvent(napi_env env, napi_callback_info cbinfo);
344 
345     /**
346      * The parent port removes the event listener.
347      *
348      * @param env NAPI environment parameters.
349      * @param cbinfo The callback information of the js layer.
350      */
351     static napi_value ParentPortRemoveEventListener(napi_env env, napi_callback_info cbinfo);
352 
353     /**
354      * Register a globalCallObject on host side.
355      *
356      * @param env NAPI environment parameters.
357      * @param cbinfo The callback information of the js layer.
358      */
359     static napi_value RegisterGlobalCallObject(napi_env env, napi_callback_info cbinfo);
360 
361     /**
362      * Unregister the specific globalCallObject on host side.
363      *
364      * @param env NAPI environment parameters.
365      * @param cbinfo The callback information of the js layer.
366      */
367     static napi_value UnregisterGlobalCallObject(napi_env env, napi_callback_info cbinfo);
368 
369     /**
370      * Post a global synchronized call request to an object registered on host side.
371      *
372      * @param env NAPI environment parameters.
373      * @param cbinfo The callback information of the js layer.
374      */
375     static napi_value GlobalCall(napi_env env, napi_callback_info cbinfo);
376 
377     static void HostOnGlobalCall(const uv_async_t* req);
378 
379     static bool CanCreateWorker(napi_env env, WorkerVersion target);
380 
381     static WorkerParams* CheckWorkerArgs(napi_env env, napi_value argsValue);
382 
383     static void WorkerThrowError(napi_env env, int32_t errCode, const char* errMessage = nullptr);
384 
385     static void WorkerDestructor(napi_env env, void* data, void* hint);
386     static void WorkerHostEnvCleanCallback(void* data);
387     static void LimitedWorkerHostEnvCleanCallback(void* data);
388 
389 #if defined(ENABLE_WORKER_EVENTHANDLER)
390     static std::shared_ptr<OHOS::AppExecFwk::EventHandler> GetMainThreadHandler();
391 #endif
392 
393     void StartExecuteInThread(napi_env env, const char* script);
394 
395     bool UpdateWorkerState(RunnerState state);
396     bool UpdateHostState(HostState state);
397 
IsNotTerminate()398     bool IsNotTerminate() const
399     {
400         return runnerState_.load(std::memory_order_acquire) <= RUNNING;
401     }
402 
IsRunning()403     bool IsRunning() const
404     {
405         return runnerState_.load(std::memory_order_acquire) == RUNNING;
406     }
407 
IsTerminated()408     bool IsTerminated() const
409     {
410         return runnerState_.load(std::memory_order_acquire) >= TERMINATED;
411     }
412 
IsTerminating()413     bool IsTerminating() const
414     {
415         return runnerState_.load(std::memory_order_acquire) == TERMINATEING;
416     }
417 
SetScriptMode(ScriptMode mode)418     void SetScriptMode(ScriptMode mode)
419     {
420         scriptMode_ = mode;
421     }
422 
423     void AddListenerInner(napi_env env, const char* type, const WorkerListener* listener);
424     void RemoveListenerInner(napi_env env, const char* type, napi_ref callback);
425     void RemoveAllListenerInner();
426     void EraseWorker();
GetWorkerLoop()427     uv_loop_t* GetWorkerLoop() const
428     {
429         if (workerEnv_ != nullptr) {
430             return NapiHelper::GetLibUV(workerEnv_);
431         }
432         return nullptr;
433     }
434 
SetWorkerEnv(napi_env workerEnv)435     void SetWorkerEnv(napi_env workerEnv)
436     {
437         workerEnv_ = workerEnv;
438         if (workerEnvCallback_) {
439             workerEnvCallback_(workerEnv_);
440         }
441     }
442 
GetScript()443     std::string GetScript() const
444     {
445         return script_;
446     }
447 
GetName()448     std::string GetName() const
449     {
450         return name_;
451     }
452 
ClearWorkerTasks()453     bool ClearWorkerTasks()
454     {
455         if (hostEnv_ != nullptr) {
456             workerMessageQueue_.Clear(hostEnv_);
457             return true;
458         }
459         return false;
460     }
461 
HostIsStop()462     bool HostIsStop() const
463     {
464         return hostState_.load(std::memory_order_acquire) == INACTIVE;
465     }
466 
IsSameWorkerEnv(napi_env env)467     bool IsSameWorkerEnv(napi_env env) const
468     {
469         return workerEnv_ == env;
470     }
471 
Loop()472     void Loop() const
473     {
474         uv_loop_t* loop = GetWorkerLoop();
475         if (loop != nullptr) {
476             uv_run(loop, UV_RUN_DEFAULT);
477         } else {
478             return;
479         }
480     }
481 
RegisterCallbackForWorkerEnv(std::function<void (napi_env)> callback)482     void RegisterCallbackForWorkerEnv(std::function<void (napi_env)> callback)
483     {
484         workerEnvCallback_ = callback;
485         if (workerEnv_ != nullptr) {
486             workerEnvCallback_(workerEnv_);
487         }
488     }
489 
GetWorkerEnv()490     napi_env GetWorkerEnv() const
491     {
492         return workerEnv_;
493     }
494 
GetHostEnv()495     napi_env GetHostEnv() const
496     {
497         return hostEnv_;
498     }
499 
500 private:
501     void WorkerOnMessageInner();
502     void HostOnMessageInner();
503     void HostOnErrorInner();
504     void HostOnMessageErrorInner();
505     void HostOnGlobalCallInner();
506     void WorkerOnMessageErrorInner();
507     void WorkerOnErrorInner(napi_value error);
508 
509     void HandleHostException() const;
510     void HandleException();
511     void HandleUncaughtException(napi_value exception);
512     bool CallWorkerFunction(size_t argc, const napi_value* argv, const char* methodName, bool tryCatch);
513     void CallHostFunction(size_t argc, const napi_value* argv, const char* methodName) const;
514 
515     bool HandleEventListeners(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type);
516     void ParentPortHandleEventListeners(napi_env env, napi_value recv, size_t argc,
517                                         const napi_value* argv, const char* type, bool tryCatch);
518     void TerminateInner();
519 
520     void PostMessageInner(MessageDataType data);
521     void PostMessageToHostInner(MessageDataType data);
522 
523     void TerminateWorker();
524 
525     void CloseInner();
526 
527     void PublishWorkerOverSignal();
528     void CloseWorkerCallback();
529     void CloseHostCallback();
530 
531     void PostWorkerOverTask();
532     void PostWorkerErrorTask();
533     void PostWorkerMessageTask();
534     void PostWorkerGlobalCallTask();
535     static bool IsValidWorker(Worker* worker);
536     static bool IsValidLimitedWorker(Worker* limitedWorker);
537     static void HostEnvCleanCallbackInner(Worker* worker);
538 
539     void InitHostHandle(uv_loop_t* loop);
540     void CloseHostHandle();
541 
542     void ReleaseWorkerThreadContent();
543     void ReleaseHostThreadContent();
544     bool PrepareForWorkerInstance();
545     void ParentPortAddListenerInner(napi_env env, const char* type, const WorkerListener* listener);
546     void ParentPortRemoveAllListenerInner();
547     void ParentPortRemoveListenerInner(napi_env env, const char* type, napi_ref callback);
548     void GetContainerScopeId(napi_env env);
549 
550     void AddGlobalCallObject(const std::string &instanceName, napi_ref obj);
551     bool RemoveGlobalCallObject(const std::string &instanceName);
552     void ClearGlobalCallObject();
553     void AddGlobalCallError(int32_t errCode, napi_value errData = nullptr);
554     void HandleGlobalCallError(napi_env env);
555     void ClearGlobalCallError(napi_env env);
556     void InitGlobalCallStatus(napi_env env);
557     void IncreaseGlobalCallId();
558 
559     void ClearHostMessage(napi_env env);
560 
561 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
562     static void HandleDebuggerTask(const uv_async_t* req);
563     void DebuggerOnPostTask(std::function<void()>&& task);
564 #endif
565 
566     std::string script_ {};
567     std::string fileName_ {};
568     std::string name_ {};
569     ScriptMode scriptMode_ {CLASSIC};
570     WorkerType workerType_ {WorkerType::THREAD_WORKER};
571     bool isLimitedWorker_ {false};
572     bool isRelativePath_ {false};
573     int32_t scopeId_ {-1};
574 
575     MessageQueue workerMessageQueue_ {};
576     MessageQueue hostMessageQueue_ {};
577     std::mutex globalCallMutex_;
578     MarkedMessageQueue hostGlobalCallQueue_ {};
579     MessageQueue workerGlobalCallQueue_ {};
580     MessageQueue errorQueue_ {};
581 
582     uv_async_t* workerOnMessageSignal_ = nullptr;
583     uv_async_t* hostOnMessageSignal_ = nullptr;
584     uv_async_t* hostOnErrorSignal_ = nullptr;
585     uv_async_t* hostOnGlobalCallSignal_ = nullptr;
586 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
587     uv_async_t debuggerOnPostTaskSignal_ {};
588     std::mutex debuggerMutex_;
589     std::queue<DebuggerPostTask> debuggerQueue_ {};
590 #endif
591 
592     std::atomic<RunnerState> runnerState_ {STARTING};
593     std::atomic<HostState> hostState_ {ACTIVE};
594     std::unique_ptr<WorkerRunner> runner_ {};
595 
596     std::atomic<bool> isErrorExit_ = false;
597 
598     napi_env hostEnv_ {nullptr};
599     napi_env workerEnv_ {nullptr};
600 
601     napi_ref workerRef_ {nullptr};
602     napi_ref workerPort_ {nullptr};
603 
604     std::map<std::string, std::list<WorkerListener*>> eventListeners_ {};
605     std::map<std::string, std::list<WorkerListener*>> parentPortEventListeners_ {};
606     std::unordered_map<std::string, napi_ref> globalCallObjects_ {};
607     std::queue<std::pair<int32_t, napi_value>> globalCallErrors_ {};
608     std::atomic<uint32_t> globalCallId_ = 1; // 0: reserved for error check
609 
610     std::recursive_mutex liveStatusLock_ {};
611     std::mutex workerOnmessageMutex_ {};
612 
613     std::condition_variable cv_;
614     std::atomic<bool> globalCallSuccess_ = true;
615     std::function<void(napi_env)> workerEnvCallback_;
616 
617     bool isMainThreadWorker_ = true;
618     bool isNewVersion_ = true;
619     std::atomic<bool> isTerminated_ = false;
620     std::atomic<bool> isHostEnvExited_ = false;
621 
622     std::shared_ptr<WorkerWrapper> workerWrapper_ = nullptr;
623 
624     friend class WorkersTest;
625 };
626 } // namespace Commonlibrary::Concurrent::WorkerModule
627 #endif // JS_CONCURRENT_MODULE_WORKER_WORKER_H
628