Skip to content
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
8 changes: 4 additions & 4 deletions libcxx/include/__atomic/contention_t.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
// instead.
#if defined(_LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE)

# ifdef __linux__
# if defined(__linux__) || defined(__Fuchsia__)
using __cxx_contention_t _LIBCPP_NODEBUG = int32_t;
# elif defined(__APPLE__)
using __cxx_contention_t _LIBCPP_NODEBUG = int64_t;
Expand All @@ -37,15 +37,15 @@ using __cxx_contention_t _LIBCPP_NODEBUG = int32_t;
using __cxx_contention_t _LIBCPP_NODEBUG = int64_t;
# else
using __cxx_contention_t _LIBCPP_NODEBUG = int64_t;
# endif // __linux__
# endif // __linux__ || __Fuchsia__

#else // _LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE

# if defined(__linux__) || (defined(_AIX) && !defined(__64BIT__))
# if defined(__linux__) || defined(__Fuchsia__) || (defined(_AIX) && !defined(__64BIT__))
using __cxx_contention_t _LIBCPP_NODEBUG = int32_t;
# else
using __cxx_contention_t _LIBCPP_NODEBUG = int64_t;
# endif // __linux__ || (_AIX && !__64BIT__)
# endif // __linux__ || __Fuchsia__ || (_AIX && !__64BIT__)

#endif // _LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE

Expand Down
4 changes: 2 additions & 2 deletions libcxx/include/__cxx03/__atomic/contention_t.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@

_LIBCPP_BEGIN_NAMESPACE_STD

#if defined(__linux__) || (defined(_AIX) && !defined(__64BIT__))
#if defined(__linux__) || defined(__Fuchsia__) || (defined(_AIX) && !defined(__64BIT__))
using __cxx_contention_t = int32_t;
#else
using __cxx_contention_t = int64_t;
#endif // __linux__ || (_AIX && !__64BIT__)
#endif // __linux__ || __Fuchsia__ || (_AIX && !__64BIT__)

using __cxx_atomic_contention_t = __cxx_atomic_impl<__cxx_contention_t>;

Expand Down
35 changes: 35 additions & 0 deletions libcxx/src/atomic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
# include <memory>
# include <windows.h>

#elif defined(__Fuchsia__)

# include <zircon/syscalls.h>

#else // <- Add other operating systems here

// Baseline needs no new headers
Expand Down Expand Up @@ -254,6 +258,37 @@ static void __platform_wake_by_address(void const* __ptr, bool __notify_one) {
}
}

#elif defined(__Fuchsia__)

template <std::size_t _Size>
static inline zx_futex_t const* __get_zx_futex(void const* __ptr) {
static_assert(_Size == sizeof(zx_futex_t), "Can only wait/wake on zx_futex_t-compatible value");

// Implicitly link against the vDSO system call ABI without requiring the
// final link to specify -lzircon explicitly when statically linking libc++.
# pragma comment(lib, "zircon")

return reinterpret_cast<zx_futex_t const*>(__ptr);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just wanted to bring you attention of
libcxx/include/__atomic/atomic_waitable_traits.h

and here is the design intent of how things work.

atomic_waitable_traits.h defines a concept __has_native_atomic_wait that decides which types can directly call the platform futex wait, and which types needs to go through the global contention table proxy for platform wait.

And there are two ABI modes.

For the default ABI, __has_native_atomic_wait basically says only the __cxx_contention_t satisfies, which is int32_t in your case, can directly go to platform futex wait. I assume here zx_futex_t is exactly int32_t otherwise we might have UB

For another ABI which has _LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE defined, which is controlled by the platform owner. in this case, i believe your platform does not enable it. but say, one day you decide to enable this ABI, the way it works is that. you can define __has_native_atomic_wait the way you want, the default concept in this mode is: any trivially copyable type with unique value representations, whose sizes matches __cxx_contention_t. in this case, __ptr and __val can be pointers to a wide range of types, e.g. a uint32_t, struct S { int32_t i;}; etc... In which case, I am not sure how reinterpret_cast<zx_futex_t const*> can be well defined. (anyway you don't need to worry about this if you don't plan to enable this ABI)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do have _LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE enabled by dint of the fact that we always use ABI version 2. I'd be happy for it to be unconditionally enabled for Fuchsia, but I didn't see any precedent for that setting being controlled by anything but _LIBCPP_ABI_VERSION. In what sense is it controlled by the platform owner?

AFAICT there is no change to __atomic/atomic_waitable_traits.h required because __cxx_contention_t is already always int32_t (as my changes here to __atomic/contention_t.h) so the default case uses sizeof(__cxx_contention_t). For the same reason, the #ifdef __linux__ branch there seems superfluous.

I'm not sure what the concern is about the reinterpret_cast. zx_futex_t is just an integer type with the same size as int32_t, so it's layout compatible with any integer type or struct of integer type of that size.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In what sense is it controlled by the platform owner?

Vendors can configure those individual ABI macros in the __config_site. See the doc here
https://libcxx.llvm.org/ABIGuarantees.html#abi-flags

And yes if you opt in _LIBCPP_ABI_VERSION to 2, you get all of these individual macros.

AFAICT there is no change to __atomic/atomic_waitable_traits.h required because __cxx_contention_t is already always int32_t

I think in your case, yes no action required. I mentioned that because some platforms can support multiple sizes for the platform wait. For example, Apple's ulock wait support both 4 bytes and 8 bytes wait.

I'm not sure what the concern is about the reinterpret_cast

I think it is kind of "it works in practice" but I think it is a language UB if you reinterpret_cast from memory location when there is actually no object of that type has started life time (implicitly or explicitly)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, so by "platform owner" you meant "person who sets the libc++ build parameters", not the owner of a particular platform's platform-specific code in libc++ sources. These are not always the same, and for Fuchsia it varies where I am both of those people and where I am only the latter. :-)

There is no language-based access here. The cast is just getting the pointer value to a system call, so regardless of the complexities of trivial object lifetime that might actually apply here, it's really outside the scope of those language rules anyway.

Thanks for the explanations! I think this change is good to go as is now. It just needs the approval from @philnik777 to be mergeable.

}

template <std::size_t _Size, class MaybeTimeout>
static void __platform_wait_on_address(void const* __ptr, void const* __val, MaybeTimeout maybe_timeout_ns) {
zx_futex_t val;
std::memcpy(&val, __val, _Size);
zx_instant_mono_t deadline;
if constexpr (is_same_v<MaybeTimeout, NoTimeout>) {
deadline = ZX_TIME_INFINITE;
} else {
deadline = _zx_deadline_after(maybe_timeout_ns);
}
_zx_futex_wait(__get_zx_futex<_Size>(__ptr), val, ZX_HANDLE_INVALID, deadline);
}

template <std::size_t _Size>
static void __platform_wake_by_address(void const* __ptr, bool __notify_one) {
_zx_futex_wake(__get_zx_futex<_Size>(__ptr), __notify_one ? 1 : UINT32_MAX);
}

#else // <- Add other operating systems here

// Baseline is just a timed backoff
Expand Down
Loading