Generic promise facilities (#26916)

* Poll type for promises library

* Library to talk about things that look like promises if you squint

* Promise helpers, and basic type erasure

* build

* Changes to sync required for promise activities

* sanitized

* Automated change: Fix sanity tests

* suppressions

* try to fix windows failure

* Automated change: Fix sanity tests

Co-authored-by: ctiller <ctiller@users.noreply.github.com>
reviewable/pr26954/r4
Craig Tiller 3 years ago committed by GitHub
parent bb4311baed
commit 2e56b42ada
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      BUILD
  2. 37
      CMakeLists.txt
  3. 14
      build_autogenerated.yaml
  4. 31
      src/core/lib/promise/detail/promise_like.h
  5. 86
      src/core/lib/promise/promise.h
  6. 12
      test/core/promise/BUILD
  7. 42
      test/core/promise/promise_test.cc
  8. 24
      tools/run_tests/generated/tests.json

16
BUILD

@ -856,6 +856,22 @@ grpc_cc_library(
deps = ["gpr_platform"],
)
grpc_cc_library(
name = "promise",
external_deps = [
"absl/types:optional",
],
language = "c++",
public_hdrs = [
"src/core/lib/promise/promise.h",
],
deps = [
"gpr_platform",
"poll",
"promise_like",
],
)
grpc_cc_library(
name = "promise_like",
language = "c++",

37
CMakeLists.txt generated

@ -914,6 +914,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx poll_test)
add_dependencies(buildtests_cxx popularity_count_test)
add_dependencies(buildtests_cxx port_sharing_end2end_test)
add_dependencies(buildtests_cxx promise_test)
add_dependencies(buildtests_cxx proto_server_reflection_test)
add_dependencies(buildtests_cxx proto_utils_test)
add_dependencies(buildtests_cxx qps_json_driver)
@ -12972,6 +12973,42 @@ target_link_libraries(port_sharing_end2end_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(promise_test
test/core/promise/promise_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(promise_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(promise_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::optional
absl::variant
)
endif()
if(gRPC_BUILD_TESTS)

@ -6047,6 +6047,20 @@ targets:
- test/cpp/end2end/test_service_impl.cc
deps:
- grpc++_test_util
- name: promise_test
gtest: true
build: test
language: c++
headers:
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/poll.h
- src/core/lib/promise/promise.h
src:
- test/core/promise/promise_test.cc
deps:
- absl/types:optional
- absl/types:variant
uses_polling: false
- name: proto_server_reflection_test
gtest: true
build: test

@ -47,31 +47,30 @@
namespace grpc_core {
namespace promise_detail {
template <typename T, typename Ignored = void>
class PromiseLike;
template <typename F>
class PromiseLike<F, typename absl::enable_if_t<PollTraits<decltype(
std::declval<F>()())>::is_poll()>> {
private:
GPR_NO_UNIQUE_ADDRESS F f_;
template <typename T>
struct PollWrapper {
static Poll<T> Wrap(T&& x) { return Poll<T>(std::forward<T>(x)); }
};
public:
explicit PromiseLike(F&& f) : f_(std::forward<F>(f)) {}
using Result = typename PollTraits<decltype(f_())>::Type;
Poll<Result> operator()() { return f_(); }
template <typename T>
struct PollWrapper<Poll<T>> {
static Poll<T> Wrap(Poll<T>&& x) { return x; }
};
template <typename T>
auto WrapInPoll(T&& x) -> decltype(PollWrapper<T>::Wrap(std::forward<T>(x))) {
return PollWrapper<T>::Wrap(std::forward<T>(x));
}
template <typename F>
class PromiseLike<F, typename absl::enable_if_t<!PollTraits<decltype(
std::declval<F>()())>::is_poll()>> {
class PromiseLike {
private:
GPR_NO_UNIQUE_ADDRESS F f_;
public:
explicit PromiseLike(F&& f) : f_(std::forward<F>(f)) {}
using Result = decltype(f_());
Poll<Result> operator()() { return f_(); }
auto operator()() -> decltype(WrapInPoll(f_())) { return WrapInPoll(f_()); }
using Result = typename PollTraits<decltype(WrapInPoll(f_()))>::Type;
};
} // namespace promise_detail

@ -0,0 +1,86 @@
// Copyright 2021 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GRPC_CORE_LIB_PROMISE_PROMISE_H
#define GRPC_CORE_LIB_PROMISE_PROMISE_H
#include <grpc/impl/codegen/port_platform.h>
#include <functional>
#include "absl/types/optional.h"
#include "src/core/lib/promise/detail/promise_like.h"
#include "src/core/lib/promise/poll.h"
namespace grpc_core {
// A Promise is any functor that takes no arguments and returns Poll<T>.
// Most of the time we just pass around the functor, but occasionally
// it pays to have a type erased variant, which we define here.
template <typename T>
using Promise = std::function<Poll<T>()>;
// Helper to execute a promise immediately and return either the result or
// nothing.
template <typename Promise>
auto NowOrNever(Promise promise)
-> absl::optional<typename promise_detail::PromiseLike<Promise>::Result> {
auto r = promise_detail::PromiseLike<Promise>(std::move(promise))();
if (auto* p = absl::get_if<kPollReadyIdx>(&r)) {
return std::move(*p);
}
return {};
}
// A promise that never completes.
template <typename T>
struct Never {
Poll<T> operator()() { return Pending(); }
};
namespace promise_detail {
// A promise that immediately completes.
template <typename T>
class Immediate {
public:
explicit Immediate(T value) : value_(std::move(value)) {}
Poll<T> operator()() { return std::move(value_); }
private:
T value_;
};
} // namespace promise_detail
// Return \a value immediately
template <typename T>
promise_detail::Immediate<T> Immediate(T value) {
return promise_detail::Immediate<T>(std::move(value));
}
// Typecheck that a promise returns the expected return type.
// usage: auto promise = WithResult<int>([]() { return 3; });
// NOTE: there are tests in promise_test.cc that are commented out because they
// should fail to compile. When modifying this code these should be uncommented
// and their miscompilation verified.
template <typename T, typename F>
auto WithResult(F f) ->
typename std::enable_if<std::is_same<decltype(f()), Poll<T>>::value,
F>::type {
return f;
}
} // namespace grpc_core
#endif // GRPC_CORE_LIB_PROMISE_PROMISE_H

@ -29,3 +29,15 @@ grpc_cc_test(
"//test/core/util:grpc_suppressions",
],
)
grpc_cc_test(
name = "promise_test",
srcs = ["promise_test.cc"],
external_deps = ["gtest"],
language = "c++",
uses_polling = False,
deps = [
"//:promise",
"//test/core/util:grpc_suppressions",
],
)

@ -0,0 +1,42 @@
// Copyright 2021 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/core/lib/promise/promise.h"
#include <gtest/gtest.h>
namespace grpc_core {
TEST(PromiseTest, Works) {
Promise<int> x = []() { return 42; };
EXPECT_EQ(x(), Poll<int>(42));
}
TEST(PromiseTest, Immediate) { EXPECT_EQ(Immediate(42)(), Poll<int>(42)); }
TEST(PromiseTest, WithResult) {
EXPECT_EQ(WithResult<int>(Immediate(42))(), Poll<int>(42));
// Fails to compile: WithResult<int>(Immediate(std::string("hello")));
// Fails to compile: WithResult<int>(Immediate(42.9));
}
TEST(PromiseTest, NowOrNever) {
EXPECT_EQ(NowOrNever(Immediate(42)), absl::optional<int>(42));
}
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

@ -5633,6 +5633,30 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "promise_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save