>From 0a6dc3028b79ce4f1051bbd3e9d458372d385690 Mon Sep 17 00:00:00 2001 From: Bruno Haible 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 + 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 + 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 . */ + +/* Written by Bruno Haible , 2021. */ + +#include + +/* Specification. */ +#include "simple-atomic.h" + +#if defined _WIN32 && ! defined __CYGWIN__ +/* Native Windows. */ + +# include + +void +memory_barrier (void) +{ + /* MemoryBarrier + */ + MemoryBarrier (); +} + +unsigned int +atomic_compare_and_swap (unsigned int volatile *vp, + unsigned int cmp, + unsigned int newval) +{ + /* 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 + */ + 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 (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: + */ + +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 . */ + +/* Written by Bruno Haible , 2021. */ + +#ifndef _SIMPLE_ATOMIC_H +#define _SIMPLE_ATOMIC_H + +#include + +#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