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