From: Martin Willi Date: Wed, 10 Jan 2024 15:38:54 +0000 (+0100) Subject: atomics: Add a ref_get() variant returning non-zero on overflows X-Git-Tag: android-2.5.0~9^2~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0cd46df37761b24f8d172e6af7c1884b5a7f4b89;p=thirdparty%2Fstrongswan.git 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. --- diff --git a/src/libstrongswan/tests/suites/test_utils.c b/src/libstrongswan/tests/suites/test_utils.c index e4463f2d00..4662fa8867 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 0fcd99e18e..58f1cd92ec 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 */