}
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
*/
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));
#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
*/