summaryrefslogtreecommitdiff
path: root/test/test_atomic.cc
blob: 9851e7babf81e4b81b034634f041303b06309cbd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (C) 2026 Richard Qin */

/*
 * Boot Self-Test: lume::atomic<T>
 *
 * Tests:
 *   1. Default construction (zero-initialized)
 *   2. Explicit construction
 *   3. store / load round-trip with different memory orders
 *   4. exchange returns old value
 *   5. fetch_add / fetch_sub arithmetic
 *   6. compare_exchange success and failure paths
 */

#include <lume/selftest.h>
#include <lume/atomic.h>

void selftest_atomic() {
    st_begin("atomic: default construction is zero");
    {
        lume::atomic<uint32> a;
        ST_ASSERT_EQ(a.load(), 0U);
    }
    st_pass();

    st_begin("atomic: explicit construction");
    {
        lume::atomic<uint64> a(42);
        ST_ASSERT_EQ(a.load(), 42ULL);
    }
    st_pass();

    st_begin("atomic: store/load round-trip");
    {
        lume::atomic<uint32> a;
        a.store(0xDEAD, __ATOMIC_RELEASE);
        ST_ASSERT_EQ(a.load(__ATOMIC_ACQUIRE), 0xDEADU);

        a.store(0, __ATOMIC_SEQ_CST);
        ST_ASSERT_EQ(a.load(__ATOMIC_SEQ_CST), 0U);
    }
    st_pass();

    st_begin("atomic: exchange returns old value");
    {
        lume::atomic<uint32> a(10);
        uint32 old = a.exchange(20);
        ST_ASSERT_EQ(old, 10U);
        ST_ASSERT_EQ(a.load(), 20U);
    }
    st_pass();

    st_begin("atomic: fetch_add / fetch_sub");
    {
        lume::atomic<uint32> a(100);

        uint32 old = a.fetch_add(5);
        ST_ASSERT_EQ(old, 100U);
        ST_ASSERT_EQ(a.load(), 105U);

        old = a.fetch_sub(10);
        ST_ASSERT_EQ(old, 105U);
        ST_ASSERT_EQ(a.load(), 95U);

        // Underflow wraps (unsigned)
        lume::atomic<uint32> b(0);
        old = b.fetch_sub(1);
        ST_ASSERT_EQ(old, 0U);
        ST_ASSERT_EQ(b.load(), 0xFFFFFFFFU);
    }
    st_pass();

    st_begin("atomic: compare_exchange success and failure");
    {
        lume::atomic<uint32> a(42);

        // Success path: expected matches, swap happens
        uint32 expected = 42;
        bool ok = a.compare_exchange(expected, 99);
        ST_ASSERT(ok);
        ST_ASSERT_EQ(a.load(), 99U);
        ST_ASSERT_EQ(expected, 42U);  // expected unchanged on success

        // Failure path: expected doesn't match, expected gets updated
        expected = 0;  // wrong
        ok = a.compare_exchange(expected, 200);
        ST_ASSERT(!ok);
        ST_ASSERT_EQ(a.load(), 99U);       // value unchanged
        ST_ASSERT_EQ(expected, 99U);        // expected updated to actual value
    }
    st_pass();

    st_begin("atomic: 64-bit fetch_add correctness");
    {
        lume::atomic<uint64> a(0xFFFFFFFF00000000ULL);
        uint64 old = a.fetch_add(1);
        ST_ASSERT_EQ(old, 0xFFFFFFFF00000000ULL);
        ST_ASSERT_EQ(a.load(), 0xFFFFFFFF00000001ULL);
    }
    st_pass();
}