xref: /aosp_15_r20/external/pigweed/pw_async2/public/pw_async2/poll.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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   using OutputType = T;
57 
58   /// Basic constructors.
59   Poll() = delete;
60   constexpr Poll(const Poll&) = default;
61   constexpr Poll& operator=(const Poll&) = default;
62   constexpr Poll(Poll&&) = default;
63   constexpr Poll& operator=(Poll&&) = default;
64 
65   /// Constructs a new ``Poll<T>`` from a ``Poll<U>`` where ``T`` is
66   /// constructible from ``U``.
67   ///
68   /// To avoid ambiguity, this constructor is disabled if ``T`` is also
69   /// constructible from ``Poll<U>``.
70   ///
71   /// This constructor is explicit if and only if the corresponding construction
72   /// of ``T`` from ``U`` is explicit.
73   template <typename U,
74             internal_poll::EnableIfImplicitlyConvertible<T, const U&> = 0>
Poll(const Poll<U> & other)75   constexpr Poll(const Poll<U>& other) : value_(other.value_) {}
76   template <typename U,
77             internal_poll::EnableIfExplicitlyConvertible<T, const U&> = 0>
Poll(const Poll<U> & other)78   explicit constexpr Poll(const Poll<U>& other) : value_(other.value_) {}
79 
80   template <typename U,
81             internal_poll::EnableIfImplicitlyConvertible<T, U&&> = 0>
Poll(Poll<U> && other)82   constexpr Poll(Poll<U>&& other)  // NOLINT
83       : value_(std::move(other.value_)) {}
84   template <typename U,
85             internal_poll::EnableIfExplicitlyConvertible<T, U&&> = 0>
Poll(Poll<U> && other)86   explicit constexpr Poll(Poll<U>&& other) : value_(std::move(other.value_)) {}
87 
88   // Constructs the inner value `T` in-place using the provided args, using the
89   // `T(U)` (direct-initialization) constructor. This constructor is only valid
90   // if `T` can be constructed from a `U`. Can accept move or copy constructors.
91   //
92   // This constructor is explicit if `U` is not convertible to `T`. To avoid
93   // ambiguity, this constructor is disabled if `U` is a `Poll<J>`, where
94   // `J` is convertible to `T`.
95   template <typename U = T,
96             internal_poll::EnableIfImplicitlyInitializable<T, U> = 0>
Poll(U && u)97   constexpr Poll(U&& u)  // NOLINT
98       : Poll(std::in_place, std::forward<U>(u)) {}
99 
100   template <typename U = T,
101             internal_poll::EnableIfExplicitlyInitializable<T, U> = 0>
Poll(U && u)102   explicit constexpr Poll(U&& u)  // NOLINT
103       : Poll(std::in_place, std::forward<U>(u)) {}
104 
105   // In-place construction of ``Ready`` variant.
106   template <typename... Args>
Poll(std::in_place_t,Args &&...args)107   constexpr Poll(std::in_place_t, Args&&... args)
108       : value_(std::in_place, std::move(args)...) {}
109 
110   // Convert from `T`
Poll(T && value)111   constexpr Poll(T&& value) : value_(std::move(value)) {}
112   constexpr Poll& operator=(T&& value) {
113     value_ = std::optional<T>(std::move(value));
114     return *this;
115   }
116 
117   // Convert from `Pending`
Poll(PendingType)118   constexpr Poll(PendingType) noexcept : value_() {}
119   constexpr Poll& operator=(PendingType) noexcept {
120     value_ = std::nullopt;
121     return *this;
122   }
123 
124   /// Returns whether or not this value is ``Ready``.
IsReady()125   constexpr bool IsReady() const noexcept { return value_.has_value(); }
126 
127   /// Returns whether or not this value is ``Pending``.
IsPending()128   constexpr bool IsPending() const noexcept { return !value_.has_value(); }
129 
130   /// Returns a ``Poll<>`` without the inner value whose readiness matches that
131   /// of ``this``.
Readiness()132   constexpr Poll<> Readiness() const noexcept {
133     if (IsReady()) {
134       return ReadyType();
135     } else {
136       return PendingType();
137     }
138   }
139 
140   /// Returns the inner value.
141   ///
142   /// This must only be called if ``IsReady()`` returned ``true``.
value()143   constexpr T& value() & noexcept { return *value_; }
value()144   constexpr const T& value() const& noexcept { return *value_; }
value()145   constexpr T&& value() && noexcept { return std::move(*value_); }
value()146   constexpr const T&& value() const&& noexcept { return std::move(*value_); }
147 
148   /// Accesses the inner value.
149   ///
150   /// This must only be called if ``IsReady()`` returned ``true``.
151   constexpr const T* operator->() const noexcept { return &*value_; }
152   constexpr T* operator->() noexcept { return &*value_; }
153 
154   /// Returns the inner value.
155   ///
156   /// This must only be called if ``IsReady()`` returned ``true``.
157   constexpr const T& operator*() const& noexcept { return *value_; }
158   constexpr T& operator*() & noexcept { return *value_; }
159   constexpr const T&& operator*() const&& noexcept {
160     return std::move(*value_);
161   }
162   constexpr T&& operator*() && noexcept { return std::move(*value_); }
163 
164   /// Ignores the ``Poll`` value.
165   ///
166   /// This method does nothing except prevent ``no_discard`` or
167   /// unused variable warnings from firing.
IgnorePoll()168   constexpr void IgnorePoll() const {}
169 
170  private:
171   template <typename U>
172   friend class Poll;
173   std::optional<T> value_;
174 };
175 
176 // Deduction guide to allow ``Poll(v)`` rather than ``Poll<T>(v)``.
177 template <typename T>
178 Poll(T value) -> Poll<T>;
179 
180 /// Returns whether two instances of ``Poll<T>`` are equal.
181 ///
182 /// Note that this comparison operator will return ``true`` if both
183 /// values are currently ``Pending``, even if the eventual results
184 /// of each operation might differ.
185 template <typename T>
186 constexpr bool operator==(const Poll<T>& lhs, const Poll<T>& rhs) {
187   if (lhs.IsReady() && rhs.IsReady()) {
188     return *lhs == *rhs;
189   }
190   return lhs.IsReady() == rhs.IsReady();
191 }
192 
193 /// Returns whether two instances of ``Poll<T>`` are unequal.
194 ///
195 /// Note that this comparison operator will return ``false`` if both
196 /// values are currently ``Pending``, even if the eventual results
197 /// of each operation might differ.
198 template <typename T>
199 constexpr bool operator!=(const Poll<T>& lhs, const Poll<T>& rhs) {
200   return !(lhs == rhs);
201 }
202 
203 /// Returns whether ``lhs`` is pending.
204 template <typename T>
205 constexpr bool operator==(const Poll<T>& lhs, PendingType) {
206   return lhs.IsPending();
207 }
208 
209 /// Returns whether ``lhs`` is not pending.
210 template <typename T>
211 constexpr bool operator!=(const Poll<T>& lhs, PendingType) {
212   return !lhs.IsPending();
213 }
214 
215 /// Returns whether ``rhs`` is pending.
216 template <typename T>
217 constexpr bool operator==(PendingType, const Poll<T>& rhs) {
218   return rhs.IsPending();
219 }
220 
221 /// Returns whether ``rhs`` is not pending.
222 template <typename T>
223 constexpr bool operator!=(PendingType, const Poll<T>& rhs) {
224   return !rhs.IsPending();
225 }
226 
227 // ``ReadyType`` is the value type for `Poll<T>` and has no value,
228 // so it should always compare equal.
229 constexpr bool operator==(ReadyType, ReadyType) { return true; }
230 constexpr bool operator!=(ReadyType, ReadyType) { return false; }
231 
232 // The ``Pending`` case holds no value, so is always equal.
233 constexpr bool operator==(PendingType, PendingType) { return true; }
234 constexpr bool operator!=(PendingType, PendingType) { return false; }
235 
236 /// Returns a value indicating completion.
Ready()237 inline constexpr Poll<> Ready() { return Poll(ReadyType{}); }
238 
239 /// Returns a value indicating completion with some result
240 /// (constructed in-place).
241 template <typename T, typename... Args>
Ready(std::in_place_t,Args &&...args)242 constexpr Poll<T> Ready(std::in_place_t, Args&&... args) {
243   return Poll<T>(std::in_place, std::forward<Args>(args)...);
244 }
245 
246 /// Returns a value indicating completion with some result.
247 template <typename T>
Ready(T && value)248 constexpr Poll<std::remove_reference_t<T>> Ready(T&& value) {
249   return Poll<std::remove_reference_t<T>>(std::forward<T>(value));
250 }
251 
252 /// Returns a value indicating that an operation was not yet able to complete.
Pending()253 inline constexpr PendingType Pending() { return PendingType(); }
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