From 2127aeb544a4ace1363de1127577a409b048251c Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 18 Jan 2026 19:48:12 +0100 Subject: [PATCH 01/15] gh-769: Add libuv dep, update ci workflows and add smoke test --- .devcontainer/Containerfile | 1 + .github/workflows/macos.yml | 2 +- .github/workflows/ubuntu.yml | 1 + cmake/Modules/Findlibuv.cmake | 61 ++++++++++++++++++ conanfile.py | 4 ++ misc/experimental/CMakeLists.txt | 1 + misc/experimental/libuv/CMakeLists.txt | 31 +++++++++ .../libuv/src/libuv_smoke_test.cc | 64 +++++++++++++++++++ 8 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 cmake/Modules/Findlibuv.cmake create mode 100644 misc/experimental/libuv/CMakeLists.txt create mode 100644 misc/experimental/libuv/src/libuv_smoke_test.cc diff --git a/.devcontainer/Containerfile b/.devcontainer/Containerfile index 622f1fd2b..cc2b2f8ca 100644 --- a/.devcontainer/Containerfile +++ b/.devcontainer/Containerfile @@ -68,6 +68,7 @@ RUN DEBIAN_FRONTEND="noninteractive" sudo apt-get update && \ libjansson-dev \ libxml2-dev \ libzip-dev \ + libuv1-dev \ rapidjson-dev \ uuid-dev && \ sudo apt-get clean diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index eceb860b5..f8279ae81 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -80,7 +80,7 @@ jobs: uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c #v3.3.0 - name: Install dependencies run: | - brew install lcov jansson rapidjson libzip ccache ninja openssl@1.1 google-benchmark + brew install lcov jansson rapidjson libzip ccache ninja openssl@1.1 google-benchmark libuv - name: Prepare ccache timestamp id: ccache_cache_timestamp run: | diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index dc0964f4a..f2a0e5147 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -123,6 +123,7 @@ jobs: libjansson-dev \ libcurl4-openssl-dev \ libbenchmark-dev \ + libuv1-dev \ default-jdk \ cmake \ libffi-dev \ diff --git a/cmake/Modules/Findlibuv.cmake b/cmake/Modules/Findlibuv.cmake new file mode 100644 index 000000000..4c71e79f6 --- /dev/null +++ b/cmake/Modules/Findlibuv.cmake @@ -0,0 +1,61 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +# - Try to find libuv +# Once done this will define +# libuv_FOUND - System has libuv +# LIBUV_INCLUDE_DIR - The libuv include directory +# LIBUV_LIBRARY - The libuv library +# uv - Imported target for libuv + +find_package(libuv CONFIG QUIET) + +if (NOT libuv_FOUND) + find_package(PkgConfig QUIET) + if (PkgConfig_FOUND) + pkg_check_modules(LIBUV QUIET libuv) + endif () +endif () + +if (NOT libuv_FOUND) + find_path(LIBUV_INCLUDE_DIR + NAMES uv.h + HINTS ${LIBUV_INCLUDEDIR} ${LIBUV_INCLUDE_DIRS} + ) + + find_library(LIBUV_LIBRARY + NAMES uv libuv + HINTS ${LIBUV_LIBDIR} ${LIBUV_LIBRARY_DIRS} + ) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(libuv DEFAULT_MSG LIBUV_LIBRARY LIBUV_INCLUDE_DIR) + mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY) +endif () + +if (libuv_FOUND AND NOT TARGET libuv::uv AND TARGET libuv::libuv) + #Note: libuv cmake config possible defines libuv::libuv target + # and conan libuv package defines uv target + # so create an alias target uv for consistency + add_library(libuv::uv ALIAS libuv::libuv) +elseif (libuv_FOUND AND NOT TARGET libuv::uv AND LIBUV_LIBRARY AND LIBUV_INCLUDE_DIR) + add_library(libuv::uv SHARED IMPORTED) + set_target_properties(libuv::uv PROPERTIES + IMPORTED_LOCATION "${LIBUV_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${LIBUV_INCLUDE_DIR}" + ) +endif () diff --git a/conanfile.py b/conanfile.py index bb1c63907..bf4925354 100644 --- a/conanfile.py +++ b/conanfile.py @@ -336,6 +336,8 @@ def configure(self): self.options['mosquitto'].shared = True if self.options.enable_testing: self.options['mosquitto'].broker = True + if self.options.build_experimental: + self.options['libuv'].shared = True def requirements(self): if self.options.build_utils: @@ -370,6 +372,8 @@ def requirements(self): self.requires("zlib/1.3.1", override=True) if self.options.build_event_admin_remote_provider_mqtt: self.requires("mosquitto/[>=2.0.3 <3.0.0]") + if self.options.build_experimental: + self.requires("libuv/[>=1.49.2 <2.0.0]") self.validate() def generate(self): diff --git a/misc/experimental/CMakeLists.txt b/misc/experimental/CMakeLists.txt index f4de02bf2..51307cb0a 100644 --- a/misc/experimental/CMakeLists.txt +++ b/misc/experimental/CMakeLists.txt @@ -18,5 +18,6 @@ celix_subproject(EXPERIMENTAL "Options to enable building the experimental - non stable - bundles/libraries. " OFF) if (EXPERIMENTAL) add_subdirectory(bundles) + add_subdirectory(libuv) add_subdirectory(rust) endif () diff --git a/misc/experimental/libuv/CMakeLists.txt b/misc/experimental/libuv/CMakeLists.txt new file mode 100644 index 000000000..02b35ecf7 --- /dev/null +++ b/misc/experimental/libuv/CMakeLists.txt @@ -0,0 +1,31 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +find_package(libuv REQUIRED) + +if (NOT TARGET libuv::uv AND TARGET uv) + #Note: conan libuv package 1.49.2 defines uv target, but 1.51.0 defines lubuv::uv target + add_library(libuv::uv ALIAS uv) +endif () + +if (ENABLE_TESTING) + add_executable(libuv_smoke_test + src/libuv_smoke_test.cc + ) + target_link_libraries(libuv_smoke_test PRIVATE libuv::uv GTest::gtest GTest::gtest_main) + add_test(NAME libuv_smoke_test COMMAND libuv_smoke_test) +endif () diff --git a/misc/experimental/libuv/src/libuv_smoke_test.cc b/misc/experimental/libuv/src/libuv_smoke_test.cc new file mode 100644 index 000000000..820d7d0c1 --- /dev/null +++ b/misc/experimental/libuv/src/libuv_smoke_test.cc @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 +#include + +TEST(LibuvSmokeTest, CanInitAndCloseLoopTest) { + uv_loop_t loop; + EXPECT_EQ(0, uv_loop_init(&loop)); + EXPECT_EQ(0, uv_loop_close(&loop)); +} + +namespace { +struct ThreadState { + uv_mutex_t mutex; + uv_cond_t cond; + bool ready; +}; + +void threadMain(void* data) { + auto* state = static_cast(data); + uv_mutex_lock(&state->mutex); + state->ready = true; + uv_cond_signal(&state->cond); + uv_mutex_unlock(&state->mutex); +} +} // namespace + +TEST(LibuvSmokeTest, CanUseThreadMutexAndConditionTest) { + ThreadState state{}; + state.ready = false; + + ASSERT_EQ(0, uv_mutex_init(&state.mutex)); + ASSERT_EQ(0, uv_cond_init(&state.cond)); + + uv_thread_t thread; + ASSERT_EQ(0, uv_thread_create(&thread, threadMain, &state)); + + uv_mutex_lock(&state.mutex); + while (!state.ready) { + uv_cond_wait(&state.cond, &state.mutex); + } + uv_mutex_unlock(&state.mutex); + + EXPECT_EQ(0, uv_thread_join(&thread)); + uv_cond_destroy(&state.cond); + uv_mutex_destroy(&state.mutex); +} From e2d3ceee194a4e62269bc79f0882fcb9828131a0 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 18 Jan 2026 20:20:35 +0100 Subject: [PATCH 02/15] gh-769: Add celix_uv_cleanup.h --- libs/utils/CMakeLists.txt | 10 +- libs/utils/gtest/CMakeLists.txt | 6 +- libs/utils/gtest/src/UvThreadsTestSuite.cc | 120 ++++++++++++ libs/utils/include/celix_uv_cleanup.h | 181 ++++++++++++++++++ misc/experimental/CMakeLists.txt | 1 - misc/experimental/libuv/CMakeLists.txt | 31 --- .../libuv/src/libuv_smoke_test.cc | 64 ------- 7 files changed, 314 insertions(+), 99 deletions(-) create mode 100644 libs/utils/gtest/src/UvThreadsTestSuite.cc create mode 100644 libs/utils/include/celix_uv_cleanup.h delete mode 100644 misc/experimental/libuv/CMakeLists.txt delete mode 100644 misc/experimental/libuv/src/libuv_smoke_test.cc diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index e28317b49..bbb7474a1 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -18,7 +18,13 @@ celix_subproject(UTILS "Option to enable building the Utilities library" ON) if (UTILS) find_package(libzip REQUIRED) - find_package(jansson REQUIRED) #TODO add jansson dep info to build (conan) and documentation info + find_package(jansson REQUIRED) + find_package(libuv REQUIRED) + + if (NOT TARGET libuv::uv AND TARGET uv) + #Note: conan libuv package 1.49.2 defines uv target, but 1.51.0 defines libuv::uv target + add_library(libuv::uv ALIAS uv) + endif () set(MEMSTREAM_SOURCES ) set(MEMSTREAM_INCLUDES ) @@ -46,7 +52,7 @@ if (UTILS) ${MEMSTREAM_SOURCES} ) set(UTILS_PRIVATE_DEPS libzip::zip jansson::jansson) - set(UTILS_PUBLIC_DEPS) + set(UTILS_PUBLIC_DEPS libuv::uv) add_library(utils SHARED ${UTILS_SRC}) diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt index 4a25f7d41..c7cf68fcf 100644 --- a/libs/utils/gtest/CMakeLists.txt +++ b/libs/utils/gtest/CMakeLists.txt @@ -35,6 +35,7 @@ add_executable(test_utils src/VersionTestSuite.cc src/ErrTestSuite.cc src/ThreadsTestSuite.cc + src/UvThreadsTestSuite.cc src/CelixErrnoTestSuite.cc src/CelixUtilsAutoCleanupTestSuite.cc src/ArrayListTestSuite.cc @@ -42,7 +43,7 @@ add_executable(test_utils src/CxxExceptionsTestSuite.cc ) -target_link_libraries(test_utils PRIVATE utils_cut Celix::utils GTest::gtest GTest::gtest_main libzip::zip) +target_link_libraries(test_utils PRIVATE utils_cut Celix::utils GTest::gtest GTest::gtest_main libzip::zip libuv::uv) target_include_directories(test_utils PRIVATE ../src) #for version_private (needs refactoring of test) celix_deprecated_utils_headers(test_utils) @@ -153,7 +154,10 @@ if (EI_TESTS) Celix::fts_ei utils_cut Celix::utils_ei + libuv::uv + -Wl,--whole-archive Celix::ifaddrs_ei + -Wl,--no-whole-archive Celix::threads_ei Celix::malloc_ei Celix::asprintf_ei diff --git a/libs/utils/gtest/src/UvThreadsTestSuite.cc b/libs/utils/gtest/src/UvThreadsTestSuite.cc new file mode 100644 index 000000000..0ae40bfaf --- /dev/null +++ b/libs/utils/gtest/src/UvThreadsTestSuite.cc @@ -0,0 +1,120 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 "celix_uv_cleanup.h" + +#include +#include + +class UvThreadsTestSuite : public ::testing::Test { +}; + +static void uvThreadIncrement(void* data) { + auto* counter = static_cast*>(data); + counter->fetch_add(1); +} + +TEST_F(UvThreadsTestSuite, ThreadAutoCleanupTest) { + std::atomic counter{0}; + { + celix_auto(uv_thread_t) thread; + ASSERT_EQ(0, uv_thread_create(&thread, uvThreadIncrement, &counter)); + } //thread out of scope -> join + EXPECT_EQ(1, counter.load()); +} + +TEST_F(UvThreadsTestSuite, MutexGuardTest) { + uv_mutex_t mutex; + ASSERT_EQ(0, uv_mutex_init(&mutex)); + celix_autoptr(uv_mutex_t) mutexCleanup = &mutex; + + { + celix_auto(celix_uv_mutex_lock_guard_t) guard = celixUvMutexLockGuard_init(&mutex); + EXPECT_NE(0, uv_mutex_trylock(&mutex)); + } //guard out of scope -> unlock + + EXPECT_EQ(0, uv_mutex_trylock(&mutex)); + uv_mutex_unlock(&mutex); +} + +TEST_F(UvThreadsTestSuite, MutexStealTest) { + uv_mutex_t mutex; + ASSERT_EQ(0, uv_mutex_init(&mutex)); + celix_autoptr(uv_mutex_t) mutexCleanup = &mutex; + celix_steal_ptr(mutexCleanup); + uv_mutex_destroy(&mutex); +} + +TEST_F(UvThreadsTestSuite, RwlockGuardTest) { + uv_rwlock_t lock; + ASSERT_EQ(0, uv_rwlock_init(&lock)); + celix_autoptr(uv_rwlock_t) lockCleanup = &lock; + + { + celix_auto(celix_uv_rwlock_wlock_guard_t) guard = celixUvRwlockWlockGuard_init(&lock); + EXPECT_NE(0, uv_rwlock_tryrdlock(&lock)); + EXPECT_NE(0, uv_rwlock_trywrlock(&lock)); + } //guard out of scope -> unlock + + { + celix_auto(celix_uv_rwlock_rlock_guard_t) guard = celixUvRwlockRlockGuard_init(&lock); + + EXPECT_EQ(0, uv_rwlock_tryrdlock(&lock)); + uv_rwlock_rdunlock(&lock); + + EXPECT_NE(0, uv_rwlock_trywrlock(&lock)); + } //guard out of scope -> unlock + + EXPECT_EQ(0, uv_rwlock_trywrlock(&lock)); + uv_rwlock_wrunlock(&lock); +} + +TEST_F(UvThreadsTestSuite, RwlockStealdTest) { + uv_rwlock_t lock; + ASSERT_EQ(0, uv_rwlock_init(&lock)); + celix_autoptr(uv_rwlock_t) lockCleanup = &lock; + celix_steal_ptr(lockCleanup); + uv_rwlock_destroy(&lock); +} + +TEST_F(UvThreadsTestSuite, ConditionAutoCleanupTest) { + uv_cond_t cond; + ASSERT_EQ(0, uv_cond_init(&cond)); + celix_autoptr(uv_cond_t) condCleanup = &cond; +} + +TEST_F(UvThreadsTestSuite, ConditionStealTest) { + uv_cond_t cond; + ASSERT_EQ(0, uv_cond_init(&cond)); + celix_autoptr(uv_cond_t) condCleanup = &cond; + celix_steal_ptr(condCleanup); + uv_cond_destroy(&cond); +} + +TEST_F(UvThreadsTestSuite, LocalThreadStorageKeyAutoCleanupTest) { + uv_key_t key; + ASSERT_EQ(0, uv_key_create(&key)); + celix_autoptr(uv_key_t) keyCleanup = &key; +} + +TEST_F(UvThreadsTestSuite, LocalThreadStorageKeyStealTest) { + uv_key_t key; + ASSERT_EQ(0, uv_key_create(&key)); + celix_autoptr(uv_key_t) keyCleanup = &key; + celix_steal_ptr(keyCleanup); + uv_key_delete(&key); +} diff --git a/libs/utils/include/celix_uv_cleanup.h b/libs/utils/include/celix_uv_cleanup.h new file mode 100644 index 000000000..a4ced5dc1 --- /dev/null +++ b/libs/utils/include/celix_uv_cleanup.h @@ -0,0 +1,181 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 CELIX_UV_CLEANUP_H +#define CELIX_UV_CLEANUP_H + +#include + +#include "celix_cleanup.h" + +#ifdef __cplusplus +extern "C" { +#endif + +CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(uv_thread_t, uv_thread_join) +CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(uv_mutex_t, uv_mutex_destroy) +CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(uv_rwlock_t, uv_rwlock_destroy) +CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(uv_cond_t, uv_cond_destroy) +CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(uv_key_t, uv_key_delete) + +/** + * @brief Lock guard for uv mutexes. + */ +typedef struct celix_uv_mutex_lock_guard { + uv_mutex_t* mutex; +} celix_uv_mutex_lock_guard_t; + +/** + * @brief Initialize a lock guard for @a mutex. + * + * Lock a mutex and return a celix_uv_mutex_lock_guard_t. + * Unlock with celixUvMutexLockGuard_deinit(). Using uv_mutex_lock() on a mutex + * while a celix_uv_mutex_lock_guard_t exists can lead to undefined behaviour. + * + * No allocation is performed, it is equivalent to a uv_mutex_lock() call. + * This is intended to be used with celix_auto(). + * + * @param mutex A mutex to lock. + * @return An initialized lock guard to be used with celix_auto(). + */ +static CELIX_UNUSED inline celix_uv_mutex_lock_guard_t celixUvMutexLockGuard_init(uv_mutex_t* mutex) { + celix_uv_mutex_lock_guard_t guard; + guard.mutex = mutex; + uv_mutex_lock(mutex); + return guard; +} + +/** + * @brief Deinitialize a lock guard for a mutex. + * + * Unlock the mutex of a guard. + * No memory is freed, it is equivalent to a uv_mutex_unlock() call. + * + * @param guard A celix_uv_mutex_lock_guard_t. + */ +static CELIX_UNUSED inline void celixUvMutexLockGuard_deinit(celix_uv_mutex_lock_guard_t* guard) { + if (guard->mutex) { + uv_mutex_unlock(guard->mutex); + guard->mutex = NULL; + } +} + +CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_uv_mutex_lock_guard_t, celixUvMutexLockGuard_deinit) + +/** + * @brief A RAII style write lock guard for uv_rwlock_t. + * + * The lock is obtained in the constructor and released in the destructor. + * This is intended to be used with celix_auto(). + */ +typedef struct celix_uv_rwlock_wlock_guard { + uv_rwlock_t* lock; +} celix_uv_rwlock_wlock_guard_t; + +/** + * @brief Initialize a write lock guard for @a lock. + * + * Obtain a write lock on @a lock and return a celix_uv_rwlock_wlock_guard_t. + * Unlock with celixUvRwlockWlockGuard_deinit(). Using uv_rwlock_wrunlock() + * on @lock while a celix_uv_rwlock_wlock_guard_t exists can lead to undefined behaviour. + * + * No allocation is performed, it is equivalent to a uv_rwlock_wrlock() call. + * This is intended to be used with celix_auto(). + * + * @param lock A read-write lock to lock. + * @return An initialized write lock guard to be used with celix_auto(). + */ +static CELIX_UNUSED inline celix_uv_rwlock_wlock_guard_t celixUvRwlockWlockGuard_init(uv_rwlock_t* lock) { + celix_uv_rwlock_wlock_guard_t guard; + guard.lock = lock; + uv_rwlock_wrlock(lock); + return guard; +} + +/** + * @brief Deinitialize a write lock guard. + * + * Release a write lock on the read-write lock contained in @a guard. + * See celixUvRwlockWlockGuard_init() for details. + * No memory is freed, it is equivalent to a uv_rwlock_wrunlock() call. + * + * @param guard A celix_uv_rwlock_wlock_guard_t. + */ +static CELIX_UNUSED inline void celixUvRwlockWlockGuard_deinit(celix_uv_rwlock_wlock_guard_t* guard) { + if (guard->lock) { + uv_rwlock_wrunlock(guard->lock); + guard->lock = NULL; + } +} + +CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_uv_rwlock_wlock_guard_t, celixUvRwlockWlockGuard_deinit) + +/** + * @brief A RAII style read lock guard for uv_rwlock_t. + * + * The lock is obtained in the constructor and released in the destructor. + * This is intended to be used with celix_auto(). + */ +typedef struct celix_uv_rwlock_rlock_guard { + uv_rwlock_t* lock; +} celix_uv_rwlock_rlock_guard_t; + +/** + * @brief Initialize a read lock guard for a lock. + * + * Obtain a read lock on a lock and return a celix_uv_rwlock_rlock_guard_t. + * Unlock with celixUvRwlockRlockGuard_deinit(). Using uv_rwlock_rdunlock() + * on @lock while a celix_uv_rwlock_rlock_guard_t exists can lead to undefined behaviour. + * + * No allocation is performed, it is equivalent to a uv_rwlock_rdlock() call. + * This is intended to be used with celix_auto(). + * + * @param lock A read-write lock to lock. + * @return A guard to be used with celix_auto(). + */ +static CELIX_UNUSED inline celix_uv_rwlock_rlock_guard_t celixUvRwlockRlockGuard_init(uv_rwlock_t* lock) { + celix_uv_rwlock_rlock_guard_t guard; + guard.lock = lock; + uv_rwlock_rdlock(lock); + return guard; +} + +/** + * @brief Deinitialize a read lock guard. + * + * Release a read lock on the read-write lock contained in a guard. + * See celixUvRwlockRlockGuard_init() for details. + * No memory is freed, it is equivalent to a uv_rwlock_rdunlock() call. + * + * @param guard A celix_uv_rwlock_rlock_guard_t. + */ +static CELIX_UNUSED inline void celixUvRwlockRlockGuard_deinit(celix_uv_rwlock_rlock_guard_t* guard) { + if (guard->lock) { + uv_rwlock_rdunlock(guard->lock); + guard->lock = NULL; + } +} + +CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_uv_rwlock_rlock_guard_t, celixUvRwlockRlockGuard_deinit) + +#ifdef __cplusplus +} +#endif + +#endif /* CELIX_UV_CLEANUP_H */ diff --git a/misc/experimental/CMakeLists.txt b/misc/experimental/CMakeLists.txt index 51307cb0a..f4de02bf2 100644 --- a/misc/experimental/CMakeLists.txt +++ b/misc/experimental/CMakeLists.txt @@ -18,6 +18,5 @@ celix_subproject(EXPERIMENTAL "Options to enable building the experimental - non stable - bundles/libraries. " OFF) if (EXPERIMENTAL) add_subdirectory(bundles) - add_subdirectory(libuv) add_subdirectory(rust) endif () diff --git a/misc/experimental/libuv/CMakeLists.txt b/misc/experimental/libuv/CMakeLists.txt deleted file mode 100644 index 02b35ecf7..000000000 --- a/misc/experimental/libuv/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you 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. - -find_package(libuv REQUIRED) - -if (NOT TARGET libuv::uv AND TARGET uv) - #Note: conan libuv package 1.49.2 defines uv target, but 1.51.0 defines lubuv::uv target - add_library(libuv::uv ALIAS uv) -endif () - -if (ENABLE_TESTING) - add_executable(libuv_smoke_test - src/libuv_smoke_test.cc - ) - target_link_libraries(libuv_smoke_test PRIVATE libuv::uv GTest::gtest GTest::gtest_main) - add_test(NAME libuv_smoke_test COMMAND libuv_smoke_test) -endif () diff --git a/misc/experimental/libuv/src/libuv_smoke_test.cc b/misc/experimental/libuv/src/libuv_smoke_test.cc deleted file mode 100644 index 820d7d0c1..000000000 --- a/misc/experimental/libuv/src/libuv_smoke_test.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 -#include - -TEST(LibuvSmokeTest, CanInitAndCloseLoopTest) { - uv_loop_t loop; - EXPECT_EQ(0, uv_loop_init(&loop)); - EXPECT_EQ(0, uv_loop_close(&loop)); -} - -namespace { -struct ThreadState { - uv_mutex_t mutex; - uv_cond_t cond; - bool ready; -}; - -void threadMain(void* data) { - auto* state = static_cast(data); - uv_mutex_lock(&state->mutex); - state->ready = true; - uv_cond_signal(&state->cond); - uv_mutex_unlock(&state->mutex); -} -} // namespace - -TEST(LibuvSmokeTest, CanUseThreadMutexAndConditionTest) { - ThreadState state{}; - state.ready = false; - - ASSERT_EQ(0, uv_mutex_init(&state.mutex)); - ASSERT_EQ(0, uv_cond_init(&state.cond)); - - uv_thread_t thread; - ASSERT_EQ(0, uv_thread_create(&thread, threadMain, &state)); - - uv_mutex_lock(&state.mutex); - while (!state.ready) { - uv_cond_wait(&state.cond, &state.mutex); - } - uv_mutex_unlock(&state.mutex); - - EXPECT_EQ(0, uv_thread_join(&thread)); - uv_cond_destroy(&state.cond); - uv_mutex_destroy(&state.mutex); -} From 1b0b0934a4f562230f6b7ea4d586ee75b119db77 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 18 Jan 2026 20:24:05 +0100 Subject: [PATCH 03/15] gh-769: Update conan libuv dep to build_utils --- conanfile.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/conanfile.py b/conanfile.py index bf4925354..f5c5c6ecd 100644 --- a/conanfile.py +++ b/conanfile.py @@ -309,6 +309,7 @@ def configure(self): # https://github.com/conan-io/conan/issues/14528#issuecomment-1685344080 if self.options.build_utils: self.options['libzip'].shared = True + self.options['libuv'].shared = True if self.options.build_framework: self.options['util-linux-libuuid'].shared = True if ((self.options.build_framework and self.options.framework_curlinit) @@ -336,12 +337,11 @@ def configure(self): self.options['mosquitto'].shared = True if self.options.enable_testing: self.options['mosquitto'].broker = True - if self.options.build_experimental: - self.options['libuv'].shared = True def requirements(self): if self.options.build_utils: self.requires("libzip/[>=1.7.3 <2.0.0]") + self.requires("libuv/[>=1.49.2 <2.0.0]") if self.options.build_framework: self.requires("util-linux-libuuid/[>=2.39 <3.0.0]") if self.settings.os == "Macos": @@ -372,8 +372,6 @@ def requirements(self): self.requires("zlib/1.3.1", override=True) if self.options.build_event_admin_remote_provider_mqtt: self.requires("mosquitto/[>=2.0.3 <3.0.0]") - if self.options.build_experimental: - self.requires("libuv/[>=1.49.2 <2.0.0]") self.validate() def generate(self): From e0af03093c28a468765d685bb3e4333b99ca7170 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 18 Jan 2026 21:54:04 +0100 Subject: [PATCH 04/15] Exclude try locks on macos rwlock test --- libs/utils/gtest/src/UvThreadsTestSuite.cc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/libs/utils/gtest/src/UvThreadsTestSuite.cc b/libs/utils/gtest/src/UvThreadsTestSuite.cc index 0ae40bfaf..bfc4cbd17 100644 --- a/libs/utils/gtest/src/UvThreadsTestSuite.cc +++ b/libs/utils/gtest/src/UvThreadsTestSuite.cc @@ -47,7 +47,7 @@ TEST_F(UvThreadsTestSuite, MutexGuardTest) { EXPECT_NE(0, uv_mutex_trylock(&mutex)); } //guard out of scope -> unlock - EXPECT_EQ(0, uv_mutex_trylock(&mutex)); + ASSERT_EQ(0, uv_mutex_trylock(&mutex)); uv_mutex_unlock(&mutex); } @@ -64,22 +64,24 @@ TEST_F(UvThreadsTestSuite, RwlockGuardTest) { ASSERT_EQ(0, uv_rwlock_init(&lock)); celix_autoptr(uv_rwlock_t) lockCleanup = &lock; +#ifndef __APPLE__ //On MacOs uv_rwlock_tryrdlock and uv_rwlock_trywrlock on write locked lock results in SIGABRT { - celix_auto(celix_uv_rwlock_wlock_guard_t) guard = celixUvRwlockWlockGuard_init(&lock); + celix_auto(celix_uv_rwlock_wlock_guard_t) writeGuard = celixUvRwlockWlockGuard_init(&lock); EXPECT_NE(0, uv_rwlock_tryrdlock(&lock)); EXPECT_NE(0, uv_rwlock_trywrlock(&lock)); - } //guard out of scope -> unlock + } //guard out of scope -> unlock write lock +#endif { - celix_auto(celix_uv_rwlock_rlock_guard_t) guard = celixUvRwlockRlockGuard_init(&lock); + celix_auto(celix_uv_rwlock_rlock_guard_t) readGuard = celixUvRwlockRlockGuard_init(&lock); EXPECT_EQ(0, uv_rwlock_tryrdlock(&lock)); uv_rwlock_rdunlock(&lock); EXPECT_NE(0, uv_rwlock_trywrlock(&lock)); - } //guard out of scope -> unlock + } //guard out of scope -> unlock read lock - EXPECT_EQ(0, uv_rwlock_trywrlock(&lock)); + ASSERT_EQ(0, uv_rwlock_trywrlock(&lock)); uv_rwlock_wrunlock(&lock); } From feb9d8ec82c0584df97799bf225604a5d07ce74f Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 18 Jan 2026 22:26:02 +0100 Subject: [PATCH 05/15] Remove libuv linkng from ei tests --- libs/utils/gtest/CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt index c7cf68fcf..84fe3a4c2 100644 --- a/libs/utils/gtest/CMakeLists.txt +++ b/libs/utils/gtest/CMakeLists.txt @@ -154,10 +154,7 @@ if (EI_TESTS) Celix::fts_ei utils_cut Celix::utils_ei - libuv::uv - -Wl,--whole-archive Celix::ifaddrs_ei - -Wl,--no-whole-archive Celix::threads_ei Celix::malloc_ei Celix::asprintf_ei From 23a3408a6f9ebdd725e9a31834b2f0c6aeeb8c9a Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 19 Jan 2026 19:55:03 +0100 Subject: [PATCH 06/15] gh-769: Move trylock test for rwlock to in sep thread --- libs/utils/gtest/src/UvThreadsTestSuite.cc | 28 +++++++++++++++------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/libs/utils/gtest/src/UvThreadsTestSuite.cc b/libs/utils/gtest/src/UvThreadsTestSuite.cc index bfc4cbd17..2e5d65015 100644 --- a/libs/utils/gtest/src/UvThreadsTestSuite.cc +++ b/libs/utils/gtest/src/UvThreadsTestSuite.cc @@ -59,26 +59,36 @@ TEST_F(UvThreadsTestSuite, MutexStealTest) { uv_mutex_destroy(&mutex); } +static void uvThreadTryLocksForWriteLock(void* data) { + auto* lock = static_cast(data); + EXPECT_NE(0, uv_rwlock_tryrdlock(lock)); + EXPECT_NE(0, uv_rwlock_trywrlock(lock)); +} + +static void uvThreadTryLocksForReadLock(void* data) { + auto* lock = static_cast(data); + EXPECT_NE(0, uv_rwlock_trywrlock(lock)); + EXPECT_EQ(0, uv_rwlock_tryrdlock(lock)); //additional read lock on read lock should succeed + uv_rwlock_rdunlock(lock); +} + TEST_F(UvThreadsTestSuite, RwlockGuardTest) { uv_rwlock_t lock; ASSERT_EQ(0, uv_rwlock_init(&lock)); celix_autoptr(uv_rwlock_t) lockCleanup = &lock; -#ifndef __APPLE__ //On MacOs uv_rwlock_tryrdlock and uv_rwlock_trywrlock on write locked lock results in SIGABRT { celix_auto(celix_uv_rwlock_wlock_guard_t) writeGuard = celixUvRwlockWlockGuard_init(&lock); - EXPECT_NE(0, uv_rwlock_tryrdlock(&lock)); - EXPECT_NE(0, uv_rwlock_trywrlock(&lock)); + uv_thread_t thread; + ASSERT_EQ(0, uv_thread_create(&thread, uvThreadTryLocksForWriteLock, &lock)); + EXPECT_EQ(0, uv_thread_join(&thread)); } //guard out of scope -> unlock write lock -#endif { celix_auto(celix_uv_rwlock_rlock_guard_t) readGuard = celixUvRwlockRlockGuard_init(&lock); - - EXPECT_EQ(0, uv_rwlock_tryrdlock(&lock)); - uv_rwlock_rdunlock(&lock); - - EXPECT_NE(0, uv_rwlock_trywrlock(&lock)); + uv_thread_t thread; + ASSERT_EQ(0, uv_thread_create(&thread, uvThreadTryLocksForReadLock, &lock)); + EXPECT_EQ(0, uv_thread_join(&thread)); } //guard out of scope -> unlock read lock ASSERT_EQ(0, uv_rwlock_trywrlock(&lock)); From 5ef11d9375d9a55d7e3117d3ac8d643691b420f6 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 19 Jan 2026 20:22:14 +0100 Subject: [PATCH 07/15] gh-769: Move trylock test for mutex to a sep thread --- libs/utils/gtest/src/UvThreadsTestSuite.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libs/utils/gtest/src/UvThreadsTestSuite.cc b/libs/utils/gtest/src/UvThreadsTestSuite.cc index 2e5d65015..25879c1ef 100644 --- a/libs/utils/gtest/src/UvThreadsTestSuite.cc +++ b/libs/utils/gtest/src/UvThreadsTestSuite.cc @@ -37,6 +37,11 @@ TEST_F(UvThreadsTestSuite, ThreadAutoCleanupTest) { EXPECT_EQ(1, counter.load()); } +static void uvThreadTryLockForMutex(void* data) { + auto* mutex = static_cast(data); + EXPECT_NE(0, uv_mutex_trylock(mutex)); +} + TEST_F(UvThreadsTestSuite, MutexGuardTest) { uv_mutex_t mutex; ASSERT_EQ(0, uv_mutex_init(&mutex)); @@ -44,7 +49,9 @@ TEST_F(UvThreadsTestSuite, MutexGuardTest) { { celix_auto(celix_uv_mutex_lock_guard_t) guard = celixUvMutexLockGuard_init(&mutex); - EXPECT_NE(0, uv_mutex_trylock(&mutex)); + uv_thread_t thread; + ASSERT_EQ(0, uv_thread_create(&thread, uvThreadTryLockForMutex, &mutex)); + EXPECT_EQ(0, uv_thread_join(&thread)); } //guard out of scope -> unlock ASSERT_EQ(0, uv_mutex_trylock(&mutex)); From 9f46175ba0164d12480c1e786a628f35d4e6f584 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 19 Jan 2026 20:37:22 +0100 Subject: [PATCH 08/15] gh-769: Refactor celix_uv_cleanup func and struct naming --- libs/utils/gtest/src/UvThreadsTestSuite.cc | 6 +-- libs/utils/include/celix_uv_cleanup.h | 56 +++++++++++----------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/libs/utils/gtest/src/UvThreadsTestSuite.cc b/libs/utils/gtest/src/UvThreadsTestSuite.cc index 25879c1ef..c7dd9bf2d 100644 --- a/libs/utils/gtest/src/UvThreadsTestSuite.cc +++ b/libs/utils/gtest/src/UvThreadsTestSuite.cc @@ -48,7 +48,7 @@ TEST_F(UvThreadsTestSuite, MutexGuardTest) { celix_autoptr(uv_mutex_t) mutexCleanup = &mutex; { - celix_auto(celix_uv_mutex_lock_guard_t) guard = celixUvMutexLockGuard_init(&mutex); + celix_auto(celix_uv_mutex_lock_guard_t) guard = celix_uvMutexLockGuard_init(&mutex); uv_thread_t thread; ASSERT_EQ(0, uv_thread_create(&thread, uvThreadTryLockForMutex, &mutex)); EXPECT_EQ(0, uv_thread_join(&thread)); @@ -85,14 +85,14 @@ TEST_F(UvThreadsTestSuite, RwlockGuardTest) { celix_autoptr(uv_rwlock_t) lockCleanup = &lock; { - celix_auto(celix_uv_rwlock_wlock_guard_t) writeGuard = celixUvRwlockWlockGuard_init(&lock); + celix_auto(celix_uv_write_lock_guard_t) writeGuard = celix_uvWriteLockGuard_init(&lock); uv_thread_t thread; ASSERT_EQ(0, uv_thread_create(&thread, uvThreadTryLocksForWriteLock, &lock)); EXPECT_EQ(0, uv_thread_join(&thread)); } //guard out of scope -> unlock write lock { - celix_auto(celix_uv_rwlock_rlock_guard_t) readGuard = celixUvRwlockRlockGuard_init(&lock); + celix_auto(celix_uv_read_lock_guard_t) readGuard = celix_uvReadLockGuard_init(&lock); uv_thread_t thread; ASSERT_EQ(0, uv_thread_create(&thread, uvThreadTryLocksForReadLock, &lock)); EXPECT_EQ(0, uv_thread_join(&thread)); diff --git a/libs/utils/include/celix_uv_cleanup.h b/libs/utils/include/celix_uv_cleanup.h index a4ced5dc1..d269666b7 100644 --- a/libs/utils/include/celix_uv_cleanup.h +++ b/libs/utils/include/celix_uv_cleanup.h @@ -42,7 +42,7 @@ typedef struct celix_uv_mutex_lock_guard { } celix_uv_mutex_lock_guard_t; /** - * @brief Initialize a lock guard for @a mutex. + * @brief Initialize a lock guard for a mutex. * * Lock a mutex and return a celix_uv_mutex_lock_guard_t. * Unlock with celixUvMutexLockGuard_deinit(). Using uv_mutex_lock() on a mutex @@ -54,7 +54,7 @@ typedef struct celix_uv_mutex_lock_guard { * @param mutex A mutex to lock. * @return An initialized lock guard to be used with celix_auto(). */ -static CELIX_UNUSED inline celix_uv_mutex_lock_guard_t celixUvMutexLockGuard_init(uv_mutex_t* mutex) { +static CELIX_UNUSED inline celix_uv_mutex_lock_guard_t celix_uvMutexLockGuard_init(uv_mutex_t* mutex) { celix_uv_mutex_lock_guard_t guard; guard.mutex = mutex; uv_mutex_lock(mutex); @@ -69,14 +69,14 @@ static CELIX_UNUSED inline celix_uv_mutex_lock_guard_t celixUvMutexLockGuard_ini * * @param guard A celix_uv_mutex_lock_guard_t. */ -static CELIX_UNUSED inline void celixUvMutexLockGuard_deinit(celix_uv_mutex_lock_guard_t* guard) { +static CELIX_UNUSED inline void celix_uvMutexLockGuard_deinit(celix_uv_mutex_lock_guard_t* guard) { if (guard->mutex) { uv_mutex_unlock(guard->mutex); guard->mutex = NULL; } } -CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_uv_mutex_lock_guard_t, celixUvMutexLockGuard_deinit) +CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_uv_mutex_lock_guard_t, celix_uvMutexLockGuard_deinit) /** * @brief A RAII style write lock guard for uv_rwlock_t. @@ -84,16 +84,16 @@ CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_uv_mutex_lock_guard_t, celixUvMutexLo * The lock is obtained in the constructor and released in the destructor. * This is intended to be used with celix_auto(). */ -typedef struct celix_uv_rwlock_wlock_guard { +typedef struct celix_uv_write_lock_guard { uv_rwlock_t* lock; -} celix_uv_rwlock_wlock_guard_t; +} celix_uv_write_lock_guard_t; /** - * @brief Initialize a write lock guard for @a lock. + * @brief Initialize a write lock guard for a lock. * - * Obtain a write lock on @a lock and return a celix_uv_rwlock_wlock_guard_t. - * Unlock with celixUvRwlockWlockGuard_deinit(). Using uv_rwlock_wrunlock() - * on @lock while a celix_uv_rwlock_wlock_guard_t exists can lead to undefined behaviour. + * Obtain a write lock on a lock and return a celix_uv_write_lock_guard_t. + * Unlock with celixUvWriteLockGuard_deinit(). Using uv_rwlock_wrunlock() + * on lock while a celix_uv_write_lock_guard_t exists can lead to undefined behaviour. * * No allocation is performed, it is equivalent to a uv_rwlock_wrlock() call. * This is intended to be used with celix_auto(). @@ -101,8 +101,8 @@ typedef struct celix_uv_rwlock_wlock_guard { * @param lock A read-write lock to lock. * @return An initialized write lock guard to be used with celix_auto(). */ -static CELIX_UNUSED inline celix_uv_rwlock_wlock_guard_t celixUvRwlockWlockGuard_init(uv_rwlock_t* lock) { - celix_uv_rwlock_wlock_guard_t guard; +static CELIX_UNUSED inline celix_uv_write_lock_guard_t celix_uvWriteLockGuard_init(uv_rwlock_t* lock) { + celix_uv_write_lock_guard_t guard; guard.lock = lock; uv_rwlock_wrlock(lock); return guard; @@ -111,20 +111,20 @@ static CELIX_UNUSED inline celix_uv_rwlock_wlock_guard_t celixUvRwlockWlockGuard /** * @brief Deinitialize a write lock guard. * - * Release a write lock on the read-write lock contained in @a guard. - * See celixUvRwlockWlockGuard_init() for details. + * Release a write lock on the read-write lock contained in a guard. + * See celix_uvWriteLockGuard_init() for details. * No memory is freed, it is equivalent to a uv_rwlock_wrunlock() call. * - * @param guard A celix_uv_rwlock_wlock_guard_t. + * @param guard A celix_uv_write_lock_guard_t. */ -static CELIX_UNUSED inline void celixUvRwlockWlockGuard_deinit(celix_uv_rwlock_wlock_guard_t* guard) { +static CELIX_UNUSED inline void celix_uvWriteLockGuard_deinit(celix_uv_write_lock_guard_t* guard) { if (guard->lock) { uv_rwlock_wrunlock(guard->lock); guard->lock = NULL; } } -CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_uv_rwlock_wlock_guard_t, celixUvRwlockWlockGuard_deinit) +CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_uv_write_lock_guard_t, celix_uvWriteLockGuard_deinit) /** * @brief A RAII style read lock guard for uv_rwlock_t. @@ -132,16 +132,16 @@ CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_uv_rwlock_wlock_guard_t, celixUvRwloc * The lock is obtained in the constructor and released in the destructor. * This is intended to be used with celix_auto(). */ -typedef struct celix_uv_rwlock_rlock_guard { +typedef struct celix_uv_read_lock_guard { uv_rwlock_t* lock; -} celix_uv_rwlock_rlock_guard_t; +} celix_uv_read_lock_guard_t; /** * @brief Initialize a read lock guard for a lock. * - * Obtain a read lock on a lock and return a celix_uv_rwlock_rlock_guard_t. - * Unlock with celixUvRwlockRlockGuard_deinit(). Using uv_rwlock_rdunlock() - * on @lock while a celix_uv_rwlock_rlock_guard_t exists can lead to undefined behaviour. + * Obtain a read lock on a lock and return a celix_uv_read_lock_guard_t. + * Unlock with celix_uvReadLockGuard_deinit(). Using uv_rwlock_rdunlock() + * on lock while a celix_uv_read_lock_guard_t exists can lead to undefined behaviour. * * No allocation is performed, it is equivalent to a uv_rwlock_rdlock() call. * This is intended to be used with celix_auto(). @@ -149,8 +149,8 @@ typedef struct celix_uv_rwlock_rlock_guard { * @param lock A read-write lock to lock. * @return A guard to be used with celix_auto(). */ -static CELIX_UNUSED inline celix_uv_rwlock_rlock_guard_t celixUvRwlockRlockGuard_init(uv_rwlock_t* lock) { - celix_uv_rwlock_rlock_guard_t guard; +static CELIX_UNUSED inline celix_uv_read_lock_guard_t celix_uvReadLockGuard_init(uv_rwlock_t* lock) { + celix_uv_read_lock_guard_t guard; guard.lock = lock; uv_rwlock_rdlock(lock); return guard; @@ -160,19 +160,19 @@ static CELIX_UNUSED inline celix_uv_rwlock_rlock_guard_t celixUvRwlockRlockGuard * @brief Deinitialize a read lock guard. * * Release a read lock on the read-write lock contained in a guard. - * See celixUvRwlockRlockGuard_init() for details. + * See celix_uvReadLockGuard_init() for details. * No memory is freed, it is equivalent to a uv_rwlock_rdunlock() call. * - * @param guard A celix_uv_rwlock_rlock_guard_t. + * @param guard A celix_uv_read_lock_guard_t. */ -static CELIX_UNUSED inline void celixUvRwlockRlockGuard_deinit(celix_uv_rwlock_rlock_guard_t* guard) { +static CELIX_UNUSED inline void celix_uvReadLockGuard_deinit(celix_uv_read_lock_guard_t* guard) { if (guard->lock) { uv_rwlock_rdunlock(guard->lock); guard->lock = NULL; } } -CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_uv_rwlock_rlock_guard_t, celixUvRwlockRlockGuard_deinit) +CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_uv_read_lock_guard_t, celix_uvReadLockGuard_deinit) #ifdef __cplusplus } From e4bf6236b2aa4cc14a8e6e07ae0b62e8491e2a99 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 26 Jan 2026 21:36:08 +0100 Subject: [PATCH 09/15] Update ci config 1.x config to use conan 1.66 --- .github/workflows/coverage.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- libs/utils/CMakeLists.txt | 5 ----- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index b6c997ef9..eb307b92f 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -19,7 +19,7 @@ jobs: - name: Install conan and lcov run: | sudo apt-get install -yq --no-install-recommends lcov - sudo pip install conan==1.62.0 coverage + sudo pip install conan==1.66.0 coverage - name: Setup Conan Profile run: | conan profile new default --detect diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index f2a0e5147..3555e776a 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c #v3.3.0 - name: Install build dependencies run: | - sudo pip install -U conan==1.62.0 + sudo pip install -U conan==1.66.0 sudo apt-get install -yq --no-install-recommends ninja-build - name: Setup Conan Profile env: diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index bbb7474a1..513314b2b 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -21,11 +21,6 @@ if (UTILS) find_package(jansson REQUIRED) find_package(libuv REQUIRED) - if (NOT TARGET libuv::uv AND TARGET uv) - #Note: conan libuv package 1.49.2 defines uv target, but 1.51.0 defines libuv::uv target - add_library(libuv::uv ALIAS uv) - endif () - set(MEMSTREAM_SOURCES ) set(MEMSTREAM_INCLUDES ) From f7dce1bd78c8c2b97dc48a9cff93a995704b8e4b Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 26 Jan 2026 21:36:39 +0100 Subject: [PATCH 10/15] Remove celix_auto support for uv_thread_t --- libs/utils/gtest/src/UvThreadsTestSuite.cc | 14 -------------- libs/utils/include/celix_uv_cleanup.h | 1 - 2 files changed, 15 deletions(-) diff --git a/libs/utils/gtest/src/UvThreadsTestSuite.cc b/libs/utils/gtest/src/UvThreadsTestSuite.cc index c7dd9bf2d..6f062a7d2 100644 --- a/libs/utils/gtest/src/UvThreadsTestSuite.cc +++ b/libs/utils/gtest/src/UvThreadsTestSuite.cc @@ -23,20 +23,6 @@ class UvThreadsTestSuite : public ::testing::Test { }; -static void uvThreadIncrement(void* data) { - auto* counter = static_cast*>(data); - counter->fetch_add(1); -} - -TEST_F(UvThreadsTestSuite, ThreadAutoCleanupTest) { - std::atomic counter{0}; - { - celix_auto(uv_thread_t) thread; - ASSERT_EQ(0, uv_thread_create(&thread, uvThreadIncrement, &counter)); - } //thread out of scope -> join - EXPECT_EQ(1, counter.load()); -} - static void uvThreadTryLockForMutex(void* data) { auto* mutex = static_cast(data); EXPECT_NE(0, uv_mutex_trylock(mutex)); diff --git a/libs/utils/include/celix_uv_cleanup.h b/libs/utils/include/celix_uv_cleanup.h index d269666b7..aab25460a 100644 --- a/libs/utils/include/celix_uv_cleanup.h +++ b/libs/utils/include/celix_uv_cleanup.h @@ -28,7 +28,6 @@ extern "C" { #endif -CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(uv_thread_t, uv_thread_join) CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(uv_mutex_t, uv_mutex_destroy) CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(uv_rwlock_t, uv_rwlock_destroy) CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(uv_cond_t, uv_cond_destroy) From 0e61b47fd8ac5dd861834c33c09e46a12d55e55b Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 26 Jan 2026 21:36:55 +0100 Subject: [PATCH 11/15] Add missing find_dependencies in CelixDeps.cmake.in --- cmake/CelixDeps.cmake.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/CelixDeps.cmake.in b/cmake/CelixDeps.cmake.in index bea259e79..4e64bb977 100644 --- a/cmake/CelixDeps.cmake.in +++ b/cmake/CelixDeps.cmake.in @@ -2,6 +2,8 @@ $<$>:find_dependency(libuuid)> $<$>:find_dependency(libuuid)> $<$>:find_dependency(libuuid)> $<$>:find_dependency(libzip)> +$<$>:find_dependency(jansson)> +$<$>:find_dependency(libuv)> $<$>:find_dependency(ZLIB)> $<$>:find_dependency(libffi)> $<$>:find_dependency(jansson)> From f425e1072102c91639b8afe4ab8b280bb78cc654 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Mon, 26 Jan 2026 21:40:50 +0100 Subject: [PATCH 12/15] Update CelixConfig.cmake include Modules before find_dep --- cmake/CelixConfig.cmake | 16 +++------------- misc/Dockerfile.Android | 2 +- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/cmake/CelixConfig.cmake b/cmake/CelixConfig.cmake index a97ccc8a2..20c07935d 100644 --- a/cmake/CelixConfig.cmake +++ b/cmake/CelixConfig.cmake @@ -35,6 +35,9 @@ include(CMakeFindDependencyMacro) set(THREADS_PREFER_PTHREAD_FLAG ON) find_dependency(Threads) +# The rest is added to ensure backwards compatibility with project using the cmake lib/include var instead of targets. +set(CELIX_CMAKE_MODULES_DIR ${CELIX_REL_INSTALL_DIR}/share/celix/cmake/Modules) + #adds celix optional dependencies include("${CELIX_REL_INSTALL_DIR}/share/celix/cmake/CelixDeps.cmake") @@ -42,9 +45,6 @@ include("${CELIX_REL_INSTALL_DIR}/share/celix/cmake/CelixDeps.cmake") include("${CELIX_REL_INSTALL_DIR}/share/celix/cmake/Targets.cmake") include("${CELIX_REL_INSTALL_DIR}/share/celix/cmake/CelixTargets.cmake") -# The rest is added to ensure backwards compatiblity with project using the cmake lib/include var instead of targets. -set(CELIX_CMAKE_MODULES_DIR ${CELIX_REL_INSTALL_DIR}/share/celix/cmake/Modules) - set(CELIX_FRAMEWORK_INCLUDE_DIR "${CELIX_REL_INSTALL_DIR}/include/celix") set(CELIX_UTILS_INCLUDE_DIR "${CELIX_REL_INSTALL_DIR}/include/utils") set(CELIX_DFI_INCLUDE_DIR "${CELIX_REL_INSTALL_DIR}/include/dfi") @@ -69,16 +69,6 @@ if (TARGET Celix::etcdlib) set(CELIX_ETCD_LIB Celix::etcdlib) endif () -if (TARGET Celix::dependency_manager_so) - set(CELIX_DM_LIB Celix::dependency_manager_so) - set(CELIX_DM_INCLUDE_DIR $) - set(CELIX_DM_STATIC_LIB Celix::dependency_manager_static) -endif () -if (TARGET Celix::dependency_manager_cxx) - set(CELIX_DM_STATIC_CXX_LIB Celix::dependency_manager_cxx) - set(CELIX_DM_CXX_STATIC_LIB $) -endif () - set(CELIX_BUNDLES_DIR ${CELIX_REL_INSTALL_DIR}/share/celix/bundles) set(CELIX_SHELL_BUNDLE ${CELIX_BUNDLES_DIR}/shell.zip) set(CELIX_SHELL_TUI_BUNDLE ${CELIX_BUNDLES_DIR}/shell_tui.zip) \ No newline at end of file diff --git a/misc/Dockerfile.Android b/misc/Dockerfile.Android index 8e27f09f6..1a8a3f4d0 100644 --- a/misc/Dockerfile.Android +++ b/misc/Dockerfile.Android @@ -132,6 +132,6 @@ RUN curl -L -O ftp://sourceware.org/pub/libffi/libffi-3.2.1.tar.gz && \ RUN git clone -b master --single-branch https://github.com/apache/celix.git celix -CMD mkdir -p celix/build-android && cd celix/build-android && cmake -DANDROID=TRUE -DENABLE_TESTING=OFF -DBUILD_EXAMPLES=OFF -DBUILD_DEPENDENCY_MANAGER_CXX=OFF -DBUILD_REMOTE_SERVICE_ADMIN=ON -DBUILD_REMOTE_SHELL=ON -DBUILD_RSA_DISCOVERY_CONFIGURED=ON -DBUILD_RSA_DISCOVERY_ETCD=ON -DBUILD_RSA_EXAMPLES=ON -DBUILD_RSA_REMOTE_SERVICE_ADMIN_HTTP=ON -DBUILD_RSA_REMOTE_SERVICE_ADMIN_DFI=OFF -DBUILD_RSA_TOPOLOGY_MANAGER=ON -DFFI_LIBRARY=/build/output/libffi/lib/libffi.a -DFFI_INCLUDE_DIR=/build/output/libffi/lib/libffi-3.2.1/include -DJANSSON_LIBRARY=/build/output/jansson/lib/libjansson.a -DJANSSON_INCLUDE_DIR=/build/output/jansson/include -DCURL_LIBRARY=/build/output/curl/lib/libcurl.a -DCURL_INCLUDE_DIR=/build/output/curl/include -DLIBXML2_LIBRARIES=/build/output/libxml2/lib/libxml2.a -DLIBXML2_INCLUDE_DIR=/build/output/libxml2/include/libxml2 -DZLIB_LIBRARY=/build/output/zlib/lib/libz.a -DZLIB_INCLUDE_DIR=/build/output/zlib/include -DUUID_LIBRARY=/build/output/uuid/lib/libuuid.a -DUUID_INCLUDE_DIR=/build/output/uuid/include -DCMAKE_INSTALL_PREFIX:PATH=/build/output/celix .. && make && make install +CMD mkdir -p celix/build-android && cd celix/build-android && cmake -DANDROID=TRUE -DENABLE_TESTING=OFF -DBUILD_EXAMPLES=OFF -DBUILD_REMOTE_SERVICE_ADMIN=ON -DBUILD_REMOTE_SHELL=ON -DBUILD_RSA_DISCOVERY_CONFIGURED=ON -DBUILD_RSA_DISCOVERY_ETCD=ON -DBUILD_RSA_EXAMPLES=ON -DBUILD_RSA_REMOTE_SERVICE_ADMIN_HTTP=ON -DBUILD_RSA_REMOTE_SERVICE_ADMIN_DFI=OFF -DBUILD_RSA_TOPOLOGY_MANAGER=ON -DFFI_LIBRARY=/build/output/libffi/lib/libffi.a -DFFI_INCLUDE_DIR=/build/output/libffi/lib/libffi-3.2.1/include -DJANSSON_LIBRARY=/build/output/jansson/lib/libjansson.a -DJANSSON_INCLUDE_DIR=/build/output/jansson/include -DCURL_LIBRARY=/build/output/curl/lib/libcurl.a -DCURL_INCLUDE_DIR=/build/output/curl/include -DLIBXML2_LIBRARIES=/build/output/libxml2/lib/libxml2.a -DLIBXML2_INCLUDE_DIR=/build/output/libxml2/include/libxml2 -DZLIB_LIBRARY=/build/output/zlib/lib/libz.a -DZLIB_INCLUDE_DIR=/build/output/zlib/include -DUUID_LIBRARY=/build/output/uuid/lib/libuuid.a -DUUID_INCLUDE_DIR=/build/output/uuid/include -DCMAKE_INSTALL_PREFIX:PATH=/build/output/celix .. && make && make install # done From d8c8cb4b499019a84e03a8c76b33235653d158c8 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 27 Jan 2026 16:39:28 +0800 Subject: [PATCH 13/15] Update ubuntu ci to use conan 2.x --- .github/workflows/ubuntu.yml | 55 +++++++++++++++--------------------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 3555e776a..0e60cf73c 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c #v3.3.0 - name: Install build dependencies run: | - sudo pip install -U conan==1.66.0 + sudo pip install -U conan sudo apt-get install -yq --no-install-recommends ninja-build - name: Setup Conan Profile env: @@ -36,21 +36,17 @@ jobs: CXX: ${{ matrix.compiler[1] }} run: | # build profile - conan profile new release --detect - conan profile update settings.build_type=Release release - #Note no backwards compatibility for gcc5 needed, setting libcxx to c++11. - conan profile update settings.compiler.libcxx=libstdc++11 release - conan profile show release - echo "[tool_requires]" >> `conan config home`/profiles/release - echo "cmake/3.26.4" >> `conan config home`/profiles/release + conan profile detect -f --name release + sed -i 's/compiler.cppstd=gnu14/compiler.cppstd=gnu17/g' `conan profile path release` + echo "[tool_requires]" >> `conan profile path release` + echo "cmake/3.26.4" >> `conan profile path release` + # host profile - conan profile new default --detect - conan profile update settings.build_type=${{ matrix.type }} default - #Note no backwards compatibility for gcc5 needed, setting libcxx to c++11. - conan profile update settings.compiler.libcxx=libstdc++11 default - conan profile show default - echo "[tool_requires]" >> `conan config home`/profiles/default - echo "cmake/3.26.4" >> `conan config home`/profiles/default + conan profile detect -f + sed -i 's/build_type=Release/build_type=${{ matrix.type }}/g' `conan profile path default` + sed -i 's/compiler.cppstd=gnu14/compiler.cppstd=gnu17/g' `conan profile path default` + echo "[tool_requires]" >> `conan profile path default` + echo "cmake/3.26.4" >> `conan profile path default` - name: Conan Cache id: cache-conan uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf #v4.2.2 @@ -71,29 +67,22 @@ jobs: key: ${{ runner.os }}-test-ccache-${{ matrix.compiler[0] }}-${{ matrix.type }}-${{ steps.ccache_cache_timestamp.outputs.timestamp }} restore-keys: | ${{ runner.os }}-test-ccache-${{ matrix.compiler[0] }}-${{ matrix.type }}- - - name: Configure and install dependencies + - name: Install Dependencies and Build env: CC: ${{ matrix.compiler[0] }} CXX: ${{ matrix.compiler[1] }} CONAN_BUILD_OPTIONS: | - -o celix:enable_testing=True - -o celix:enable_benchmarking=True - -o celix:enable_address_sanitizer=True - -o celix:enable_undefined_sanitizer=True - -o celix:build_all=True - -o celix:enable_cmake_warning_tests=True - -o celix:enable_testing_on_ci=True - -o celix:framework_curlinit=False - -o celix:enable_ccache=True + -o celix/*:enable_testing=True + -o celix/*:enable_benchmarking=True + -o celix/*:enable_address_sanitizer=True + -o celix/*:enable_undefined_sanitizer=True + -o celix/*:build_all=True + -o celix/*:enable_cmake_warning_tests=True + -o celix/*:enable_testing_on_ci=True + -o celix/*:framework_curlinit=False + -o celix/*:enable_ccache=True run: | - conan install . celix/ci -c tools.cmake.cmaketoolchain:generator=Ninja -pr:b release -pr:h default -if build ${CONAN_BUILD_OPTIONS} -b missing - - name: Build - env: - CC: ${{ matrix.compiler[0] }} - CXX: ${{ matrix.compiler[1] }} - CONAN_CMAKE_GENERATOR: Ninja - run: | - conan build . -bf build + conan build . -c tools.cmake.cmaketoolchain:generator=Ninja -pr:b release -pr:h default -of build ${CONAN_BUILD_OPTIONS} -b missing - name: Test run: | cd build From 387a0bb9b7fcc2efd289b22e9eccd8cebc7f869b Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 27 Jan 2026 17:06:55 +0800 Subject: [PATCH 14/15] Update coverage to use conan 2.x --- .github/workflows/coverage.yml | 37 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index eb307b92f..5f1d582d5 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -19,15 +19,21 @@ jobs: - name: Install conan and lcov run: | sudo apt-get install -yq --no-install-recommends lcov - sudo pip install conan==1.66.0 coverage + sudo pip install -U conan coverage - name: Setup Conan Profile run: | - conan profile new default --detect - conan profile update settings.build_type=Debug default - #Note no backwards compatiblity for gcc5 needed, setting libcxx to c++11. - conan profile update settings.compiler.libcxx=libstdc++11 default - echo "[tool_requires]" >> `conan config home`/profiles/default - echo "cmake/3.26.4" >> `conan config home`/profiles/default + # build profile + conan profile detect -f --name release + sed -i 's/compiler.cppstd=gnu14/compiler.cppstd=gnu17/g' `conan profile path release` + echo "[tool_requires]" >> `conan profile path release` + echo "cmake/3.26.4" >> `conan profile path release` + + # host profile + conan profile detect -f + sed -i 's/build_type=Release/build_type=Debug/g' `conan profile path default` + sed -i 's/compiler.cppstd=gnu14/compiler.cppstd=gnu17/g' `conan profile path default` + echo "[tool_requires]" >> `conan profile path default` + echo "cmake/3.26.4" >> `conan profile path default` - name: Conan Cache id: cache-conan uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf #v4.2.2 @@ -48,19 +54,16 @@ jobs: key: ${{ runner.os }}-gcov-ccache-${{ steps.ccache_cache_timestamp.outputs.timestamp }} restore-keys: | ${{ runner.os }}-gcov-ccache- - - name: Install Dependencies + - name: Install Dependencies and Build env: CONAN_BUILD_OPTIONS: | - -o celix:enable_testing=True - -o celix:build_all=True - -o celix:enable_code_coverage=True - -o celix:enable_testing_on_ci=True - -o celix:enable_ccache=True + -o celix/*:enable_testing=True + -o celix/*:build_all=True + -o celix/*:enable_code_coverage=True + -o celix/*:enable_testing_on_ci=True + -o celix/*:enable_ccache=True run: | - conan install . celix/ci -pr:b default -pr:h default -if build ${CONAN_BUILD_OPTIONS} -b missing --require-override=openssl/1.1.1s - - name: Build - run: | - conan build . -bf build + conan build . -pr:b release -pr:h default -of build ${CONAN_BUILD_OPTIONS} -b missing --require-override=openssl/1.1.1s - name: Test with coverage run: | cd build From 8571cbcbbeb67d903ccf73a812b600e58639df2d Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 27 Jan 2026 17:08:59 +0800 Subject: [PATCH 15/15] Remove require-override in coverage build. --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5f1d582d5..4ad7a2339 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -63,7 +63,7 @@ jobs: -o celix/*:enable_testing_on_ci=True -o celix/*:enable_ccache=True run: | - conan build . -pr:b release -pr:h default -of build ${CONAN_BUILD_OPTIONS} -b missing --require-override=openssl/1.1.1s + conan build . -pr:b release -pr:h default -of build ${CONAN_BUILD_OPTIONS} -b missing - name: Test with coverage run: | cd build