[prev in list] [next in list] [prev in thread] [next in thread]
List: gnulib-bug
Subject: new module 'simple-atomic'
From: Bruno Haible <bruno () clisp ! org>
Date: 2021-02-15 3:37:30
Message-ID: 3872225.Oj10xBsPvQ () omega
[Download RAW message or body]
Most lock-free algorithms use the compare-and-swap primitive in one form or
another. In order to be able to implement such algorithms, here is a module
that implements compare-and-swap for 32-bit words and pointer-sized words.
It's far from a complete <stdatomic.h>, just the bare essentials.
Support for compilers that are not GCC compatible does require work, namely
finding the instructions generated by GCC and putting them in a form that
the non-GCC compiler can grok. So far, this has only been needed for AIX
and Solaris, and fortunately both support inline asms (so that no external .s
file is needed).
2021-02-14 Bruno Haible <bruno@clisp.org>
simple-atomic: Add tests.
* tests/test-simple-atomic.c: New file.
* modules/simple-atomic-tests: New file.
simple-atomic: New module.
* lib/simple-atomic.h: New file.
* lib/simple-atomic.c: New file, based on lib/asyncsafe-spin.c.
* modules/simple-atomic: New file.
["0001-simple-atomic-New-module.patch" (0001-simple-atomic-New-module.patch)]
From 0a6dc3028b79ce4f1051bbd3e9d458372d385690 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Mon, 15 Feb 2021 04:25:38 +0100
Subject: [PATCH 1/2] simple-atomic: New module.
* lib/simple-atomic.h: New file.
* lib/simple-atomic.c: New file, based on lib/asyncsafe-spin.c.
* modules/simple-atomic: New file.
---
ChangeLog | 7 +
lib/simple-atomic.c | 355 ++++++++++++++++++++++++++++++++++++++++++++++++++
lib/simple-atomic.h | 49 +++++++
modules/simple-atomic | 25 ++++
4 files changed, 436 insertions(+)
create mode 100644 lib/simple-atomic.c
create mode 100644 lib/simple-atomic.h
create mode 100644 modules/simple-atomic
diff --git a/ChangeLog b/ChangeLog
index 9b35795..5d7337c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
2021-02-14 Bruno Haible <bruno@clisp.org>
+ simple-atomic: New module.
+ * lib/simple-atomic.h: New file.
+ * lib/simple-atomic.c: New file, based on lib/asyncsafe-spin.c.
+ * modules/simple-atomic: New file.
+
+2021-02-14 Bruno Haible <bruno@clisp.org>
+
Fix distinction of 32-bit/64-bit mode with xlc 13.1.3 on AIX.
* m4/host-cpu-c-abi.m4 (gl_HOST_CPU_C_ABI, gl_HOST_CPU_C_ABI_32BIT):
Test __LP64__ instead of _ARCH_PPC64.
diff --git a/lib/simple-atomic.c b/lib/simple-atomic.c
new file mode 100644
index 0000000..17625fb
--- /dev/null
+++ b/lib/simple-atomic.c
@@ -0,0 +1,355 @@
+/* Simple atomic operations for multithreading.
+ Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+ 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 2, 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/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2021. */
+
+#include <config.h>
+
+/* Specification. */
+#include "simple-atomic.h"
+
+#if defined _WIN32 && ! defined __CYGWIN__
+/* Native Windows. */
+
+# include <windows.h>
+
+void
+memory_barrier (void)
+{
+ /* MemoryBarrier
+ <https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-memorybarrier> \
*/ + MemoryBarrier ();
+}
+
+unsigned int
+atomic_compare_and_swap (unsigned int volatile *vp,
+ unsigned int cmp,
+ unsigned int newval)
+{
+ /* InterlockedCompareExchange
+ <https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-interlockedcompareexchange> \
*/ + return InterlockedCompareExchange ((LONG volatile *) vp,
+ (LONG) newval, (LONG) cmp);
+}
+
+uintptr_t
+atomic_compare_and_swap_ptr (uintptr_t volatile *vp,
+ uintptr_t cmp,
+ uintptr_t newval)
+{
+ /* InterlockedCompareExchangePointer
+ <https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-interlockedcompareexchangepointer> \
*/ + return InterlockedCompareExchangePointer ((void * volatile *) vp,
+ (void *) newval, (void *) cmp);
+}
+
+#elif HAVE_PTHREAD_H
+/* Some other platform that supports multi-threading.
+
+ We don't use the C11 <stdatomic.h> (available in GCC >= 4.9) because it would
+ require to link with -latomic. */
+
+# if (((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) \
+ && !defined __sparc__) \
+ || __clang_major__ >= 3) \
+ && !defined __ibmxl__
+/* Use GCC built-ins (available in GCC >= 4.1, except on SPARC, and
+ clang >= 3.0).
+ Documentation:
+ <https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html> */
+
+void
+memory_barrier (void)
+{
+ __sync_synchronize ();
+}
+
+unsigned int
+atomic_compare_and_swap (unsigned int volatile *vp,
+ unsigned int cmp,
+ unsigned int newval)
+{
+ return __sync_val_compare_and_swap (vp, cmp, newval);
+}
+
+uintptr_t
+atomic_compare_and_swap_ptr (uintptr_t volatile *vp,
+ uintptr_t cmp,
+ uintptr_t newval)
+{
+ return __sync_val_compare_and_swap (vp, cmp, newval);
+}
+
+# elif defined _AIX
+/* AIX */
+/* For older versions of GCC or xlc, use inline assembly.
+ __compare_and_swap and __compare_and_swaplp are not sufficient here. */
+
+void
+memory_barrier (void)
+{
+ asm volatile ("sync");
+}
+
+unsigned int
+atomic_compare_and_swap (unsigned int volatile *vp,
+ unsigned int cmp,
+ unsigned int newval)
+{
+ asm volatile ("sync");
+
+ unsigned int oldval;
+ asm volatile (
+# if defined __GNUC__ || defined __clang__
+ "1: lwarx %0,0,%1\n"
+ " cmpw 0,%0,%2\n"
+ " bne 0,2f\n"
+ " stwcx. %3,0,%1\n"
+ " bne 0,1b\n"
+ "2:"
+# else /* another label syntax */
+ ".L01: lwarx %0,0,%1\n"
+ " cmpw 0,%0,%2\n"
+ " bne 0,.L02\n"
+ " stwcx. %3,0,%1\n"
+ " bne 0,.L01\n"
+ ".L02:"
+# endif
+ : "=&r" (oldval)
+ : "r" (vp), "r" (cmp), "r" (newval)
+ : "cr0");
+
+ asm volatile ("isync");
+ return oldval;
+}
+
+uintptr_t
+atomic_compare_and_swap_ptr (uintptr_t volatile *vp,
+ uintptr_t cmp,
+ uintptr_t newval)
+{
+ asm volatile ("sync");
+
+ uintptr_t oldval;
+ asm volatile (
+# if defined __GNUC__ || defined __clang__
+# if defined __powerpc64__ || defined __LP64__
+ "1: ldarx %0,0,%1\n"
+ " cmpd 0,%0,%2\n"
+ " bne 0,2f\n"
+ " stdcx. %3,0,%1\n"
+ " bne 0,1b\n"
+ "2:"
+# else
+ "1: lwarx %0,0,%1\n"
+ " cmpw 0,%0,%2\n"
+ " bne 0,2f\n"
+ " stwcx. %3,0,%1\n"
+ " bne 0,1b\n"
+ "2:"
+# endif
+# else /* another label syntax */
+# if defined __powerpc64__ || defined __LP64__
+ ".L01: ldarx %0,0,%1\n"
+ " cmpd 0,%0,%2\n"
+ " bne 0,.L02\n"
+ " stdcx. %3,0,%1\n"
+ " bne 0,.L01\n"
+ ".L02:"
+# else
+ ".L01: lwarx %0,0,%1\n"
+ " cmpw 0,%0,%2\n"
+ " bne 0,.L02\n"
+ " stwcx. %3,0,%1\n"
+ " bne 0,.L01\n"
+ ".L02:"
+# endif
+# endif
+ : "=&r" (oldval)
+ : "r" (vp), "r" (cmp), "r" (newval)
+ : "cr0");
+
+ asm volatile ("isync");
+ return oldval;
+}
+
+# elif (defined __GNUC__ || defined __clang__ || defined __SUNPRO_C) && (defined \
__sparc || defined __i386 || defined __x86_64__) +/* For older versions of GCC or \
clang, use inline assembly. + GCC, clang, and the Oracle Studio C 12 compiler \
understand GCC's extended + asm syntax, but the plain Oracle Studio C 11 compiler \
understands only + simple asm. */
+
+void
+memory_barrier (void)
+{
+# if defined __GNUC__ || defined __clang__ || __SUNPRO_C >= 0x590
+# if defined __i386 || defined __x86_64__
+ asm volatile ("mfence");
+# endif
+# if defined __sparc
+ asm volatile ("membar 2");
+# endif
+# else
+# if defined __i386 || defined __x86_64__
+ asm ("mfence");
+# endif
+# if defined __sparc
+ asm ("membar 2");
+# endif
+# endif
+}
+
+unsigned int
+atomic_compare_and_swap (unsigned int volatile *vp,
+ unsigned int cmp,
+ unsigned int newval)
+{
+# if defined __GNUC__ || defined __clang__ || __SUNPRO_C >= 0x590
+ unsigned int oldval;
+# if defined __i386 || defined __x86_64__
+ asm volatile (" lock\n cmpxchgl %3,(%1)"
+ : "=a" (oldval) : "r" (vp), "a" (cmp), "r" (newval) : "memory");
+# endif
+# if defined __sparc
+ asm volatile (" cas [%1],%2,%3\n"
+ " mov %3,%0"
+ : "=r" (oldval) : "r" (vp), "r" (cmp), "r" (newval) : "memory");
+# endif
+ return oldval;
+# else /* __SUNPRO_C */
+# if defined __x86_64__
+ asm (" movl %esi,%eax\n"
+ " lock\n cmpxchgl %edx,(%rdi)");
+# elif defined __i386
+ asm (" movl 16(%ebp),%ecx\n"
+ " movl 12(%ebp),%eax\n"
+ " movl 8(%ebp),%edx\n"
+ " lock\n cmpxchgl %ecx,(%edx)");
+# endif
+# if defined __sparc
+ asm (" cas [%i0],%i1,%i2\n"
+ " mov %i2,%i0");
+# endif
+# endif
+}
+
+uintptr_t
+atomic_compare_and_swap_ptr (uintptr_t volatile *vp,
+ uintptr_t cmp,
+ uintptr_t newval)
+{
+# if defined __GNUC__ || defined __clang__ || __SUNPRO_C >= 0x590
+ uintptr_t oldval;
+# if defined __x86_64__
+ asm volatile (" lock\n cmpxchgq %3,(%1)"
+ : "=a" (oldval) : "r" (vp), "a" (cmp), "r" (newval) : "memory");
+# elif defined __i386
+ asm volatile (" lock\n cmpxchgl %3,(%1)"
+ : "=a" (oldval) : "r" (vp), "a" (cmp), "r" (newval) : "memory");
+# endif
+# if defined __sparc && (defined __sparcv9 || defined __arch64__)
+ asm volatile (" casx [%1],%2,%3\n"
+ " mov %3,%0"
+ : "=r" (oldval) : "r" (vp), "r" (cmp), "r" (newval) : "memory");
+# elif defined __sparc
+ asm volatile (" cas [%1],%2,%3\n"
+ " mov %3,%0"
+ : "=r" (oldval) : "r" (vp), "r" (cmp), "r" (newval) : "memory");
+# endif
+ return oldval;
+# else /* __SUNPRO_C */
+# if defined __x86_64__
+ asm (" movl %rsi,%rax\n"
+ " lock\n cmpxchgq %rdx,(%rdi)");
+# elif defined __i386
+ asm (" movl 16(%ebp),%ecx\n"
+ " movl 12(%ebp),%eax\n"
+ " movl 8(%ebp),%edx\n"
+ " lock\n cmpxchgl %ecx,(%edx)");
+# endif
+# if defined __sparc && (defined __sparcv9 || defined __arch64__)
+ asm (" casx [%i0],%i1,%i2\n"
+ " mov %i2,%i0");
+# elif defined __sparc
+ asm (" cas [%i0],%i1,%i2\n"
+ " mov %i2,%i0");
+# endif
+# endif
+}
+
+# else
+/* Fallback code. It has some race conditions. The unit test will fail. */
+
+void
+memory_barrier (void)
+{
+}
+
+unsigned int
+atomic_compare_and_swap (unsigned int volatile *vp,
+ unsigned int cmp,
+ unsigned int newval)
+{
+ unsigned int oldval = *vp;
+ if (oldval == cmp)
+ *vp = newval;
+ return oldval;
+}
+
+uintptr_t
+atomic_compare_and_swap_ptr (uintptr_t volatile *vp,
+ uintptr_t cmp,
+ uintptr_t newval)
+{
+ uintptr_t oldval = *vp;
+ if (oldval == cmp)
+ *vp = newval;
+ return oldval;
+}
+
+# endif
+
+#else
+/* A platform that does not support multi-threading. */
+
+void
+memory_barrier (void)
+{
+}
+
+unsigned int
+atomic_compare_and_swap (unsigned int volatile *vp,
+ unsigned int cmp,
+ unsigned int newval)
+{
+ unsigned int oldval = *vp;
+ if (oldval == cmp)
+ *vp = newval;
+ return oldval;
+}
+
+uintptr_t
+atomic_compare_and_swap_ptr (uintptr_t volatile *vp,
+ uintptr_t cmp,
+ uintptr_t newval)
+{
+ uintptr_t oldval = *vp;
+ if (oldval == cmp)
+ *vp = newval;
+ return oldval;
+}
+
+#endif
diff --git a/lib/simple-atomic.h b/lib/simple-atomic.h
new file mode 100644
index 0000000..5a34e66
--- /dev/null
+++ b/lib/simple-atomic.h
@@ -0,0 +1,49 @@
+/* Simple atomic operations for multithreading.
+ Copyright (C) 2021 Free Software Foundation, Inc.
+
+ 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 2, 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/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2021. */
+
+#ifndef _SIMPLE_ATOMIC_H
+#define _SIMPLE_ATOMIC_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Guarantees that memory stores that are in code before this call
+ are finished before this call, and that memory loads that are in code
+ after this call are started after this call. */
+extern void memory_barrier (void);
+
+/* Stores NEWVAL in *VP if the old value *VP is == CMP.
+ Returns the old value. */
+extern unsigned int atomic_compare_and_swap (unsigned int volatile *vp,
+ unsigned int cmp,
+ unsigned int newval);
+
+/* Stores NEWVAL in *VP if the old value *VP is == CMP.
+ Returns the old value. */
+extern uintptr_t atomic_compare_and_swap_ptr (uintptr_t volatile *vp,
+ uintptr_t cmp,
+ uintptr_t newval);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SIMPLE_ATOMIC_H */
diff --git a/modules/simple-atomic b/modules/simple-atomic
new file mode 100644
index 0000000..5f3f63d
--- /dev/null
+++ b/modules/simple-atomic
@@ -0,0 +1,25 @@
+Description:
+Simple atomic operations for multithreading.
+
+Files:
+lib/simple-atomic.h
+lib/simple-atomic.c
+
+Depends-on:
+stdint
+sparcv8+
+
+configure.ac:
+AC_CHECK_HEADERS_ONCE([pthread.h])
+
+Makefile.am:
+lib_SOURCES += simple-atomic.c
+
+Include:
+"simple-atomic.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
--
2.7.4
["0002-simple-atomic-Add-tests.patch" (0002-simple-atomic-Add-tests.patch)]
From 43e5eca63a07d4119b104c4f3cb2aefe3dc9f2ea Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Mon, 15 Feb 2021 04:29:18 +0100
Subject: [PATCH 2/2] simple-atomic: Add tests.
* tests/test-simple-atomic.c: New file.
* modules/simple-atomic-tests: New file.
---
ChangeLog | 4 +
modules/simple-atomic-tests | 14 +++
tests/test-simple-atomic.c | 234 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 252 insertions(+)
create mode 100644 modules/simple-atomic-tests
create mode 100644 tests/test-simple-atomic.c
diff --git a/ChangeLog b/ChangeLog
index 5d7337c..29984bc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
2021-02-14 Bruno Haible <bruno@clisp.org>
+ simple-atomic: Add tests.
+ * tests/test-simple-atomic.c: New file.
+ * modules/simple-atomic-tests: New file.
+
simple-atomic: New module.
* lib/simple-atomic.h: New file.
* lib/simple-atomic.c: New file, based on lib/asyncsafe-spin.c.
diff --git a/modules/simple-atomic-tests b/modules/simple-atomic-tests
new file mode 100644
index 0000000..a57b9fc
--- /dev/null
+++ b/modules/simple-atomic-tests
@@ -0,0 +1,14 @@
+Files:
+tests/test-simple-atomic.c
+tests/macros.h
+
+Depends-on:
+thread
+yield
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-simple-atomic
+check_PROGRAMS += test-simple-atomic
+test_simple_atomic_LDADD = $(LDADD) @LIBMULTITHREAD@ @YIELD_LIB@
diff --git a/tests/test-simple-atomic.c b/tests/test-simple-atomic.c
new file mode 100644
index 0000000..04baf05
--- /dev/null
+++ b/tests/test-simple-atomic.c
@@ -0,0 +1,234 @@
+/* Test of simple atomic operations for multithreading.
+ Copyright (C) 2021 Free Software Foundation, Inc.
+
+ 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/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2021. */
+
+#include <config.h>
+
+#include "simple-atomic.h"
+
+#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
+
+/* Whether to help the scheduler through explicit yield().
+ Uncomment this to see if the operating system has a fair scheduler. */
+#define EXPLICIT_YIELD 1
+
+/* Number of simultaneous threads. */
+#define THREAD_COUNT 4
+
+/* Number of operations performed in each thread.
+ With a smaller count, say 100, we often get an "OK" result even with the racy
+ implementation. */
+#define REPEAT_COUNT 1000
+
+#if EXPLICIT_YIELD
+# define yield() gl_thread_yield ()
+#else
+# define yield()
+#endif
+
+#include "glthread/thread.h"
+#include "glthread/yield.h"
+
+#include "macros.h"
+
+/* Counters for each thread. */
+static unsigned int counter[THREAD_COUNT][5];
+
+/* A variable of type 'unsigned int'. */
+static unsigned int int_variable;
+
+static void *
+int_mutator_thread (void *arg)
+{
+ int *pcounter = (int *) arg;
+ int repeat;
+
+ for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
+ {
+ if (atomic_compare_and_swap (&int_variable, 0, 10) == 0)
+ pcounter[0]++;
+ yield ();
+
+ if (atomic_compare_and_swap (&int_variable, 14, 17) == 14)
+ pcounter[1]++;
+ yield ();
+
+ if (atomic_compare_and_swap (&int_variable, 20, 0) == 20)
+ pcounter[2]++;
+ yield ();
+
+ if (atomic_compare_and_swap (&int_variable, 10, 14) == 10)
+ pcounter[3]++;
+ yield ();
+
+ if (atomic_compare_and_swap (&int_variable, 17, 20) == 17)
+ pcounter[4]++;
+ yield ();
+ }
+
+ return NULL;
+}
+
+/* A variable of type 'uintptr_t'. */
+static uintptr_t ptr_variable;
+
+static void *
+ptr_mutator_thread (void *arg)
+{
+ int *pcounter = (int *) arg;
+ int repeat;
+
+ for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
+ {
+ if (atomic_compare_and_swap_ptr (&ptr_variable, 0, 10) == 0)
+ pcounter[0]++;
+ yield ();
+
+ if (atomic_compare_and_swap_ptr (&ptr_variable, 14, 17) == 14)
+ pcounter[1]++;
+ yield ();
+
+ if (atomic_compare_and_swap_ptr (&ptr_variable, 20, 0) == 20)
+ pcounter[2]++;
+ yield ();
+
+ if (atomic_compare_and_swap_ptr (&ptr_variable, 10, 14) == 10)
+ pcounter[3]++;
+ yield ();
+
+ if (atomic_compare_and_swap_ptr (&ptr_variable, 17, 20) == 17)
+ pcounter[4]++;
+ yield ();
+ }
+
+ return NULL;
+}
+
+int
+main ()
+{
+ /* Check simple uses of atomic_compare_and_swap. */
+ {
+ unsigned int x[3] = { 0xDEADBEEFU, 11, 0xDEADBEEFU };
+
+ ASSERT (atomic_compare_and_swap (&x[1], 0, 17) == 11);
+ ASSERT (x[1] == 11);
+
+ ASSERT (atomic_compare_and_swap (&x[1], 4, 11) == 11);
+ ASSERT (x[1] == 11);
+
+ ASSERT (atomic_compare_and_swap (&x[1], 11, 15) == 11);
+ ASSERT (x[1] == 15);
+
+ ASSERT (x[0] == 0xDEADBEEFU);
+ ASSERT (x[2] == 0xDEADBEEFU);
+ }
+
+ /* Check simple uses of atomic_compare_and_swap_ptr. */
+ {
+ uintptr_t v1 = ~(uintptr_t)0 / 3;
+ uintptr_t v2 = ~(uintptr_t)0 / 5 * 4;
+ uintptr_t v3 = ~(uintptr_t)0 / 7 * 3;
+ uintptr_t x[3] = { 0xDEADBEEFU, v1, 0xDEADBEEFU };
+
+ ASSERT (atomic_compare_and_swap_ptr (&x[1], 0, v3) == v1);
+ ASSERT (x[1] == v1);
+
+ ASSERT (atomic_compare_and_swap_ptr (&x[1], 4, v1) == v1);
+ ASSERT (x[1] == v1);
+
+ ASSERT (atomic_compare_and_swap_ptr (&x[1], v1, v2) == v1);
+ ASSERT (x[1] == v2);
+
+ ASSERT (x[0] == 0xDEADBEEFU);
+ ASSERT (x[2] == 0xDEADBEEFU);
+ }
+
+ /* Check atomicity of atomic_compare_and_swap. */
+ {
+ void * (*funcs[2]) (void *) = { int_mutator_thread, ptr_mutator_thread };
+ int f;
+ for (f = 0; f < 2; f++)
+ {
+ void * (*func) (void *) = funcs[f];
+ int i, j;
+ gl_thread_t threads[THREAD_COUNT];
+
+ /* Initialization. */
+ for (i = 0; i < THREAD_COUNT; i++)
+ for (j = 0; j < 5; j++)
+ counter[i][j] = 0;
+
+ /* Spawn the threads. */
+ for (i = 0; i < THREAD_COUNT; i++)
+ threads[i] = gl_thread_create (func, &counter[i][0]);
+
+ /* Wait for the threads to terminate. */
+ for (i = 0; i < THREAD_COUNT; i++)
+ gl_thread_join (threads[i], NULL);
+
+ /* Sum up the work that the threads have done. */
+ unsigned int sum[5];
+ for (j = 0; j < 5; j++)
+ {
+ sum[j] = 0;
+ for (i = 0; i < THREAD_COUNT; i++)
+ sum[j] += counter[i][j];
+ }
+
+ /* If things went atomically, the threads have moved the variable's
+ value through the cycle 0 -> 10 -> 14 -> 17 -> 20 -> 0 ... a large
+ number of times.
+ sum[0] is the number of transitions 0 -> 10.
+ sum[3] is the number of transitions 10 -> 14.
+ sum[1] is the number of transitions 14 -> 17.
+ sum[4] is the number of transitions 17 -> 20.
+ sum[2] is the number of transitions 20 -> 0.
+ Since the cycle started at 0 and ends anywhere (namely, when all
+ threads when through their loop REPEAT_COUNT times), the sequence
+ sum[0], sum[3], sum[1], sum[4], sum[2], sum[0] - 1
+ must be monotonically decreasing.
+
+ If things did not go atomically, the counters don't exhibit this
+ pattern. */
+ printf ("Counters: %u %u %u %u %u\n",
+ sum[0], sum[3], sum[1], sum[4], sum[2]);
+ ASSERT ((sum[0] == sum[3] || sum[0] == sum[3] + 1)
+ && (sum[3] == sum[1] || sum[3] == sum[1] + 1)
+ && (sum[1] == sum[4] || sum[1] == sum[4] + 1)
+ && (sum[4] == sum[2] || sum[4] == sum[2] + 1)
+ && (sum[2] + 1 == sum[0] || sum[2] == sum[0]));
+ }
+ }
+
+ return 0;
+}
+
+#else
+
+/* No multithreading available. */
+
+#include <stdio.h>
+
+int
+main ()
+{
+ fputs ("Skipping test: multithreading not enabled\n", stderr);
+ return 77;
+}
+
+#endif
--
2.7.4
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic