diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..f433b1a53f5b830a205fd2df78e2b34974656c7b --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/OAT.xml b/OAT.xml new file mode 100644 index 0000000000000000000000000000000000000000..e7ffe698d19486f77961f350929fce3538ed91f8 --- /dev/null +++ b/OAT.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jsapi/BUILD.gn b/jsapi/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..ff9a25824ebec138c051ec7c4c00533d6652c558 --- /dev/null +++ b/jsapi/BUILD.gn @@ -0,0 +1,51 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/ohos.gni") + +ohos_shared_library("worker") { + include_dirs = [ + "//base/compileruntime/js_worker_module/jsapi/worker", + "//base/compileruntime/js_worker_module/jsapi/interfaces/innerkits/worker_core/include", + ] + + sources = [ + "worker/message_queue.cpp", + "worker/native_module_worker.cpp", + "worker/thread.cpp", + "worker/worker.cpp", + "worker/worker_helper.cpp", + "worker/worker_runner.cpp", + ] + + deps = [ + "//base/compileruntime/js_worker_module/jsapi/interfaces/innerkits/worker_core:worker_init", + "//foundation/ace/napi:ace_napi", + "//foundation/ace/napi:ace_napi_quickjs", + ] + + if (is_standard_system) { + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + } else { + external_deps = [ "hilog:libhilog" ] + } + + subsystem_name = "ccruntime" + part_name = "jsapi_worker" + + relative_install_dir = "module" +} + +group("jsapi_packages") { + deps = [ ":worker" ] +} diff --git a/jsapi/interfaces/innerkits/worker_core/BUILD.gn b/jsapi/interfaces/innerkits/worker_core/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..e4b5abde63cc91af2f856288179a1f9e48cb0db2 --- /dev/null +++ b/jsapi/interfaces/innerkits/worker_core/BUILD.gn @@ -0,0 +1,39 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/ohos.gni") + +config("worker_init_config") { + include_dirs = [ "//base/compileruntime/js_worker_module/jsapi/interfaces/innerkits/worker_core/include" ] +} + +ohos_shared_library("worker_init") { + include_dirs = [ "//base/compileruntime/js_worker_module/jsapi/interfaces/innerkits/worker_core/include" ] + + sources = + [ "//base/compileruntime/js_worker_module/jsapi/worker/worker_init.cpp" ] + + deps = [ + "//foundation/ace/napi:ace_napi", + "//foundation/ace/napi:ace_napi_quickjs", + ] + + public_configs = [ ":worker_init_config" ] + + subsystem_name = "ccruntime" + part_name = "jsapi_worker" +} + +group("jsapi_init_packages") { + deps = [ ":worker_init" ] +} diff --git a/jsapi/interfaces/innerkits/worker_core/include/worker_init.h b/jsapi/interfaces/innerkits/worker_core/include/worker_init.h new file mode 100644 index 0000000000000000000000000000000000000000..2f33c9976cafc8f3bd8c1e153bbcfb9956155b86 --- /dev/null +++ b/jsapi/interfaces/innerkits/worker_core/include/worker_init.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_CCRUNTIME_JSAPI_INTERFACES_INNERKITS_WORKER_CORE_INCLUDE_H +#define FOUNDATION_CCRUNTIME_JSAPI_INTERFACES_INNERKITS_WORKER_CORE_INCLUDE_H + +#include + +#include "native_engine/native_engine.h" + +using InitWorkerFunc = std::function; +using GetAssetFunc = std::function&)>; +using OffWorkerFunc = std::function; + +namespace OHOS::CCRuntime::Worker { +class WorkerCore { +public: + static InitWorkerFunc initWorkerFunc; + static void RegisterInitWorkerFunc(InitWorkerFunc func); + + static GetAssetFunc getAssertFunc; + static void RegisterAssetFunc(GetAssetFunc func); + + static OffWorkerFunc offWorkerFunc; + static void RegisterOffWorkerFunc(OffWorkerFunc func); +}; +} // namespace OHOS::CCRuntime::Worker +#endif // FOUNDATION_CCRUNTIME_JSAPI_INTERFACES_INNERKITS_WORKER_CORE_INCLUDE_H diff --git a/jsapi/worker/message_queue.cpp b/jsapi/worker/message_queue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ef3de08f23840ce0da12fc1944c5859f5bff944 --- /dev/null +++ b/jsapi/worker/message_queue.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "message_queue.h" + +namespace OHOS::CCRuntime::Worker { +void MessageQueue::EnQueue(MessageDataType data) +{ + queueLock_.lock(); + queue_.push(data); + queueLock_.unlock(); +} + +bool MessageQueue::DeQueue(MessageDataType *data) +{ + queueLock_.lock(); + if (queue_.empty()) { + queueLock_.unlock(); + return false; + } + *data = queue_.front(); + queue_.pop(); + queueLock_.unlock(); + return true; +} + +bool MessageQueue::IsEmpty() const +{ + return queue_.empty(); +} + +void MessageQueue::Clear(napi_env env) +{ + queueLock_.lock(); + size_t size = queue_.size(); + for (size_t i = 0; i < size; i++) { + MessageDataType data = queue_.front(); + napi_delete_serialization_data(env, data); + queue_.pop(); + } + queueLock_.unlock(); +} +} // namespace worker \ No newline at end of file diff --git a/jsapi/worker/message_queue.h b/jsapi/worker/message_queue.h new file mode 100644 index 0000000000000000000000000000000000000000..ff4c7c5c37c23e5b847571902593921ddc674dd5 --- /dev/null +++ b/jsapi/worker/message_queue.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_CCRUNTIME_JSAPI_WORKER_MESSAGE_QUEUE_H +#define FOUNDATION_CCRUNTIME_JSAPI_WORKER_MESSAGE_QUEUE_H + +#include +#include +#include "napi/native_api.h" + +namespace OHOS::CCRuntime::Worker { +using MessageDataType = napi_value; +class MessageQueue final { +public: + void EnQueue(MessageDataType data); + bool DeQueue(MessageDataType *data); + bool IsEmpty() const; + void Clear(napi_env env); + size_t GetSize() const + { + return queue_.size(); + } + +private: + std::mutex queueLock_; + std::queue queue_; +}; +} // namespace OHOS::CCRuntime::Worker +#endif // FOUNDATION_CCRUNTIME_JSAPI_WORKER_MESSAGE_QUEUE_H diff --git a/jsapi/worker/native_module_worker.cpp b/jsapi/worker/native_module_worker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c7386e2a959553c3a79ef4e8a0da8614a6a733de --- /dev/null +++ b/jsapi/worker/native_module_worker.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "worker.h" + +/* + * module define + */ +static napi_module g_workerModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = OHOS::CCRuntime::Worker::Worker::InitWorker, + .nm_modname = "worker", + .nm_priv = ((void*)0), + .reserved = { 0 }, +}; +/* + * module register + */ +extern "C" __attribute__((constructor)) void Register() +{ + napi_module_register(&g_workerModule); +} \ No newline at end of file diff --git a/jsapi/worker/thread.cpp b/jsapi/worker/thread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..79dacaa44772ac6f06cc1aa70a828ecf2005de8c --- /dev/null +++ b/jsapi/worker/thread.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "thread.h" + +namespace OHOS::CCRuntime::Worker { +Thread::Thread() : tId_() {} + +bool Thread::Start() +{ + int ret = uv_thread_create(&tId_, [](void* arg) { + Thread* thread = reinterpret_cast(arg); + thread->Run(); + }, this); + return ret != 0; +} +} // namespace OHOS::CCRuntime::Worker \ No newline at end of file diff --git a/jsapi/worker/thread.h b/jsapi/worker/thread.h new file mode 100644 index 0000000000000000000000000000000000000000..c0e047047620d621652a6f824e2a05aa7b2d9e21 --- /dev/null +++ b/jsapi/worker/thread.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_CCRUNTIME_JSAPI_WORKER_THREAD_H +#define FOUNDATION_CCRUNTIME_JSAPI_WORKER_THREAD_H + +#include + +namespace OHOS::CCRuntime::Worker { +class Thread { +public: + Thread(); + virtual ~Thread() = default; + bool Start(); + virtual void Run() = 0; + + uv_thread_t GetThreadId() const + { + return tId_; + } + +private: + uv_thread_t tId_ {0}; +}; +} // namespace OHOS::CCRuntime::Worker + +#endif // #define FOUNDATION_CCRUNTIME_JSAPI_WORKER_THREAD_H \ No newline at end of file diff --git a/jsapi/worker/worker.cpp b/jsapi/worker/worker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a3a1aaf75ec79d1d068769c29a7f6c7a74a88a63 --- /dev/null +++ b/jsapi/worker/worker.cpp @@ -0,0 +1,1055 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "worker.h" + +#include "worker_init.h" + +namespace OHOS::CCRuntime::Worker { +const static int MAXWORKERS = 50; +static std::list g_workers; +static std::mutex g_workersMutex; + +Worker::Worker(NativeEngine* env, napi_ref thisVar) + : script_(nullptr), name_(nullptr), scriptMode_(CLASSIC), runnerState_(STARTING), runner_(nullptr), + mainEngine_(env), workerEngine_(nullptr), workerWrapper_(thisVar), parentPort_(nullptr) +{} + +void Worker::StartExecuteInThread(napi_env env, const char* script) +{ + // 1. init mainOnMessageSignal_ in main loop + auto engine = reinterpret_cast(env); + uv_loop_t* loop = engine->GetUVLoop(); + if (loop == nullptr) { + napi_throw_error(env, nullptr, "worker::engine loop is null"); + return; + } + uv_async_init(loop, &mainOnMessageSignal_, reinterpret_cast(Worker::MainOnMessage)); + uv_async_init(loop, &mainOnErrorSignal_, reinterpret_cast(Worker::MainOnError)); + + // 2. copy the script + script_ = strdup(script); + CloseHelp::DeletePointer(script, true); + + // 4. create WorkerRunner to Execute + if (runner_ == nullptr) { + runner_ = new WorkerRunner(WorkerStartCallback(ExecuteInThread, this)); + } + runner_->Execute(); // start a new thread +} + +void Worker::CloseInner() +{ + UpdateWorkerState(TERMINATEING); + TerminateWorker(); +} + +napi_value Worker::CloseWorker(napi_env env, napi_callback_info cbinfo) +{ + Worker* worker = nullptr; + napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, (void**)&worker); + if (worker != nullptr) { + worker->CloseInner(); + } + return NapiValueHelp::GetUndefinedValue(env); +} + +void CallWorkCallback(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type) +{ + napi_value callback = nullptr; + napi_get_named_property(env, recv, type, &callback); + if (NapiValueHelp::IsCallable(env, callback)) { + napi_value callbackResult = nullptr; + napi_call_function(env, recv, callback, 1, argv, &callbackResult); + } +} + +void Worker::PrepareForWorkerInstance(const Worker* worker) +{ + napi_env env = reinterpret_cast(const_cast(worker->GetWorkerEngine())); + // 1. init worker environment + if (OHOS::CCRuntime::Worker::WorkerCore::initWorkerFunc != NULL) { + OHOS::CCRuntime::Worker::WorkerCore::initWorkerFunc(const_cast(worker->GetWorkerEngine())); + } + // 2. Execute script + if (OHOS::CCRuntime::Worker::WorkerCore::getAssertFunc == NULL) { + HILOG_ERROR("worker::getAssertFunc is null"); + napi_throw_error(env, nullptr, "worker::getAssertFunc is null"); + return; + } + std::vector scriptContent; + OHOS::CCRuntime::Worker::WorkerCore::getAssertFunc(std::string(worker->GetScript()), scriptContent); + std::string stringContent(scriptContent.begin(), scriptContent.end()); + HILOG_INFO("worker:: stringContent = %{private}s", stringContent.c_str()); + napi_value scriptStringNapiValue = nullptr; + napi_create_string_utf8(env, stringContent.c_str(), stringContent.length(), &scriptStringNapiValue); + + napi_value execScriptResult = nullptr; + napi_run_script(env, scriptStringNapiValue, &execScriptResult); + if (execScriptResult == nullptr) { + // An exception occurred when running the script. + HILOG_ERROR("worker:: run script exception occurs, will handle exception"); + (const_cast(worker))->HandleException(worker->GetWorkerEngine()); + return; + } + + // 3. register postMessage in DedicatedWorkerGlobalScope + napi_value postFunctionObj = nullptr; + napi_create_function(env, "postMessage", NAPI_AUTO_LENGTH, Worker::PostMessageToMain, + const_cast(worker), &postFunctionObj); + NapiValueHelp::SetNamePropertyInGlobal(env, "postMessage", postFunctionObj); + // 4. register close in DedicatedWorkerGlobalScope + napi_value closeFuncObj = nullptr; + napi_create_function(env, "close", NAPI_AUTO_LENGTH, Worker::CloseWorker, + const_cast(worker), &closeFuncObj); + NapiValueHelp::SetNamePropertyInGlobal(env, "close", closeFuncObj); + // 5. register worker name in DedicatedWorkerGlobalScope + if (worker->GetName() != nullptr) { + napi_value nameValue = nullptr; + napi_create_string_utf8(env, worker->GetName(), strlen(worker->GetName()), &nameValue); + NapiValueHelp::SetNamePropertyInGlobal(env, "name", nameValue); + } +} + +bool Worker::UpdateWorkerState(RunnerState state) +{ + bool done = false; + do { + RunnerState oldState = runnerState_.load(std::memory_order_acquire); + if (oldState < state) { + done = runnerState_.compare_exchange_strong(oldState, state); + } else { + return false; + } + } while (!done); + return true; +} + +void Worker::PublishWorkerOverSignal() +{ + // post NULL tell main worker is not running + mainMessageQueue_.EnQueue(NULL); + uv_async_send(&mainOnMessageSignal_); + // mainEngine_->TriggerPostTask(); +} + +void Worker::ExecuteInThread(const void* data) +{ + auto worker = reinterpret_cast(const_cast(data)); + // 1. create a runtime, nativeengine + napi_env env = reinterpret_cast(const_cast(worker->GetMainEngine())); + napi_env newEnv = nullptr; + napi_create_runtime(env, &newEnv); + if (newEnv == nullptr) { + napi_throw_error(env, nullptr, "Worker create runtime error"); + return; + } + NativeEngine *workerEngine = reinterpret_cast(newEnv); + // mark worker env is subThread + workerEngine->MarkSubThread(); + worker->SetWorkerEngine(workerEngine); + + uv_loop_t* loop = worker->GetWorkerLoop(); + if (loop == nullptr) { + napi_throw_error(env, nullptr, "Worker loop is nullptr"); + return; + } + uv_async_init(loop, &worker->workerOnMessageSignal_, reinterpret_cast(Worker::WorkerOnMessage)); + + if (worker->UpdateWorkerState(RUNNING)) { + // 2. add some preparation for the worker + PrepareForWorkerInstance(worker); + // 3. start worker loop + const NativeEngine* workerEngine = worker->GetWorkerEngine(); + if (workerEngine == nullptr) { + HILOG_ERROR("worker::worker engine is null"); + } else { + uv_async_send(&worker->workerOnMessageSignal_); + const_cast(workerEngine)->Loop(LOOP_DEFAULT); + } + } else { + worker->CloseInner(); + } + worker->PublishWorkerOverSignal(); +} + +void Worker::MainOnMessage(const uv_async_t* req) +{ + Worker* worker = DereferenceHelp::DereferenceOf(&Worker::mainOnMessageSignal_, req); + if (worker == nullptr) { + HILOG_ERROR("worker::worker is null"); + return; + } + worker->MainOnMessageInner(worker->GetMainEngine()); +} + +void Worker::MainOnErrorInner(const NativeEngine* engine) +{ + napi_env env = reinterpret_cast(const_cast(engine)); + napi_value callback = nullptr; + napi_value obj = nullptr; + napi_get_reference_value(env, workerWrapper_, &obj); + napi_get_named_property(env, obj, "onerror", &callback); + bool isCallable = NapiValueHelp::IsCallable(env, callback); + if (!isCallable) { + HILOG_ERROR("worker:: worker onerror is not Callable"); + return; + } + MessageDataType data; + while (errorQueue_.DeQueue(&data)) { + napi_value result = nullptr; + napi_deserialize(env, data, &result); + + napi_value argv[1] = { result }; + napi_value callbackResult = nullptr; + napi_call_function(env, obj, callback, 1, argv, &callbackResult); + + // handle listeners + HandleEventListeners(env, obj, 1, argv, "error"); + } +} + +void Worker::MainOnError(const uv_async_t* req) +{ + Worker* worker = DereferenceHelp::DereferenceOf(&Worker::mainOnErrorSignal_, req); + if (worker == nullptr) { + HILOG_ERROR("worker::worker is null"); + return; + } + worker->MainOnErrorInner(worker->GetMainEngine()); + worker->TerminateInner(); +} + +void Worker::WorkerOnMessage(const uv_async_t* req) +{ + Worker* worker = DereferenceHelp::DereferenceOf(&Worker::workerOnMessageSignal_, req); + if (worker == nullptr) { + HILOG_ERROR("worker::worker is null"); + return; + } + worker->WorkerOnMessageInner(worker->GetWorkerEngine()); +} + +void Worker::CloseMainCallback() const +{ + napi_value exitValue = nullptr; + napi_env env = reinterpret_cast(const_cast(GetMainEngine())); + napi_create_int32(env, 1, &exitValue); + napi_value argv[1] = { exitValue }; + CallMainFunction(GetMainEngine(), 1, argv, "onexit"); + + std::lock_guard lock(g_workersMutex); + std::list::iterator it = std::find(g_workers.begin(), g_workers.end(), this); + if (it != g_workers.end()) { + g_workers.erase(it); + } + CloseHelp::DeletePointer(this, false); +} + +void Worker::HandleEventListeners(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type) +{ + std::string listener(type); + auto iter = eventListeners_.find(listener); + if (iter == eventListeners_.end()) { + HILOG_INFO("worker:: there is no listener for type %{public}s", type); + return; + } + + std::list& listeners = iter->second; + std::list::iterator it = listeners.begin(); + while (it != listeners.end()) { + WorkerListener* data = *it++; + napi_ref callback = data->GetCallback(); + napi_value callbackObj = nullptr; + napi_get_reference_value(env, callback, &callbackObj); + napi_value callbackResult = nullptr; + napi_call_function(env, recv, callbackObj, argc, argv, &callbackResult); + if (!data->NextIsAvailable()) { + listeners.remove(data); + CloseHelp::DeletePointer(data, false); + } + } +} + +void Worker::MainOnMessageInner(const NativeEngine* engine) +{ + napi_env env = reinterpret_cast(const_cast(engine)); + + napi_value callback = nullptr; + napi_value obj = nullptr; + napi_get_reference_value(env, workerWrapper_, &obj); + napi_get_named_property(env, obj, "onmessage", &callback); + bool isCallable = NapiValueHelp::IsCallable(env, callback); + + MessageDataType data = nullptr; + while (mainMessageQueue_.DeQueue(&data)) { + // receive close signal. + if (data == nullptr) { + HILOG_INFO("worker:: worker received close signal"); + uv_unref((uv_handle_t*)&mainOnMessageSignal_); + uv_close((uv_handle_t*)&mainOnMessageSignal_, nullptr); + + uv_unref((uv_handle_t*)&mainOnErrorSignal_); + uv_close((uv_handle_t*)&mainOnErrorSignal_, nullptr); + CloseMainCallback(); + return; + } + if (!isCallable) { + // onmessage is not func, no need to continue + HILOG_ERROR("worker:: worker onmessage is not a callable"); + return; + } + // handle data, call worker onMessage function to handle. + napi_value result = nullptr; + napi_deserialize(env, data, &result); + napi_value event = nullptr; + napi_create_object(env, &event); + napi_set_named_property(env, event, "data", result); + napi_value argv[1] = { event }; + napi_value callbackResult = nullptr; + napi_call_function(env, obj, callback, 1, argv, &callbackResult); + // handle listeners. + HandleEventListeners(env, obj, 1, argv, "message"); + } +} + +void Worker::TerminateWorker() +{ + // when there is no active handle, worker loop will stop automatic. + std::lock_guard lock(workerAsyncMutex_); + uv_close((uv_handle_t*)&workerOnMessageSignal_, nullptr); + CloseWorkerCallback(); + uv_loop_t* loop = GetWorkerLoop(); + if (loop != nullptr) { + uv_stop(loop); + } + UpdateWorkerState(TERMINATED); +} + +void Worker::HandleException(const NativeEngine* engine) +{ + // obj.message, obj.filename, obj.lineno, obj.colno + napi_env env = reinterpret_cast(const_cast(engine)); + napi_value exception = nullptr; + napi_create_object(env, &exception); + + napi_get_exception_info_for_worker(env, exception); + + // add obj.filename + napi_value filenameValue = nullptr; + napi_create_string_utf8(env, script_, strlen(script_), &filenameValue); + napi_set_named_property(env, exception, "filename", filenameValue); + + // WorkerGlobalScope onerror + WorkerOnErrorInner(engine, exception); + + if (mainEngine_ != nullptr) { + napi_value data = nullptr; + napi_serialize(env, exception, NapiValueHelp::GetUndefinedValue(env), &data); + errorQueue_.EnQueue(data); + uv_async_send(&mainOnErrorSignal_); + // mainEngine_->TriggerPostTask(); + } else { + HILOG_ERROR("worker:: main engine is nullptr."); + } +} + +void Worker::WorkerOnMessageInner(const NativeEngine* engine) +{ + if (IsTerminated()) { + return; + } + MessageDataType data = nullptr; + napi_env env = reinterpret_cast(const_cast(engine)); + while (workerMessageQueue_.DeQueue(&data)) { + if (data == NULL || IsTerminating()) { + HILOG_INFO("worker:: worker reveive terminate signal"); + TerminateWorker(); + return; + } + napi_value result = nullptr; + napi_status status = napi_deserialize(env, data, &result); + if (status != napi_ok || result == nullptr) { + WorkerOnMessageErrorInner(workerEngine_); + return; + } + + napi_value event = nullptr; + napi_create_object(env, &event); + napi_set_named_property(env, event, "data", result); + napi_value argv[1] = { event }; + bool callFeedback = CallWorkerFunction(engine, 1, argv, "onmessage", true); + if (!callFeedback) { + // onmessage is not function, exit the loop directly. + return; + } + } +} + +void Worker::MainOnMessageErrorInner(const NativeEngine* engine) +{ + napi_env env = reinterpret_cast(const_cast(engine)); + napi_value obj = nullptr; + napi_get_reference_value(env, workerWrapper_, &obj); + CallMainFunction(engine, 0, nullptr, "onmessageerror"); + // handle listeners + HandleEventListeners(env, obj, 0, nullptr, "messageerror"); +} + +void Worker::WorkerOnMessageErrorInner(const NativeEngine* engine) +{ + CallWorkerFunction(engine, 0, nullptr, "onmessageerror", true); +} + +napi_value Worker::PostMessage(napi_env env, napi_callback_info cbinfo) +{ + size_t argc = NapiValueHelp::GetCallbackInfoArgc(env, cbinfo); + if (argc < 1) { + napi_throw_error(env, nullptr, "Worker param count must be more than 1 with new"); + return nullptr; + } + napi_value* argv = new napi_value[argc]; + [[maybe_unused]] ObjectScope scope(argv, true); + napi_value thisVar = nullptr; + napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, nullptr); + Worker* worker = nullptr; + napi_unwrap(env, thisVar, (void**)&worker); + + if (worker == nullptr) { + HILOG_ERROR("worker:: worker is nullptr when PostMessage, maybe worker is terminated"); + return nullptr; + } + + if (worker->IsTerminated() || worker->IsTerminating()) { + HILOG_INFO("worker:: worker not in running state"); + return nullptr; + } + + napi_value data = nullptr; + napi_status serializeStatus = napi_ok; + if (argc >= WORKERPARAMNUM) { + if (!NapiValueHelp::IsArray(argv[1])) { + napi_throw_error(env, nullptr, "Transfer list must be an Array"); + return nullptr; + } + serializeStatus = napi_serialize(env, argv[0], argv[1], &data); + } else { + serializeStatus = napi_serialize(env, argv[0], NapiValueHelp::GetUndefinedValue(env), &data); + } + if (serializeStatus != napi_ok || data == nullptr) { + worker->MainOnMessageErrorInner(worker->GetMainEngine()); + return nullptr; + } + + if (data != nullptr) { + worker->PostMessageInner(data); + } + + return NapiValueHelp::GetUndefinedValue(env); +} + +napi_value Worker::PostMessageToMain(napi_env env, napi_callback_info cbinfo) +{ + size_t argc = NapiValueHelp::GetCallbackInfoArgc(env, cbinfo); + if (argc < 1) { + napi_throw_error(env, nullptr, "Worker param count must be more than 1 with new"); + return nullptr; + } + napi_value* argv = new napi_value[argc]; + [[maybe_unused]] ObjectScope scope(argv, true); + Worker* worker = nullptr; + napi_get_cb_info(env, cbinfo, &argc, argv, nullptr, (void**)&worker); + + if (worker == nullptr) { + HILOG_ERROR("worker:: when post message to main occur worker is nullptr"); + return nullptr; + } + + if (!worker->IsRunning()) { + // if worker is not running, don't send any message to main thread + HILOG_INFO("worker:: when post message to main occur worker is not in running."); + return nullptr; + } + + napi_value data = nullptr; + if (argc >= WORKERPARAMNUM) { + if (!NapiValueHelp::IsArray(argv[1])) { + napi_throw_error(env, nullptr, "Transfer list must be an Array"); + return nullptr; + } + napi_serialize(env, argv[0], argv[1], &data); + } else { + napi_serialize(env, argv[0], NapiValueHelp::GetUndefinedValue(env), &data); + } + + if (data != nullptr) { + worker->PostMessageToMainInner(data); + } + + return NapiValueHelp::GetUndefinedValue(env); +} + +void Worker::PostMessageToMainInner(MessageDataType data) +{ + if (mainEngine_ != nullptr) { + mainMessageQueue_.EnQueue(data); + uv_async_send(&mainOnMessageSignal_); + // mainEngine_->TriggerPostTask(); + } else { + HILOG_ERROR("worker:: worker main engine is nullptr."); + } +} + +void Worker::PostMessageInner(MessageDataType data) +{ + if (IsTerminating()) { + HILOG_INFO("worker:: worker is terminating, will not handle andy worker."); + return; + } + if (IsTerminated()) { + HILOG_INFO("worker:: worker has been terminated."); + return; + } + std::lock_guard lock(workerAsyncMutex_); + workerMessageQueue_.EnQueue(data); + if (IsRunning()) { + uv_async_send(&workerOnMessageSignal_); + } +} + +napi_value Worker::Terminate(napi_env env, napi_callback_info cbinfo) +{ + napi_value thisVar = nullptr; + napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr); + Worker* worker = nullptr; + napi_unwrap(env, thisVar, (void**)&worker); + if (worker == nullptr) { + HILOG_ERROR("worker:: worker is nullptr when Terminate, maybe worker is terminated"); + return nullptr; + } + if (worker->IsTerminated() || worker->IsTerminating()) { + HILOG_INFO("worker:: worker is not in running"); + return nullptr; + } + worker->TerminateInner(); + return NapiValueHelp::GetUndefinedValue(env); +} + +void Worker::TerminateInner() +{ + // 1. send null signal + PostMessageInner(NULL); + UpdateWorkerState(TERMINATEING); +} + +Worker::~Worker() +{ + CloseHelp::DeletePointer(script_, true); + script_ = nullptr; + CloseHelp::DeletePointer(name_, true); + name_ = nullptr; + napi_env env = reinterpret_cast(mainEngine_); + workerMessageQueue_.Clear(env); + mainMessageQueue_.Clear(env); + // set thisVar's nativepointer is null + napi_value thisVar = nullptr; + napi_get_reference_value(env, workerWrapper_, &thisVar); + Worker* worker = nullptr; + napi_remove_wrap(env, thisVar, (void**)&worker); + + napi_delete_reference(env, workerWrapper_); + workerWrapper_ = nullptr; + + napi_delete_reference(env, parentPort_); + parentPort_ = nullptr; + + CloseHelp::DeletePointer(runner_, false); + runner_ = nullptr; + + CloseHelp::DeletePointer(workerEngine_, false); + workerEngine_ = nullptr; + + mainEngine_ = nullptr; + RemoveAllListenerInner(); +} + +napi_value Worker::WorkerConstructor(napi_env env, napi_callback_info cbinfo) +{ + // check argv count + size_t argc = NapiValueHelp::GetCallbackInfoArgc(env, cbinfo); + if (argc < 1) { + napi_throw_error(env, nullptr, "Worker param count must be more than 1 with new"); + return nullptr; + } + + // check 1st param is string + napi_value thisVar = nullptr; + void* data = nullptr; + napi_value* args = new napi_value[argc]; + [[maybe_unused]] ObjectScope scope(args, true); + napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data); + if (!NapiValueHelp::IsString(args[0])) { + napi_throw_error(env, nullptr, "Worker 1st param must be string with new"); + return nullptr; + } + + std::lock_guard lock(g_workersMutex); + if (g_workers.size() >= MAXWORKERS) { + napi_throw_error(env, nullptr, "Too many workers, the number of workers exceeds the maximum."); + return nullptr; + } + + // 2. new worker instance + NativeEngine* engine = reinterpret_cast(env); + Worker* worker = new Worker(engine, nullptr); + g_workers.push_back(worker); + + if (argc > 1 && NapiValueHelp::IsObject(args[1])) { + napi_value nameValue = nullptr; + napi_get_named_property(env, args[1], "name", &nameValue); + if (NapiValueHelp::IsString(nameValue)) { + char* nameStr = NapiValueHelp::GetString(env, nameValue); + if (nameStr == nullptr) { + napi_throw_error(env, nullptr, "worker name create error, please check."); + return nullptr; + } + worker->name_ = strdup(nameStr); + CloseHelp::DeletePointer(nameStr, true); + } + + napi_value typeValue = nullptr; + napi_get_named_property(env, args[1], "type", &typeValue); + if (NapiValueHelp::IsString(typeValue)) { + char* typeStr = NapiValueHelp::GetString(env, typeValue); + if (typeStr == nullptr) { + napi_throw_error(env, nullptr, "worker type create error, please check."); + return nullptr; + } + if (strcmp("classic", typeStr) == 0) { + worker->SetScriptMode(CLASSIC); + CloseHelp::DeletePointer(typeStr, true); + } else if (strcmp("module", typeStr) == 0) { + worker->SetScriptMode(MODULE); + napi_throw_error(env, nullptr, "unsupport module"); + CloseHelp::DeletePointer(typeStr, true); + CloseHelp::DeletePointer(worker, false); + return nullptr; + } else { + worker->SetScriptMode(MODULE); + napi_throw_error(env, nullptr, "unsupport module"); + CloseHelp::DeletePointer(typeStr, true); + CloseHelp::DeletePointer(worker, false); + return nullptr; + } + } + } + + // 3. execute StartExecuteInThread; + char* script = NapiValueHelp::GetString(env, args[0]); + if (script == nullptr) { + napi_throw_error(env, nullptr, "worker script create error, please check."); + return nullptr; + } + HILOG_INFO("worker:: script is %{public}s", script); + worker->StartExecuteInThread(env, script); + napi_wrap( + env, thisVar, worker, + [](napi_env env, void* data, void* hint) { + Worker* worker = (Worker*)data; + auto iter = std::find(g_workers.begin(), g_workers.end(), worker); + if (iter == g_workers.end()) { + return; + } + if (worker->IsTerminated() || worker->IsTerminating()) { + HILOG_INFO("worker:: worker is not in running"); + return; + } + worker->TerminateInner(); + }, + nullptr, nullptr); + napi_create_reference(env, thisVar, 1, &worker->workerWrapper_); + return thisVar; +} + +napi_value Worker::AddListener(napi_env env, napi_callback_info cbinfo, ListenerMode mode) +{ + size_t argc = NapiValueHelp::GetCallbackInfoArgc(env, cbinfo); + if (argc < WORKERPARAMNUM) { + napi_throw_error(env, nullptr, "Worker param count must be more than WORKPARAMNUM with on"); + return nullptr; + } + // check 1st param is string + napi_value thisVar = nullptr; + void* data = nullptr; + napi_value* args = new napi_value[argc]; + [[maybe_unused]] ObjectScope scope(args, true); + napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data); + if (!NapiValueHelp::IsString(args[0])) { + napi_throw_error(env, nullptr, "Worker 1st param must be string with on"); + return nullptr; + } + if (!NapiValueHelp::IsCallable(env, args[1])) { + napi_throw_error(env, nullptr, "Worker 2st param must be callable with on"); + return nullptr; + } + Worker* worker = nullptr; + napi_unwrap(env, thisVar, (void**)&worker); + if (worker == nullptr) { + HILOG_ERROR("worker:: worker is nullptr when addListener, maybe worker is terminated"); + return nullptr; + } + + auto listener = new WorkerListener(worker, mode); + if (mode == ONCE && argc > WORKERPARAMNUM) { + if (NapiValueHelp::IsObject(args[2])) { + napi_value onceValue = nullptr; + napi_get_named_property(env, args[2], "once", &onceValue); + bool isOnce = false; + napi_get_value_bool(env, onceValue, &isOnce); + if (!isOnce) { + listener->SetMode(PERMANENT); + } + } + } + listener->SetCallable(env, args[1]); + char* typeStr = NapiValueHelp::GetString(env, args[0]); + if (typeStr == nullptr) { + CloseHelp::DeletePointer(listener, false); + napi_throw_error(env, nullptr, "worker listener type create error, please check."); + return nullptr; + } + worker->AddListenerInner(env, typeStr, listener); + CloseHelp::DeletePointer(typeStr, true); + return NapiValueHelp::GetUndefinedValue(env); +} + +bool Worker::WorkerListener::operator==(const WorkerListener& listener) const +{ + if (listener.worker_ == nullptr) { + return false; + } + napi_env env = reinterpret_cast(const_cast(listener.worker_->GetMainEngine())); + napi_ref ref = listener.GetCallback(); + napi_value obj = nullptr; + napi_get_reference_value(env, ref, &obj); + + napi_value compareObj = nullptr; + napi_get_reference_value(env, callback_, &compareObj); + return obj == compareObj; +} + +void Worker::AddListenerInner(napi_env env, const char* type, const WorkerListener* listener) +{ + std::string typestr(type); + auto iter = eventListeners_.find(typestr); + if (iter == eventListeners_.end()) { + std::list listeners; + listeners.emplace_back(const_cast(listener)); + eventListeners_[typestr] = listeners; + } else { + std::list& listenerList = iter->second; + std::list::iterator it = std::find_if( + listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, listener->GetCallback())); + if (it != listenerList.end()) { + return; + } + listenerList.emplace_back(const_cast(listener)); + } +} + +void Worker::RemoveListenerInner(napi_env env, const char* type, napi_ref callback) +{ + std::string typestr(type); + auto iter = eventListeners_.find(typestr); + if (iter == eventListeners_.end()) { + return; + } + std::list& listenerList = iter->second; + if (callback != nullptr) { + std::list::iterator it = + std::find_if(listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, callback)); + if (it != listenerList.end()) { + listenerList.erase(it); + CloseHelp::DeletePointer(*it, false); + } + } else { + for (auto it = listenerList.begin(); it != listenerList.end(); it++) { + CloseHelp::DeletePointer(*it, false); + } + eventListeners_.erase(typestr); + } +} + +napi_value Worker::On(napi_env env, napi_callback_info cbinfo) +{ + return AddListener(env, cbinfo, PERMANENT); +} + +napi_value Worker::Once(napi_env env, napi_callback_info cbinfo) +{ + return AddListener(env, cbinfo, ONCE); +} + +napi_value Worker::RemoveListener(napi_env env, napi_callback_info cbinfo) +{ + size_t argc = NapiValueHelp::GetCallbackInfoArgc(env, cbinfo); + if (argc < 1) { + napi_throw_error(env, nullptr, "Worker param count must be more than 2 with on"); + return nullptr; + } + // check 1st param is string + napi_value thisVar = nullptr; + void* data = nullptr; + napi_value* args = new napi_value[argc]; + [[maybe_unused]] ObjectScope scope(args, true); + napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data); + if (!NapiValueHelp::IsString(args[0])) { + napi_throw_error(env, nullptr, "Worker 1st param must be string with on"); + return nullptr; + } + + Worker* worker = nullptr; + napi_unwrap(env, thisVar, (void**)&worker); + if (worker == nullptr) { + HILOG_ERROR("worker:: worker is nullptr when RemoveListener, maybe worker is terminated"); + return nullptr; + } + + napi_ref callback = nullptr; + if (argc > 1 && !NapiValueHelp::IsCallable(env, args[1])) { + napi_throw_error(env, nullptr, "Worker 2st param must be callable with on"); + return nullptr; + } + if (argc > 1 && NapiValueHelp::IsCallable(env, args[1])) { + napi_create_reference(env, args[1], 1, &callback); + } + + char* typeStr = NapiValueHelp::GetString(env, args[0]); + if (typeStr == nullptr) { + napi_throw_error(env, nullptr, "worker listener type create error, please check."); + return nullptr; + } + worker->RemoveListenerInner(env, typeStr, callback); + CloseHelp::DeletePointer(typeStr, true); + napi_delete_reference(env, callback); + return NapiValueHelp::GetUndefinedValue(env); +} + +napi_value Worker::Off(napi_env env, napi_callback_info cbinfo) +{ + return RemoveListener(env, cbinfo); +} + +napi_value Worker::AddEventListener(napi_env env, napi_callback_info cbinfo) +{ + return AddListener(env, cbinfo, PERMANENT); +} + +napi_value Worker::DispatchEvent(napi_env env, napi_callback_info cbinfo) +{ + size_t argc = NapiValueHelp::GetCallbackInfoArgc(env, cbinfo); + if (argc < 1) { + napi_throw_error(env, nullptr, "worker:: DispatchEvent param count must be more than 1"); + return NapiValueHelp::GetBooleanValue(env, false); + } + + // check 1st param is string + napi_value thisVar = nullptr; + void* data = nullptr; + napi_value* args = new napi_value[argc]; + [[maybe_unused]] ObjectScope scope(args, true); + napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data); + + if (!NapiValueHelp::IsObject(args[0])) { + napi_throw_error(env, nullptr, "worker DispatchEvent 1st param must be Event"); + return NapiValueHelp::GetBooleanValue(env, false); + } + + Worker* worker = nullptr; + napi_unwrap(env, thisVar, (void**)&worker); + if (worker == nullptr) { + HILOG_ERROR("worker:: worker is nullptr when DispatchEvent, maybe worker is terminated"); + return NapiValueHelp::GetBooleanValue(env, false); + } + + napi_value typeValue = nullptr; + napi_get_named_property(env, args[0], "type", &typeValue); + if (!NapiValueHelp::IsString(typeValue)) { + napi_throw_error(env, nullptr, "worker event type must be string"); + return NapiValueHelp::GetBooleanValue(env, false); + } + + napi_value obj = nullptr; + napi_get_reference_value(env, worker->workerWrapper_, &obj); + napi_value argv[1] = { args[0] }; + + char* typeStr = NapiValueHelp::GetString(env, typeValue); + if (typeStr == nullptr) { + napi_throw_error(env, nullptr, "worker listener type create error, please check."); + return NapiValueHelp::GetBooleanValue(env, false); + } + if (strcmp(typeStr, "error") == 0) { + CallWorkCallback(env, obj, 1, argv, "onerror"); + } else if (strcmp(typeStr, "messageerror") == 0) { + CallWorkCallback(env, obj, 1, argv, "onmessageerror"); + } else if (strcmp(typeStr, "message") == 0) { + CallWorkCallback(env, obj, 1, argv, "onmessage"); + } + + worker->HandleEventListeners(env, obj, 1, argv, typeStr); + + CloseHelp::DeletePointer(typeStr, true); + return NapiValueHelp::GetBooleanValue(env, true); +} + +napi_value Worker::RemoveEventListener(napi_env env, napi_callback_info cbinfo) +{ + return RemoveListener(env, cbinfo); +} + +void Worker::RemoveAllListenerInner() +{ + for (auto iter = eventListeners_.begin(); iter != eventListeners_.end(); iter++) { + std::list& listeners = iter->second; + for (auto item = listeners.begin(); item != listeners.end(); item++) { + WorkerListener* listener = *item; + CloseHelp::DeletePointer(listener, false); + } + } + eventListeners_.clear(); +} + +napi_value Worker::RemoveAllListener(napi_env env, napi_callback_info cbinfo) +{ + napi_value thisVar = nullptr; + napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr); + Worker* worker = nullptr; + napi_unwrap(env, thisVar, (void**)&worker); + if (worker == nullptr) { + HILOG_ERROR("worker:: worker is nullptr when RemoveAllListener, maybe worker is terminated"); + return nullptr; + } + + worker->RemoveAllListenerInner(); + return NapiValueHelp::GetUndefinedValue(env); +} + +napi_value Worker::InitWorker(napi_env env, napi_value exports) +{ + NativeEngine *engine = reinterpret_cast(env); + if (engine->IsMainThread()) { + const char className[] = "Worker"; + napi_property_descriptor properties[] = { + DECLARE_NAPI_FUNCTION("postMessage", PostMessage), + DECLARE_NAPI_FUNCTION("terminate", Terminate), + DECLARE_NAPI_FUNCTION("on", On), + DECLARE_NAPI_FUNCTION("once", Once), + DECLARE_NAPI_FUNCTION("off", Off), + DECLARE_NAPI_FUNCTION("addEventListener", AddEventListener), + DECLARE_NAPI_FUNCTION("dispatchEvent", DispatchEvent), + DECLARE_NAPI_FUNCTION("removeEventListener", RemoveEventListener), + DECLARE_NAPI_FUNCTION("removeAllListener", RemoveAllListener), + }; + napi_value workerClazz = nullptr; + napi_define_class(env, className, sizeof(className), Worker::WorkerConstructor, nullptr, + sizeof(properties) / sizeof(properties[0]), properties, &workerClazz); + napi_set_named_property(env, exports, "Worker", workerClazz); + } else { + NativeEngine *engine = reinterpret_cast(env); + Worker *worker = nullptr; + for (auto item = g_workers.begin(); item != g_workers.end(); item++) { + if ((*item)->GetWorkerEngine() == engine) { + worker = *item; + } + } + if (worker == nullptr) { + napi_throw_error(env, nullptr, "worker:: worker is null"); + return exports; + } + + napi_property_descriptor properties[] = { + DECLARE_NAPI_FUNCTION_WITH_DATA("postMessage", PostMessageToMain, worker), + DECLARE_NAPI_FUNCTION_WITH_DATA("close", CloseWorker, worker), + }; + const char propertyName[] = "parentPort"; + napi_value parentPortObj = nullptr; + napi_create_object(env, &parentPortObj); + napi_define_properties(env, parentPortObj, sizeof(properties) / sizeof(properties[0]), properties); + napi_set_named_property(env, exports, propertyName, parentPortObj); + + // register worker parentPort. + napi_create_reference(env, parentPortObj, 1, &worker->parentPort_); + } + return exports; +} + +void Worker::WorkerOnErrorInner(const NativeEngine* engine, napi_value error) +{ + napi_value argv[1] = { error }; + CallWorkerFunction(engine, 1, argv, "onerror", false); +} + +bool Worker::CallWorkerFunction( + const NativeEngine* engine, int argc, const napi_value* argv, const char* methodName, bool tryCatch) +{ + if (engine == nullptr) { + return false; + } + napi_env env = reinterpret_cast(const_cast(engine)); + napi_value callback = NapiValueHelp::GetNamePropertyInParentPort(env, parentPort_, methodName); + bool isCallable = NapiValueHelp::IsCallable(env, callback); + if (!isCallable) { + HILOG_ERROR("worker:: WorkerGlobalScope %{public}s is not Callable", methodName); + return false; + } + napi_value undefinedValue = NapiValueHelp::GetUndefinedValue(env); + napi_value callbackResult = nullptr; + napi_call_function(env, undefinedValue, callback, argc, argv, &callbackResult); + if (tryCatch && callbackResult == nullptr) { + // handle exception + HandleException(GetWorkerEngine()); + } + return true; +} + +void Worker::CloseWorkerCallback() +{ + CallWorkerFunction(GetWorkerEngine(), 0, nullptr, "onclose", true); + // off worker inited environment + if (OHOS::CCRuntime::Worker::WorkerCore::offWorkerFunc != NULL) { + OHOS::CCRuntime::Worker::WorkerCore::offWorkerFunc(const_cast(GetWorkerEngine())); + } +} + +void Worker::CallMainFunction( + const NativeEngine* engine, int argc, const napi_value* argv, const char* methodName) const +{ + if (engine == nullptr) { + return; + } + napi_env env = reinterpret_cast(const_cast(engine)); + napi_value callback = nullptr; + napi_value obj = nullptr; + napi_get_reference_value(env, workerWrapper_, &obj); + napi_get_named_property(env, obj, methodName, &callback); + bool isCallable = NapiValueHelp::IsCallable(env, callback); + if (!isCallable) { + HILOG_ERROR("worker:: worker %{public}s is not Callable", methodName); + return; + } + napi_value callbackResult = nullptr; + napi_call_function(env, obj, callback, argc, argv, &callbackResult); +} +} // namespace OHOS::CCRuntime::Worker diff --git a/jsapi/worker/worker.h b/jsapi/worker/worker.h new file mode 100644 index 0000000000000000000000000000000000000000..61e7a7e6284c557c39eb0b2949cc2aeadb90841c --- /dev/null +++ b/jsapi/worker/worker.h @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_CCRUNTIME_JSAPI_WORKER_H +#define FOUNDATION_CCRUNTIME_JSAPI_WORKER_H + +#include +#include +#include +#include +#include "message_queue.h" +#include "napi/native_node_api.h" +#include "native_engine/native_engine.h" +#include "utils/log.h" +#include "worker_helper.h" +#include "worker_runner.h" + +namespace OHOS::CCRuntime::Worker { +class Worker { +public: + static const int8_t WORKERPARAMNUM = 2; + + enum RunnerState { STARTING, RUNNING, TERMINATEING, TERMINATED }; + + enum ListenerMode { ONCE, PERMANENT }; + + enum ScriptMode { CLASSIC, MODULE }; + + class WorkerListener { + public: + WorkerListener() : callback_(nullptr), worker_(nullptr), mode_(PERMANENT) {} + + explicit WorkerListener(Worker* worker) : callback_(nullptr), worker_(worker), mode_(PERMANENT) {} + + WorkerListener(Worker* worker, ListenerMode mode) : callback_(nullptr), worker_(worker), mode_(mode) {} + + ~WorkerListener() + { + callback_ = nullptr; + worker_ = nullptr; + } + + bool NextIsAvailable() const + { + return mode_ != ONCE; + } + + ListenerMode GetListenerMode() const + { + return mode_; + } + + napi_ref GetCallback() const + { + return callback_; + } + + void SetCallable(napi_env env, napi_value value) + { + napi_create_reference(env, value, 1, &callback_); + } + + void SetMode(ListenerMode mode) + { + mode_ = mode; + } + + bool operator==(const WorkerListener& listener) const; + + private: + napi_ref callback_ {NULL}; + Worker* worker_ {nullptr}; + ListenerMode mode_ {PERMANENT}; + }; + + struct FindWorkerListener { + FindWorkerListener(napi_env env, napi_ref ref) : env_(env), ref_(ref) {} + + bool operator()(const WorkerListener* listener) const + { + napi_ref compareRef = listener->GetCallback(); + napi_value compareObj = nullptr; + napi_get_reference_value(env_, compareRef, &compareObj); + + napi_value obj = nullptr; + napi_get_reference_value(env_, ref_, &obj); + bool isEqual = false; + napi_strict_equals(env_, compareObj, obj, &isEqual); + return isEqual; + } + + napi_env env_; + napi_ref ref_; + }; + using FindWorkerListener = struct FindWorkerListener; + + Worker(NativeEngine* env, napi_ref thisVar); + ~Worker(); + + static void MainOnMessage(const uv_async_t* req); + static void MainOnError(const uv_async_t* req); + static void WorkerOnMessage(const uv_async_t* req); + static void ExecuteInThread(const void* data); + static void PrepareForWorkerInstance(const Worker* worker); + + static napi_value PostMessage(napi_env env, napi_callback_info cbinfo); + static napi_value PostMessageToMain(napi_env env, napi_callback_info cbinfo); + static napi_value Terminate(napi_env env, napi_callback_info cbinfo); + static napi_value CloseWorker(napi_env env, napi_callback_info cbinfo); + static napi_value On(napi_env env, napi_callback_info cbinfo); + static napi_value Once(napi_env env, napi_callback_info cbinfo); + static napi_value Off(napi_env env, napi_callback_info cbinfo); + static napi_value AddEventListener(napi_env env, napi_callback_info cbinfo); + static napi_value DispatchEvent(napi_env env, napi_callback_info cbinfo); + static napi_value RemoveEventListener(napi_env env, napi_callback_info cbinfo); + static napi_value RemoveAllListener(napi_env env, napi_callback_info cbinfo); + + static napi_value AddListener(napi_env env, napi_callback_info cbinfo, ListenerMode mode); + static napi_value RemoveListener(napi_env env, napi_callback_info cbinfo); + + static napi_value WorkerConstructor(napi_env env, napi_callback_info cbinfo); + static napi_value InitWorker(napi_env env, napi_value exports); + + void StartExecuteInThread(napi_env env, const char* script); + + bool UpdateWorkerState(RunnerState runnerState); + + bool IsRunning() const + { + return runnerState_.load(std::memory_order_acquire) == RUNNING; + } + + bool IsTerminated() const + { + return runnerState_.load(std::memory_order_acquire) >= TERMINATED; + } + + bool IsTerminating() const + { + return runnerState_.load(std::memory_order_acquire) == TERMINATEING; + } + + void SetScriptMode(ScriptMode mode) + { + scriptMode_ = mode; + } + + void AddListenerInner(napi_env env, const char* type, const WorkerListener* listener); + void RemoveListenerInner(napi_env env, const char* type, napi_ref callback); + void RemoveAllListenerInner(); + + uv_loop_t* GetWorkerLoop() const + { + if (workerEngine_ != nullptr) { + return workerEngine_->GetUVLoop(); + } + return nullptr; + } + + const NativeEngine* GetWorkerEngine() const + { + return workerEngine_; + } + + void SetWorkerEngine(NativeEngine* engine) + { + workerEngine_ = engine; + } + + const NativeEngine* GetMainEngine() const + { + return mainEngine_; + } + + const char* GetScript() const + { + return script_; + } + + const char* GetName() const + { + return name_; + } + + uv_loop_t* GetMainLoop() const + { + if (mainEngine_ != nullptr) { + return mainEngine_->GetUVLoop(); + } + return nullptr; + } + +private: + void WorkerOnMessageInner(const NativeEngine* engine); + void MainOnMessageInner(const NativeEngine* engine); + void MainOnErrorInner(const NativeEngine* engine); + void MainOnMessageErrorInner(const NativeEngine* engine); + void WorkerOnMessageErrorInner(const NativeEngine* engine); + void WorkerOnErrorInner(const NativeEngine* engine, napi_value error); + + void HandleException(const NativeEngine* engine); + bool CallWorkerFunction(const NativeEngine* engine, int argc, const napi_value* argv, + const char* methodName, bool tryCatch); + void CallMainFunction(const NativeEngine* engine, int argc, const napi_value* argv, const char* methodName) const; + + void HandleEventListeners(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type); + void TerminateInner(); + + void PostMessageInner(MessageDataType data); + void PostMessageToMainInner(MessageDataType data); + + void TerminateWorker(); + void CloseInner(); + + void PublishWorkerOverSignal(); + void CloseWorkerCallback(); + void CloseMainCallback() const; + + char* script_ {nullptr}; + char* name_ {nullptr}; + ScriptMode scriptMode_ {CLASSIC}; + + MessageQueue workerMessageQueue_; + MessageQueue mainMessageQueue_; + MessageQueue errorQueue_; + + uv_async_t workerOnMessageSignal_; + uv_async_t mainOnMessageSignal_; + uv_async_t mainOnErrorSignal_; + + std::atomic runnerState_; + WorkerRunner* runner_ {nullptr}; + + NativeEngine* mainEngine_ {nullptr}; + NativeEngine* workerEngine_ {nullptr}; + + napi_ref workerWrapper_ {nullptr}; + napi_ref parentPort_ {nullptr}; + + std::map> eventListeners_; + + std::mutex workerAsyncMutex_; + + friend class WorkerListener; +}; +} // namespace OHOS::CCRuntime::Worker +#endif // FOUNDATION_CCRUNTIME_JSAPI_WORKER_H \ No newline at end of file diff --git a/jsapi/worker/worker_helper.cpp b/jsapi/worker/worker_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..71f45a0a264bb08126f05ecc9dbb0cc4b497cd2c --- /dev/null +++ b/jsapi/worker/worker_helper.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "worker_helper.h" + +#include "native_engine/native_value.h" + +namespace OHOS::CCRuntime::Worker { +const static int32_t MAXCHARLENGTH = 200; + +bool NapiValueHelp::IsString(napi_value value) +{ + auto valNative = reinterpret_cast(value); + return valNative == nullptr ? false : valNative->TypeOf() == NATIVE_STRING; +} + +bool NapiValueHelp::IsArray(napi_value value) +{ + auto valNative = reinterpret_cast(value); + return valNative == nullptr ? false : valNative->IsArray(); +} + +bool NapiValueHelp::IsConstructor(napi_env env, napi_callback_info cbInfo) +{ + napi_value* funcObj = nullptr; + napi_get_new_target(env, cbInfo, funcObj); + return funcObj != nullptr; +} + +size_t NapiValueHelp::GetCallbackInfoArgc(napi_env env, napi_callback_info cbInfo) +{ + size_t argc = 0; + napi_get_cb_info(env, cbInfo, &argc, nullptr, nullptr, nullptr); + return argc; +} + +napi_value NapiValueHelp::GetNamePropertyInParentPort(napi_env env, napi_ref parentPort, const char* name) +{ + napi_value obj = nullptr; + napi_get_reference_value(env, parentPort, &obj); + + napi_value value = nullptr; + napi_get_named_property(env, obj, name, &value); + + return value; +} + +napi_value NapiValueHelp::GetUndefinedValue(napi_env env) +{ + napi_value result = nullptr; + napi_get_undefined(env, &result); + return result; +} + +bool NapiValueHelp::IsCallable(napi_env env, napi_value value) +{ + bool result = false; + napi_is_callable(env, value, &result); + return result; +} + +bool NapiValueHelp::IsCallable(napi_env env, napi_ref value) +{ + napi_value obj = nullptr; + napi_get_reference_value(env, value, &obj); + if (obj == nullptr) { + return false; + } + return IsCallable(env, obj); +} + +void NapiValueHelp::SetNamePropertyInGlobal(napi_env env, const char* name, napi_value value) +{ + napi_value object = nullptr; + napi_get_global(env, &object); + napi_set_named_property(env, object, name, value); +} + +bool NapiValueHelp::IsObject(napi_value value) +{ + auto nativeValue = reinterpret_cast(value); + return nativeValue->TypeOf() == NATIVE_OBJECT; +} + +char* NapiValueHelp::GetString(napi_env env, napi_value value) +{ + size_t bufferSize = 0; + size_t strLength = 0; + napi_get_value_string_utf8(env, value, nullptr, 0, &bufferSize); + if (bufferSize > MAXCHARLENGTH) { + return nullptr; + } + char* buffer = new char[bufferSize + 1] { 0 }; + napi_get_value_string_utf8(env, value, buffer, bufferSize + 1, &strLength); + return buffer; +} + +napi_value NapiValueHelp::GetBooleanValue(napi_env env, bool value) +{ + napi_value result = nullptr; + napi_get_boolean(env, value, &result); + return result; +} +} // namespace OHOS::CCRuntime::Worker \ No newline at end of file diff --git a/jsapi/worker/worker_helper.h b/jsapi/worker/worker_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..81b5b26f9c263faf22b102330d2b2eb7e9231b24 --- /dev/null +++ b/jsapi/worker/worker_helper.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_CCRUNTIME_JSAPI_WORKER_HELP_H +#define FOUNDATION_CCRUNTIME_JSAPI_WORKER_HELP_H + +#include "napi/native_api.h" +#include "napi/native_node_api.h" + +namespace OHOS::CCRuntime::Worker { +class DereferenceHelp { +public: + template + static Outer* DereferenceOf(const Inner Outer::*field, const Inner* pointer) + { + if (field != nullptr && pointer != nullptr) { + uintptr_t fieldOffset = (uintptr_t)&(static_cast(0)->*field); + Outer* outPointer = reinterpret_cast((uintptr_t)(pointer) - fieldOffset); + return outPointer; + } + return nullptr; + } +}; + +class NapiValueHelp { +public: + static bool IsString(napi_value value); + static bool IsArray(napi_value value); + static bool IsConstructor(napi_env env, napi_callback_info cbInfo); + static bool IsCallable(napi_env env, napi_value value); + static bool IsCallable(napi_env env, napi_ref value); + static size_t GetCallbackInfoArgc(napi_env env, napi_callback_info cbInfo); + static napi_value GetNamePropertyInParentPort(napi_env env, napi_ref parentPort, const char* name); + static void SetNamePropertyInGlobal(napi_env env, const char* name, napi_value value); + static napi_value GetUndefinedValue(napi_env env); + static bool IsObject(napi_value value); + static char* GetString(napi_env env, napi_value value); + static napi_value GetBooleanValue(napi_env env, bool value); +}; + +class CloseHelp { +public: + template + static void DeletePointer(const T* value, bool isArray) + { + if (value == nullptr) { + return; + } + if (isArray) { + delete[] value; + } else { + delete value; + } + } +}; + +template +class ObjectScope { +public: + ObjectScope(T* data, bool isArray) : data_(data), isArray_(isArray) {} + ~ObjectScope() + { + if (data_ == nullptr) { + return; + } + if (isArray_) { + delete[] data_; + } else { + delete data_; + } + } + +private: + T* data_; + bool isArray_; +}; +} // namespace OHOS::CCRuntime::Worker +#endif // FOUNDATION_CCRUNTIME_JSAPI_NAPI_VALUE_HELP_H diff --git a/jsapi/worker/worker_init.cpp b/jsapi/worker/worker_init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dcb33ed18fc012a968c466237504a16addcc407a --- /dev/null +++ b/jsapi/worker/worker_init.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "worker_init.h" + +namespace OHOS::CCRuntime::Worker { +InitWorkerFunc WorkerCore::initWorkerFunc = NULL; +GetAssetFunc WorkerCore::getAssertFunc = NULL; +OffWorkerFunc WorkerCore::offWorkerFunc = NULL; + +void WorkerCore::RegisterInitWorkerFunc(InitWorkerFunc func) +{ + if (func != nullptr) { + WorkerCore::initWorkerFunc = func; + } +} + +void WorkerCore::RegisterAssetFunc(GetAssetFunc func) +{ + if (func != nullptr) { + WorkerCore::getAssertFunc = func; + } +} + +void WorkerCore::RegisterOffWorkerFunc(OffWorkerFunc func) +{ + if (func != nullptr) { + WorkerCore::offWorkerFunc = func; + } +} +} // namespace OHOS::CCRuntime::Worker \ No newline at end of file diff --git a/jsapi/worker/worker_runner.cpp b/jsapi/worker/worker_runner.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b6aa4e7961180f7709286fc5147c86cc69e722a8 --- /dev/null +++ b/jsapi/worker/worker_runner.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "worker_runner.h" + +#include +#include +#include "worker_helper.h" + +namespace OHOS::CCRuntime::Worker { +WorkerRunner::WorkerRunner(WorkerStartCallback callback) : callback_(callback), selfThreadId_(uv_thread_self()) {} + +WorkerRunner::~WorkerRunner() +{ + CloseHelp::DeletePointer(workerInnerRunner_, false); +} + +void WorkerRunner::WorkerInnerRunner::Run() +{ + if (runner_ != nullptr) { + runner_->Run(); + } +} + +WorkerRunner::WorkerInnerRunner::WorkerInnerRunner(const WorkerRunner* runner) : runner_(runner) {} + +void WorkerRunner::Run() const +{ + if (callback_.callback != nullptr) { + callback_.callback(callback_.data); + } +} + +bool WorkerRunner::Execute() +{ + workerInnerRunner_ = new WorkerInnerRunner(this); + return workerInnerRunner_->Start(); +} +} // namespace OHOS::CCRuntime::Worker diff --git a/jsapi/worker/worker_runner.h b/jsapi/worker/worker_runner.h new file mode 100644 index 0000000000000000000000000000000000000000..1835ce42957ca5aef1f8dc57939f50a72855c370 --- /dev/null +++ b/jsapi/worker/worker_runner.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_CCRUNTIME_JSAPI_WORK_RUNNER_H +#define FOUNDATION_CCRUNTIME_JSAPI_WORK_RUNNER_H + +#include + +#include "native_engine/native_engine.h" +#include "thread.h" + +namespace OHOS::CCRuntime::Worker { +struct WorkerStartCallback { + using CallbackFunction = std::function; + + explicit WorkerStartCallback(CallbackFunction function = nullptr, void* dataArgs = nullptr) + : callback(function), data(dataArgs) + {} + + CallbackFunction callback; + void* data; +}; + +class WorkerRunner { +public: + // real thread execute + class WorkerInnerRunner : public Thread { + public: + explicit WorkerInnerRunner(const WorkerRunner* runner); + ~WorkerInnerRunner() = default; + void Run() override; + + private: + const WorkerRunner* runner_; + }; + + explicit WorkerRunner(WorkerStartCallback callback); + ~WorkerRunner(); + + bool Execute(); + void Run() const; + void Stop(); + +private: + WorkerInnerRunner* workerInnerRunner_ {nullptr}; + WorkerStartCallback callback_; + uv_thread_t selfThreadId_ {0}; +}; +} // namespace OHOS::CCRuntime::Worker +#endif // FOUNDATION_CCRUNTIME_JSAPI_WORK_RUNNER_H \ No newline at end of file diff --git a/ohos.build b/ohos.build new file mode 100755 index 0000000000000000000000000000000000000000..d9c505f45db7517af957a8d358d3dd0c9bf8eda3 --- /dev/null +++ b/ohos.build @@ -0,0 +1,28 @@ +{ + "subsystem": "ccruntime", + "parts": { + "jsapi": { + "variants": [ + "wearable", + "phone" + ], + "module_list": [ + "//base/compileruntime/js_worker_module/jsapi:jsapi_packages", + "//base/compileruntime/js_worker_module/jsapi/interfaces/innerkits/worker_core:jsapi_init_packages" + ], + "inner_kits": [ + { + "header": { + "header_base": "//base/compileruntime/js_worker_module/jsapi/interfaces/innerkits/worker_core/include", + "header_files": [ + "worker_init.h" + ] + }, + "name": "//base/compileruntime/js_worker_module/jsapi/interfaces/innerkits/worker_core:worker_init" + } + ], + "test_list": [ + ] + } + } +}