Skip to content

Commit 2021b1b

Browse files
committed
NativeAOT sample links correctly now
1 parent 37d6917 commit 2021b1b

File tree

8 files changed

+1020
-6
lines changed

8 files changed

+1020
-6
lines changed
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include <__thread/timed_backoff_policy.h>
10+
#include <atomic>
11+
#include <climits>
12+
#include <functional>
13+
#include <thread>
14+
15+
#include "include/apple_availability.h"
16+
17+
#ifdef __linux__
18+
19+
# include <linux/futex.h>
20+
# include <sys/syscall.h>
21+
# include <unistd.h>
22+
23+
// libc++ uses SYS_futex as a universal syscall name. However, on 32 bit architectures
24+
// with a 64 bit time_t, we need to specify SYS_futex_time64.
25+
# if !defined(SYS_futex) && defined(SYS_futex_time64)
26+
# define SYS_futex SYS_futex_time64
27+
# endif
28+
# define _LIBCPP_FUTEX(...) syscall(SYS_futex, __VA_ARGS__)
29+
30+
#elif defined(__FreeBSD__)
31+
32+
# include <sys/types.h>
33+
# include <sys/umtx.h>
34+
35+
# define _LIBCPP_FUTEX(...) syscall(SYS_futex, __VA_ARGS__)
36+
37+
#elif defined(__OpenBSD__)
38+
39+
# include <sys/futex.h>
40+
41+
// OpenBSD has no indirect syscalls
42+
# define _LIBCPP_FUTEX(...) futex(__VA_ARGS__)
43+
44+
#else // <- Add other operating systems here
45+
46+
// Baseline needs no new headers
47+
48+
# define _LIBCPP_FUTEX(...) syscall(SYS_futex, __VA_ARGS__)
49+
50+
#endif
51+
52+
_LIBCPP_BEGIN_NAMESPACE_STD
53+
54+
#ifdef __linux__
55+
56+
static void
57+
__libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr, __cxx_contention_t __val) {
58+
static constexpr timespec __timeout = {2, 0};
59+
_LIBCPP_FUTEX(__ptr, FUTEX_WAIT_PRIVATE, __val, &__timeout, 0, 0);
60+
}
61+
62+
static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr, bool __notify_one) {
63+
_LIBCPP_FUTEX(__ptr, FUTEX_WAKE_PRIVATE, __notify_one ? 1 : INT_MAX, 0, 0, 0);
64+
}
65+
66+
#elif defined(__APPLE__) && defined(_LIBCPP_USE_ULOCK)
67+
68+
extern "C" int __ulock_wait(
69+
uint32_t operation, void* addr, uint64_t value, uint32_t timeout); /* timeout is specified in microseconds */
70+
extern "C" int __ulock_wake(uint32_t operation, void* addr, uint64_t wake_value);
71+
72+
// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/sys/ulock.h#L82
73+
# define UL_COMPARE_AND_WAIT64 5
74+
# define ULF_WAKE_ALL 0x00000100
75+
76+
static void
77+
__libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr, __cxx_contention_t __val) {
78+
static_assert(sizeof(__cxx_atomic_contention_t) == 8, "Waiting on 8 bytes value");
79+
__ulock_wait(UL_COMPARE_AND_WAIT64, const_cast<__cxx_atomic_contention_t*>(__ptr), __val, 0);
80+
}
81+
82+
static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr, bool __notify_one) {
83+
static_assert(sizeof(__cxx_atomic_contention_t) == 8, "Waking up on 8 bytes value");
84+
__ulock_wake(
85+
UL_COMPARE_AND_WAIT64 | (__notify_one ? 0 : ULF_WAKE_ALL), const_cast<__cxx_atomic_contention_t*>(__ptr), 0);
86+
}
87+
88+
#elif defined(__FreeBSD__) && __SIZEOF_LONG__ == 8
89+
/*
90+
* Since __cxx_contention_t is int64_t even on 32bit FreeBSD
91+
* platforms, we have to use umtx ops that work on the long type, and
92+
* limit its use to architectures where long and int64_t are synonyms.
93+
*/
94+
95+
static void
96+
__libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr, __cxx_contention_t __val) {
97+
_umtx_op(const_cast<__cxx_atomic_contention_t*>(__ptr), UMTX_OP_WAIT, __val, nullptr, nullptr);
98+
}
99+
100+
static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr, bool __notify_one) {
101+
_umtx_op(const_cast<__cxx_atomic_contention_t*>(__ptr), UMTX_OP_WAKE, __notify_one ? 1 : INT_MAX, nullptr, nullptr);
102+
}
103+
104+
#else // <- Add other operating systems here
105+
106+
// Baseline is just a timed backoff
107+
108+
static void
109+
__libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr, __cxx_contention_t __val) {
110+
__libcpp_thread_poll_with_backoff(
111+
[=]() -> bool { return !__cxx_nonatomic_compare_equal(__cxx_atomic_load(__ptr, memory_order_relaxed), __val); },
112+
__libcpp_timed_backoff_policy());
113+
}
114+
115+
static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile*, bool) {}
116+
117+
#endif // __linux__
118+
119+
static constexpr size_t __libcpp_contention_table_size = (1 << 8); /* < there's no magic in this number */
120+
121+
struct alignas(64) /* aim to avoid false sharing */ __libcpp_contention_table_entry {
122+
__cxx_atomic_contention_t __contention_state;
123+
__cxx_atomic_contention_t __platform_state;
124+
inline constexpr __libcpp_contention_table_entry() : __contention_state(0), __platform_state(0) {}
125+
};
126+
127+
static __libcpp_contention_table_entry __libcpp_contention_table[__libcpp_contention_table_size];
128+
129+
static hash<void const volatile*> __libcpp_contention_hasher;
130+
131+
static __libcpp_contention_table_entry* __libcpp_contention_state(void const volatile* p) {
132+
return &__libcpp_contention_table[__libcpp_contention_hasher(p) & (__libcpp_contention_table_size - 1)];
133+
}
134+
135+
/* Given an atomic to track contention and an atomic to actually wait on, which may be
136+
the same atomic, we try to detect contention to avoid spuriously calling the platform. */
137+
138+
static void __libcpp_contention_notify(__cxx_atomic_contention_t volatile* __contention_state,
139+
__cxx_atomic_contention_t const volatile* __platform_state,
140+
bool __notify_one) {
141+
if (0 != __cxx_atomic_load(__contention_state, memory_order_seq_cst))
142+
// We only call 'wake' if we consumed a contention bit here.
143+
__libcpp_platform_wake_by_address(__platform_state, __notify_one);
144+
}
145+
static __cxx_contention_t
146+
__libcpp_contention_monitor_for_wait(__cxx_atomic_contention_t volatile* /*__contention_state*/,
147+
__cxx_atomic_contention_t const volatile* __platform_state) {
148+
// We will monitor this value.
149+
return __cxx_atomic_load(__platform_state, memory_order_acquire);
150+
}
151+
static void __libcpp_contention_wait(__cxx_atomic_contention_t volatile* __contention_state,
152+
__cxx_atomic_contention_t const volatile* __platform_state,
153+
__cxx_contention_t __old_value) {
154+
__cxx_atomic_fetch_add(__contention_state, __cxx_contention_t(1), memory_order_seq_cst);
155+
// We sleep as long as the monitored value hasn't changed.
156+
__libcpp_platform_wait_on_address(__platform_state, __old_value);
157+
__cxx_atomic_fetch_sub(__contention_state, __cxx_contention_t(1), memory_order_release);
158+
}
159+
160+
/* When the incoming atomic is the wrong size for the platform wait size, need to
161+
launder the value sequence through an atomic from our table. */
162+
163+
static void __libcpp_atomic_notify(void const volatile* __location) {
164+
auto const __entry = __libcpp_contention_state(__location);
165+
// The value sequence laundering happens on the next line below.
166+
__cxx_atomic_fetch_add(&__entry->__platform_state, __cxx_contention_t(1), memory_order_release);
167+
__libcpp_contention_notify(
168+
&__entry->__contention_state,
169+
&__entry->__platform_state,
170+
false /* when laundering, we can't handle notify_one */);
171+
}
172+
_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(void const volatile* __location) noexcept {
173+
__libcpp_atomic_notify(__location);
174+
}
175+
_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(void const volatile* __location) noexcept {
176+
__libcpp_atomic_notify(__location);
177+
}
178+
_LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t __libcpp_atomic_monitor(void const volatile* __location) noexcept {
179+
auto const __entry = __libcpp_contention_state(__location);
180+
return __libcpp_contention_monitor_for_wait(&__entry->__contention_state, &__entry->__platform_state);
181+
}
182+
_LIBCPP_EXPORTED_FROM_ABI void
183+
__libcpp_atomic_wait(void const volatile* __location, __cxx_contention_t __old_value) noexcept {
184+
auto const __entry = __libcpp_contention_state(__location);
185+
__libcpp_contention_wait(&__entry->__contention_state, &__entry->__platform_state, __old_value);
186+
}
187+
188+
/* When the incoming atomic happens to be the platform wait size, we still need to use the
189+
table for the contention detection, but we can use the atomic directly for the wait. */
190+
191+
_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile* __location) noexcept {
192+
__libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, true);
193+
}
194+
_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile* __location) noexcept {
195+
__libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, false);
196+
}
197+
// This function is never used, but still exported for ABI compatibility.
198+
_LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t
199+
__libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile* __location) noexcept {
200+
return __libcpp_contention_monitor_for_wait(&__libcpp_contention_state(__location)->__contention_state, __location);
201+
}
202+
_LIBCPP_EXPORTED_FROM_ABI void
203+
__libcpp_atomic_wait(__cxx_atomic_contention_t const volatile* __location, __cxx_contention_t __old_value) noexcept {
204+
__libcpp_contention_wait(&__libcpp_contention_state(__location)->__contention_state, __location, __old_value);
205+
}
206+
207+
_LIBCPP_END_NAMESPACE_STD

0 commit comments

Comments
 (0)