[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