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

[Mono] Consider switching to C11 #90404

Closed
lambdageek opened this issue Aug 11, 2023 · 14 comments
Closed

[Mono] Consider switching to C11 #90404

lambdageek opened this issue Aug 11, 2023 · 14 comments
Labels
area-Build-mono enhancement Product code improvement that does NOT require public API changes/additions runtime-mono specific to the Mono runtime
Milestone

Comments

@lambdageek
Copy link
Member

lambdageek commented Aug 11, 2023

Currently Mono is supposed to use C99. As a result, we're relying on platform or compiler intrinsics for some functionality that has been added to the standard since then such as stdatomic.h.

We should consider upgrading Mono to a more recent language standard.

This came up as part of #88279, for example.

Some concrete things we could take advantage of:

  • stdatomic.h for atomic reads/writes, CAS, etc
  • _Alignas
  • static assertions
  • _Noreturn
  • possibly thread locals (although the JIT likes to take advantage of understanding TLS layout, so we need to ensure we don't take a perf hit)

Some reasons we might not want to do it:

  • Anecdotally, I've heard that MSVC C11 support has some issues - we will need to understand the specific pitfalls to avoid so we don't break mono-aot-cross running on Win32 and also Mono on non-Windows Win32 platforms
@lambdageek lambdageek added this to the 9.0.0 milestone Aug 11, 2023
@lambdageek
Copy link
Member Author

@lambdageek lambdageek added enhancement Product code improvement that does NOT require public API changes/additions runtime-mono specific to the Mono runtime labels Aug 11, 2023
@lambdageek lambdageek changed the title [Mono] switch to C11 [Mono] Consider switching to C11 Aug 11, 2023
@AaronRobinsonMSFT
Copy link
Member

/cc @jkoritzinsky

@MichalPetryka
Copy link
Contributor

  • I've heard that MSVC C11 support has some issues

MSVC doesn't support C11, only C17.

@AaronRobinsonMSFT
Copy link
Member

  • I've heard that MSVC C11 support has some issues

MSVC doesn't support C11, only C17.

Yes it does - https://devblogs.microsoft.com/cppblog/c11-and-c17-standard-support-arriving-in-msvc/. There is no support for optional features and there are bugs around atomics, but it has support.

@am11
Copy link
Member

am11 commented Aug 11, 2023

In libraries, we are using C11 for couple of years:

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

and e.g. its 'Generic Selection' feature.:
#define LIMIT_MAX(T) _Generic(((T)0), \

VCR handles it just fine. Only these features are not supported: https://devblogs.microsoft.com/cppblog/c11-and-c17-standard-support-arriving-in-msvc/#whats-not which we don't use in libs.

@AaronRobinsonMSFT
Copy link
Member

The atomics in C11 is recent and experimental, announced at https://devblogs.microsoft.com/cppblog/c11-atomics-in-visual-studio-2022-version-17-5-preview-2/.

@barcharcraz
Copy link

Hi, I'm on the C++ libraries team and I'm the one who wrote the library support for c11 atomics and threads. We actually thought of providing gcc style atomic intrinsics that could work in c99 mode, but because of how our C++ atomics are designed MSVC really, really needs that new _Atomic keyword.

C11 threads will probably work fine in C99 mode, with the possible exception of the new _Thread_local/thread_local keyword. The TLS layout for implicit TLS is the same as __declspec(thread) or c++ thread_local. For the tss_* family of functions the implementation is new, and not shared with the Tls* or Fls* functions, because tss_* needs to support destructors with slightly different semantics than fls. The layout used here is very basic, with an array for global data and a per-thread array for per-thread data that's allocated when you use a relevant function for the first time on a particular thread.

All the C11 threads stuff is new, and distinct from C++ threads. The ABI is also somewhat better protected than for C++ threads, so layouts and implementation are subject to change (in ABI compatible ways).

@barcharcraz
Copy link

oh, I should say that C11 threads is just implemented in terms of windows threading functions (CreateThread and friends), and its threads are "normal" threads and don't have any extra state or anything. They do briefly allocate some memory in order to thunk between cdecl and stdcall, but that's it.

Note that this does mean thrd_t thread handles work a little differently for our C11 threads than they tend to in pthreads based implementations. In particular you can't detach yourself by writing thrd_detach(thrd_current()).

@tgani-msft
Copy link
Contributor

The atomics in C11 is recent and experimental, announced at https://devblogs.microsoft.com/cppblog/c11-atomics-in-visual-studio-2022-version-17-5-preview-2/.

It's currently being actively worked on (both front-end and back-end teams) - we can talk about .NET taking a dependency on this support in our sync meetings.

@lambdageek
Copy link
Member Author

Thinking about _Atomic a little bit more, I think once we bump to C11, the guidance in Mono will be not to use _Atomic(T) or the _Atomic qualifier. There's no good compile-time way to know if that will be lockfree (sadly static_assert (atomic_is_lock_free (&someAtomicStruct)) cannot work). In general we cannot use locks inside Mono (in the runtime itself, I mean - pinvokes are fine) without making the coop GC thread state machine aware of them - otherwise we will deadlock if two threads try to take the lock at the same time that a GC is started

/* Suppose we're using hybrid suspend and T1 is in GC Unsafe and T2 is
* GC Safe. T1 will be coop suspended, and T2 will be async suspended.
* Suppose T1 is in RUNNING, and T2 just changed from RUNNING to
* BLOCKING and it is in trace_state_change to record this fact.
*
* suspend initiator: switches T1 to ASYNC_SUSPEND_REQUESTED
* suspend initiator: switches T2 to BLOCKING_SUSPEND_REQUESTED and sends a suspend signal
* T1: calls mono_threads_transition_state_poll (),
* T1: switches to SELF_SUSPENDED and starts trace_state_change ()
* T2: is still in checked_build_thread_transition for the RUNNING->BLOCKING transition and calls backtrace ()
* T2: suspend signal lands while T2 is in backtrace() holding a lock; T2 switches to BLOCKING_ASYNC_SUSPENDED () and waits for resume
* T1: calls backtrace (), waits for the lock ()
* suspend initiator: waiting for T1 to suspend.
*
* At this point we're deadlocked.

So I think at best we will end up redefining mono_atomic_cas_TYPE, mono_atomic_compare_exchange_TYPE, etc in terms of the C11 atomic ops (and adding something along the lines of static_assert ((sizeof(int32_t) == sizeof(atomic_int) && ATOMIC_INT_LOCK_FREE == 2) || (sizeof(int64_t) == sizeof(atomic_llong) && ATOMIC_LLONG_LOCK_FREE == 2)) somewhere in our headers)

lambdageek added a commit that referenced this issue Sep 1, 2023
Change the mono build to require C11 (with gnu extensions on gcc/clang platforms).
* Change `g_static_assert` to be `_Static_assert` or `static_assert` as apropriate.
* Change `_DN_STATIC_ASSERT` to be `static_assert`
* Add static asserts in `jiterp.c` when it casts between `T*` and `atomic_T*`
* Add C11 guidance to the mono coding guide doc

Contributes to #90404 

---

* Bump mono to -std=gnu99; use static_assert

* don't fall back to runtime checks for g_static_assert

* fix static assert that wasn't a constant expression

* use static_assert in shared containers

* bump C standard in offsets-tool.py

* use _Static_assert before C23

   Dont' include assert.h in glib.h because some of our older 3P code includes assert.h on its own and there are conflicts

* use CMAKE_C_STANDARD and related properties

* jiterp: static_assert that atomic ops are (less likely) to go wrong

   Not every C implementation guarantees that atomic operations on arbitrary types are lock free.  So for example, casting between atomic_ushort* and uint16_t* might not actually be ok to do.  We can't assert that they're inter-castable, but at least assert that they're the same size and that atomic_ushort is always lock-free. There might still be restrictions (for example atomic_ushort might have to be aligned differently) but this should at least catch obvious data corruption.

* Add C11 guidance to the Mono coding guide

* jiterp: long is 32-bits on wasm; use llong
@lambdageek
Copy link
Member Author

This initial work has been completed.

Mono now requires a C11 compiler - and we use static_assert in some places.

Some platforms now opt into using C11 atomics for the mono_atomic_XYZ functions.

We are not using c11 atomics for MSVC builds yet, but we could switching. Created #91748 for follow-up work.

We are using "strong" not "weak" atomics. Created #91747 for follow-up work.

@lambdageek lambdageek reopened this Sep 8, 2023
@lambdageek
Copy link
Member Author

We're reverting the atomics PR because of some build failures #91812

A proper fix is in progress in #91808 but it's failing due to some weird clang problem on our linux-arm64 crossrootfs

@lewing
Copy link
Member

lewing commented Jul 29, 2024

@lambdageek what is the status of this?

@lambdageek
Copy link
Member Author

We're done. #91808 was good.

@github-actions github-actions bot locked and limited conversation to collaborators Aug 30, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Build-mono enhancement Product code improvement that does NOT require public API changes/additions runtime-mono specific to the Mono runtime
Projects
None yet
Development

No branches or pull requests

7 participants