blob: ba9e8ea9bb8487f3fa30310be2b9191646c2fd5e [file] [log] [blame]
Olivier Giroux161e6e82020-02-18 09:58:34 -05001// -*- C++ -*-
2//===--------------------------- barrier ----------------------------------===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef _LIBCPP_BARRIER
11#define _LIBCPP_BARRIER
12
13/*
14 barrier synopsis
15
16namespace std
17{
18
19 template<class CompletionFunction = see below>
20 class barrier
21 {
22 public:
23 using arrival_token = see below;
24
25 constexpr explicit barrier(ptrdiff_t phase_count,
26 CompletionFunction f = CompletionFunction());
27 ~barrier();
28
29 barrier(const barrier&) = delete;
30 barrier& operator=(const barrier&) = delete;
31
32 [[nodiscard]] arrival_token arrive(ptrdiff_t update = 1);
33 void wait(arrival_token&& arrival) const;
34
35 void arrive_and_wait();
36 void arrive_and_drop();
37
38 private:
39 CompletionFunction completion; // exposition only
40 };
41
42}
43
44*/
45
46#include <__config>
Louis Dionne73912b22020-11-04 15:01:25 -050047#include <__availability>
Olivier Giroux161e6e82020-02-18 09:58:34 -050048#include <atomic>
49#ifndef _LIBCPP_HAS_NO_TREE_BARRIER
50# include <memory>
51#endif
52
53#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
54#pragma GCC system_header
55#endif
56
57#ifdef _LIBCPP_HAS_NO_THREADS
58# error <barrier> is not supported on this single threaded system
59#endif
60
Arthur O'Dwyer2422fa22020-12-02 18:55:01 -050061_LIBCPP_PUSH_MACROS
62#include <__undef_macros>
63
Louis Dionne1ab95312020-02-24 18:12:44 -050064#if _LIBCPP_STD_VER >= 14
Olivier Giroux161e6e82020-02-18 09:58:34 -050065
66_LIBCPP_BEGIN_NAMESPACE_STD
67
68struct __empty_completion
69{
70 inline _LIBCPP_INLINE_VISIBILITY
71 void operator()() noexcept
72 {
73 }
74};
75
76#ifndef _LIBCPP_HAS_NO_TREE_BARRIER
77
78/*
79
80The default implementation of __barrier_base is a classic tree barrier.
81
82It looks different from literature pseudocode for two main reasons:
83 1. Threads that call into std::barrier functions do not provide indices,
84 so a numbering step is added before the actual barrier algorithm,
85 appearing as an N+1 round to the N rounds of the tree barrier.
86 2. A great deal of attention has been paid to avoid cache line thrashing
87 by flattening the tree structure into cache-line sized arrays, that
88 are indexed in an efficient way.
89
90*/
91
92using __barrier_phase_t = uint8_t;
93
94class __barrier_algorithm_base;
95
Louis Dionne48a828b2020-02-24 10:08:41 -050096_LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI
Olivier Giroux161e6e82020-02-18 09:58:34 -050097__barrier_algorithm_base* __construct_barrier_algorithm_base(ptrdiff_t& __expected);
98
Louis Dionne48a828b2020-02-24 10:08:41 -050099_LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI
Olivier Giroux161e6e82020-02-18 09:58:34 -0500100bool __arrive_barrier_algorithm_base(__barrier_algorithm_base* __barrier,
101 __barrier_phase_t __old_phase);
102
Louis Dionne48a828b2020-02-24 10:08:41 -0500103_LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI
Olivier Giroux161e6e82020-02-18 09:58:34 -0500104void __destroy_barrier_algorithm_base(__barrier_algorithm_base* __barrier);
105
106template<class _CompletionF>
107class __barrier_base {
108
109 ptrdiff_t __expected;
110 unique_ptr<__barrier_algorithm_base,
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500111 void (*)(__barrier_algorithm_base*)> __base;
Olivier Giroux161e6e82020-02-18 09:58:34 -0500112 __atomic_base<ptrdiff_t> __expected_adjustment;
113 _CompletionF __completion;
114 __atomic_base<__barrier_phase_t> __phase;
115
116public:
117 using arrival_token = __barrier_phase_t;
118
119 static constexpr ptrdiff_t max() noexcept {
120 return numeric_limits<ptrdiff_t>::max();
121 }
122
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500123 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500124 __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF())
125 : __expected(__expected), __base(__construct_barrier_algorithm_base(this->__expected),
126 &__destroy_barrier_algorithm_base),
127 __expected_adjustment(0), __completion(move(__completion)), __phase(0)
128 {
129 }
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500130 [[nodiscard]] _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500131 arrival_token arrive(ptrdiff_t update)
132 {
133 auto const __old_phase = __phase.load(memory_order_relaxed);
134 for(; update; --update)
135 if(__arrive_barrier_algorithm_base(__base.get(), __old_phase)) {
136 __completion();
137 __expected += __expected_adjustment.load(memory_order_relaxed);
138 __expected_adjustment.store(0, memory_order_relaxed);
139 __phase.store(__old_phase + 2, memory_order_release);
140 __phase.notify_all();
141 }
142 return __old_phase;
143 }
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500144 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500145 void wait(arrival_token&& __old_phase) const
146 {
147 auto const __test_fn = [=]() -> bool {
148 return __phase.load(memory_order_acquire) != __old_phase;
149 };
150 __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy());
151 }
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500152 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500153 void arrive_and_drop()
154 {
155 __expected_adjustment.fetch_sub(1, memory_order_relaxed);
156 (void)arrive(1);
157 }
158};
159
160#else
161
162/*
163
164The alternative implementation of __barrier_base is a central barrier.
165
166Two versions of this algorithm are provided:
167 1. A fairly straightforward implementation of the litterature for the
168 general case where the completion function is not empty.
169 2. An optimized implementation that exploits 2's complement arithmetic
170 and well-defined overflow in atomic arithmetic, to handle the phase
171 roll-over for free.
172
173*/
174
175template<class _CompletionF>
176class __barrier_base {
177
178 __atomic_base<ptrdiff_t> __expected;
179 __atomic_base<ptrdiff_t> __arrived;
180 _CompletionF __completion;
181 __atomic_base<bool> __phase;
182public:
183 using arrival_token = bool;
184
185 static constexpr ptrdiff_t max() noexcept {
186 return numeric_limits<ptrdiff_t>::max();
187 }
188
189 _LIBCPP_INLINE_VISIBILITY
190 __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF())
191 : __expected(__expected), __arrived(__expected), __completion(move(__completion)), __phase(false)
192 {
193 }
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500194 [[nodiscard]] _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500195 arrival_token arrive(ptrdiff_t update)
196 {
197 auto const __old_phase = __phase.load(memory_order_relaxed);
198 auto const __result = __arrived.fetch_sub(update, memory_order_acq_rel) - update;
199 auto const new_expected = __expected.load(memory_order_relaxed);
200 if(0 == __result) {
201 __completion();
202 __arrived.store(new_expected, memory_order_relaxed);
203 __phase.store(!__old_phase, memory_order_release);
204 __phase.notify_all();
205 }
206 return __old_phase;
207 }
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500208 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500209 void wait(arrival_token&& __old_phase) const
210 {
211 __phase.wait(__old_phase, memory_order_acquire);
212 }
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500213 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500214 void arrive_and_drop()
215 {
216 __expected.fetch_sub(1, memory_order_relaxed);
217 (void)arrive(1);
218 }
219};
220
221template<>
222class __barrier_base<__empty_completion> {
223
224 static constexpr uint64_t __expected_unit = 1ull;
225 static constexpr uint64_t __arrived_unit = 1ull << 32;
226 static constexpr uint64_t __expected_mask = __arrived_unit - 1;
227 static constexpr uint64_t __phase_bit = 1ull << 63;
228 static constexpr uint64_t __arrived_mask = (__phase_bit - 1) & ~__expected_mask;
229
230 __atomic_base<uint64_t> __phase_arrived_expected;
231
232 static _LIBCPP_INLINE_VISIBILITY
233 constexpr uint64_t __init(ptrdiff_t __count) _NOEXCEPT
234 {
235 return ((uint64_t(1u << 31) - __count) << 32)
236 | (uint64_t(1u << 31) - __count);
237 }
238
239public:
240 using arrival_token = uint64_t;
241
242 static constexpr ptrdiff_t max() noexcept {
243 return ptrdiff_t(1u << 31) - 1;
244 }
245
246 _LIBCPP_INLINE_VISIBILITY
247 explicit inline __barrier_base(ptrdiff_t __count, __empty_completion = __empty_completion())
248 : __phase_arrived_expected(__init(__count))
249 {
250 }
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500251 [[nodiscard]] inline _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500252 arrival_token arrive(ptrdiff_t update)
253 {
254 auto const __inc = __arrived_unit * update;
255 auto const __old = __phase_arrived_expected.fetch_add(__inc, memory_order_acq_rel);
256 if((__old ^ (__old + __inc)) & __phase_bit) {
257 __phase_arrived_expected.fetch_add((__old & __expected_mask) << 32, memory_order_relaxed);
258 __phase_arrived_expected.notify_all();
259 }
260 return __old & __phase_bit;
261 }
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500262 inline _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500263 void wait(arrival_token&& __phase) const
264 {
265 auto const __test_fn = [=]() -> bool {
266 uint64_t const __current = __phase_arrived_expected.load(memory_order_acquire);
267 return ((__current & __phase_bit) != __phase);
268 };
269 __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy());
270 }
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500271 inline _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500272 void arrive_and_drop()
273 {
274 __phase_arrived_expected.fetch_add(__expected_unit, memory_order_relaxed);
275 (void)arrive(1);
276 }
277};
278
279#endif //_LIBCPP_HAS_NO_TREE_BARRIER
280
281template<class _CompletionF = __empty_completion>
282class barrier {
283
284 __barrier_base<_CompletionF> __b;
285public:
286 using arrival_token = typename __barrier_base<_CompletionF>::arrival_token;
287
288 static constexpr ptrdiff_t max() noexcept {
289 return __barrier_base<_CompletionF>::max();
290 }
291
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500292 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500293 barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())
Arthur O'Dwyer07b22492020-11-27 11:02:06 -0500294 : __b(__count, _VSTD::move(__completion)) {
Olivier Giroux161e6e82020-02-18 09:58:34 -0500295 }
296
297 barrier(barrier const&) = delete;
298 barrier& operator=(barrier const&) = delete;
299
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500300 [[nodiscard]] _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500301 arrival_token arrive(ptrdiff_t update = 1)
302 {
303 return __b.arrive(update);
304 }
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500305 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500306 void wait(arrival_token&& __phase) const
307 {
Arthur O'Dwyer07b22492020-11-27 11:02:06 -0500308 __b.wait(_VSTD::move(__phase));
Olivier Giroux161e6e82020-02-18 09:58:34 -0500309 }
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500310 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500311 void arrive_and_wait()
312 {
313 wait(arrive());
314 }
Louis Dionne3dde4ab2020-02-24 10:09:29 -0500315 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
Olivier Giroux161e6e82020-02-18 09:58:34 -0500316 void arrive_and_drop()
317 {
318 __b.arrive_and_drop();
319 }
320};
321
322_LIBCPP_END_NAMESPACE_STD
323
Louis Dionne1ab95312020-02-24 18:12:44 -0500324#endif // _LIBCPP_STD_VER >= 14
325
Arthur O'Dwyer2422fa22020-12-02 18:55:01 -0500326_LIBCPP_POP_MACROS
327
Olivier Giroux161e6e82020-02-18 09:58:34 -0500328#endif //_LIBCPP_BARRIER