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
parent
5dc93179eb
commit
7ea742f3d5
8 changed files with 296 additions and 10 deletions
@ -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
|
@ -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(); |
||||
} |
Loading…
Reference in new issue