16a8e18858
This is a remnant of poorly executed refactoring. libstdc++-v3/ChangeLog: * include/std/barrier (__tree_barrier::_M_arrive): Remove unnecessary hasher instantiation.
260 lines
7.6 KiB
C++
260 lines
7.6 KiB
C++
// <barrier> -*- C++ -*-
|
|
|
|
// Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
|
//
|
|
// This file is part of the GNU ISO C++ Library. This library is free
|
|
// software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License along
|
|
// with this library; see the file COPYING3. If not see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
// This implementation is based on libcxx/include/barrier
|
|
//===-- barrier.h --------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===---------------------------------------------------------------===//
|
|
|
|
/** @file include/barrier
|
|
* This is a Standard C++ Library header.
|
|
*/
|
|
|
|
#ifndef _GLIBCXX_BARRIER
|
|
#define _GLIBCXX_BARRIER 1
|
|
|
|
#pragma GCC system_header
|
|
|
|
#if __cplusplus > 201703L
|
|
#include <bits/atomic_base.h>
|
|
#if __cpp_lib_atomic_wait && __cpp_aligned_new
|
|
#include <bits/std_thread.h>
|
|
#include <bits/unique_ptr.h>
|
|
|
|
#include <array>
|
|
|
|
#define __cpp_lib_barrier 201907L
|
|
|
|
namespace std _GLIBCXX_VISIBILITY(default)
|
|
{
|
|
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|
|
|
struct __empty_completion
|
|
{
|
|
_GLIBCXX_ALWAYS_INLINE void
|
|
operator()() noexcept
|
|
{ }
|
|
};
|
|
|
|
/*
|
|
|
|
The default implementation of __tree_barrier is a classic tree barrier.
|
|
|
|
It looks different from literature pseudocode for two main reasons:
|
|
1. Threads that call into std::barrier functions do not provide indices,
|
|
so a numbering step is added before the actual barrier algorithm,
|
|
appearing as an N+1 round to the N rounds of the tree barrier.
|
|
2. A great deal of attention has been paid to avoid cache line thrashing
|
|
by flattening the tree structure into cache-line sized arrays, that
|
|
are indexed in an efficient way.
|
|
|
|
*/
|
|
|
|
enum class __barrier_phase_t : unsigned char { };
|
|
|
|
template<typename _CompletionF>
|
|
class __tree_barrier
|
|
{
|
|
using __atomic_phase_ref_t = std::__atomic_ref<__barrier_phase_t>;
|
|
using __atomic_phase_const_ref_t = std::__atomic_ref<const __barrier_phase_t>;
|
|
static constexpr auto __phase_alignment =
|
|
__atomic_phase_ref_t::required_alignment;
|
|
|
|
using __tickets_t = std::array<__barrier_phase_t, 64>;
|
|
struct alignas(64) /* naturally-align the heap state */ __state_t
|
|
{
|
|
alignas(__phase_alignment) __tickets_t __tickets;
|
|
};
|
|
|
|
ptrdiff_t _M_expected;
|
|
unique_ptr<__state_t[]> _M_state;
|
|
__atomic_base<ptrdiff_t> _M_expected_adjustment;
|
|
_CompletionF _M_completion;
|
|
|
|
alignas(__phase_alignment) __barrier_phase_t _M_phase;
|
|
|
|
bool
|
|
_M_arrive(__barrier_phase_t __old_phase, size_t __current)
|
|
{
|
|
const auto __old_phase_val = static_cast<unsigned char>(__old_phase);
|
|
const auto __half_step =
|
|
static_cast<__barrier_phase_t>(__old_phase_val + 1);
|
|
const auto __full_step =
|
|
static_cast<__barrier_phase_t>(__old_phase_val + 2);
|
|
|
|
size_t __current_expected = _M_expected;
|
|
__current %= ((_M_expected + 1) >> 1);
|
|
|
|
for (int __round = 0; ; ++__round)
|
|
{
|
|
if (__current_expected <= 1)
|
|
return true;
|
|
size_t const __end_node = ((__current_expected + 1) >> 1),
|
|
__last_node = __end_node - 1;
|
|
for ( ; ; ++__current)
|
|
{
|
|
if (__current == __end_node)
|
|
__current = 0;
|
|
auto __expect = __old_phase;
|
|
__atomic_phase_ref_t __phase(_M_state[__current]
|
|
.__tickets[__round]);
|
|
if (__current == __last_node && (__current_expected & 1))
|
|
{
|
|
if (__phase.compare_exchange_strong(__expect, __full_step,
|
|
memory_order_acq_rel))
|
|
break; // I'm 1 in 1, go to next __round
|
|
}
|
|
else if (__phase.compare_exchange_strong(__expect, __half_step,
|
|
memory_order_acq_rel))
|
|
{
|
|
return false; // I'm 1 in 2, done with arrival
|
|
}
|
|
else if (__expect == __half_step)
|
|
{
|
|
if (__phase.compare_exchange_strong(__expect, __full_step,
|
|
memory_order_acq_rel))
|
|
break; // I'm 2 in 2, go to next __round
|
|
}
|
|
}
|
|
__current_expected = __last_node + 1;
|
|
__current >>= 1;
|
|
}
|
|
}
|
|
|
|
public:
|
|
using arrival_token = __barrier_phase_t;
|
|
|
|
static constexpr ptrdiff_t
|
|
max() noexcept
|
|
{ return __PTRDIFF_MAX__; }
|
|
|
|
__tree_barrier(ptrdiff_t __expected, _CompletionF __completion)
|
|
: _M_expected(__expected), _M_expected_adjustment(0),
|
|
_M_completion(move(__completion)),
|
|
_M_phase(static_cast<__barrier_phase_t>(0))
|
|
{
|
|
size_t const __count = (_M_expected + 1) >> 1;
|
|
|
|
_M_state = std::make_unique<__state_t[]>(__count);
|
|
}
|
|
|
|
[[nodiscard]] arrival_token
|
|
arrive(ptrdiff_t __update)
|
|
{
|
|
std::hash<std::thread::id> __hasher;
|
|
size_t __current = __hasher(std::this_thread::get_id());
|
|
__atomic_phase_ref_t __phase(_M_phase);
|
|
const auto __old_phase = __phase.load(memory_order_relaxed);
|
|
const auto __cur = static_cast<unsigned char>(__old_phase);
|
|
for(; __update; --__update)
|
|
{
|
|
if(_M_arrive(__old_phase, __current))
|
|
{
|
|
_M_completion();
|
|
_M_expected += _M_expected_adjustment.load(memory_order_relaxed);
|
|
_M_expected_adjustment.store(0, memory_order_relaxed);
|
|
auto __new_phase = static_cast<__barrier_phase_t>(__cur + 2);
|
|
__phase.store(__new_phase, memory_order_release);
|
|
__phase.notify_all();
|
|
}
|
|
}
|
|
return __old_phase;
|
|
}
|
|
|
|
void
|
|
wait(arrival_token&& __old_phase) const
|
|
{
|
|
__atomic_phase_const_ref_t __phase(_M_phase);
|
|
auto const __test_fn = [=]
|
|
{
|
|
return __phase.load(memory_order_acquire) != __old_phase;
|
|
};
|
|
std::__atomic_wait_address(&_M_phase, __test_fn);
|
|
}
|
|
|
|
void
|
|
arrive_and_drop()
|
|
{
|
|
_M_expected_adjustment.fetch_sub(1, memory_order_relaxed);
|
|
(void)arrive(1);
|
|
}
|
|
};
|
|
|
|
template<typename _CompletionF = __empty_completion>
|
|
class barrier
|
|
{
|
|
// Note, we may introduce a "central" barrier algorithm at some point
|
|
// for more space constrained targets
|
|
using __algorithm_t = __tree_barrier<_CompletionF>;
|
|
__algorithm_t _M_b;
|
|
|
|
public:
|
|
class arrival_token final
|
|
{
|
|
public:
|
|
arrival_token(arrival_token&&) = default;
|
|
arrival_token& operator=(arrival_token&&) = default;
|
|
~arrival_token() = default;
|
|
|
|
private:
|
|
friend class barrier;
|
|
using __token = typename __algorithm_t::arrival_token;
|
|
explicit arrival_token(__token __tok) noexcept : _M_tok(__tok) { }
|
|
__token _M_tok;
|
|
};
|
|
|
|
static constexpr ptrdiff_t
|
|
max() noexcept
|
|
{ return __algorithm_t::max(); }
|
|
|
|
explicit
|
|
barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())
|
|
: _M_b(__count, std::move(__completion))
|
|
{ }
|
|
|
|
barrier(barrier const&) = delete;
|
|
barrier& operator=(barrier const&) = delete;
|
|
|
|
[[nodiscard]] arrival_token
|
|
arrive(ptrdiff_t __update = 1)
|
|
{ return arrival_token{_M_b.arrive(__update)}; }
|
|
|
|
void
|
|
wait(arrival_token&& __phase) const
|
|
{ _M_b.wait(std::move(__phase._M_tok)); }
|
|
|
|
void
|
|
arrive_and_wait()
|
|
{ wait(arrive()); }
|
|
|
|
void
|
|
arrive_and_drop()
|
|
{ _M_b.arrive_and_drop(); }
|
|
};
|
|
|
|
_GLIBCXX_END_NAMESPACE_VERSION
|
|
} // namespace
|
|
#endif // __cpp_lib_atomic_wait && __cpp_aligned_new
|
|
#endif // __cplusplus > 201703L
|
|
#endif // _GLIBCXX_BARRIER
|