diff --git a/Include/pyatomic.h b/Include/pyatomic.h index d4e19e0070b..80bd825bd75 100644 --- a/Include/pyatomic.h +++ b/Include/pyatomic.h @@ -1,12 +1,15 @@ #ifndef Py_LIMITED_API #ifndef Py_ATOMIC_H #define Py_ATOMIC_H -/* XXX: When compilers start offering a stdatomic.h with lock-free - atomic_int and atomic_address types, include that here and rewrite - the atomic operations in terms of it. */ #include "dynamic_annotations.h" +#include "pyconfig.h" + +#if defined(HAVE_STD_ATOMIC) +#include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -20,6 +23,76 @@ extern "C" { * Beware, the implementations here are deep magic. */ +#if defined(HAVE_STD_ATOMIC) + +typedef enum _Py_memory_order { + _Py_memory_order_relaxed = memory_order_relaxed, + _Py_memory_order_acquire = memory_order_acquire, + _Py_memory_order_release = memory_order_release, + _Py_memory_order_acq_rel = memory_order_acq_rel, + _Py_memory_order_seq_cst = memory_order_seq_cst +} _Py_memory_order; + +typedef struct _Py_atomic_address { + _Atomic void *_value; +} _Py_atomic_address; + +typedef struct _Py_atomic_int { + atomic_int _value; +} _Py_atomic_int; + +#define _Py_atomic_signal_fence(/*memory_order*/ ORDER) \ + atomic_signal_fence(ORDER) + +#define _Py_atomic_thread_fence(/*memory_order*/ ORDER) \ + atomic_thread_fence(ORDER) + +#define _Py_atomic_store_explicit(ATOMIC_VAL, NEW_VAL, ORDER) \ + atomic_store_explicit(&(ATOMIC_VAL)->_value, NEW_VAL, ORDER) + +#define _Py_atomic_load_explicit(ATOMIC_VAL, ORDER) \ + atomic_load_explicit(&(ATOMIC_VAL)->_value, ORDER) + +/* Use builtin atomic operations in GCC >= 4.7 */ +#elif defined(HAVE_BUILTIN_ATOMIC) + +typedef enum _Py_memory_order { + _Py_memory_order_relaxed = __ATOMIC_RELAXED, + _Py_memory_order_acquire = __ATOMIC_ACQUIRE, + _Py_memory_order_release = __ATOMIC_RELEASE, + _Py_memory_order_acq_rel = __ATOMIC_ACQ_REL, + _Py_memory_order_seq_cst = __ATOMIC_SEQ_CST +} _Py_memory_order; + +typedef struct _Py_atomic_address { + void *_value; +} _Py_atomic_address; + +typedef struct _Py_atomic_int { + int _value; +} _Py_atomic_int; + +#define _Py_atomic_signal_fence(/*memory_order*/ ORDER) \ + __atomic_signal_fence(ORDER) + +#define _Py_atomic_thread_fence(/*memory_order*/ ORDER) \ + __atomic_thread_fence(ORDER) + +#define _Py_atomic_store_explicit(ATOMIC_VAL, NEW_VAL, ORDER) \ + (assert((ORDER) == __ATOMIC_RELAXED \ + || (ORDER) == __ATOMIC_SEQ_CST \ + || (ORDER) == __ATOMIC_RELEASE), \ + __atomic_store_n(&(ATOMIC_VAL)->_value, NEW_VAL, ORDER)) + +#define _Py_atomic_load_explicit(ATOMIC_VAL, ORDER) \ + (assert((ORDER) == __ATOMIC_RELAXED \ + || (ORDER) == __ATOMIC_SEQ_CST \ + || (ORDER) == __ATOMIC_ACQUIRE \ + || (ORDER) == __ATOMIC_CONSUME), \ + __atomic_load_n(&(ATOMIC_VAL)->_value, ORDER)) + +#else + typedef enum _Py_memory_order { _Py_memory_order_relaxed, _Py_memory_order_acquire, @@ -162,6 +235,7 @@ _Py_ANNOTATE_MEMORY_ORDER(const volatile void *address, _Py_memory_order order) ((ATOMIC_VAL)->_value) #endif /* !gcc x86 */ +#endif /* Standardized shortcuts. */ #define _Py_atomic_store(ATOMIC_VAL, NEW_VAL) \ diff --git a/Misc/ACKS b/Misc/ACKS index eae72c7591c..42ff0108914 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -821,6 +821,7 @@ Ross Light Shawn Ligocki Martin Ligr Gediminas Liktaras +Vitor de Lima Grant Limberg Christopher Lindblad Ulf A. Lindgren @@ -1355,6 +1356,7 @@ Steven Taschuk Amy Taylor Monty Taylor Anatoly Techtonik +Gustavo Temple Mikhail Terekhov Victor TerrĂ³n Richard M. Tew diff --git a/Misc/NEWS b/Misc/NEWS index ef5f9aae806..5f550d2a099 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ Release date: TBA Core and Builtins ----------------- +- Issue #22038: pyatomic.h now uses stdatomic.h or GCC built-in functions for + atomic memory access if available. Patch written by Vitor de Lima and Gustavo + Temple. + - Issue #23048: Fix jumping out of an infinite while loop in the pdb. - Issue #20335: bytes constructor now raises TypeError when encoding or errors diff --git a/configure b/configure index f9af72d5b56..1ba0e9410fc 100755 --- a/configure +++ b/configure @@ -15703,6 +15703,71 @@ $as_echo "#define HAVE_IPA_PURE_CONST_BUG 1" >>confdefs.h esac fi +# Check for stdatomic.h +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdatomic.h" >&5 +$as_echo_n "checking for stdatomic.h... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + + #include + _Atomic int value = ATOMIC_VAR_INIT(1); + int main() { + int loaded_value = atomic_load(&value); + return 0; + } + + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + have_stdatomic_h=yes +else + have_stdatomic_h=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_stdatomic_h" >&5 +$as_echo "$have_stdatomic_h" >&6; } + +if test "$have_stdatomic_h" = yes; then + +$as_echo "#define HAVE_STD_ATOMIC 1" >>confdefs.h + +fi + +# Check for GCC >= 4.7 __atomic builtins +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GCC >= 4.7 __atomic builtins" >&5 +$as_echo_n "checking for GCC >= 4.7 __atomic builtins... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + + volatile int val = 1; + int main() { + __atomic_load_n(&val, __ATOMIC_SEQ_CST); + return 0; + } + + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + have_builtin_atomic=yes +else + have_builtin_atomic=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_builtin_atomic" >&5 +$as_echo "$have_builtin_atomic" >&6; } + +if test "$have_builtin_atomic" = yes; then + +$as_echo "#define HAVE_BUILTIN_ATOMIC 1" >>confdefs.h + +fi + # ensurepip option { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ensurepip" >&5 $as_echo_n "checking for ensurepip... " >&6; } diff --git a/configure.ac b/configure.ac index c7504a27dd0..da096a7c883 100644 --- a/configure.ac +++ b/configure.ac @@ -4884,6 +4884,45 @@ if test "$have_gcc_asm_for_x87" = yes; then esac fi +# Check for stdatomic.h +AC_MSG_CHECKING(for stdatomic.h) +AC_LINK_IFELSE( +[ + AC_LANG_SOURCE([[ + #include + _Atomic int value = ATOMIC_VAR_INIT(1); + int main() { + int loaded_value = atomic_load(&value); + return 0; + } + ]]) +],[have_stdatomic_h=yes],[have_stdatomic_h=no]) + +AC_MSG_RESULT($have_stdatomic_h) + +if test "$have_stdatomic_h" = yes; then + AC_DEFINE(HAVE_STD_ATOMIC, 1, [Has stdatomic.h]) +fi + +# Check for GCC >= 4.7 __atomic builtins +AC_MSG_CHECKING(for GCC >= 4.7 __atomic builtins) +AC_LINK_IFELSE( +[ + AC_LANG_SOURCE([[ + volatile int val = 1; + int main() { + __atomic_load_n(&val, __ATOMIC_SEQ_CST); + return 0; + } + ]]) +],[have_builtin_atomic=yes],[have_builtin_atomic=no]) + +AC_MSG_RESULT($have_builtin_atomic) + +if test "$have_builtin_atomic" = yes; then + AC_DEFINE(HAVE_BUILTIN_ATOMIC, 1, [Has builtin atomics]) +fi + # ensurepip option AC_MSG_CHECKING(for ensurepip) AC_ARG_WITH(ensurepip, diff --git a/pyconfig.h.in b/pyconfig.h.in index 8107c3af748..10d5f4a5f4b 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -101,6 +101,9 @@ /* Define if `unsetenv` does not return an int. */ #undef HAVE_BROKEN_UNSETENV +/* Has builtin atomics */ +#undef HAVE_BUILTIN_ATOMIC + /* Define this if you have the type _Bool. */ #undef HAVE_C99_BOOL @@ -877,6 +880,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H +/* Has stdatomic.h */ +#undef HAVE_STD_ATOMIC + /* Define to 1 if you have the `strdup' function. */ #undef HAVE_STRDUP