Warning, /include/c++/v1/__cxx03/barrier is written in an unsupported language. File is not indexed.
0001 // -*- C++ -*-
0002 //===----------------------------------------------------------------------===//
0003 //
0004 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0005 // See https://llvm.org/LICENSE.txt for license information.
0006 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0007 //
0008 //===----------------------------------------------------------------------===//
0009
0010 #ifndef _LIBCPP___CXX03_BARRIER
0011 #define _LIBCPP___CXX03_BARRIER
0012
0013 /*
0014 barrier synopsis
0015
0016 namespace std
0017 {
0018
0019 template<class CompletionFunction = see below>
0020 class barrier
0021 {
0022 public:
0023 using arrival_token = see below;
0024
0025 static constexpr ptrdiff_t max() noexcept;
0026
0027 constexpr explicit barrier(ptrdiff_t phase_count,
0028 CompletionFunction f = CompletionFunction());
0029 ~barrier();
0030
0031 barrier(const barrier&) = delete;
0032 barrier& operator=(const barrier&) = delete;
0033
0034 [[nodiscard]] arrival_token arrive(ptrdiff_t update = 1);
0035 void wait(arrival_token&& arrival) const;
0036
0037 void arrive_and_wait();
0038 void arrive_and_drop();
0039
0040 private:
0041 CompletionFunction completion; // exposition only
0042 };
0043
0044 }
0045
0046 */
0047
0048 #include <__cxx03/__config>
0049
0050 #if !defined(_LIBCPP_HAS_NO_THREADS)
0051
0052 # include <__cxx03/__assert>
0053 # include <__cxx03/__atomic/atomic_base.h>
0054 # include <__cxx03/__atomic/memory_order.h>
0055 # include <__cxx03/__memory/unique_ptr.h>
0056 # include <__cxx03/__thread/poll_with_backoff.h>
0057 # include <__cxx03/__thread/timed_backoff_policy.h>
0058 # include <__cxx03/__utility/move.h>
0059 # include <__cxx03/cstddef>
0060 # include <__cxx03/cstdint>
0061 # include <__cxx03/limits>
0062 # include <__cxx03/version>
0063
0064 # if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
0065 # pragma GCC system_header
0066 # endif
0067
0068 _LIBCPP_PUSH_MACROS
0069 # include <__cxx03/__undef_macros>
0070
0071 # if _LIBCPP_STD_VER >= 14
0072
0073 _LIBCPP_BEGIN_NAMESPACE_STD
0074
0075 struct __empty_completion {
0076 inline _LIBCPP_HIDE_FROM_ABI void operator()() noexcept {}
0077 };
0078
0079 # ifndef _LIBCPP_HAS_NO_TREE_BARRIER
0080
0081 /*
0082
0083 The default implementation of __barrier_base is a classic tree barrier.
0084
0085 It looks different from literature pseudocode for two main reasons:
0086 1. Threads that call into std::barrier functions do not provide indices,
0087 so a numbering step is added before the actual barrier algorithm,
0088 appearing as an N+1 round to the N rounds of the tree barrier.
0089 2. A great deal of attention has been paid to avoid cache line thrashing
0090 by flattening the tree structure into cache-line sized arrays, that
0091 are indexed in an efficient way.
0092
0093 */
0094
0095 using __barrier_phase_t = uint8_t;
0096
0097 class __barrier_algorithm_base;
0098
0099 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI __barrier_algorithm_base*
0100 __construct_barrier_algorithm_base(ptrdiff_t& __expected);
0101
0102 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI bool
0103 __arrive_barrier_algorithm_base(__barrier_algorithm_base* __barrier, __barrier_phase_t __old_phase) noexcept;
0104
0105 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI void
0106 __destroy_barrier_algorithm_base(__barrier_algorithm_base* __barrier) noexcept;
0107
0108 template <class _CompletionF>
0109 class __barrier_base {
0110 ptrdiff_t __expected_;
0111 unique_ptr<__barrier_algorithm_base, void (*)(__barrier_algorithm_base*)> __base_;
0112 __atomic_base<ptrdiff_t> __expected_adjustment_;
0113 _CompletionF __completion_;
0114 __atomic_base<__barrier_phase_t> __phase_;
0115
0116 public:
0117 using arrival_token = __barrier_phase_t;
0118
0119 static _LIBCPP_HIDE_FROM_ABI constexpr ptrdiff_t max() noexcept { return numeric_limits<ptrdiff_t>::max(); }
0120
0121 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI
0122 __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF())
0123 : __expected_(__expected),
0124 __base_(std::__construct_barrier_algorithm_base(this->__expected_), &__destroy_barrier_algorithm_base),
0125 __expected_adjustment_(0),
0126 __completion_(std::move(__completion)),
0127 __phase_(0) {}
0128 _LIBCPP_NODISCARD _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI arrival_token arrive(ptrdiff_t __update) {
0129 _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
0130 __update <= __expected_, "update is greater than the expected count for the current barrier phase");
0131
0132 auto const __old_phase = __phase_.load(memory_order_relaxed);
0133 for (; __update; --__update)
0134 if (__arrive_barrier_algorithm_base(__base_.get(), __old_phase)) {
0135 __completion_();
0136 __expected_ += __expected_adjustment_.load(memory_order_relaxed);
0137 __expected_adjustment_.store(0, memory_order_relaxed);
0138 __phase_.store(__old_phase + 2, memory_order_release);
0139 __phase_.notify_all();
0140 }
0141 return __old_phase;
0142 }
0143 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void wait(arrival_token&& __old_phase) const {
0144 auto const __test_fn = [this, __old_phase]() -> bool { return __phase_.load(memory_order_acquire) != __old_phase; };
0145 std::__libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy());
0146 }
0147 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void arrive_and_drop() {
0148 __expected_adjustment_.fetch_sub(1, memory_order_relaxed);
0149 (void)arrive(1);
0150 }
0151 };
0152
0153 # else
0154
0155 /*
0156
0157 The alternative implementation of __barrier_base is a central barrier.
0158
0159 Two versions of this algorithm are provided:
0160 1. A fairly straightforward implementation of the litterature for the
0161 general case where the completion function is not empty.
0162 2. An optimized implementation that exploits 2's complement arithmetic
0163 and well-defined overflow in atomic arithmetic, to handle the phase
0164 roll-over for free.
0165
0166 */
0167
0168 template <class _CompletionF>
0169 class __barrier_base {
0170 __atomic_base<ptrdiff_t> __expected;
0171 __atomic_base<ptrdiff_t> __arrived;
0172 _CompletionF __completion;
0173 __atomic_base<bool> __phase;
0174
0175 public:
0176 using arrival_token = bool;
0177
0178 static constexpr ptrdiff_t max() noexcept { return numeric_limits<ptrdiff_t>::max(); }
0179
0180 _LIBCPP_HIDE_FROM_ABI __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF())
0181 : __expected(__expected), __arrived(__expected), __completion(std::move(__completion)), __phase(false) {}
0182 [[nodiscard]] _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI arrival_token arrive(ptrdiff_t update) {
0183 auto const __old_phase = __phase.load(memory_order_relaxed);
0184 auto const __result = __arrived.fetch_sub(update, memory_order_acq_rel) - update;
0185 auto const new_expected = __expected.load(memory_order_relaxed);
0186
0187 _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
0188 update <= new_expected, "update is greater than the expected count for the current barrier phase");
0189
0190 if (0 == __result) {
0191 __completion();
0192 __arrived.store(new_expected, memory_order_relaxed);
0193 __phase.store(!__old_phase, memory_order_release);
0194 __phase.notify_all();
0195 }
0196 return __old_phase;
0197 }
0198 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void wait(arrival_token&& __old_phase) const {
0199 __phase.wait(__old_phase, memory_order_acquire);
0200 }
0201 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void arrive_and_drop() {
0202 __expected.fetch_sub(1, memory_order_relaxed);
0203 (void)arrive(1);
0204 }
0205 };
0206
0207 template <>
0208 class __barrier_base<__empty_completion> {
0209 static constexpr uint64_t __expected_unit = 1ull;
0210 static constexpr uint64_t __arrived_unit = 1ull << 32;
0211 static constexpr uint64_t __expected_mask = __arrived_unit - 1;
0212 static constexpr uint64_t __phase_bit = 1ull << 63;
0213 static constexpr uint64_t __arrived_mask = (__phase_bit - 1) & ~__expected_mask;
0214
0215 __atomic_base<uint64_t> __phase_arrived_expected;
0216
0217 static _LIBCPP_HIDE_FROM_ABI constexpr uint64_t __init(ptrdiff_t __count) _NOEXCEPT {
0218 return ((uint64_t(1u << 31) - __count) << 32) | (uint64_t(1u << 31) - __count);
0219 }
0220
0221 public:
0222 using arrival_token = uint64_t;
0223
0224 static constexpr ptrdiff_t max() noexcept { return ptrdiff_t(1u << 31) - 1; }
0225
0226 _LIBCPP_HIDE_FROM_ABI explicit inline __barrier_base(ptrdiff_t __count, __empty_completion = __empty_completion())
0227 : __phase_arrived_expected(__init(__count)) {}
0228 [[nodiscard]] inline _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI arrival_token arrive(ptrdiff_t update) {
0229 auto const __inc = __arrived_unit * update;
0230 auto const __old = __phase_arrived_expected.fetch_add(__inc, memory_order_acq_rel);
0231
0232 _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
0233 update <= __old, "update is greater than the expected count for the current barrier phase");
0234
0235 if ((__old ^ (__old + __inc)) & __phase_bit) {
0236 __phase_arrived_expected.fetch_add((__old & __expected_mask) << 32, memory_order_relaxed);
0237 __phase_arrived_expected.notify_all();
0238 }
0239 return __old & __phase_bit;
0240 }
0241 inline _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void wait(arrival_token&& __phase) const {
0242 auto const __test_fn = [=]() -> bool {
0243 uint64_t const __current = __phase_arrived_expected.load(memory_order_acquire);
0244 return ((__current & __phase_bit) != __phase);
0245 };
0246 __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy());
0247 }
0248 inline _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void arrive_and_drop() {
0249 __phase_arrived_expected.fetch_add(__expected_unit, memory_order_relaxed);
0250 (void)arrive(1);
0251 }
0252 };
0253
0254 # endif // !_LIBCPP_HAS_NO_TREE_BARRIER
0255
0256 template <class _CompletionF = __empty_completion>
0257 class _LIBCPP_DEPRECATED_ATOMIC_SYNC barrier {
0258 __barrier_base<_CompletionF> __b_;
0259
0260 public:
0261 using arrival_token = typename __barrier_base<_CompletionF>::arrival_token;
0262
0263 static _LIBCPP_HIDE_FROM_ABI constexpr ptrdiff_t max() noexcept { return __barrier_base<_CompletionF>::max(); }
0264
0265 _LIBCPP_AVAILABILITY_SYNC
0266 _LIBCPP_HIDE_FROM_ABI explicit barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())
0267 : __b_(__count, std::move(__completion)) {
0268 _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
0269 __count >= 0,
0270 "barrier::barrier(ptrdiff_t, CompletionFunction): barrier cannot be initialized with a negative value");
0271 _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
0272 __count <= max(),
0273 "barrier::barrier(ptrdiff_t, CompletionFunction): barrier cannot be initialized with "
0274 "a value greater than max()");
0275 }
0276
0277 barrier(barrier const&) = delete;
0278 barrier& operator=(barrier const&) = delete;
0279
0280 _LIBCPP_NODISCARD _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI arrival_token arrive(ptrdiff_t __update = 1) {
0281 _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__update > 0, "barrier:arrive must be called with a value greater than 0");
0282 return __b_.arrive(__update);
0283 }
0284 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void wait(arrival_token&& __phase) const {
0285 __b_.wait(std::move(__phase));
0286 }
0287 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void arrive_and_wait() { wait(arrive()); }
0288 _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void arrive_and_drop() { __b_.arrive_and_drop(); }
0289 };
0290
0291 _LIBCPP_END_NAMESPACE_STD
0292
0293 # endif // _LIBCPP_STD_VER >= 14
0294
0295 _LIBCPP_POP_MACROS
0296
0297 #endif // !defined(_LIBCPP_HAS_NO_THREADS)
0298
0299 #if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
0300 # include <__cxx03/atomic>
0301 # include <__cxx03/concepts>
0302 # include <__cxx03/iterator>
0303 # include <__cxx03/memory>
0304 # include <__cxx03/stdexcept>
0305 # include <__cxx03/variant>
0306 #endif
0307
0308 #endif //_LIBCPP___CXX03_BARRIER