Arene Base
Fundamental Utilities For Safety Critical C++
Loading...
Searching...
No Matches
barrier.hpp
Go to the documentation of this file.
1// Copyright 2024, Toyota Motor Corporation
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#ifndef INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_SYNCHRONIZATION_BARRIER_HPP_
6#define INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_SYNCHRONIZATION_BARRIER_HPP_
7
8#include <atomic>
9#include <cstddef>
10#include <cstdint>
11#include <limits>
12#include <tuple>
13#include <type_traits>
14#include <utility>
15
16// parasoft-begin-suppress AUTOSAR-A16_2_2-a-2 "Arene Base aggregate headers permitted by A16-2-2 Permit #1"
17#include "arene/base/compiler_support/attributes.hpp"
18#include "arene/base/compiler_support/expect.hpp"
19#include "arene/base/constraints.hpp"
20#include "arene/base/contracts.hpp"
21#include "arene/base/synchronization/manual_reset_event.hpp"
22// parasoft-end-suppress AUTOSAR-A16_2_2-a-2
23
24// parasoft-begin-suppress AUTOSAR-A7_1_5-a-2 "Trailing return syntax permitted by A7-1-5 Permit #1 v1.0.0"
25
26namespace arene {
27namespace base {
28
29namespace barrier_detail {
30/// @brief A default do-nothing completion function for barrier
31class default_completion_function {
32 public:
33 // parasoft-begin-suppress AUTOSAR-M0_1_8-b-2 "This is intended to do nothing"
34 /// @brief Do-nothing function call operator
35 constexpr void operator()() const noexcept {}
36 // parasoft-end-suppress AUTOSAR-M0_1_8-b-2
37};
38} // namespace barrier_detail
39
40/// @brief A backport of @c std::barrier from C++20.
41/// @tparam CompletionFunction The type of the completion function. Defaults to
42/// an invocable type with a do-nothing function call operator.
43/// @pre @c CompletionFunction must be destructible, move-constructible and
44/// nothrow-invocable.
45template <typename CompletionFunction = barrier_detail::default_completion_function>
46class barrier {
47 static_assert(std::is_destructible<CompletionFunction>::value, "The completion function must be destructible");
48 static_assert(
50 "The completion function must be move-constructible"
51 );
52 // parasoft-begin-suppress CERT_C-PRE31-c-3 "False positive: static_assert is a compile-time assert"
53 static_assert(
54 noexcept(std::declval<CompletionFunction&>()),
55 "The completion function must be nothrow-invocable with no arguments"
56 );
57 // parasoft-end-suppress CERT_C-PRE31-c-3
58
59 /// @brief Special tag type to prevent user construction of @c arrival_token
60 class private_construction_tag {
61 public:
62 /// @brief Disable aggregate initialization
63 explicit private_construction_tag() noexcept = default;
64 };
65
66 public:
67 /// @brief Get the maximum supported count
68 /// @return The maximum count
69 static constexpr auto max() noexcept -> std::ptrdiff_t { return std::numeric_limits<std::ptrdiff_t>::max(); }
70
71 /// @brief A class holding the result of a call to @c arrive, which can then be
72 /// passed to @c wait to wait for the end of the completion step associated
73 /// with the call to @c arrive.
75 /// @brief The phase that the call to @c arrive is associated with
76 std::uint64_t phase_;
77
78 public:
79 // parasoft-begin-suppress AUTOSAR-M2_10_1-a-2 "Similar names permitted by M2-10-1 Permit #1"
80 /// @brief Construct a new arrival token for the phase.
81 /// @param phase The phase number
82 arrival_token(private_construction_tag, std::uint64_t phase) noexcept
83 : phase_(phase) {}
84 // parasoft-end-suppress AUTOSAR-M2_10_1-a-2
85
86 /// @brief Default move constructor
87 arrival_token(arrival_token&& other) noexcept = default;
88 /// @brief Default move assignment
89 auto operator=(arrival_token&& other) noexcept -> arrival_token& = default;
90 /// @brief not copyable
91 arrival_token(arrival_token const& other) = delete;
92 /// @brief not copyable
93 auto operator=(arrival_token const& other) -> arrival_token& = delete;
94
95 /// @brief Default destructor
96 ~arrival_token() = default;
97
98 /// @brief Get the phase this token is associated with
99 /// @return the phase
100 auto get_phase() const noexcept -> std::uint64_t { return phase_; }
101 };
102
103 // parasoft-begin-suppress AUTOSAR-M2_10_1-a-2 "Similar names permitted by M2-10-1 Permit #1"
104 /// @brief Construct a barrier with a specified count and completion function.
105 /// @param count The initial number of arrivals required to complete each
106 /// phase
107 /// @param func The completion function to invoke at the end of each phase
108 constexpr barrier(std::ptrdiff_t count, CompletionFunction func) noexcept(
110 )
112 phase_(0),
114 count_(count) {
115 ARENE_PRECONDITION(count >= 0);
116 }
117 // parasoft-end-suppress AUTOSAR-M2_10_1-a-2
118
119 // parasoft-begin-suppress AUTOSAR-M2_10_1-a-2 "Similar names permitted by M2-10-1 Permit #1"
120 /// @brief Construct a barrier with a specified count and default-constructed
121 /// completion function.
122 /// @param count The initial number of arrivals required to complete each
123 /// phase
124 template <
125 typename F = CompletionFunction,
134 // parasoft-end-suppress AUTOSAR-M2_10_1-a-2
135
136 /// @brief Default destructor
137 ~barrier() = default;
138
139 // parasoft-begin-suppress CERT_C-EXP37-a-3 "False positive: The rule does not mention naming all parameters"
140 /// @brief Not copyable
141 barrier(barrier const&) = delete;
142 /// @brief Not copyable
143 auto operator=(barrier const&) -> barrier& = delete;
144 /// @brief Not movable
145 barrier(barrier&&) = delete;
146 /// @brief Not movable
147 auto operator=(barrier&&) -> barrier& = delete;
148 // parasoft-end-suppress CERT_C-EXP37-a-3
149
150 /// @brief Arrive at the barrier and wait for the completion step to end.
151 /// May trigger the completion step.
152 /// @pre The remaining count for the current phase it at least one.
153 void arrive_and_wait() { wait(arrive()); }
154
155 /// @brief Decrease the number of arrivals required for the subsequent phases to end,
156 /// and arrive at the barrier. May trigger the completion step. If the
157 /// required number of arrivals is reduced to zero then the barrier has no
158 /// more completion phases.
159 /// @pre The remaining count for the current phase it at least one.
161 ARENE_PRECONDITION(initial_count_.load(std::memory_order_relaxed) > 0);
162 std::ignore = initial_count_.fetch_sub(1, std::memory_order_relaxed);
163 std::ignore = arrive();
164 }
165
166 /// @brief Arrive at the barrier with the specified number of arrivals, and obtain a
167 /// token that can be used to wait for the end of the completion step
168 /// associated with the current phase.
169 /// @param arrivals The number of arrivals
170 /// @return An @c arrival_token associated with the current phase
171 /// @pre @c arrivals must be greater than zero, and less than or equal to the
172 /// remaining number of arrivals required in the current phase.
173 ARENE_NODISCARD auto arrive(std::ptrdiff_t arrivals) -> arrival_token {
174 auto const remaining_count = count_.load(std::memory_order_relaxed);
175 ARENE_PRECONDITION((arrivals > 0) && (arrivals <= remaining_count));
176 auto phase = phase_.load(std::memory_order_relaxed);
177 if (count_.fetch_sub(arrivals, std::memory_order_acq_rel) == arrivals) {
178 run_completion_step(phase);
179 }
180 return {private_construction_tag{}, phase};
181 }
182
183 /// @brief Arrive at the barrier, and obtain a token that can be used to wait for the
184 /// end of the completion step associated with the current phase.
185 /// @return An @c arrival_token associated with the current phase
186 ARENE_NODISCARD auto arrive() -> arrival_token { return arrive(1); }
187
188 /// @brief Wait for the completion step associated with the phase identified by @c
189 /// token to end.
190 /// @param token A token returned from @c arrive
191 /// @pre @c token must be associated with the current phase, or the previous
192 /// one.
193 void wait(arrival_token token) {
194 auto const current_phase = phase_.load(std::memory_order_relaxed);
195 ARENE_PRECONDITION((token.get_phase() == current_phase) || (token.get_phase() == current_phase - 1U));
196 completed_event(token.get_phase()).wait();
197 }
198
199 private:
200 /// @brief Run the completion step for the specified phase
201 /// @param completed_phase the phase being completed
202 void run_completion_step(std::uint64_t completed_phase) {
203 completion_function_();
204 auto new_phase = phase_.fetch_add(1U, std::memory_order_relaxed) + 1U;
205 count_ = initial_count_.load(std::memory_order_relaxed);
206 completed_event(new_phase).reset();
207 completed_event(completed_phase).signal();
208 }
209
210 /// @brief Get the event associated with the specified phase
211 /// @param event_phase the phase to get the event for
212 /// @return A reference to the event for the specified phase
213 auto completed_event(std::uint64_t event_phase) -> manual_reset_event& {
214 constexpr std::uint64_t two{2U};
215 return (event_phase % two) == 0U ? completed_even_ : completed_odd_;
216 }
217
218 /// @brief The completion function
219 CompletionFunction completion_function_;
220 /// @brief The current phase
221 std::atomic<std::uint64_t> phase_;
222 /// @brief The initial count of required arrivals for each phase
223 std::atomic<std::ptrdiff_t> initial_count_;
224 /// @brief The remaining count of required arrivals for the current phase
225 std::atomic<std::ptrdiff_t> count_;
226 /// @brief The completion event for odd numbered phases
227 manual_reset_event completed_odd_;
228 /// @brief The completion event for even numbered phases
229 manual_reset_event completed_even_;
230};
231} // namespace base
232} // namespace arene
233
234#endif // INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_SYNCHRONIZATION_BARRIER_HPP_
A class holding the result of a call to arrive, which can then be passed to wait to wait for the end ...
Definition barrier.hpp:74
auto get_phase() const noexcept -> std::uint64_t
Get the phase this token is associated with.
Definition barrier.hpp:100
arrival_token(arrival_token &&other) noexcept=default
Default move constructor.
auto operator=(arrival_token &&other) noexcept -> arrival_token &=default
Default move assignment.
arrival_token(arrival_token const &other)=delete
not copyable
auto operator=(arrival_token const &other) -> arrival_token &=delete
not copyable
arrival_token(private_construction_tag, std::uint64_t phase) noexcept
Construct a new arrival token for the phase.
Definition barrier.hpp:82
~arrival_token()=default
Default destructor.
A backport of std::barrier from C++20.
Definition barrier.hpp:46
ARENE_NODISCARD auto arrive() -> arrival_token
Arrive at the barrier, and obtain a token that can be used to wait for the end of the completion step...
Definition barrier.hpp:186
static constexpr auto max() noexcept -> std::ptrdiff_t
Get the maximum supported count.
Definition barrier.hpp:69
ARENE_NODISCARD auto arrive(std::ptrdiff_t arrivals) -> arrival_token
Arrive at the barrier with the specified number of arrivals, and obtain a token that can be used to w...
Definition barrier.hpp:173
barrier(barrier &&)=delete
Not movable.
auto operator=(barrier const &) -> barrier &=delete
Not copyable.
constexpr barrier(std::ptrdiff_t count, CompletionFunction func) noexcept(std::is_nothrow_move_constructible< CompletionFunction >::value)
Construct a barrier with a specified count and completion function.
Definition barrier.hpp:108
void arrive_and_wait()
Arrive at the barrier and wait for the completion step to end. May trigger the completion step.
Definition barrier.hpp:153
auto operator=(barrier &&) -> barrier &=delete
Not movable.
void arrive_and_drop()
Decrease the number of arrivals required for the subsequent phases to end, and arrive at the barrier....
Definition barrier.hpp:160
void wait(arrival_token token)
Wait for the completion step associated with the phase identified by token to end.
Definition barrier.hpp:193
barrier(barrier const &)=delete
Not copyable.
Definition array_exceptions_disabled.cpp:11
Copyright 2026, Toyota Motor Corporation.
Definition array_exceptions_disabled.cpp:10