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);
-}