154fa9ecdSOlivier Giroux// -*- C++ -*- 2eb8650a7SLouis Dionne//===----------------------------------------------------------------------===// 354fa9ecdSOlivier Giroux// 454fa9ecdSOlivier Giroux// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 554fa9ecdSOlivier Giroux// See https://llvm.org/LICENSE.txt for license information. 654fa9ecdSOlivier Giroux// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 754fa9ecdSOlivier Giroux// 854fa9ecdSOlivier Giroux//===----------------------------------------------------------------------===// 954fa9ecdSOlivier Giroux 1054fa9ecdSOlivier Giroux#ifndef _LIBCPP_BARRIER 1154fa9ecdSOlivier Giroux#define _LIBCPP_BARRIER 1254fa9ecdSOlivier Giroux 1354fa9ecdSOlivier Giroux/* 1454fa9ecdSOlivier Giroux barrier synopsis 1554fa9ecdSOlivier Giroux 1654fa9ecdSOlivier Girouxnamespace std 1754fa9ecdSOlivier Giroux{ 1854fa9ecdSOlivier Giroux 1954fa9ecdSOlivier Giroux template<class CompletionFunction = see below> 2054fa9ecdSOlivier Giroux class barrier 2154fa9ecdSOlivier Giroux { 2254fa9ecdSOlivier Giroux public: 2354fa9ecdSOlivier Giroux using arrival_token = see below; 2454fa9ecdSOlivier Giroux 25e2279c23SMarek Kurdej static constexpr ptrdiff_t max() noexcept; 26e2279c23SMarek Kurdej 2754fa9ecdSOlivier Giroux constexpr explicit barrier(ptrdiff_t phase_count, 2854fa9ecdSOlivier Giroux CompletionFunction f = CompletionFunction()); 2954fa9ecdSOlivier Giroux ~barrier(); 3054fa9ecdSOlivier Giroux 3154fa9ecdSOlivier Giroux barrier(const barrier&) = delete; 3254fa9ecdSOlivier Giroux barrier& operator=(const barrier&) = delete; 3354fa9ecdSOlivier Giroux 3454fa9ecdSOlivier Giroux [[nodiscard]] arrival_token arrive(ptrdiff_t update = 1); 3554fa9ecdSOlivier Giroux void wait(arrival_token&& arrival) const; 3654fa9ecdSOlivier Giroux 3754fa9ecdSOlivier Giroux void arrive_and_wait(); 3854fa9ecdSOlivier Giroux void arrive_and_drop(); 3954fa9ecdSOlivier Giroux 4054fa9ecdSOlivier Giroux private: 4154fa9ecdSOlivier Giroux CompletionFunction completion; // exposition only 4254fa9ecdSOlivier Giroux }; 4354fa9ecdSOlivier Giroux 4454fa9ecdSOlivier Giroux} 4554fa9ecdSOlivier Giroux 4654fa9ecdSOlivier Giroux*/ 4754fa9ecdSOlivier Giroux 48385cc25aSLouis Dionne#include <__assert> // all public C++ headers provide the assertion handler 492eadbc86SLouis Dionne#include <__availability> 50bfbd73f8SArthur O'Dwyer#include <__config> 51df51be85SLouis Dionne#include <__thread/timed_backoff_policy.h> 5254fa9ecdSOlivier Giroux#include <atomic> 53643df8faSLouis Dionne#include <limits> 5454fa9ecdSOlivier Giroux#include <memory> 5554fa9ecdSOlivier Giroux 5654fa9ecdSOlivier Giroux#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 5754fa9ecdSOlivier Giroux# pragma GCC system_header 5854fa9ecdSOlivier Giroux#endif 5954fa9ecdSOlivier Giroux 6054fa9ecdSOlivier Giroux#ifdef _LIBCPP_HAS_NO_THREADS 611c4b31c3SLouis Dionne# error "<barrier> is not supported since libc++ has been configured without support for threads." 6254fa9ecdSOlivier Giroux#endif 6354fa9ecdSOlivier Giroux 64d4303307SArthur O'Dwyer_LIBCPP_PUSH_MACROS 65d4303307SArthur O'Dwyer#include <__undef_macros> 66d4303307SArthur O'Dwyer 67ab41129bSLouis Dionne#if _LIBCPP_STD_VER >= 14 6854fa9ecdSOlivier Giroux 6954fa9ecdSOlivier Giroux_LIBCPP_BEGIN_NAMESPACE_STD 7054fa9ecdSOlivier Giroux 7154fa9ecdSOlivier Girouxstruct __empty_completion 7254fa9ecdSOlivier Giroux{ 7354fa9ecdSOlivier Giroux inline _LIBCPP_INLINE_VISIBILITY 7454fa9ecdSOlivier Giroux void operator()() noexcept 7554fa9ecdSOlivier Giroux { 7654fa9ecdSOlivier Giroux } 7754fa9ecdSOlivier Giroux}; 7854fa9ecdSOlivier Giroux 7954fa9ecdSOlivier Giroux#ifndef _LIBCPP_HAS_NO_TREE_BARRIER 8054fa9ecdSOlivier Giroux 8154fa9ecdSOlivier Giroux/* 8254fa9ecdSOlivier Giroux 8354fa9ecdSOlivier GirouxThe default implementation of __barrier_base is a classic tree barrier. 8454fa9ecdSOlivier Giroux 8554fa9ecdSOlivier GirouxIt looks different from literature pseudocode for two main reasons: 8654fa9ecdSOlivier Giroux 1. Threads that call into std::barrier functions do not provide indices, 8754fa9ecdSOlivier Giroux so a numbering step is added before the actual barrier algorithm, 8854fa9ecdSOlivier Giroux appearing as an N+1 round to the N rounds of the tree barrier. 8954fa9ecdSOlivier Giroux 2. A great deal of attention has been paid to avoid cache line thrashing 9054fa9ecdSOlivier Giroux by flattening the tree structure into cache-line sized arrays, that 9154fa9ecdSOlivier Giroux are indexed in an efficient way. 9254fa9ecdSOlivier Giroux 9354fa9ecdSOlivier Giroux*/ 9454fa9ecdSOlivier Giroux 9554fa9ecdSOlivier Girouxusing __barrier_phase_t = uint8_t; 9654fa9ecdSOlivier Giroux 9754fa9ecdSOlivier Girouxclass __barrier_algorithm_base; 9854fa9ecdSOlivier Giroux 9980e73f22SLouis Dionne_LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI 10054fa9ecdSOlivier Giroux__barrier_algorithm_base* __construct_barrier_algorithm_base(ptrdiff_t& __expected); 10154fa9ecdSOlivier Giroux 10280e73f22SLouis Dionne_LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI 10354fa9ecdSOlivier Girouxbool __arrive_barrier_algorithm_base(__barrier_algorithm_base* __barrier, 10454fa9ecdSOlivier Giroux __barrier_phase_t __old_phase); 10554fa9ecdSOlivier Giroux 10680e73f22SLouis Dionne_LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI 10754fa9ecdSOlivier Girouxvoid __destroy_barrier_algorithm_base(__barrier_algorithm_base* __barrier); 10854fa9ecdSOlivier Giroux 10954fa9ecdSOlivier Girouxtemplate<class _CompletionF> 11054fa9ecdSOlivier Girouxclass __barrier_base { 111e39095a3SLouis Dionne ptrdiff_t __expected_; 11254fa9ecdSOlivier Giroux unique_ptr<__barrier_algorithm_base, 113e39095a3SLouis Dionne void (*)(__barrier_algorithm_base*)> __base_; 114e39095a3SLouis Dionne __atomic_base<ptrdiff_t> __expected_adjustment_; 115e39095a3SLouis Dionne _CompletionF __completion_; 116e39095a3SLouis Dionne __atomic_base<__barrier_phase_t> __phase_; 11754fa9ecdSOlivier Giroux 11854fa9ecdSOlivier Girouxpublic: 11954fa9ecdSOlivier Giroux using arrival_token = __barrier_phase_t; 12054fa9ecdSOlivier Giroux 12154fa9ecdSOlivier Giroux static constexpr ptrdiff_t max() noexcept { 12254fa9ecdSOlivier Giroux return numeric_limits<ptrdiff_t>::max(); 12354fa9ecdSOlivier Giroux } 12454fa9ecdSOlivier Giroux 12546c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 12654fa9ecdSOlivier Giroux __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF()) 127e39095a3SLouis Dionne : __expected_(__expected), __base_(__construct_barrier_algorithm_base(this->__expected_), 12854fa9ecdSOlivier Giroux &__destroy_barrier_algorithm_base), 129e39095a3SLouis Dionne __expected_adjustment_(0), __completion_(std::move(__completion)), __phase_(0) 13054fa9ecdSOlivier Giroux { 13154fa9ecdSOlivier Giroux } 13246c38761SLouis Dionne [[nodiscard]] _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 133*b48c5010SNikolas Klauser arrival_token arrive(ptrdiff_t __update) 13454fa9ecdSOlivier Giroux { 135e39095a3SLouis Dionne auto const __old_phase = __phase_.load(memory_order_relaxed); 136*b48c5010SNikolas Klauser for(; __update; --__update) 137e39095a3SLouis Dionne if(__arrive_barrier_algorithm_base(__base_.get(), __old_phase)) { 138e39095a3SLouis Dionne __completion_(); 139e39095a3SLouis Dionne __expected_ += __expected_adjustment_.load(memory_order_relaxed); 140e39095a3SLouis Dionne __expected_adjustment_.store(0, memory_order_relaxed); 141e39095a3SLouis Dionne __phase_.store(__old_phase + 2, memory_order_release); 142e39095a3SLouis Dionne __phase_.notify_all(); 14354fa9ecdSOlivier Giroux } 14454fa9ecdSOlivier Giroux return __old_phase; 14554fa9ecdSOlivier Giroux } 14646c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 14754fa9ecdSOlivier Giroux void wait(arrival_token&& __old_phase) const 14854fa9ecdSOlivier Giroux { 1492e4755ffSLouis Dionne auto const __test_fn = [this, __old_phase]() -> bool { 150e39095a3SLouis Dionne return __phase_.load(memory_order_acquire) != __old_phase; 15154fa9ecdSOlivier Giroux }; 15254fa9ecdSOlivier Giroux __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy()); 15354fa9ecdSOlivier Giroux } 15446c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 15554fa9ecdSOlivier Giroux void arrive_and_drop() 15654fa9ecdSOlivier Giroux { 157e39095a3SLouis Dionne __expected_adjustment_.fetch_sub(1, memory_order_relaxed); 15854fa9ecdSOlivier Giroux (void)arrive(1); 15954fa9ecdSOlivier Giroux } 16054fa9ecdSOlivier Giroux}; 16154fa9ecdSOlivier Giroux 16254fa9ecdSOlivier Giroux#else 16354fa9ecdSOlivier Giroux 16454fa9ecdSOlivier Giroux/* 16554fa9ecdSOlivier Giroux 16654fa9ecdSOlivier GirouxThe alternative implementation of __barrier_base is a central barrier. 16754fa9ecdSOlivier Giroux 16854fa9ecdSOlivier GirouxTwo versions of this algorithm are provided: 16954fa9ecdSOlivier Giroux 1. A fairly straightforward implementation of the litterature for the 17054fa9ecdSOlivier Giroux general case where the completion function is not empty. 17154fa9ecdSOlivier Giroux 2. An optimized implementation that exploits 2's complement arithmetic 17254fa9ecdSOlivier Giroux and well-defined overflow in atomic arithmetic, to handle the phase 17354fa9ecdSOlivier Giroux roll-over for free. 17454fa9ecdSOlivier Giroux 17554fa9ecdSOlivier Giroux*/ 17654fa9ecdSOlivier Giroux 17754fa9ecdSOlivier Girouxtemplate<class _CompletionF> 17854fa9ecdSOlivier Girouxclass __barrier_base { 17954fa9ecdSOlivier Giroux 18054fa9ecdSOlivier Giroux __atomic_base<ptrdiff_t> __expected; 18154fa9ecdSOlivier Giroux __atomic_base<ptrdiff_t> __arrived; 18254fa9ecdSOlivier Giroux _CompletionF __completion; 18354fa9ecdSOlivier Giroux __atomic_base<bool> __phase; 18454fa9ecdSOlivier Girouxpublic: 18554fa9ecdSOlivier Giroux using arrival_token = bool; 18654fa9ecdSOlivier Giroux 18754fa9ecdSOlivier Giroux static constexpr ptrdiff_t max() noexcept { 18854fa9ecdSOlivier Giroux return numeric_limits<ptrdiff_t>::max(); 18954fa9ecdSOlivier Giroux } 19054fa9ecdSOlivier Giroux 19154fa9ecdSOlivier Giroux _LIBCPP_INLINE_VISIBILITY 19254fa9ecdSOlivier Giroux __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF()) 19348f8a7c4SCorentin Jabot : __expected(__expected), __arrived(__expected), __completion(std::move(__completion)), __phase(false) 19454fa9ecdSOlivier Giroux { 19554fa9ecdSOlivier Giroux } 19646c38761SLouis Dionne [[nodiscard]] _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 19754fa9ecdSOlivier Giroux arrival_token arrive(ptrdiff_t update) 19854fa9ecdSOlivier Giroux { 19954fa9ecdSOlivier Giroux auto const __old_phase = __phase.load(memory_order_relaxed); 20054fa9ecdSOlivier Giroux auto const __result = __arrived.fetch_sub(update, memory_order_acq_rel) - update; 20154fa9ecdSOlivier Giroux auto const new_expected = __expected.load(memory_order_relaxed); 20254fa9ecdSOlivier Giroux if(0 == __result) { 20354fa9ecdSOlivier Giroux __completion(); 20454fa9ecdSOlivier Giroux __arrived.store(new_expected, memory_order_relaxed); 20554fa9ecdSOlivier Giroux __phase.store(!__old_phase, memory_order_release); 20654fa9ecdSOlivier Giroux __phase.notify_all(); 20754fa9ecdSOlivier Giroux } 20854fa9ecdSOlivier Giroux return __old_phase; 20954fa9ecdSOlivier Giroux } 21046c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 21154fa9ecdSOlivier Giroux void wait(arrival_token&& __old_phase) const 21254fa9ecdSOlivier Giroux { 21354fa9ecdSOlivier Giroux __phase.wait(__old_phase, memory_order_acquire); 21454fa9ecdSOlivier Giroux } 21546c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 21654fa9ecdSOlivier Giroux void arrive_and_drop() 21754fa9ecdSOlivier Giroux { 21854fa9ecdSOlivier Giroux __expected.fetch_sub(1, memory_order_relaxed); 21954fa9ecdSOlivier Giroux (void)arrive(1); 22054fa9ecdSOlivier Giroux } 22154fa9ecdSOlivier Giroux}; 22254fa9ecdSOlivier Giroux 22354fa9ecdSOlivier Girouxtemplate<> 22454fa9ecdSOlivier Girouxclass __barrier_base<__empty_completion> { 22554fa9ecdSOlivier Giroux 22654fa9ecdSOlivier Giroux static constexpr uint64_t __expected_unit = 1ull; 22754fa9ecdSOlivier Giroux static constexpr uint64_t __arrived_unit = 1ull << 32; 22854fa9ecdSOlivier Giroux static constexpr uint64_t __expected_mask = __arrived_unit - 1; 22954fa9ecdSOlivier Giroux static constexpr uint64_t __phase_bit = 1ull << 63; 23054fa9ecdSOlivier Giroux static constexpr uint64_t __arrived_mask = (__phase_bit - 1) & ~__expected_mask; 23154fa9ecdSOlivier Giroux 23254fa9ecdSOlivier Giroux __atomic_base<uint64_t> __phase_arrived_expected; 23354fa9ecdSOlivier Giroux 23454fa9ecdSOlivier Giroux static _LIBCPP_INLINE_VISIBILITY 23554fa9ecdSOlivier Giroux constexpr uint64_t __init(ptrdiff_t __count) _NOEXCEPT 23654fa9ecdSOlivier Giroux { 23754fa9ecdSOlivier Giroux return ((uint64_t(1u << 31) - __count) << 32) 23854fa9ecdSOlivier Giroux | (uint64_t(1u << 31) - __count); 23954fa9ecdSOlivier Giroux } 24054fa9ecdSOlivier Giroux 24154fa9ecdSOlivier Girouxpublic: 24254fa9ecdSOlivier Giroux using arrival_token = uint64_t; 24354fa9ecdSOlivier Giroux 24454fa9ecdSOlivier Giroux static constexpr ptrdiff_t max() noexcept { 24554fa9ecdSOlivier Giroux return ptrdiff_t(1u << 31) - 1; 24654fa9ecdSOlivier Giroux } 24754fa9ecdSOlivier Giroux 24854fa9ecdSOlivier Giroux _LIBCPP_INLINE_VISIBILITY 24954fa9ecdSOlivier Giroux explicit inline __barrier_base(ptrdiff_t __count, __empty_completion = __empty_completion()) 25054fa9ecdSOlivier Giroux : __phase_arrived_expected(__init(__count)) 25154fa9ecdSOlivier Giroux { 25254fa9ecdSOlivier Giroux } 25346c38761SLouis Dionne [[nodiscard]] inline _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 25454fa9ecdSOlivier Giroux arrival_token arrive(ptrdiff_t update) 25554fa9ecdSOlivier Giroux { 25654fa9ecdSOlivier Giroux auto const __inc = __arrived_unit * update; 25754fa9ecdSOlivier Giroux auto const __old = __phase_arrived_expected.fetch_add(__inc, memory_order_acq_rel); 25854fa9ecdSOlivier Giroux if((__old ^ (__old + __inc)) & __phase_bit) { 25954fa9ecdSOlivier Giroux __phase_arrived_expected.fetch_add((__old & __expected_mask) << 32, memory_order_relaxed); 26054fa9ecdSOlivier Giroux __phase_arrived_expected.notify_all(); 26154fa9ecdSOlivier Giroux } 26254fa9ecdSOlivier Giroux return __old & __phase_bit; 26354fa9ecdSOlivier Giroux } 26446c38761SLouis Dionne inline _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 26554fa9ecdSOlivier Giroux void wait(arrival_token&& __phase) const 26654fa9ecdSOlivier Giroux { 26754fa9ecdSOlivier Giroux auto const __test_fn = [=]() -> bool { 26854fa9ecdSOlivier Giroux uint64_t const __current = __phase_arrived_expected.load(memory_order_acquire); 26954fa9ecdSOlivier Giroux return ((__current & __phase_bit) != __phase); 27054fa9ecdSOlivier Giroux }; 27154fa9ecdSOlivier Giroux __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy()); 27254fa9ecdSOlivier Giroux } 27346c38761SLouis Dionne inline _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 27454fa9ecdSOlivier Giroux void arrive_and_drop() 27554fa9ecdSOlivier Giroux { 27654fa9ecdSOlivier Giroux __phase_arrived_expected.fetch_add(__expected_unit, memory_order_relaxed); 27754fa9ecdSOlivier Giroux (void)arrive(1); 27854fa9ecdSOlivier Giroux } 27954fa9ecdSOlivier Giroux}; 28054fa9ecdSOlivier Giroux 2818383351dSLouis Dionne#endif // !_LIBCPP_HAS_NO_TREE_BARRIER 28254fa9ecdSOlivier Giroux 28354fa9ecdSOlivier Girouxtemplate<class _CompletionF = __empty_completion> 28454fa9ecdSOlivier Girouxclass barrier { 28554fa9ecdSOlivier Giroux 28654fa9ecdSOlivier Giroux __barrier_base<_CompletionF> __b; 28754fa9ecdSOlivier Girouxpublic: 28854fa9ecdSOlivier Giroux using arrival_token = typename __barrier_base<_CompletionF>::arrival_token; 28954fa9ecdSOlivier Giroux 29054fa9ecdSOlivier Giroux static constexpr ptrdiff_t max() noexcept { 29154fa9ecdSOlivier Giroux return __barrier_base<_CompletionF>::max(); 29254fa9ecdSOlivier Giroux } 29354fa9ecdSOlivier Giroux 29446c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 29554fa9ecdSOlivier Giroux barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF()) 296d586f92cSArthur O'Dwyer : __b(__count, _VSTD::move(__completion)) { 29754fa9ecdSOlivier Giroux } 29854fa9ecdSOlivier Giroux 29954fa9ecdSOlivier Giroux barrier(barrier const&) = delete; 30054fa9ecdSOlivier Giroux barrier& operator=(barrier const&) = delete; 30154fa9ecdSOlivier Giroux 30246c38761SLouis Dionne [[nodiscard]] _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 303*b48c5010SNikolas Klauser arrival_token arrive(ptrdiff_t __update = 1) 30454fa9ecdSOlivier Giroux { 305*b48c5010SNikolas Klauser return __b.arrive(__update); 30654fa9ecdSOlivier Giroux } 30746c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 30854fa9ecdSOlivier Giroux void wait(arrival_token&& __phase) const 30954fa9ecdSOlivier Giroux { 310d586f92cSArthur O'Dwyer __b.wait(_VSTD::move(__phase)); 31154fa9ecdSOlivier Giroux } 31246c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 31354fa9ecdSOlivier Giroux void arrive_and_wait() 31454fa9ecdSOlivier Giroux { 31554fa9ecdSOlivier Giroux wait(arrive()); 31654fa9ecdSOlivier Giroux } 31746c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 31854fa9ecdSOlivier Giroux void arrive_and_drop() 31954fa9ecdSOlivier Giroux { 32054fa9ecdSOlivier Giroux __b.arrive_and_drop(); 32154fa9ecdSOlivier Giroux } 32254fa9ecdSOlivier Giroux}; 32354fa9ecdSOlivier Giroux 32454fa9ecdSOlivier Giroux_LIBCPP_END_NAMESPACE_STD 32554fa9ecdSOlivier Giroux 326ab41129bSLouis Dionne#endif // _LIBCPP_STD_VER >= 14 327ab41129bSLouis Dionne 328d4303307SArthur O'Dwyer_LIBCPP_POP_MACROS 329d4303307SArthur O'Dwyer 33054fa9ecdSOlivier Giroux#endif //_LIBCPP_BARRIER 331