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