• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://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,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef GRPC_SRC_CORE_LIB_PROMISE_POLL_H
16 #define GRPC_SRC_CORE_LIB_PROMISE_POLL_H
17 
18 #include <grpc/support/port_platform.h>
19 
20 #include <string>
21 #include <utility>
22 
23 #include "absl/log/check.h"
24 #include "absl/strings/str_format.h"
25 #include "absl/types/optional.h"
26 #include "src/core/util/construct_destruct.h"
27 
28 namespace grpc_core {
29 
30 // A type that signals a Promise is still pending and not yet completed.
31 // Allows writing 'return Pending{}' and with automatic conversions gets
32 // upgraded to a Poll<> object.
33 struct Pending {};
34 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool operator==(const Pending&,
35                                                             const Pending&) {
36   return true;
37 }
38 
39 // A type that contains no value. Useful for simulating 'void' in promises that
40 // always need to return some kind of value.
41 struct Empty {};
42 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool operator==(const Empty&,
43                                                             const Empty&) {
44   return true;
45 }
46 template <typename Sink>
AbslStringify(Sink & sink,Empty)47 void AbslStringify(Sink& sink, Empty) {
48   sink.Append("{}");
49 }
50 
51 // The result of polling a Promise once.
52 //
53 // Can be either pending - the Promise has not yet completed, or ready -
54 // indicating that the Promise has completed AND should not be polled again.
55 template <typename T>
56 class Poll {
57  public:
58   // NOLINTNEXTLINE(google-explicit-constructor)
Poll(Pending)59   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll(Pending) : ready_(false) {}
Poll()60   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll() : ready_(false) {}
Poll(const Poll & other)61   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll(const Poll& other)
62       : ready_(other.ready_) {
63     if (ready_) Construct(&value_, other.value_);
64   }
Poll(Poll && other)65   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll(Poll&& other) noexcept
66       : ready_(other.ready_) {
67     if (ready_) Construct(&value_, std::move(other.value_));
68   }
69   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll& operator=(const Poll& other) {
70     if (ready_) {
71       if (other.ready_) {
72         value_ = other.value_;
73       } else {
74         Destruct(&value_);
75         ready_ = false;
76       }
77     } else if (other.ready_) {
78       Construct(&value_, other.value_);
79       ready_ = true;
80     }
81     return *this;
82   }
83   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll& operator=(Poll&& other) noexcept {
84     if (ready_) {
85       if (other.ready_) {
86         value_ = std::move(other.value_);
87       } else {
88         Destruct(&value_);
89         ready_ = false;
90       }
91     } else if (other.ready_) {
92       Construct(&value_, std::move(other.value_));
93       ready_ = true;
94     }
95     return *this;
96   }
97   template <typename U>
98   // NOLINTNEXTLINE(google-explicit-constructor)
Poll(U value)99   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll(U value) : ready_(true) {
100     Construct(&value_, std::move(value));
101   }
102   // NOLINTNEXTLINE(google-explicit-constructor)
Poll(T && value)103   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll(T&& value) : ready_(true) {
104     Construct(&value_, std::forward<T>(value));
105   }
~Poll()106   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION ~Poll() {
107     if (ready_) Destruct(&value_);
108   }
109 
pending()110   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION bool pending() const { return !ready_; }
ready()111   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION bool ready() const { return ready_; }
112 
value()113   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION T& value() {
114     DCHECK(ready());
115     return value_;
116   }
117 
value()118   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION const T& value() const {
119     DCHECK(ready());
120     return value_;
121   }
122 
value_if_ready()123   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION T* value_if_ready() {
124     if (ready()) return &value_;
125     return nullptr;
126   }
127 
value_if_ready()128   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION const T* value_if_ready() const {
129     if (ready()) return &value_;
130     return nullptr;
131   }
132 
133  private:
134   // Flag indicating readiness, followed by an optional value.
135   //
136   // Why not optional<T>?
137   //
138   // We have cases where we want to return absl::nullopt{} from a promise, and
139   // have that upgraded to a Poll<absl::nullopt_t> prior to a cast to some
140   // Poll<optional<T>>.
141   //
142   // Since optional<nullopt_t> is not allowed, we'd not be allowed to make
143   // Poll<nullopt_t> and so we'd need to pollute all poll handling code with
144   // some edge case handling template magic - the complexity would explode and
145   // grow over time - versus hand coding the pieces we need here and containing
146   // that quirk to one place.
147   bool ready_;
148   // We do a single element union so we can choose when to construct/destruct
149   // this value.
150   union {
151     T value_;
152   };
153 };
154 
155 template <>
156 class Poll<Empty> {
157  public:
158   // NOLINTNEXTLINE(google-explicit-constructor)
Poll(Pending)159   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll(Pending) : ready_(false) {}
Poll()160   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll() : ready_(false) {}
161   Poll(const Poll& other) = default;
162   Poll(Poll&& other) noexcept = default;
163   Poll& operator=(const Poll& other) = default;
164   Poll& operator=(Poll&& other) = default;
165   // NOLINTNEXTLINE(google-explicit-constructor)
Poll(Empty)166   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll(Empty) : ready_(true) {}
167   ~Poll() = default;
168 
pending()169   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION bool pending() const { return !ready_; }
ready()170   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION bool ready() const { return ready_; }
171 
value()172   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Empty value() const {
173     DCHECK(ready());
174     return Empty{};
175   }
176 
value_if_ready()177   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Empty* value_if_ready() {
178     static Empty value;
179     if (ready()) return &value;
180     return nullptr;
181   }
182 
value_if_ready()183   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION const Empty* value_if_ready() const {
184     static Empty value;
185     if (ready()) return &value;
186     return nullptr;
187   }
188 
189  private:
190   // Flag indicating readiness.
191   bool ready_;
192 };
193 
194 // Ensure degenerate cases are not defined:
195 
196 // Can't poll for a Pending
197 template <>
198 class Poll<Pending>;
199 
200 // Can't poll for a poll
201 template <class T>
202 class Poll<Poll<T>>;
203 
204 // PollTraits tells us whether a type is Poll<> or some other type, and is
205 // leveraged in the PromiseLike/PromiseFactory machinery to select the
206 // appropriate implementation of those concepts based upon the return type of a
207 // lambda, for example (via enable_if).
208 template <typename T>
209 struct PollTraits {
210   using Type = T;
is_pollPollTraits211   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static constexpr bool is_poll() {
212     return false;
213   }
214 };
215 
216 template <typename T>
217 struct PollTraits<Poll<T>> {
218   using Type = T;
219   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static constexpr bool is_poll() {
220     return true;
221   }
222 };
223 
224 template <typename T>
225 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool operator==(const Poll<T>& a,
226                                                             const Poll<T>& b) {
227   if (a.pending() && b.pending()) return true;
228   if (a.ready() && b.ready()) return a.value() == b.value();
229   return false;
230 }
231 
232 template <typename T, typename U, typename SfinaeVoid = void>
233 struct PollCastImpl;
234 
235 template <typename T, typename U>
236 struct PollCastImpl<T, Poll<U>> {
237   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Poll<T> Cast(Poll<U>&& poll) {
238     if (poll.pending()) return Pending{};
239     return static_cast<T>(std::move(poll.value()));
240   }
241 };
242 
243 template <typename T, typename U>
244 struct PollCastImpl<T, U, std::enable_if<!PollTraits<U>::is_poll()>> {
245   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Poll<T> Cast(U&& poll) {
246     return Poll<T>(T(std::move(poll)));
247   }
248 };
249 
250 template <typename T>
251 struct PollCastImpl<T, T> {
252   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Poll<T> Cast(T&& poll) {
253     return Poll<T>(std::move(poll));
254   }
255 };
256 
257 template <typename T>
258 struct PollCastImpl<T, Poll<T>> {
259   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static Poll<T> Cast(Poll<T>&& poll) {
260     return std::move(poll);
261   }
262 };
263 
264 template <typename T, typename U>
265 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline Poll<T> poll_cast(U poll) {
266   return PollCastImpl<T, U>::Cast(std::move(poll));
267 }
268 
269 // Convert a poll to a string
270 template <typename T, typename F>
271 std::string PollToString(
272     const Poll<T>& poll,
273     F t_to_string = [](const T& t) { return t.ToString(); }) {
274   if (poll.pending()) {
275     return "<<pending>>";
276   }
277   return t_to_string(poll.value());
278 }
279 
280 template <typename Sink, typename T>
281 void AbslStringify(Sink& sink, const Poll<T>& poll) {
282   if (poll.pending()) {
283     absl::Format(&sink, "<<pending>>");
284     return;
285   }
286   absl::Format(&sink, "%v", poll.value());
287 }
288 
289 template <typename Sink, typename T>
290 void AbslStringify(Sink& sink, const Poll<absl::StatusOr<T>>& poll) {
291   if (poll.pending()) {
292     absl::Format(&sink, "<<pending>>");
293     return;
294   }
295   if (poll.value().ok()) {
296     absl::Format(&sink, "%v", *poll.value());
297   } else {
298     absl::Format(&sink, "%v", poll.value().status());
299   }
300 }
301 
302 template <typename Sink, typename T>
303 void AbslStringify(Sink& sink, const Poll<absl::optional<T>>& poll) {
304   if (poll.pending()) {
305     absl::Format(&sink, "<<pending>>");
306     return;
307   }
308   const auto& value = poll.value();
309   if (value.has_value()) {
310     absl::Format(&sink, "%v", value);
311   } else {
312     sink.append("nullopt");
313   }
314 }
315 
316 // Hack to get metadata printing
317 template <typename Sink, typename T, typename Deleter>
318 void AbslStringify(
319     Sink& sink, const Poll<absl::optional<std::unique_ptr<T, Deleter>>>& poll) {
320   if (poll.pending()) {
321     absl::Format(&sink, "<<pending>>");
322     return;
323   }
324   const auto& value = poll.value();
325   if (value.has_value()) {
326     absl::Format(&sink, "%v", *value);
327   } else {
328     sink.Append("nullopt");
329   }
330 }
331 
332 }  // namespace grpc_core
333 
334 #endif  // GRPC_SRC_CORE_LIB_PROMISE_POLL_H
335