Skip to content

Commit

Permalink
[c11] define mono atomics in terms of standard atomics (dotnet#91489)
Browse files Browse the repository at this point in the history
* [c11] define mono atomics in terms of standard atomics

* include clang headers in offset_tool.py for wasm

* fix clang include path

   on windows libclang.dll is in bin, not lib

* disable stdatomic on android-x86

ATOMIC_LONG_LONG_LOCK_FREE is 1, not 2

* android-x64 also doesn't always have lockfree atomics

* fix typo - disable stdatomic atomics when building with MSVC

* disable stdatomic on armv7

* switch to opt-in

* Apply suggestions from code review

- Fix typos.
- Use `_Atomic(T)` (macro) instead of `_Atomic T` (qualifier).

* add volatile qualifier to casts; fixup whitespace

---------

Co-authored-by: Aaron Robinson <arobins@microsoft.com>
Co-authored-by: Johan Lorensson <lateralusx.github@gmail.com>
  • Loading branch information
3 people authored Sep 7, 2023
1 parent c53e4dc commit bb2cfd5
Show file tree
Hide file tree
Showing 2 changed files with 259 additions and 2 deletions.
4 changes: 3 additions & 1 deletion src/mono/mono/tools/offsets-tool/offsets-tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ def require_emscipten_path (args):
self.target_args += ["-target", args.abi]
else:
require_emscipten_path (args)
self.sys_includes = [args.emscripten_path + "/system/include", args.emscripten_path + "/system/include/libc", args.emscripten_path + "/system/lib/libc/musl/arch/emscripten", args.emscripten_path + "/system/lib/libc/musl/include", args.emscripten_path + "/system/lib/libc/musl/arch/generic"]
clang_path = os.path.dirname(args.libclang)
self.sys_includes = [args.emscripten_path + "/system/include", args.emscripten_path + "/system/include/libc", args.emscripten_path + "/system/lib/libc/musl/arch/emscripten", args.emscripten_path + "/system/lib/libc/musl/include", args.emscripten_path + "/system/lib/libc/musl/arch/generic",
clang_path + "/../lib/clang/16/include"]
self.target = Target ("TARGET_WASM", None, [])
self.target_args += ["-target", args.abi]

Expand Down
257 changes: 256 additions & 1 deletion src/mono/mono/utils/atomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,262 @@ F/MonoDroid( 1568): shared runtime initialization error: Cannot load library: re
Apple targets have historically being problematic, xcode 4.6 would miscompile the intrinsic.
*/

#if defined(HOST_WIN32)
/* Decide if we will use stdatomic.h */
/*
* Generally, we can enable C11 atomics if the header is available and if all the primitive types we
* care about (int, long, void*, long long) are lock-free.
*
* Note that we generally don't want the compiler's locking implementation because it may take a
* global lock, in which case if the atomic is used by both the GC implementation and runtime
* internals we may have deadlocks during GC suspend.
*
* It might be possible to use some Mono specific implementation for specific types (e.g. long long)
* on some platforms if the standard atomics for some type are not lock-free (for example: long
* long). We might be able to use a GC-aware lock, for example.
*
*/
#if defined(_MSC_VER)
/*
* we need two things:
*
* 1. MSVC atomics support is not experimental, or we pass /experimental:c11atomics
*
* 2. We build our C++ code with C++23 or later (otherwise MSVC will complain about including
* stdatomic.h)
*
*/
# undef MONO_USE_STDATOMIC
#elif defined(HOST_IOS) || defined(HOST_OSX) || defined(HOST_WATCHOS) || defined(HOST_TVOS)
# define MONO_USE_STDATOMIC 1
#elif defined(HOST_ANDROID)
/* on Android-x86 ATOMIC_LONG_LONG_LOCK_FREE == 1, not 2 like we want. */
/* on Andriod-x64 ATOMIC_LONG_LOCK_FREE == 1, not 2 */
/* on Android-armv7 ATOMIC_INT_LOCK_FREE == 1, not 2 */
# if defined(HOST_ARM64)
# define MONO_USE_STDATOMIC 1
# endif
#elif defined(HOST_LINUX)
/* FIXME: probably need arch checks */
# define MONO_USE_STDATOMIC 1
#elif defined(HOST_WASI) || defined(HOST_BROWSER)
# define MONO_USE_STDATOMIC 1
#else
# undef MONO_USE_STDATOMIC
#endif

#ifdef MONO_USE_STDATOMIC

#include<stdatomic.h>

static inline gint32
mono_atomic_cas_i32 (volatile gint32 *dest, gint32 exch, gint32 comp)
{
g_static_assert (sizeof (atomic_int) == sizeof (*dest) && ATOMIC_INT_LOCK_FREE == 2);
(void)atomic_compare_exchange_strong ((volatile atomic_int *)dest, &comp, exch);
return comp;
}

static inline gint64
mono_atomic_cas_i64 (volatile gint64 *dest, gint64 exch, gint64 comp)
{
#if SIZEOF_LONG == 8
g_static_assert (sizeof (atomic_long) == sizeof (*dest) && ATOMIC_LONG_LOCK_FREE == 2);
(void)atomic_compare_exchange_strong ((volatile atomic_long *)dest, (long*)&comp, exch);
return comp;
#elif SIZEOF_LONG_LONG == 8
g_static_assert (sizeof (atomic_llong) == sizeof (*dest) && ATOMIC_LLONG_LOCK_FREE == 2);
(void)atomic_compare_exchange_strong ((volatile atomic_llong *)dest, (long long*)&comp, exch);
return comp;
#else
#error gint64 not same size atomic_llong or atomic_long, don't define MONO_USE_STDATOMIC
#endif
}

static inline gpointer
mono_atomic_cas_ptr (volatile gpointer *dest, gpointer exch, gpointer comp)
{
g_static_assert(ATOMIC_POINTER_LOCK_FREE == 2);
(void)atomic_compare_exchange_strong ((volatile _Atomic(gpointer) *)dest, &comp, exch);
return comp;
}

static inline gint32
mono_atomic_fetch_add_i32 (volatile gint32 *dest, gint32 add);
static inline gint64
mono_atomic_fetch_add_i64 (volatile gint64 *dest, gint64 add);

static inline gint32
mono_atomic_add_i32 (volatile gint32 *dest, gint32 add)
{
// mono_atomic_add_ is supposed to return the value that is stored.
// the atomic_add intrinsic returns the previous value instead.
// so we return prev+add which should be the new value
return mono_atomic_fetch_add_i32 (dest, add) + add;
}

static inline gint64
mono_atomic_add_i64 (volatile gint64 *dest, gint64 add)
{
return mono_atomic_fetch_add_i64 (dest, add) + add;
}

static inline gint32
mono_atomic_inc_i32 (volatile gint32 *dest)
{
return mono_atomic_add_i32 (dest, 1);
}

static inline gint64
mono_atomic_inc_i64 (volatile gint64 *dest)
{
return mono_atomic_add_i64 (dest, 1);
}

static inline gint32
mono_atomic_dec_i32 (volatile gint32 *dest)
{
return mono_atomic_add_i32 (dest, -1);
}

static inline gint64
mono_atomic_dec_i64 (volatile gint64 *dest)
{
return mono_atomic_add_i64 (dest, -1);
}

static inline gint32
mono_atomic_xchg_i32 (volatile gint32 *dest, gint32 exch)
{
g_static_assert (sizeof (atomic_int) == sizeof (*dest) && ATOMIC_INT_LOCK_FREE == 2);
return atomic_exchange ((volatile atomic_int *)dest, exch);
}

static inline gint64
mono_atomic_xchg_i64 (volatile gint64 *dest, gint64 exch)
{
#if SIZEOF_LONG == 8
g_static_assert (sizeof (atomic_long) == sizeof (*dest) && ATOMIC_LONG_LOCK_FREE == 2);
return atomic_exchange ((volatile atomic_long *)dest, exch);
#elif SIZEOF_LONG_LONG == 8
g_static_assert (sizeof (atomic_llong) == sizeof (*dest) && ATOMIC_LLONG_LOCK_FREE == 2);
return atomic_exchange ((volatile atomic_llong *)dest, exch);
#else
#error gint64 not same size atomic_llong or atomic_long, don't define MONO_USE_STDATOMIC
#endif
}

static inline gpointer
mono_atomic_xchg_ptr (volatile gpointer *dest, gpointer exch)
{
g_static_assert (ATOMIC_POINTER_LOCK_FREE == 2);
return atomic_exchange ((volatile _Atomic(gpointer) *)dest, exch);
}

static inline gint32
mono_atomic_fetch_add_i32 (volatile gint32 *dest, gint32 add)
{
g_static_assert (sizeof (atomic_int) == sizeof (*dest) && ATOMIC_INT_LOCK_FREE == 2);
return atomic_fetch_add ((volatile atomic_int *)dest, add);
}

static inline gint64
mono_atomic_fetch_add_i64 (volatile gint64 *dest, gint64 add)
{
#if SIZEOF_LONG == 8
g_static_assert (sizeof (atomic_long) == sizeof (*dest) && ATOMIC_LONG_LOCK_FREE == 2);
return atomic_fetch_add ((volatile atomic_long *)dest, add);
#elif SIZEOF_LONG_LONG == 8
g_static_assert (sizeof (atomic_llong) == sizeof (*dest) && ATOMIC_LLONG_LOCK_FREE == 2);
return atomic_fetch_add ((volatile atomic_llong *)dest, add);
#else
#error gint64 not same size atomic_llong or atomic_long, don't define MONO_USE_STDATOMIC
#endif
}

static inline gint8
mono_atomic_load_i8 (volatile gint8 *src)
{
g_static_assert (sizeof (atomic_char) == sizeof (*src) && ATOMIC_CHAR_LOCK_FREE == 2);
return atomic_load ((volatile atomic_char *)src);
}

static inline gint16
mono_atomic_load_i16 (volatile gint16 *src)
{
g_static_assert (sizeof (atomic_short) == sizeof (*src) && ATOMIC_SHORT_LOCK_FREE == 2);
return atomic_load ((volatile atomic_short *)src);
}

static inline gint32 mono_atomic_load_i32 (volatile gint32 *src)
{
g_static_assert (sizeof (atomic_int) == sizeof (*src) && ATOMIC_INT_LOCK_FREE == 2);
return atomic_load ((volatile atomic_int *)src);
}

static inline gint64
mono_atomic_load_i64 (volatile gint64 *src)
{
#if SIZEOF_LONG == 8
g_static_assert (sizeof (atomic_long) == sizeof (*src) && ATOMIC_LONG_LOCK_FREE == 2);
return atomic_load ((volatile atomic_long *)src);
#elif SIZEOF_LONG_LONG == 8
g_static_assert (sizeof (atomic_llong) == sizeof (*src) && ATOMIC_LLONG_LOCK_FREE == 2);
return atomic_load ((volatile atomic_llong *)src);
#else
#error gint64 not same size atomic_llong or atomic_long, don't define MONO_USE_STDATOMIC
#endif
}

static inline gpointer
mono_atomic_load_ptr (volatile gpointer *src)
{
g_static_assert (ATOMIC_POINTER_LOCK_FREE == 2);
return atomic_load ((volatile _Atomic(gpointer) *)src);
}

static inline void
mono_atomic_store_i8 (volatile gint8 *dst, gint8 val)
{
g_static_assert (sizeof (atomic_char) == sizeof (*dst) && ATOMIC_CHAR_LOCK_FREE == 2);
atomic_store ((volatile atomic_char *)dst, val);
}

static inline void
mono_atomic_store_i16 (volatile gint16 *dst, gint16 val)
{
g_static_assert (sizeof (atomic_short) == sizeof (*dst) && ATOMIC_SHORT_LOCK_FREE == 2);
atomic_store ((volatile atomic_short *)dst, val);
}

static inline void
mono_atomic_store_i32 (volatile gint32 *dst, gint32 val)
{
g_static_assert (sizeof (atomic_int) == sizeof (*dst) && ATOMIC_INT_LOCK_FREE == 2);
atomic_store ((atomic_int *)dst, val);
}

static inline void
mono_atomic_store_i64 (volatile gint64 *dst, gint64 val)
{
#if SIZEOF_LONG == 8
g_static_assert (sizeof (atomic_long) == sizeof (*dst) && ATOMIC_LONG_LOCK_FREE == 2);
atomic_store ((volatile atomic_long *)dst, val);
#elif SIZEOF_LONG_LONG == 8
g_static_assert (sizeof (atomic_llong) == sizeof (*dst) && ATOMIC_LLONG_LOCK_FREE == 2);
atomic_store ((volatile atomic_llong *)dst, val);
#else
#error gint64 not same size atomic_llong or atomic_long, don't define MONO_USE_STDATOMIC
#endif
}

static inline void
mono_atomic_store_ptr (volatile gpointer *dst, gpointer val)
{
g_static_assert (ATOMIC_POINTER_LOCK_FREE == 2);
atomic_store ((volatile _Atomic(gpointer) *)dst, val);
}

#elif defined(HOST_WIN32)

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
Expand Down

0 comments on commit bb2cfd5

Please sign in to comment.