• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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