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