diff --git a/deps/node/src/lwnode/lwnode-public.cc b/deps/node/src/lwnode/lwnode-public.cc index 8bc27de1be..0889f4c567 100644 --- a/deps/node/src/lwnode/lwnode-public.cc +++ b/deps/node/src/lwnode/lwnode-public.cc @@ -63,6 +63,14 @@ class Runtime::Internal { return runner_.Run(*instance_); } + void Stop() { + if (instance_ == nullptr) { + return; + } + + runner_.Stop(); + } + void Free() { if (is_initialized && instance_) { DisposeNode(instance_); @@ -105,6 +113,10 @@ int Runtime::Start(int argc, char** argv, std::promise&& promise) { return result; } +void Runtime::Stop() { + internal_->runner_.Stop(); +} + std::shared_ptr Runtime::GetPort() { return internal_->runner_.GetPort(); } diff --git a/deps/node/src/node_main_lw_runner-inl.h b/deps/node/src/node_main_lw_runner-inl.h index 31e22149c8..da206cdf09 100644 --- a/deps/node/src/node_main_lw_runner-inl.h +++ b/deps/node/src/node_main_lw_runner-inl.h @@ -188,6 +188,14 @@ class LWNodeMainRunner { return exit_code; } + void Stop() { + CHECK_NOT_NULL(environment_); + if (environment_->is_stopping()) { + return; + } + environment_->ExitEnv(); + } + std::shared_ptr GetPort() { CHECK_NOT_NULL(environment_); return environment_->GetPort(); diff --git a/include/lwnode/lwnode-public.h b/include/lwnode/lwnode-public.h index f793878194..06902dcbb0 100644 --- a/include/lwnode/lwnode-public.h +++ b/include/lwnode/lwnode-public.h @@ -88,6 +88,12 @@ class LWNODE_EXPORT Runtime { **/ int Start(int argc, char** argv, std::promise&& promise); + /** + * Stop the runtime. You can use this function to stop the runtime from another + * thread. + **/ + void Stop(); + std::shared_ptr GetPort(); private: diff --git a/test/embedding/CMakeLists.txt b/test/embedding/CMakeLists.txt index 0b0deb33bd..ef473a7c6b 100644 --- a/test/embedding/CMakeLists.txt +++ b/test/embedding/CMakeLists.txt @@ -2,17 +2,24 @@ cmake_minimum_required(VERSION 3.21.3) project(embedtest) -include_directories("../../include/lwnode") -link_libraries(lwnode pthread) -add_compile_options(-std=c++17) +set(TARGET_OS "linux") +if (CMAKE_SYSTEM_PROCESSOR MATCHES "armv7l") + set(TARGET_OS "tizen/arm") +endif() +set(BUILD_TYPE "Release") if (CMAKE_BUILD_TYPE STREQUAL "Debug") add_compile_options(-O2 -g) - link_directories("../../out/linux/Debug/lib") -else() - link_directories("../../out/linux/Release/lib") + set(BUILD_TYPE "Debug") endif() +include_directories("../../include/lwnode") +find_library(LWNODE_LIB lwnode HINT "../../out/${TARGET_OS}/${BUILD_TYPE}/lib") +link_libraries(pthread ${LWNODE_LIB} -Wl,-rpath='./') + +add_compile_options(-std=c++17) + add_executable(embedtest.x embedtest.cc) add_executable(example.x example.cc) add_executable(send-message-sync.x send-message-sync.cc) +add_executable(runtime-stop.x runtime-stop.cc) diff --git a/test/embedding/packaging/lwnode-embedding.spec b/test/embedding/packaging/lwnode-embedding.spec new file mode 100644 index 0000000000..4ed8534560 --- /dev/null +++ b/test/embedding/packaging/lwnode-embedding.spec @@ -0,0 +1,58 @@ +# Copyright (c) 2025-present Samsung Electronics 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. + +Name: lwnode-embedding +Summary: - +Version: 1.0.0 +Release: 1 +Group: Development +License: Apache-2.0 and MIT and BSD-2-Clause and BSD-3-Clause and BOEHM-GC and ICU and LGPL-2.1+ and Zlib +Source: %{name}-%{version}.tar.gz + +BuildRequires: cmake +BuildRequires: make +BuildRequires: pkgconfig(dlog) + + +# Initialize the variables +%define _output_path out/tizen/arm/Release + +%description +lwnode embedding test + +%prep +%setup -q + +%build + +cmake -S test/embedding -B%{_output_path} -DCMAKE_BUILD_TYPE=Release +make -C %{_output_path} -j$(nproc) + + +%install + +%clean +rm -fr ./*.list +rm -fr ./*.manifest + +%post +/sbin/ldconfig + +%postun +/sbin/ldconfig + +%files +%defattr(-,tizenglobalapp,root,-) + +%license LICENSE LICENSE.Apache-2.0 LICENSE.NodeJS LICENSE.MIT LICENSE.BSD-2-Clause LICENSE.BSD-3-Clause LICENSE.BOEHM-GC LICENSE.ICU LICENSE.LGPL-2.1+ LICENSE.Zlib diff --git a/test/embedding/runtime-stop.cc b/test/embedding/runtime-stop.cc new file mode 100644 index 0000000000..a37767e96b --- /dev/null +++ b/test/embedding/runtime-stop.cc @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include + +#include +#include + +#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0])) + +int main(int argc, char* argv[]) { + auto runtime = std::make_shared(); + + std::promise promise; + std::future init_future = promise.get_future(); + const char* script = "test/embedding/test-20-runtime-stop.js"; + std::string path = (std::filesystem::current_path() / script).string(); + char* args[] = {const_cast(""), const_cast(path.c_str())}; + + std::thread worker = std::thread( + [&](std::promise&& promise) mutable { + // FIXME: Fix Runtime::Init() call to ensure environment initialization + // before running the loop, Runtime::Run(). This workaround passes a + // promise directly to know when that is. + runtime->Start(COUNT_OF(args), args, std::move(promise)); + }, + std::move(promise)); + + init_future.wait(); + + std::cout << "[C]sleep app" << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(3)); + runtime->Stop(); + std::cout << "[C]terminate app" << std::endl; + + worker.join(); + return 0; +} diff --git a/test/embedding/test-20-runtime-stop.js b/test/embedding/test-20-runtime-stop.js new file mode 100644 index 0000000000..c7af47fd45 --- /dev/null +++ b/test/embedding/test-20-runtime-stop.js @@ -0,0 +1,19 @@ +const lwnode = process.lwnode; +const http = require('http'); + +if (process.lwnode.hasSystemInfo('tizen')) { + console.log('[JS] start gmain-loop') + require("./gmain-loop").init(); +} + +console.log('[JS] start app') + +console.log('[JS] create server') +const server = http.createServer(); +server.listen(1234); + +lwnode.ref(); + +setInterval(() => { + console.log('[JS]loop'); +}, 1000);