From fc3576ab06a33bd3733ea37341fd3b0094e1298c Mon Sep 17 00:00:00 2001 From: Yu-Ping Wu Date: Tue, 22 Jun 2021 17:40:01 +0800 Subject: [PATCH] helpers: Introduce retry macro Introduce a macro retry(attempts, condition, expr) for retrying a condition, which is extensively used in coreboot. Example usage: if (!retry(3, read32(REG) == 0, mdelay(1)) printk(BIOS_ERR, "Error waiting for REG to be 0\n"); BUG=none TEST=make tests/commonlib/bsd/helpers-test TEST=emerge-cherry coreboot BRANCH=none Change-Id: I421e4dcab949616bd68b3a14231da744b9f74eeb Signed-off-by: Yu-Ping Wu Reviewed-on: https://review.coreboot.org/c/coreboot/+/55778 Tested-by: build bot (Jenkins) Reviewed-by: Julius Werner --- .../bsd/include/commonlib/bsd/helpers.h | 35 ++++++++++++++++ tests/commonlib/Makefile.inc | 2 + tests/commonlib/bsd/Makefile.inc | 5 +++ tests/commonlib/bsd/helpers-test.c | 41 +++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 tests/commonlib/bsd/Makefile.inc create mode 100644 tests/commonlib/bsd/helpers-test.c diff --git a/src/commonlib/bsd/include/commonlib/bsd/helpers.h b/src/commonlib/bsd/include/commonlib/bsd/helpers.h index 4e6ebeefdd..376ebaef11 100644 --- a/src/commonlib/bsd/include/commonlib/bsd/helpers.h +++ b/src/commonlib/bsd/include/commonlib/bsd/helpers.h @@ -88,4 +88,39 @@ /* Calculate size of structure member. */ #define member_size(type, member) (sizeof(((type *)0)->member)) +#define _retry_impl(attempts, condition, expr, ...) \ +({ \ + __typeof__(condition) _retry_ret = \ + (__typeof__(condition))0; \ + int _retry_attempts = (attempts); \ + do { \ + _retry_ret = (condition); \ + if (_retry_ret) \ + break; \ + if (--_retry_attempts > 0) { \ + expr; \ + } else { \ + break; \ + } \ + } while (1); \ + _retry_ret; \ +}) + +/* + * Helper macro to retry until a condition becomes true or the maximum number + * of attempts is reached. Two forms are supported: + * + * 1. retry(attempts, condition) + * 2. retry(attempts, condition, expr) + * + * @param attempts Maximum attempts. + * @param condition Condition to retry for. + * @param expr Procedure to run between each evaluation to "condition". + * + * @return Condition value if it evaluates to true within the maximum attempts; + * 0 otherwise. + */ +#define retry(attempts, condition, ...) \ + _retry_impl(attempts, condition, __VA_ARGS__) + #endif /* COMMONLIB_BSD_HELPERS_H */ diff --git a/tests/commonlib/Makefile.inc b/tests/commonlib/Makefile.inc index c620754e33..054e7db16d 100644 --- a/tests/commonlib/Makefile.inc +++ b/tests/commonlib/Makefile.inc @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only +subdirs-y += bsd + tests-y += region-test region-test-srcs += tests/commonlib/region-test.c diff --git a/tests/commonlib/bsd/Makefile.inc b/tests/commonlib/bsd/Makefile.inc new file mode 100644 index 0000000000..56664d037c --- /dev/null +++ b/tests/commonlib/bsd/Makefile.inc @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +tests-y += helpers-test + +helpers-test-srcs += tests/commonlib/bsd/helpers-test.c diff --git a/tests/commonlib/bsd/helpers-test.c b/tests/commonlib/bsd/helpers-test.c new file mode 100644 index 0000000000..ec3f98bed1 --- /dev/null +++ b/tests/commonlib/bsd/helpers-test.c @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include + +static void func(void) +{ + function_called(); +} + +static void test_retry(void **state) +{ + int count; + + /* 2-argument form */ + count = 0; + assert_true(retry(3, ++count == 1)); + count = 0; + assert_true(retry(3, ++count == 3)); + count = 0; + assert_false(retry(3, ++count == 4)); + + /* 3-argument form */ + expect_function_calls(func, 9); + assert_null(retry(10, NULL, func())); + + assert_int_equal(retry(10, 999, func()), 999); + + count = 0; + expect_function_calls(func, 3); + assert_true(retry(10, ++count == 4, func())); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_retry), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +}