• 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 <grpc/support/log.h>
24 
25 #include "src/core/lib/gprpp/construct_destruct.h"
26 
27 namespace grpc_core {
28 
29 // A type that signals a Promise is still pending and not yet completed.
30 // Allows writing 'return Pending{}' and with automatic conversions gets
31 // upgraded to a Poll<> object.
32 struct Pending {};
33 inline bool operator==(const Pending&, const Pending&) { return true; }
34 
35 // A type that contains no value. Useful for simulating 'void' in promises that
36 // always need to return some kind of value.
37 struct Empty {};
38 inline bool operator==(const Empty&, const Empty&) { return true; }
39 
40 // The result of polling a Promise once.
41 //
42 // Can be either pending - the Promise has not yet completed, or ready -
43 // indicating that the Promise has completed AND should not be polled again.
44 template <typename T>
45 class Poll {
46  public:
47   // NOLINTNEXTLINE(google-explicit-constructor)
Poll(Pending)48   Poll(Pending) : ready_(false) {}
Poll()49   Poll() : ready_(false) {}
Poll(const Poll & other)50   Poll(const Poll& other) : ready_(other.ready_) {
51     if (ready_) Construct(&value_, other.value_);
52   }
Poll(Poll && other)53   Poll(Poll&& other) noexcept : ready_(other.ready_) {
54     if (ready_) Construct(&value_, std::move(other.value_));
55   }
56   Poll& operator=(const Poll& other) {
57     if (ready_) {
58       if (other.ready_) {
59         value_ = other.value_;
60       } else {
61         Destruct(&value_);
62         ready_ = false;
63       }
64     } else if (other.ready_) {
65       Construct(&value_, other.value_);
66       ready_ = true;
67     }
68     return *this;
69   }
70   Poll& operator=(Poll&& other) noexcept {
71     if (ready_) {
72       if (other.ready_) {
73         value_ = std::move(other.value_);
74       } else {
75         Destruct(&value_);
76         ready_ = false;
77       }
78     } else if (other.ready_) {
79       Construct(&value_, std::move(other.value_));
80       ready_ = true;
81     }
82     return *this;
83   }
84   template <typename U>
85   // NOLINTNEXTLINE(google-explicit-constructor)
Poll(U value)86   Poll(U value) : ready_(true) {
87     Construct(&value_, std::move(value));
88   }
89   // NOLINTNEXTLINE(google-explicit-constructor)
Poll(T && value)90   Poll(T&& value) : ready_(true) { Construct(&value_, std::forward<T>(value)); }
~Poll()91   ~Poll() {
92     if (ready_) Destruct(&value_);
93   }
94 
pending()95   bool pending() const { return !ready_; }
ready()96   bool ready() const { return ready_; }
97 
value()98   T& value() {
99     GPR_DEBUG_ASSERT(ready());
100     return value_;
101   }
102 
value()103   const T& value() const {
104     GPR_DEBUG_ASSERT(ready());
105     return value_;
106   }
107 
value_if_ready()108   T* value_if_ready() {
109     if (ready()) return &value_;
110     return nullptr;
111   }
112 
value_if_ready()113   const T* value_if_ready() const {
114     if (ready()) return &value_;
115     return nullptr;
116   }
117 
118  private:
119   // Flag indicating readiness, followed by an optional value.
120   //
121   // Why not optional<T>?
122   //
123   // We have cases where we want to return absl::nullopt{} from a promise, and
124   // have that upgraded to a Poll<absl::nullopt_t> prior to a cast to some
125   // Poll<optional<T>>.
126   //
127   // Since optional<nullopt_t> is not allowed, we'd not be allowed to make
128   // Poll<nullopt_t> and so we'd need to pollute all poll handling code with
129   // some edge case handling template magic - the complexity would explode and
130   // grow over time - versus hand coding the pieces we need here and containing
131   // that quirk to one place.
132   bool ready_;
133   // We do a single element union so we can choose when to construct/destruct
134   // this value.
135   union {
136     T value_;
137   };
138 };
139 
140 template <>
141 class Poll<Empty> {
142  public:
143   // NOLINTNEXTLINE(google-explicit-constructor)
Poll(Pending)144   Poll(Pending) : ready_(false) {}
Poll()145   Poll() : ready_(false) {}
146   Poll(const Poll& other) = default;
147   Poll(Poll&& other) noexcept = default;
148   Poll& operator=(const Poll& other) = default;
149   Poll& operator=(Poll&& other) = default;
150   // NOLINTNEXTLINE(google-explicit-constructor)
Poll(Empty)151   Poll(Empty) : ready_(true) {}
152   ~Poll() = default;
153 
pending()154   bool pending() const { return !ready_; }
ready()155   bool ready() const { return ready_; }
156 
value()157   Empty value() const {
158     GPR_DEBUG_ASSERT(ready());
159     return Empty{};
160   }
161 
value_if_ready()162   Empty* value_if_ready() {
163     static Empty value;
164     if (ready()) return &value;
165     return nullptr;
166   }
167 
value_if_ready()168   const Empty* value_if_ready() const {
169     static Empty value;
170     if (ready()) return &value;
171     return nullptr;
172   }
173 
174  private:
175   // Flag indicating readiness.
176   bool ready_;
177 };
178 
179 // Ensure degenerate cases are not defined:
180 
181 // Can't poll for a Pending
182 template <>
183 class Poll<Pending>;
184 
185 // Can't poll for a poll
186 template <class T>
187 class Poll<Poll<T>>;
188 
189 // PollTraits tells us whether a type is Poll<> or some other type, and is
190 // leveraged in the PromiseLike/PromiseFactory machinery to select the
191 // appropriate implementation of those concepts based upon the return type of a
192 // lambda, for example (via enable_if).
193 template <typename T>
194 struct PollTraits {
195   using Type = T;
is_pollPollTraits196   static constexpr bool is_poll() { return false; }
197 };
198 
199 template <typename T>
200 struct PollTraits<Poll<T>> {
201   using Type = T;
202   static constexpr bool is_poll() { return true; }
203 };
204 
205 template <typename T>
206 bool operator==(const Poll<T>& a, const Poll<T>& b) {
207   if (a.pending() && b.pending()) return true;
208   if (a.ready() && b.ready()) return a.value() == b.value();
209   return false;
210 }
211 
212 template <typename T, typename U, typename SfinaeVoid = void>
213 struct PollCastImpl;
214 
215 template <typename T, typename U>
216 struct PollCastImpl<T, Poll<U>> {
217   static Poll<T> Cast(Poll<U>&& poll) {
218     if (poll.pending()) return Pending{};
219     return static_cast<T>(std::move(poll.value()));
220   }
221 };
222 
223 template <typename T, typename U>
224 struct PollCastImpl<T, U, std::enable_if<!PollTraits<U>::is_poll()>> {
225   static Poll<T> Cast(U&& poll) { return Poll<T>(T(std::move(poll))); }
226 };
227 
228 template <typename T>
229 struct PollCastImpl<T, T> {
230   static Poll<T> Cast(T&& poll) { return Poll<T>(std::move(poll)); }
231 };
232 
233 template <typename T>
234 struct PollCastImpl<T, Poll<T>> {
235   static Poll<T> Cast(Poll<T>&& poll) { return std::move(poll); }
236 };
237 
238 template <typename T, typename U>
239 Poll<T> poll_cast(U poll) {
240   return PollCastImpl<T, U>::Cast(std::move(poll));
241 }
242 
243 // Convert a poll to a string
244 template <typename T, typename F>
245 std::string PollToString(
246     const Poll<T>& poll,
247     F t_to_string = [](const T& t) { return t.ToString(); }) {
248   if (poll.pending()) {
249     return "<<pending>>";
250   }
251   return t_to_string(poll.value());
252 }
253 
254 }  // namespace grpc_core
255 
256 #endif  // GRPC_SRC_CORE_LIB_PROMISE_POLL_H
257