]> Savannah Git Hosting - gnulib.git/commitdiff
simple-atomic: New module.
authorBruno Haible <bruno@clisp.org>
Mon, 15 Feb 2021 03:25:38 +0000 (04:25 +0100)
committerBruno Haible <bruno@clisp.org>
Mon, 15 Feb 2021 03:25:38 +0000 (04:25 +0100)
* lib/simple-atomic.h: New file.
* lib/simple-atomic.c: New file, based on lib/asyncsafe-spin.c.
* modules/simple-atomic: New file.

ChangeLog
lib/simple-atomic.c [new file with mode: 0644]
lib/simple-atomic.h [new file with mode: 0644]
modules/simple-atomic [new file with mode: 0644]

index 9b357957c4123feb6a178ea51da020cd39452bde..5d7337c36f1e3155a621cd7866704a01e706fe85 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+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.
diff --git a/lib/simple-atomic.c b/lib/simple-atomic.c
new file mode 100644 (file)
index 0000000..17625fb
--- /dev/null
@@ -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 (file)
index 0000000..5a34e66
--- /dev/null
@@ -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 (file)
index 0000000..5f3f63d
--- /dev/null
@@ -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