1 /*- 2 * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 #include <assert.h> 29 #include <stdatomic.h> 30 #include <stdbool.h> 31 #include <stddef.h> 32 #include <stdint.h> 33 #include <stdlib.h> 34 #include <string.h> 35 36 /* 37 * Tool for testing the logical behaviour of operations on atomic 38 * integer types. These tests make no attempt to actually test whether 39 * the functions are atomic or provide the right barrier semantics. 40 * 41 * For every type, we create an array of 16 elements and repeat the test 42 * on every element in the array. This allows us to test whether the 43 * atomic operations have no effect on surrounding values. This is 44 * especially useful for the smaller integer types, as it may be the 45 * case that these operations are implemented by processing entire words 46 * (e.g. on MIPS). 47 * 48 * Before the randomised loop, a small surface check exercises lock-free 49 * macros, fences, atomic_flag, atomic_bool, pointer fetch_add/fetch_sub 50 * stride, kill_dependency, atomic_is_lock_free, and when available 51 * ATOMIC_VAR_INIT / ATOMIC_FLAG_INIT (pre-C23). 52 */ 53 54 #define CHECK_LOCK_FREE(name) \ 55 _Static_assert((name) == 0 || (name) == 1 || (name) == 2, \ 56 #name " out of range") 57 58 CHECK_LOCK_FREE(ATOMIC_BOOL_LOCK_FREE); 59 CHECK_LOCK_FREE(ATOMIC_CHAR_LOCK_FREE); 60 CHECK_LOCK_FREE(ATOMIC_SHORT_LOCK_FREE); 61 CHECK_LOCK_FREE(ATOMIC_INT_LOCK_FREE); 62 CHECK_LOCK_FREE(ATOMIC_LONG_LOCK_FREE); 63 CHECK_LOCK_FREE(ATOMIC_LLONG_LOCK_FREE); 64 CHECK_LOCK_FREE(ATOMIC_POINTER_LOCK_FREE); 65 #ifdef ATOMIC_CHAR8_T_LOCK_FREE 66 CHECK_LOCK_FREE(ATOMIC_CHAR8_T_LOCK_FREE); 67 #endif 68 #ifdef ATOMIC_CHAR16_T_LOCK_FREE 69 CHECK_LOCK_FREE(ATOMIC_CHAR16_T_LOCK_FREE); 70 #endif 71 #ifdef ATOMIC_CHAR32_T_LOCK_FREE 72 CHECK_LOCK_FREE(ATOMIC_CHAR32_T_LOCK_FREE); 73 #endif 74 #ifdef ATOMIC_WCHAR_T_LOCK_FREE 75 CHECK_LOCK_FREE(ATOMIC_WCHAR_T_LOCK_FREE); 76 #endif 77 78 #if defined(__STDC_VERSION_STDATOMIC_H__) 79 _Static_assert(__STDC_VERSION_STDATOMIC_H__ == 202311L, 80 "__STDC_VERSION_STDATOMIC_H__"); 81 #endif 82 83 static void 84 test_surface(void) 85 { 86 atomic_flag f = ATOMIC_FLAG_INIT; 87 atomic_int probe; 88 atomic_bool b; 89 _Atomic(int *) ap; 90 int arr[4]; 91 int dep; 92 static const memory_order k_orders[] = { 93 memory_order_relaxed, 94 memory_order_consume, 95 memory_order_acquire, 96 memory_order_release, 97 memory_order_acq_rel, 98 memory_order_seq_cst, 99 }; 100 size_t k; 101 102 dep = kill_dependency(123); 103 assert(dep == 123); 104 105 atomic_init(&probe, 0); 106 assert(atomic_is_lock_free(&probe) == 107 atomic_is_lock_free((atomic_int *)NULL)); 108 109 for (k = 0; k < sizeof(k_orders) / sizeof(k_orders[0]); k++) { 110 atomic_thread_fence(k_orders[k]); 111 atomic_signal_fence(k_orders[k]); 112 } 113 114 assert(atomic_flag_test_and_set_explicit(&f, 115 memory_order_seq_cst) == 0); 116 assert(atomic_flag_test_and_set_explicit(&f, 117 memory_order_acquire) != 0); 118 atomic_flag_clear_explicit(&f, memory_order_relaxed); 119 120 atomic_init(&b, false); 121 atomic_store_explicit(&b, true, memory_order_relaxed); 122 assert(atomic_load_explicit(&b, memory_order_relaxed) == true); 123 124 memset(arr, 0, sizeof(arr)); 125 atomic_init(&ap, &arr[3]); 126 atomic_fetch_sub_explicit(&ap, 2, memory_order_relaxed); 127 assert(ap == &arr[1]); 128 atomic_fetch_add_explicit(&ap, 1, memory_order_relaxed); 129 assert(ap == &arr[2]); 130 } 131 132 #ifdef ATOMIC_VAR_INIT 133 static void 134 test_atomic_var_init(void) 135 { 136 atomic_int x = ATOMIC_VAR_INIT(42); 137 138 assert(atomic_load_explicit(&x, memory_order_relaxed) == 42); 139 atomic_store_explicit(&x, 7, memory_order_relaxed); 140 assert(atomic_load_explicit(&x, memory_order_relaxed) == 7); 141 142 { 143 atomic_flag f = ATOMIC_FLAG_INIT; 144 145 assert(atomic_flag_test_and_set_explicit(&f, 146 memory_order_seq_cst) == 0); 147 atomic_flag_clear_explicit(&f, memory_order_relaxed); 148 } 149 } 150 #endif 151 152 static inline intmax_t 153 rndnum(void) 154 { 155 intmax_t v; 156 157 arc4random_buf(&v, sizeof(v)); 158 return (v); 159 } 160 161 #define DO_FETCH_TEST(T, a, name, result) do { \ 162 T v1 = atomic_load(a); \ 163 T v2 = rndnum(); \ 164 assert(atomic_##name(a, v2) == v1); \ 165 assert(atomic_load(a) == (T)(result)); \ 166 } while (0) 167 168 #define DO_COMPARE_EXCHANGE_TEST(T, a, name) do { \ 169 T v1 = atomic_load(a); \ 170 T v2 = rndnum(); \ 171 T v3 = rndnum(); \ 172 if (atomic_compare_exchange_##name(a, &v2, v3)) \ 173 assert(v1 == v2); \ 174 else \ 175 assert(atomic_compare_exchange_##name(a, &v2, v3)); \ 176 assert(atomic_load(a) == v3); \ 177 } while (0) 178 179 #define DO_ALL_TESTS(T, a) do { \ 180 { \ 181 T v1 = rndnum(); \ 182 atomic_init(a, v1); \ 183 assert(atomic_load(a) == v1); \ 184 } \ 185 { \ 186 T v1 = rndnum(); \ 187 atomic_store(a, v1); \ 188 assert(atomic_load(a) == v1); \ 189 } \ 190 \ 191 DO_FETCH_TEST(T, a, exchange, v2); \ 192 DO_FETCH_TEST(T, a, fetch_add, v1 + v2); \ 193 DO_FETCH_TEST(T, a, fetch_and, v1 & v2); \ 194 DO_FETCH_TEST(T, a, fetch_or, v1 | v2); \ 195 DO_FETCH_TEST(T, a, fetch_sub, v1 - v2); \ 196 DO_FETCH_TEST(T, a, fetch_xor, v1 ^ v2); \ 197 \ 198 DO_COMPARE_EXCHANGE_TEST(T, a, weak); \ 199 DO_COMPARE_EXCHANGE_TEST(T, a, strong); \ 200 } while (0) 201 202 #define TEST_TYPE(T) do { \ 203 int j; \ 204 struct { _Atomic(T) v[16]; } list, cmp; \ 205 arc4random_buf(&cmp, sizeof(cmp)); \ 206 for (j = 0; j < 16; j++) { \ 207 list = cmp; \ 208 DO_ALL_TESTS(T, &list.v[j]); \ 209 list.v[j] = cmp.v[j]; \ 210 assert(memcmp(&list, &cmp, sizeof(list)) == 0); \ 211 } \ 212 } while (0) 213 214 int 215 main(void) 216 { 217 int i; 218 219 test_surface(); 220 #ifdef ATOMIC_VAR_INIT 221 test_atomic_var_init(); 222 #endif 223 for (i = 0; i < 1000; i++) { 224 TEST_TYPE(int8_t); 225 TEST_TYPE(uint8_t); 226 TEST_TYPE(int16_t); 227 TEST_TYPE(uint16_t); 228 TEST_TYPE(int32_t); 229 TEST_TYPE(uint32_t); 230 TEST_TYPE(int64_t); 231 TEST_TYPE(uint64_t); 232 } 233 234 return (0); 235 } 236