Skip to content

bpo-39465: Add pycore_atomic_funcs.h internal header #20766

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Include/internal/pycore_atomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ extern "C" {
#include "dynamic_annotations.h" /* _Py_ANNOTATE_MEMORY_ORDER */
#include "pyconfig.h"

#if defined(HAVE_STD_ATOMIC)
#include <stdatomic.h>
#ifdef HAVE_STD_ATOMIC
# include <stdatomic.h>
#endif


Expand Down Expand Up @@ -62,7 +62,7 @@ typedef struct _Py_atomic_int {
#define _Py_atomic_load_explicit(ATOMIC_VAL, ORDER) \
atomic_load_explicit(&((ATOMIC_VAL)->_value), ORDER)

/* Use builtin atomic operations in GCC >= 4.7 */
// Use builtin atomic operations in GCC >= 4.7 and clang
#elif defined(HAVE_BUILTIN_ATOMIC)

typedef enum _Py_memory_order {
Expand Down
94 changes: 94 additions & 0 deletions Include/internal/pycore_atomic_funcs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/* Atomic functions: similar to pycore_atomic.h, but don't need
to declare variables as atomic.

Py_ssize_t type:

* value = _Py_atomic_size_get(&var)
* _Py_atomic_size_set(&var, value)

Use sequentially-consistent ordering (__ATOMIC_SEQ_CST memory order):
enforce total ordering with all other atomic functions.
*/
#ifndef Py_ATOMIC_FUNC_H
#define Py_ATOMIC_FUNC_H
#ifdef __cplusplus
extern "C" {
#endif

#ifndef Py_BUILD_CORE
# error "this header requires Py_BUILD_CORE define"
#endif

#if defined(_MSC_VER)
# include <intrin.h> // _InterlockedExchange()
#endif


// Use builtin atomic operations in GCC >= 4.7 and clang
#ifdef HAVE_BUILTIN_ATOMIC

static inline Py_ssize_t _Py_atomic_size_get(Py_ssize_t *var)
{
return __atomic_load_n(var, __ATOMIC_SEQ_CST);
}

static inline void _Py_atomic_size_set(Py_ssize_t *var, Py_ssize_t value)
{
__atomic_store_n(var, value, __ATOMIC_SEQ_CST);
}

#elif defined(_MSC_VER)

static inline Py_ssize_t _Py_atomic_size_get(Py_ssize_t *var)
{
#if SIZEOF_VOID_P == 8
Py_BUILD_ASSERT(sizeof(__int64) == sizeof(*var));
volatile __int64 *volatile_var = (volatile __int64 *)var;
__int64 old;
do {
old = *volatile_var;
} while(_InterlockedCompareExchange64(volatile_var, old, old) != old);
#else
Py_BUILD_ASSERT(sizeof(long) == sizeof(*var));
volatile long *volatile_var = (volatile long *)var;
long old;
do {
old = *volatile_var;
} while(_InterlockedCompareExchange(volatile_var, old, old) != old);
#endif
return old;
}

static inline void _Py_atomic_size_set(Py_ssize_t *var, Py_ssize_t value)
{
#if SIZEOF_VOID_P == 8
Py_BUILD_ASSERT(sizeof(__int64) == sizeof(*var));
volatile __int64 *volatile_var = (volatile __int64 *)var;
_InterlockedExchange64(volatile_var, value);
#else
Py_BUILD_ASSERT(sizeof(long) == sizeof(*var));
volatile long *volatile_var = (volatile long *)var;
_InterlockedExchange(volatile_var, value);
#endif
}

#else
// Fallback implementation using volatile

static inline Py_ssize_t _Py_atomic_size_get(Py_ssize_t *var)
{
volatile Py_ssize_t *volatile_var = (volatile Py_ssize_t *)var;
return *volatile_var;
}

static inline void _Py_atomic_size_set(Py_ssize_t *var, Py_ssize_t value)
{
volatile Py_ssize_t *volatile_var = (volatile Py_ssize_t *)var;
*volatile_var = value;
}
#endif

#ifdef __cplusplus
}
#endif
#endif /* Py_ATOMIC_FUNC_H */
1 change: 1 addition & 0 deletions Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,7 @@ PYTHON_HEADERS= \
$(srcdir)/Include/internal/pycore_abstract.h \
$(srcdir)/Include/internal/pycore_accu.h \
$(srcdir)/Include/internal/pycore_atomic.h \
$(srcdir)/Include/internal/pycore_atomic_funcs.h \
$(srcdir)/Include/internal/pycore_bitutils.h \
$(srcdir)/Include/internal/pycore_bytes_methods.h \
$(srcdir)/Include/internal/pycore_call.h \
Expand Down
13 changes: 13 additions & 0 deletions Modules/_testinternalcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#define PY_SSIZE_T_CLEAN

#include "Python.h"
#include "pycore_atomic_funcs.h" // _Py_atomic_int_get()
#include "pycore_bitutils.h" // _Py_bswap32()
#include "pycore_gc.h" // PyGC_Head
#include "pycore_hashtable.h" // _Py_hashtable_new()
Expand Down Expand Up @@ -267,6 +268,17 @@ test_set_config(PyObject *Py_UNUSED(self), PyObject *dict)
}


static PyObject*
test_atomic_funcs(PyObject *self, PyObject *Py_UNUSED(args))
{
// Test _Py_atomic_size_get() and _Py_atomic_size_set()
Py_ssize_t var = 1;
_Py_atomic_size_set(&var, 2);
assert(_Py_atomic_size_get(&var) == 2);
Py_RETURN_NONE;
}


static PyMethodDef TestMethods[] = {
{"get_configs", get_configs, METH_NOARGS},
{"get_recursion_depth", get_recursion_depth, METH_NOARGS},
Expand All @@ -276,6 +288,7 @@ static PyMethodDef TestMethods[] = {
{"test_hashtable", test_hashtable, METH_NOARGS},
{"get_config", test_get_config, METH_NOARGS},
{"set_config", test_set_config, METH_O},
{"test_atomic_funcs", test_atomic_funcs, METH_NOARGS},
{NULL, NULL} /* sentinel */
};

Expand Down
1 change: 1 addition & 0 deletions PCbuild/pythoncore.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
<ClInclude Include="..\Include\internal\pycore_abstract.h" />
<ClInclude Include="..\Include\internal\pycore_accu.h" />
<ClInclude Include="..\Include\internal\pycore_atomic.h" />
<ClInclude Include="..\Include\internal\pycore_atomic_funcs.h" />
<ClInclude Include="..\Include\internal\pycore_bitutils.h" />
<ClInclude Include="..\Include\internal\pycore_bytes_methods.h" />
<ClInclude Include="..\Include\internal\pycore_call.h" />
Expand Down
3 changes: 3 additions & 0 deletions PCbuild/pythoncore.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,9 @@
<ClInclude Include="..\Include\internal\pycore_atomic.h">
<Filter>Include\internal</Filter>
</ClInclude>
<ClInclude Include="..\Include\internal\pycore_atomic_funcs.h">
<Filter>Include</Filter>
</ClInclude>
<ClInclude Include="..\Include\internal\pycore_bitutils.h">
<Filter>Include\internal</Filter>
</ClInclude>
Expand Down
12 changes: 7 additions & 5 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -15429,6 +15429,7 @@ _ACEOF

fi


EXT_SUFFIX=.${SOABI}${SHLIB_SUFFIX}

{ $as_echo "$as_me:${as_lineno-$LINENO}: checking LDVERSION" >&5
Expand Down Expand Up @@ -17095,16 +17096,17 @@ $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; }
# Check for GCC >= 4.7 and clang __atomic builtin functions
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for builtin __atomic_load_n and __atomic_store_n functions" >&5
$as_echo_n "checking for builtin __atomic_load_n and __atomic_store_n functions... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */


volatile int val = 1;
int val;
int main() {
__atomic_load_n(&val, __ATOMIC_SEQ_CST);
__atomic_store_n(&val, 1, __ATOMIC_SEQ_CST);
(void)__atomic_load_n(&val, __ATOMIC_SEQ_CST);
return 0;
}

Expand Down
11 changes: 6 additions & 5 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -5586,14 +5586,15 @@ if test "$have_stdatomic_h" = yes; then
[Has stdatomic.h with atomic_int and atomic_uintptr_t])
fi

# Check for GCC >= 4.7 __atomic builtins
AC_MSG_CHECKING(for GCC >= 4.7 __atomic builtins)
# Check for GCC >= 4.7 and clang __atomic builtin functions
AC_MSG_CHECKING(for builtin __atomic_load_n and __atomic_store_n functions)
AC_LINK_IFELSE(
[
AC_LANG_SOURCE([[
volatile int val = 1;
int val;
int main() {
__atomic_load_n(&val, __ATOMIC_SEQ_CST);
__atomic_store_n(&val, 1, __ATOMIC_SEQ_CST);
(void)__atomic_load_n(&val, __ATOMIC_SEQ_CST);
return 0;
}
]])
Expand All @@ -5602,7 +5603,7 @@ AC_LINK_IFELSE(
AC_MSG_RESULT($have_builtin_atomic)

if test "$have_builtin_atomic" = yes; then
AC_DEFINE(HAVE_BUILTIN_ATOMIC, 1, [Has builtin atomics])
AC_DEFINE(HAVE_BUILTIN_ATOMIC, 1, [Has builtin __atomic_load_n() and __atomic_store_n() functions])
fi

# ensurepip option
Expand Down
8 changes: 4 additions & 4 deletions pyconfig.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
/* Define if `unsetenv` does not return an int. */
#undef HAVE_BROKEN_UNSETENV

/* Has builtin atomics */
/* Has builtin __atomic_load_n() and __atomic_store_n() functions */
#undef HAVE_BUILTIN_ATOMIC

/* Define to 1 if you have the 'chflags' function. */
Expand Down Expand Up @@ -287,6 +287,9 @@
/* Define to 1 if you have the `dup3' function. */
#undef HAVE_DUP3

/* Define if you have the '_dyld_shared_cache_contains_path' function. */
#undef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH

/* Defined when any dynamic module loading is enabled. */
#undef HAVE_DYNAMIC_LOADING

Expand Down Expand Up @@ -787,9 +790,6 @@
/* Define if you have the 'prlimit' functions. */
#undef HAVE_PRLIMIT

/* Define if you have the '_dyld_shared_cache_contains_path' function. */
#undef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH

/* Define to 1 if you have the <process.h> header file. */
#undef HAVE_PROCESS_H

Expand Down