From 0cd46df37761b24f8d172e6af7c1884b5a7f4b89 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 10 Jan 2024 16:38:54 +0100 Subject: [PATCH] atomics: Add a ref_get() variant returning non-zero on overflows This is useful for users using ref_get() for unique identifier allocation, but the zero value has special meaning. --- src/libstrongswan/tests/suites/test_utils.c | 24 ++++++++++++++++++ src/libstrongswan/utils/utils/atomics.h | 27 +++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_utils.c b/src/libstrongswan/tests/suites/test_utils.c index e4463f2d0..4662fa886 100644 --- a/src/libstrongswan/tests/suites/test_utils.c +++ b/src/libstrongswan/tests/suites/test_utils.c @@ -256,6 +256,26 @@ START_TEST(test_round) } END_TEST +/******************************************************************************* + * ref_get/put + */ + +START_TEST(test_refs) +{ + refcount_t r = 0xfffffffe; + + ck_assert_int_eq(ref_cur(&r), 0xfffffffe); + ck_assert_int_eq(ref_get(&r), 0xffffffff); + ck_assert_int_eq(ref_get_nonzero(&r), 1); + ck_assert_int_eq(ref_get_nonzero(&r), 2); + ck_assert_int_eq(ref_cur(&r), 2); + ck_assert(!ref_put(&r)); + ck_assert_int_eq(ref_cur(&r), 1); + ck_assert(ref_put(&r)); + ck_assert_int_eq(ref_cur(&r), 0); +} +END_TEST + /******************************************************************************* * streq */ @@ -1272,6 +1292,10 @@ Suite *utils_suite_create() tcase_add_test(tc, test_round); suite_add_tcase(s, tc); + tc = tcase_create("refcount"); + tcase_add_test(tc, test_refs); + suite_add_tcase(s, tc); + tc = tcase_create("string helper"); tcase_add_loop_test(tc, test_streq, 0, countof(streq_data)); tcase_add_loop_test(tc, test_strneq, 0, countof(strneq_data)); diff --git a/src/libstrongswan/utils/utils/atomics.h b/src/libstrongswan/utils/utils/atomics.h index 0fcd99e18..58f1cd92e 100644 --- a/src/libstrongswan/utils/utils/atomics.h +++ b/src/libstrongswan/utils/utils/atomics.h @@ -124,6 +124,33 @@ bool cas_ptr(void **ptr, void *oldval, void *newval); #endif /* HAVE_GCC_ATOMIC_OPERATIONS */ +/** + * Get a new reference, but skip zero on overflow. + * + * If a reference counter is used to allocate unique identifiers, the + * refcount value may overflow if it is never decremented. The 0 identifier + * may have special semantics, hence returning can be problematic for some + * users. + * + * This call does an additional ref_get() if ref_get() overflows and returns + * zero. This ensures that zero is never returned, in the assumption that it + * has special meaning. + * + * @param ref pointer to ref counter + * @return new value of ref + */ +static inline refcount_t ref_get_nonzero(refcount_t *ref) +{ + refcount_t v; + + v = ref_get(ref); + if (v == 0) + { + v = ref_get(ref); + } + return v; +} + /** * Initialize atomics utility functions */