Skip to content
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

[c11] define mono atomics in terms of standard atomics #91489

Merged
merged 17 commits into from
Sep 7, 2023
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
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>
lambdageek marked this conversation as resolved.
Show resolved Hide resolved

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
Loading