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