summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Vašek <david.vasek@nic.cz>2022-11-25 21:14:00 +0100
committerDavid Vašek <david.vasek@nic.cz>2023-11-14 16:27:57 +0100
commit3f37ed29404341e1c5d267e39ac31af1e5266899 (patch)
tree95298dd0a3cd3f6e70e1e2513da6f2ac0cf5ab4b
parentMerge branch 'improvements' into 'master' (diff)
downloadknot-3f37ed29404341e1c5d267e39ac31af1e5266899.tar.xz
knot-3f37ed29404341e1c5d267e39ac31af1e5266899.zip
contrib: universal atomic operations
-rw-r--r--Knot.files2
-rw-r--r--src/contrib/Makefile.inc1
-rw-r--r--src/contrib/atomic.h64
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile.am1
-rw-r--r--tests/contrib/test_atomic.c95
6 files changed, 164 insertions, 0 deletions
diff --git a/Knot.files b/Knot.files
index f394d0eac..d4afe2399 100644
--- a/Knot.files
+++ b/Knot.files
@@ -1,4 +1,5 @@
src/contrib/asan.h
+src/contrib/atomic.h
src/contrib/base32hex.c
src/contrib/base32hex.h
src/contrib/base64.c
@@ -620,6 +621,7 @@ tests-fuzz/knotd_wrap/server.c
tests-fuzz/knotd_wrap/tcp-handler.c
tests-fuzz/knotd_wrap/udp-handler.c
tests-fuzz/main.c
+tests/contrib/test_atomic.c
tests/contrib/test_base32hex.c
tests/contrib/test_base64.c
tests/contrib/test_base64url.c
diff --git a/src/contrib/Makefile.inc b/src/contrib/Makefile.inc
index d35c7d204..09ecdde90 100644
--- a/src/contrib/Makefile.inc
+++ b/src/contrib/Makefile.inc
@@ -24,6 +24,7 @@ EXTRA_DIST += \
libcontrib_la_SOURCES = \
contrib/asan.h \
+ contrib/atomic.h \
contrib/base32hex.c \
contrib/base32hex.h \
contrib/base64.c \
diff --git a/src/contrib/atomic.h b/src/contrib/atomic.h
new file mode 100644
index 000000000..adc2d5a46
--- /dev/null
+++ b/src/contrib/atomic.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \brief C11 atomic operations with fallbacks.
+ */
+
+#pragma once
+
+#if (__STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_ATOMICS__)
+ #define KNOT_HAVE_ATOMIC /* C11 */
+
+ #include <stdatomic.h>
+
+ #define ATOMIC_SET(dst, val) atomic_store_explicit(&(dst), (val), memory_order_relaxed)
+ #define ATOMIC_GET(src) atomic_load_explicit(&(src), memory_order_relaxed)
+ #define ATOMIC_ADD(dst, val) (void)atomic_fetch_add_explicit(&(dst), (val), memory_order_relaxed)
+ #define ATOMIC_SUB(dst, val) (void)atomic_fetch_sub_explicit(&(dst), (val), memory_order_relaxed)
+
+ typedef atomic_uint_fast64_t knot_atomic_uint64_t;
+#elif defined(HAVE_ATOMIC) /* GCC */
+ #define KNOT_HAVE_ATOMIC
+
+ #include <stdint.h>
+
+ #define ATOMIC_SET(dst, val) __atomic_store_n(&(dst), (val), __ATOMIC_RELAXED)
+ #define ATOMIC_GET(src) __atomic_load_n(&(src), __ATOMIC_RELAXED)
+ #define ATOMIC_ADD(dst, val) __atomic_add_fetch(&(dst), (val), __ATOMIC_RELAXED)
+ #define ATOMIC_SUB(dst, val) __atomic_sub_fetch(&(dst), (val), __ATOMIC_RELAXED)
+
+ typedef uint64_t knot_atomic_uint64_t;
+#elif defined(HAVE_SYNC_ATOMIC) /* obsolete GCC, partial support only. */
+ #warning "Full atomic operations not availabe, using partially unreliable replacement."
+ #define ATOMIC_SET(dst, val) ((dst) = (val))
+ #define ATOMIC_GET(src) __sync_fetch_and_or(&(src), 0)
+ #define ATOMIC_ADD(dst, val) __sync_add_and_fetch(&(dst), (val))
+ #define ATOMIC_SUB(dst, val) __sync_sub_and_fetch(&(dst), (val))
+
+ typedef uint64_t knot_atomic_uint64_t;
+#else /* Fallback, non-atomic. */
+ #warning "Atomic operations not availabe, using unreliable replacement."
+
+ #include <stdint.h>
+
+ #define ATOMIC_SET(dst, val) ((dst) = (val))
+ #define ATOMIC_GET(src) (src)
+ #define ATOMIC_ADD(dst, val) ((dst) += (val))
+ #define ATOMIC_SUB(dst, val) ((dst) -= (val))
+
+ typedef uint64_t knot_atomic_uint64_t;
+#endif
diff --git a/tests/.gitignore b/tests/.gitignore
index e6b410cc5..a9763744b 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,6 +1,7 @@
/tap/runtests
/runtests.log
+/contrib/test_atomic
/contrib/test_base32hex
/contrib/test_base64
/contrib/test_base64url
diff --git a/tests/Makefile.am b/tests/Makefile.am
index eb6f1aa85..83966a4bf 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -48,6 +48,7 @@ libtap_la_SOURCES = \
EXTRA_PROGRAMS = tap/runtests
check_PROGRAMS = \
+ contrib/test_atomic \
contrib/test_base32hex \
contrib/test_base64 \
contrib/test_base64url \
diff --git a/tests/contrib/test_atomic.c b/tests/contrib/test_atomic.c
new file mode 100644
index 000000000..2956c7a6e
--- /dev/null
+++ b/tests/contrib/test_atomic.c
@@ -0,0 +1,95 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <signal.h>
+#include <tap/basic.h>
+
+#include "contrib/atomic.h"
+#include "knot/server/dthreads.h"
+
+#define THREADS 16
+#define CYCLES1 100000
+#define CYCLES2 2000000
+#define UPPER 0xffffffff00000000
+#define LOWER 0x00000000ffffffff
+
+static volatile knot_atomic_uint64_t counter_add = 0;
+static volatile knot_atomic_uint64_t counter_sub = 0;
+static volatile knot_atomic_uint64_t atomic_var;
+static int errors_set_get = 0;
+
+static int thread_add(struct dthread *thread)
+{
+ for (int i = 0; i < CYCLES1; i++) {
+ ATOMIC_ADD(counter_add, 7);
+ ATOMIC_SUB(counter_sub, 7);
+ }
+
+ return 0;
+}
+
+static int thread_set(struct dthread *thread)
+{
+ u_int64_t val = (dt_get_id(thread) % 2) ? UPPER : LOWER;
+
+ for (int i = 0; i < CYCLES2; i++) {
+ ATOMIC_SET(atomic_var, val);
+ volatile u_int64_t read = ATOMIC_GET(atomic_var);
+ if (read != UPPER && read != LOWER) {
+ // Non-atomic counter, won't be accurate!
+ // However, it's sufficient for fault detection.
+ errors_set_get++;
+ }
+ }
+
+ return 0;
+}
+
+// Signal handler
+static void interrupt_handle(int s)
+{
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // Register service and signal handler
+ struct sigaction sa;
+ sa.sa_handler = interrupt_handle;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, NULL); // Interrupt
+
+ // Test for atomicity of ATOMIC_ADD and ATOMIC_SUB.
+ dt_unit_t *unit = dt_create(THREADS, thread_add, NULL, NULL);
+ dt_start(unit);
+ dt_join(unit);
+ dt_delete(&unit);
+
+ is_int(THREADS * CYCLES1 * 7, counter_add, "atomicity of ATOMIC_ADD");
+ is_int(THREADS * CYCLES1 * 7, -counter_sub, "atomicity of ATOMIC_SUB");
+
+ // Test for atomicity of ATOMIC_SET and ATOMIC_GET.
+ unit = dt_create(THREADS, thread_set, NULL, NULL);
+ dt_start(unit);
+ dt_join(unit);
+ dt_delete(&unit);
+
+ is_int(0, errors_set_get, "atomicity of ATOMIC_SET / ATOMIC_GET");
+
+ return 0;
+}