]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
macro: flip ONCE macro to make log_once() and friend actually log once
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 10 Sep 2025 23:39:17 +0000 (08:39 +0900)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 17 Sep 2025 19:50:19 +0000 (21:50 +0200)
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
src/test/test-log.c
src/test/test-macro.c

index 2f2b3df277afd58ec926294d432767b72675175c..ba2551cd9ebaf983b955ff13bcb4cf1f5d261614 100644 (file)
 #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)
index 87cd36287727c5cd0f61e210cbaaeed1db4c696f..67385971238270a887759856ea146a57571433c2 100644 (file)
@@ -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();
index 427a6b1490fff11292fb7e5b593c9c3c10e47f6b..a05a1082defd5979aa5184cf9a77a6846580f7c4 100644 (file)
@@ -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);