• 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_MAP_H
16 #define GRPC_SRC_CORE_LIB_PROMISE_MAP_H
17 
18 #include <grpc/support/port_platform.h>
19 #include <stddef.h>
20 
21 #include <tuple>
22 #include <utility>
23 
24 #include "absl/status/status.h"
25 #include "absl/status/statusor.h"
26 #include "absl/strings/str_cat.h"
27 #include "src/core/lib/promise/detail/promise_like.h"
28 #include "src/core/lib/promise/poll.h"
29 
30 namespace grpc_core {
31 
32 namespace promise_detail {
33 
34 // Implementation of mapping combinator - use this via the free function below!
35 // Promise is the type of promise to poll on, Fn is a function that takes the
36 // result of Promise and maps it to some new type.
37 template <typename Promise, typename Fn, typename SfinaeVoid = void>
38 class Map;
39 
40 template <typename Promise, typename Fn>
41 class Map<Promise, Fn,
42           absl::enable_if_t<!std::is_void<
43 #if (defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L) || \
44     (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
45               std::invoke_result_t<Fn, typename PromiseLike<Promise>::Result>
46 #else
47               typename std::result_of<Fn(
48                   typename PromiseLike<Promise>::Result)>::type
49 #endif
50               >::value>> {
51  public:
Map(Promise promise,Fn fn)52   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Map(Promise promise, Fn fn)
53       : promise_(std::move(promise)), fn_(std::move(fn)) {}
54 
55   Map(const Map&) = delete;
56   Map& operator=(const Map&) = delete;
57   // NOLINTNEXTLINE(performance-noexcept-move-constructor): clang6 bug
58   Map(Map&& other) = default;
59   // NOLINTNEXTLINE(performance-noexcept-move-constructor): clang6 bug
60   Map& operator=(Map&& other) = default;
61 
62   using PromiseResult = typename PromiseLike<Promise>::Result;
63   using Result =
64       RemoveCVRef<decltype(std::declval<Fn>()(std::declval<PromiseResult>()))>;
65 
operator()66   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll<Result> operator()() {
67     Poll<PromiseResult> r = promise_();
68     if (auto* p = r.value_if_ready()) {
69       return fn_(std::move(*p));
70     }
71     return Pending();
72   }
73 
74  private:
75   PromiseLike<Promise> promise_;
76   Fn fn_;
77 };
78 
79 template <typename Promise, typename Fn>
80 class Map<Promise, Fn,
81           absl::enable_if_t<std::is_void<
82 #if (defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L) || \
83     (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
84               std::invoke_result_t<Fn, typename PromiseLike<Promise>::Result>
85 #else
86               typename std::result_of<Fn(
87                   typename PromiseLike<Promise>::Result)>::type
88 #endif
89               >::value>> {
90  public:
Map(Promise promise,Fn fn)91   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Map(Promise promise, Fn fn)
92       : promise_(std::move(promise)), fn_(std::move(fn)) {}
93 
94   Map(const Map&) = delete;
95   Map& operator=(const Map&) = delete;
96   // NOLINTNEXTLINE(performance-noexcept-move-constructor): clang6 bug
97   Map(Map&& other) = default;
98   // NOLINTNEXTLINE(performance-noexcept-move-constructor): clang6 bug
99   Map& operator=(Map&& other) = default;
100 
101   using PromiseResult = typename PromiseLike<Promise>::Result;
102   using Result = Empty;
103 
operator()104   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll<Result> operator()() {
105     Poll<PromiseResult> r = promise_();
106     if (auto* p = r.value_if_ready()) {
107       fn_(std::move(*p));
108       return Empty{};
109     }
110     return Pending();
111   }
112 
113  private:
114   PromiseLike<Promise> promise_;
115   Fn fn_;
116 };
117 
118 }  // namespace promise_detail
119 
120 // Mapping combinator.
121 // Takes a promise, and a synchronous function to mutate its result, and
122 // returns a promise.
123 template <typename Promise, typename Fn>
124 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline promise_detail::Map<Promise, Fn>
Map(Promise promise,Fn fn)125 Map(Promise promise, Fn fn) {
126   return promise_detail::Map<Promise, Fn>(std::move(promise), std::move(fn));
127 }
128 
129 // Maps a promise to a new promise that returns a tuple of the original result
130 // and a bool indicating whether there was ever a Pending{} value observed from
131 // polling.
132 template <typename Promise>
CheckDelayed(Promise promise)133 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline auto CheckDelayed(Promise promise) {
134   using P = promise_detail::PromiseLike<Promise>;
135   return [delayed = false, promise = P(std::move(promise))]() mutable
136              -> Poll<std::tuple<typename P::Result, bool>> {
137     auto r = promise();
138     if (r.pending()) {
139       delayed = true;
140       return Pending{};
141     }
142     return std::make_tuple(std::move(r.value()), delayed);
143   };
144 }
145 
146 // Callable that takes a tuple and returns one element
147 template <size_t kElem>
148 struct JustElem {
149   template <typename... A>
150   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION auto operator()(std::tuple<A...>&& t)
151       const -> decltype(std::get<kElem>(std::forward<std::tuple<A...>>(t))) {
152     return std::get<kElem>(std::forward<std::tuple<A...>>(t));
153   }
154   template <typename... A>
155   GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION auto operator()(
156       const std::tuple<A...>& t) const -> decltype(std::get<kElem>(t)) {
157     return std::get<kElem>(t);
158   }
159 };
160 
161 namespace promise_detail {
162 template <typename Fn>
163 class MapError {
164  public:
MapError(Fn fn)165   explicit MapError(Fn fn) : fn_(std::move(fn)) {}
operator()166   absl::Status operator()(absl::Status status) {
167     if (status.ok()) return status;
168     return fn_(std::move(status));
169   }
170   template <typename T>
operator()171   absl::StatusOr<T> operator()(absl::StatusOr<T> status) {
172     if (status.ok()) return status;
173     return fn_(std::move(status.status()));
174   }
175 
176  private:
177   Fn fn_;
178 };
179 }  // namespace promise_detail
180 
181 // Map status->better status in the case of errors
182 template <typename Promise, typename Fn>
MapErrors(Promise promise,Fn fn)183 auto MapErrors(Promise promise, Fn fn) {
184   return Map(std::move(promise), promise_detail::MapError<Fn>(std::move(fn)));
185 }
186 
187 // Simple mapper to add a prefix to the message of an error
188 template <typename Promise>
AddErrorPrefix(absl::string_view prefix,Promise promise)189 auto AddErrorPrefix(absl::string_view prefix, Promise promise) {
190   return MapErrors(std::move(promise), [prefix](absl::Status status) {
191     absl::Status out(status.code(), absl::StrCat(prefix, status.message()));
192     status.ForEachPayload(
193         [&out](absl::string_view name, const absl::Cord& value) {
194           out.SetPayload(name, value);
195         });
196     return out;
197   });
198 }
199 
200 }  // namespace grpc_core
201 
202 #endif  // GRPC_SRC_CORE_LIB_PROMISE_MAP_H
203