This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a commit to branch feature/769-libuv-package-and-thread-header
in repository https://gitbox.apache.org/repos/asf/celix.git


The following commit(s) were added to 
refs/heads/feature/769-libuv-package-and-thread-header by this push:
     new e2d3ceee1 gh-769: Add celix_uv_cleanup.h
e2d3ceee1 is described below

commit e2d3ceee194a4e62269bc79f0882fcb9828131a0
Author: Pepijn Noltes <[email protected]>
AuthorDate: Sun Jan 18 20:20:35 2026 +0100

    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 ----
 misc/experimental/libuv/src/libuv_smoke_test.cc |  64 ---------
 7 files changed, 314 insertions(+), 99 deletions(-)

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 <atomic>
+#include <gtest/gtest.h>
+
+class UvThreadsTestSuite : public ::testing::Test {
+};
+
+static void uvThreadIncrement(void* data) {
+    auto* counter = static_cast<std::atomic<int>*>(data);
+    counter->fetch_add(1);
+}
+
+TEST_F(UvThreadsTestSuite, ThreadAutoCleanupTest) {
+    std::atomic<int> 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 <uv.h>
+
+#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 <gtest/gtest.h>
-#include <uv.h>
-
-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<ThreadState*>(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);
-}

Reply via email to