1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15
16 #include <optional>
17
18 #include "pw_async2/internal/poll_internal.h"
19 #include "pw_polyfill/language_feature_macros.h"
20 #include "pw_string/to_string.h"
21
22 namespace pw {
23 namespace async2 {
24
25 /// A type whose value indicates that an operation was able to complete (or
26 /// was ready to produce an output).
27 ///
28 /// This type is used as the contentless "value" type for ``Poll``
29 /// types that do not return a value.
30 struct ReadyType {};
31
32 /// A type whose value indicates an operation was not yet able to complete.
33 ///
34 /// This is analogous to ``std::nullopt_t``, but for ``Poll``.
35 struct PW_NODISCARD_STR(
36 "`Poll`-returning functions may or may not have completed. Their "
37 "return value should be examined.") PendingType {};
38
39 /// A value that may or may not be ready yet.
40 ///
41 /// ``Poll<T>`` most commonly appears as the return type of an function
42 /// that checks the current status of an asynchronous operation. If
43 /// the operation has completed, it returns with ``Ready(value)``. Otherwise,
44 /// it returns ``Pending`` to indicate that the operations has not yet
45 /// completed, and the caller should try again in the future.
46 ///
47 /// ``Poll<T>`` itself is "plain old data" and does not change on its own.
48 /// To check the current status of an operation, the caller must invoke
49 /// the ``Poll<T>`` returning function again and examine the newly returned
50 /// ``Poll<T>``.
51 template <typename T = ReadyType>
52 class PW_NODISCARD_STR(
53 "`Poll`-returning functions may or may not have completed. Their "
54 "return value should be examined.") Poll {
55 public:
56 /// Basic constructors.
57 Poll() = delete;
58 constexpr Poll(const Poll&) = default;
59 constexpr Poll& operator=(const Poll&) = default;
60 constexpr Poll(Poll&&) = default;
61 constexpr Poll& operator=(Poll&&) = default;
62
63 /// Constructs a new ``Poll<T>`` from a ``Poll<U>`` where ``T`` is
64 /// constructible from ``U``.
65 ///
66 /// To avoid ambiguity, this constructor is disabled if ``T`` is also
67 /// constructible from ``Poll<U>``.
68 ///
69 /// This constructor is explicit if and only if the corresponding construction
70 /// of ``T`` from ``U`` is explicit.
71 template <typename U,
72 internal_poll::EnableIfImplicitlyConvertible<T, const U&> = 0>
Poll(const Poll<U> & other)73 constexpr Poll(const Poll<U>& other) : value_(other.value_) {}
74 template <typename U,
75 internal_poll::EnableIfExplicitlyConvertible<T, const U&> = 0>
Poll(const Poll<U> & other)76 explicit constexpr Poll(const Poll<U>& other) : value_(other.value_) {}
77
78 template <typename U,
79 internal_poll::EnableIfImplicitlyConvertible<T, U&&> = 0>
Poll(Poll<U> && other)80 constexpr Poll(Poll<U>&& other) // NOLINT
81 : value_(std::move(other.value_)) {}
82 template <typename U,
83 internal_poll::EnableIfExplicitlyConvertible<T, U&&> = 0>
Poll(Poll<U> && other)84 explicit constexpr Poll(Poll<U>&& other) : value_(std::move(other.value_)) {}
85
86 // Constructs the inner value `T` in-place using the provided args, using the
87 // `T(U)` (direct-initialization) constructor. This constructor is only valid
88 // if `T` can be constructed from a `U`. Can accept move or copy constructors.
89 //
90 // This constructor is explicit if `U` is not convertible to `T`. To avoid
91 // ambiguity, this constructor is disabled if `U` is a `Poll<J>`, where
92 // `J` is convertible to `T`.
93 template <typename U = T,
94 internal_poll::EnableIfImplicitlyInitializable<T, U> = 0>
Poll(U && u)95 constexpr Poll(U&& u) // NOLINT
96 : Poll(std::in_place, std::forward<U>(u)) {}
97
98 template <typename U = T,
99 internal_poll::EnableIfExplicitlyInitializable<T, U> = 0>
Poll(U && u)100 explicit constexpr Poll(U&& u) // NOLINT
101 : Poll(std::in_place, std::forward<U>(u)) {}
102
103 // In-place construction of ``Ready`` variant.
104 template <typename... Args>
Poll(std::in_place_t,Args &&...args)105 constexpr Poll(std::in_place_t, Args&&... args)
106 : value_(std::in_place, std::move(args)...) {}
107
108 // Convert from `T`
Poll(T && value)109 constexpr Poll(T&& value) : value_(std::move(value)) {}
110 constexpr Poll& operator=(T&& value) {
111 value_ = std::optional<T>(std::move(value));
112 return *this;
113 }
114
115 // Convert from `Pending`
Poll(PendingType)116 constexpr Poll(PendingType) noexcept : value_() {}
117 constexpr Poll& operator=(PendingType) noexcept {
118 value_ = std::nullopt;
119 return *this;
120 }
121
122 /// Returns whether or not this value is ``Ready``.
IsReady()123 constexpr bool IsReady() const noexcept { return value_.has_value(); }
124
125 /// Returns whether or not this value is ``Pending``.
IsPending()126 constexpr bool IsPending() const noexcept { return !value_.has_value(); }
127
128 /// Returns a ``Poll<>`` without the inner value whose readiness matches that
129 /// of ``this``.
Readiness()130 constexpr Poll<> Readiness() const noexcept {
131 if (IsReady()) {
132 return ReadyType();
133 } else {
134 return PendingType();
135 }
136 }
137
138 /// Returns the inner value.
139 ///
140 /// This must only be called if ``IsReady()`` returned ``true``.
value()141 constexpr T& value() & noexcept { return *value_; }
value()142 constexpr const T& value() const& noexcept { return *value_; }
value()143 constexpr T&& value() && noexcept { return std::move(*value_); }
value()144 constexpr const T&& value() const&& noexcept { return std::move(*value_); }
145
146 /// Accesses the inner value.
147 ///
148 /// This must only be called if ``IsReady()`` returned ``true``.
149 constexpr const T* operator->() const noexcept { return &*value_; }
150 constexpr T* operator->() noexcept { return &*value_; }
151
152 /// Returns the inner value.
153 ///
154 /// This must only be called if ``IsReady()`` returned ``true``.
155 constexpr const T& operator*() const& noexcept { return *value_; }
156 constexpr T& operator*() & noexcept { return *value_; }
157 constexpr const T&& operator*() const&& noexcept {
158 return std::move(*value_);
159 }
160 constexpr T&& operator*() && noexcept { return std::move(*value_); }
161
162 /// Ignores the ``Poll`` value.
163 ///
164 /// This method does nothing except prevent ``no_discard`` or
165 /// unused variable warnings from firing.
IgnorePoll()166 constexpr void IgnorePoll() const {}
167
168 private:
169 template <typename U>
170 friend class Poll;
171 std::optional<T> value_;
172 };
173
174 // Deduction guide to allow ``Poll(v)`` rather than ``Poll<T>(v)``.
175 template <typename T>
176 Poll(T value) -> Poll<T>;
177
178 /// Returns whether two instances of ``Poll<T>`` are equal.
179 ///
180 /// Note that this comparison operator will return ``true`` if both
181 /// values are currently ``Pending``, even if the eventual results
182 /// of each operation might differ.
183 template <typename T>
184 constexpr bool operator==(const Poll<T>& lhs, const Poll<T>& rhs) {
185 if (lhs.IsReady() && rhs.IsReady()) {
186 return *lhs == *rhs;
187 }
188 return lhs.IsReady() == rhs.IsReady();
189 }
190
191 /// Returns whether two instances of ``Poll<T>`` are unequal.
192 ///
193 /// Note that this comparison operator will return ``false`` if both
194 /// values are currently ``Pending``, even if the eventual results
195 /// of each operation might differ.
196 template <typename T>
197 constexpr bool operator!=(const Poll<T>& lhs, const Poll<T>& rhs) {
198 return !(lhs == rhs);
199 }
200
201 /// Returns whether ``lhs`` is pending.
202 template <typename T>
203 constexpr bool operator==(const Poll<T>& lhs, PendingType) {
204 return lhs.IsPending();
205 }
206
207 /// Returns whether ``lhs`` is not pending.
208 template <typename T>
209 constexpr bool operator!=(const Poll<T>& lhs, PendingType) {
210 return !lhs.IsPending();
211 }
212
213 /// Returns whether ``rhs`` is pending.
214 template <typename T>
215 constexpr bool operator==(PendingType, const Poll<T>& rhs) {
216 return rhs.IsPending();
217 }
218
219 /// Returns whether ``rhs`` is not pending.
220 template <typename T>
221 constexpr bool operator!=(PendingType, const Poll<T>& rhs) {
222 return !rhs.IsPending();
223 }
224
225 // ``ReadyType`` is the value type for `Poll<T>` and has no value,
226 // so it should always compare equal.
227 constexpr bool operator==(ReadyType, ReadyType) { return true; }
228 constexpr bool operator!=(ReadyType, ReadyType) { return false; }
229
230 // The ``Pending`` case holds no value, so is always equal.
231 constexpr bool operator==(PendingType, PendingType) { return true; }
232 constexpr bool operator!=(PendingType, PendingType) { return false; }
233
234 /// Returns a value indicating completion.
Ready()235 inline constexpr Poll<> Ready() { return Poll(ReadyType{}); }
236
237 /// Returns a value indicating completion with some result
238 /// (constructed in-place).
239 template <typename T, typename... Args>
Ready(std::in_place_t,Args &&...args)240 constexpr Poll<T> Ready(std::in_place_t, Args&&... args) {
241 return Poll<T>(std::in_place, std::forward<Args>(args)...);
242 }
243
244 /// Returns a value indicating completion with some result.
245 template <typename T>
Ready(T && value)246 constexpr Poll<T> Ready(T&& value) {
247 return Poll<T>(std::forward<T>(value));
248 }
249
250 /// Returns a value indicating that an operation was not yet able to complete.
Pending()251 inline constexpr PendingType Pending() { return PendingType(); }
252
253 #undef PW_NODISCARD_STR
254
255 } // namespace async2
256
257 // --- ToString implementations for ``Poll`` types ---
258
259 template <>
ToString(const async2::ReadyType &,span<char> buffer)260 inline StatusWithSize ToString(const async2::ReadyType&, span<char> buffer) {
261 return ToString("Ready", buffer);
262 }
263
264 template <>
ToString(const async2::PendingType &,span<char> buffer)265 inline StatusWithSize ToString(const async2::PendingType&, span<char> buffer) {
266 return ToString("Pending", buffer);
267 }
268
269 // Implement ``ToString`` for ``Poll<T>``.
270 template <typename T>
ToString(const async2::Poll<T> & poll,span<char> buffer)271 inline StatusWithSize ToString(const async2::Poll<T>& poll, span<char> buffer) {
272 if (poll.IsReady()) {
273 StatusWithSize s;
274 s.UpdateAndAdd(ToString("Ready(", buffer));
275 s.UpdateAndAdd(ToString(*poll, buffer.subspan(s.size())));
276 s.UpdateAndAdd(ToString(")", buffer.subspan(s.size())));
277 s.ZeroIfNotOk();
278 return s;
279 }
280 return ToString(async2::PendingType{}, buffer);
281 }
282
283 template <>
ToString(const async2::Poll<> & poll,span<char> buffer)284 inline StatusWithSize ToString(const async2::Poll<>& poll, span<char> buffer) {
285 if (poll.IsReady()) {
286 return ToString(async2::ReadyType{}, buffer);
287 }
288 return ToString(async2::PendingType{}, buffer);
289 }
290
291 } // namespace pw
292