From acd33c5df8ba35bcfa595ea0be9f74afa6aab289 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 11 Sep 2025 08:39:17 +0900 Subject: [PATCH] macro: flip ONCE macro to make log_once() and friend actually log once Previously, ONCE is false for the first time, and true for later times, hence log_once() and log_once_errno() suppress logging in the first call, rather than later calls. Fortunately, ONCE macro is only used in log_once() and log_once_errno(), hence this only fixes spurious logging. --- src/fundamental/macro-fundamental.h | 16 ++++++++-------- src/test/test-log.c | 18 ++++++++++++++++++ src/test/test-macro.c | 16 ++++++++++++++++ 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h index 2f2b3df277a..ba2551cd9eb 100644 --- a/src/fundamental/macro-fundamental.h +++ b/src/fundamental/macro-fundamental.h @@ -144,15 +144,15 @@ #define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq)) #define UNIQ __COUNTER__ -/* Note that this works differently from pthread_once(): this macro does - * not synchronize code execution, i.e. code that is run conditionalized - * on this macro will run concurrently to all other code conditionalized - * the same way, there's no ordering or completion enforced. */ +/* The macro is true when the code block is called first time, and is false for the second and later times. + * Note that this works differently from pthread_once(): this macro does not synchronize code execution, i.e. + * code that is run conditionalized on this macro will run concurrently to all other code conditionalized the + * same way, there's no ordering or completion enforced. */ #define ONCE __ONCE(UNIQ_T(_once_, UNIQ)) -#define __ONCE(o) \ - ({ \ - static bool (o) = false; \ - __atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \ +#define __ONCE(o) \ + ({ \ + static bool (o) = false; \ + !__atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \ }) #define U64_KB UINT64_C(1024) diff --git a/src/test/test-log.c b/src/test/test-log.c index 87cd3628772..67385971238 100644 --- a/src/test/test-log.c +++ b/src/test/test-log.c @@ -42,6 +42,22 @@ static void test_file(void) { assert_se(startswith(__FILE__, RELATIVE_SOURCE_PATH "/")); } +static void test_log_once_impl(void) { + log_once(LOG_INFO, "This should be logged in LOG_INFO at first, then in LOG_DEBUG later."); + log_once(LOG_DEBUG, "This should be logged only once in LOG_DEBUG."); + ASSERT_ERROR(log_once_errno(LOG_INFO, SYNTHETIC_ERRNO(ENOANO), + "This should be logged with errno in LOG_INFO at first, then in LOG_DEBUG later: %m"), + ENOANO); + ASSERT_ERROR(log_once_errno(LOG_DEBUG, SYNTHETIC_ERRNO(EBADMSG), + "This should be logged only once with errno in LOG_DEBUG: %m"), + EBADMSG); +} + +static void test_log_once(void) { + for (unsigned i = 0; i < 4; i++) + test_log_once_impl(); +} + static void test_log_struct(void) { log_struct(LOG_INFO, "MESSAGE=Waldo PID="PID_FMT" (no errno)", getpid_cached(), @@ -233,6 +249,8 @@ int main(int argc, char* argv[]) { assert_se(log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), "foo") == -EUCLEAN); + test_log_once(); + for (int target = 0; target < _LOG_TARGET_MAX; target++) { log_set_target(target); log_open(); diff --git a/src/test/test-macro.c b/src/test/test-macro.c index 427a6b1490f..a05a1082def 100644 --- a/src/test/test-macro.c +++ b/src/test/test-macro.c @@ -1102,4 +1102,20 @@ TEST(u64_multiply_safe) { assert_se(u64_multiply_safe(UINT64_MAX, UINT64_MAX) == 0); } +static void test_once_impl(void) { + static unsigned count = 0; + + if (ONCE) { + log_info("This should be logged only once."); + count++; + } + + ASSERT_EQ(count, 1u); +} + +TEST(once) { + for (unsigned i = 0; i < 20; i++) + test_once_impl(); +} + DEFINE_TEST_MAIN(LOG_INFO); -- 2.47.3