Promise if construct (#26914)

* Poll type for promises library

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

* Library to talk about things that make promises

* Promises if construct

* build

* Automated change: Fix sanity tests

Co-authored-by: ctiller <ctiller@users.noreply.github.com>
reviewable/pr27031/r1
Craig Tiller 4 years ago committed by GitHub
parent 5dc93179eb
commit 7ea742f3d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      BUILD
  2. 37
      CMakeLists.txt
  3. 15
      build_autogenerated.yaml
  4. 17
      src/core/lib/promise/detail/promise_factory.h
  5. 130
      src/core/lib/promise/if.h
  6. 12
      test/core/promise/BUILD
  7. 57
      test/core/promise/if_test.cc
  8. 24
      tools/run_tests/generated/tests.json

14
BUILD

@ -908,6 +908,20 @@ grpc_cc_library(
],
)
grpc_cc_library(
name = "if",
external_deps = [
"absl/status:statusor",
],
language = "c++",
public_hdrs = ["src/core/lib/promise/if.h"],
deps = [
"gpr_platform",
"poll",
"promise_factory",
],
)
grpc_cc_library(
name = "promise_status",
external_deps = [

37
CMakeLists.txt generated

@ -886,6 +886,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx hpack_encoder_index_test)
add_dependencies(buildtests_cxx http2_client)
add_dependencies(buildtests_cxx hybrid_end2end_test)
add_dependencies(buildtests_cxx if_test)
add_dependencies(buildtests_cxx init_test)
add_dependencies(buildtests_cxx initial_settings_frame_bad_client_test)
add_dependencies(buildtests_cxx insecure_security_connector_test)
@ -11960,6 +11961,42 @@ target_link_libraries(hybrid_end2end_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(if_test
test/core/promise/if_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(if_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(if_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::statusor
absl::variant
)
endif()
if(gRPC_BUILD_TESTS)

@ -5681,6 +5681,21 @@ targets:
- test/cpp/end2end/test_service_impl.cc
deps:
- grpc++_test_util
- name: if_test
gtest: true
build: test
language: c++
headers:
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/if.h
- src/core/lib/promise/poll.h
src:
- test/core/promise/if_test.cc
deps:
- absl/status:statusor
- absl/types:variant
uses_polling: false
- name: init_test
gtest: true
build: test

@ -162,16 +162,16 @@ class PromiseFactory {
public:
using Arg = A;
using Promise =
decltype(PromiseFactoryImpl(std::move(f_), std::declval<A>()));
explicit PromiseFactory(F f) : f_(std::move(f)) {}
auto Once(Arg&& a)
-> decltype(PromiseFactoryImpl(std::move(f_), std::forward<Arg>(a))) {
Promise Once(Arg&& a) {
return PromiseFactoryImpl(std::move(f_), std::forward<Arg>(a));
}
auto Repeated(Arg&& a) const
-> decltype(PromiseFactoryImpl(f_, std::forward<Arg>(a))) {
Promise Repeated(Arg&& a) const {
return PromiseFactoryImpl(f_, std::forward<Arg>(a));
}
};
@ -183,16 +183,13 @@ class PromiseFactory<void, F> {
public:
using Arg = void;
using Promise = decltype(PromiseFactoryImpl(std::move(f_)));
explicit PromiseFactory(F f) : f_(std::move(f)) {}
auto Once() -> decltype(PromiseFactoryImpl(std::move(f_))) {
return PromiseFactoryImpl(std::move(f_));
}
Promise Once() { return PromiseFactoryImpl(std::move(f_)); }
auto Repeated() const -> decltype(PromiseFactoryImpl(f_)) {
return PromiseFactoryImpl(f_);
}
Promise Repeated() const { return PromiseFactoryImpl(f_); }
};
} // namespace promise_detail

@ -0,0 +1,130 @@
// 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_IF_H
#define GRPC_CORE_LIB_PROMISE_IF_H
#include <grpc/impl/codegen/port_platform.h>
#include "absl/status/statusor.h"
#include "absl/types/variant.h"
#include "src/core/lib/promise/detail/promise_factory.h"
#include "src/core/lib/promise/poll.h"
namespace grpc_core {
namespace promise_detail {
template <typename CallPoll, typename T, typename F>
typename CallPoll::PollResult ChooseIf(CallPoll call_poll, bool result,
T* if_true, F* if_false) {
if (result) {
auto promise = if_true->Once();
return call_poll(promise);
} else {
auto promise = if_false->Once();
return call_poll(promise);
}
}
template <typename CallPoll, typename T, typename F>
typename CallPoll::PollResult ChooseIf(CallPoll call_poll,
absl::StatusOr<bool> result, T* if_true,
F* if_false) {
if (!result.ok()) {
return typename CallPoll::PollResult(result.status());
} else if (*result) {
auto promise = if_true->Once();
return call_poll(promise);
} else {
auto promise = if_false->Once();
return call_poll(promise);
}
}
template <typename C, typename T, typename F>
class If {
private:
using TrueFactory = promise_detail::PromiseFactory<void, T>;
using FalseFactory = promise_detail::PromiseFactory<void, F>;
using ConditionPromise = PromiseLike<C>;
using TruePromise = typename TrueFactory::Promise;
using FalsePromise = typename FalseFactory::Promise;
using Result =
typename PollTraits<decltype(std::declval<TruePromise>()())>::Type;
public:
If(C condition, T if_true, F if_false)
: state_(Evaluating{ConditionPromise(std::move(condition)),
TrueFactory(std::move(if_true)),
FalseFactory(std::move(if_false))}) {}
Poll<Result> operator()() {
return absl::visit(CallPoll<false>{this}, state_);
}
private:
struct Evaluating {
ConditionPromise condition;
TrueFactory if_true;
FalseFactory if_false;
};
using State = absl::variant<Evaluating, TruePromise, FalsePromise>;
State state_;
template <bool kSetState>
struct CallPoll {
using PollResult = Poll<Result>;
If* const self;
PollResult operator()(Evaluating& evaluating) const {
static_assert(
!kSetState,
"shouldn't need to set state coming through the initial branch");
auto r = evaluating.condition();
if (auto* p = absl::get_if<kPollReadyIdx>(&r)) {
return ChooseIf(CallPoll<true>{self}, std::move(*p),
&evaluating.if_true, &evaluating.if_false);
}
return Pending();
}
template <class Promise>
PollResult operator()(Promise& promise) const {
auto r = promise();
if (kSetState && absl::holds_alternative<Pending>(r)) {
self->state_.template emplace<Promise>(std::move(promise));
}
return r;
}
};
};
} // namespace promise_detail
// If promise combinator.
// Takes 3 promise factories, and evaluates the first.
// If it returns failure, returns failure for the entire combinator.
// If it returns true, evaluates the second promise.
// If it returns false, evaluates the third promise.
template <typename C, typename T, typename F>
promise_detail::If<C, T, F> If(C condition, T if_true, F if_false) {
return promise_detail::If<C, T, F>(std::move(condition), std::move(if_true),
std::move(if_false));
}
} // namespace grpc_core
#endif // GRPC_CORE_LIB_PROMISE_IF_H

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

@ -0,0 +1,57 @@
// 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/if.h"
#include <gtest/gtest.h>
namespace grpc_core {
TEST(IfTest, ChooseTrue) {
EXPECT_EQ(If([]() { return true; }, []() { return 1; }, []() { return 2; })(),
Poll<int>(1));
}
TEST(IfTest, ChooseFalse) {
EXPECT_EQ(
If([]() { return false; }, []() { return 1; }, []() { return 2; })(),
Poll<int>(2));
}
TEST(IfTest, ChooseSuccesfulTrue) {
EXPECT_EQ(If([]() { return absl::StatusOr<bool>(true); },
[]() { return absl::StatusOr<int>(1); },
[]() { return absl::StatusOr<int>(2); })(),
Poll<absl::StatusOr<int>>(absl::StatusOr<int>(1)));
}
TEST(IfTest, ChooseSuccesfulFalse) {
EXPECT_EQ(If([]() { return absl::StatusOr<bool>(false); },
[]() { return absl::StatusOr<int>(1); },
[]() { return absl::StatusOr<int>(2); })(),
Poll<absl::StatusOr<int>>(absl::StatusOr<int>(2)));
}
TEST(IfTest, ChooseFailure) {
EXPECT_EQ(If([]() { return absl::StatusOr<bool>(); },
[]() { return absl::StatusOr<int>(1); },
[]() { return absl::StatusOr<int>(2); })(),
Poll<absl::StatusOr<int>>(absl::StatusOr<int>()));
}
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

@ -5083,6 +5083,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": "if_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save