Skip to content

Commit

Permalink
[promises] Fix order of eval bug in try_concurrently_test (#31490)
Browse files Browse the repository at this point in the history
* [promises] Fix order of eval bug in try_concurrently_test

* iwyu

* comment
  • Loading branch information
ctiller committed Oct 28, 2022
1 parent beecba8 commit b19f87d
Showing 1 changed file with 28 additions and 24 deletions.
52 changes: 28 additions & 24 deletions test/core/promise/try_concurrently_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <vector>

#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "gtest/gtest.h"

namespace grpc_core {
Expand All @@ -29,38 +30,41 @@ class PromiseFactory {
public:
// Create a promise that resolves to Ok but has a memory allocation (to verify
// destruction)
auto OkPromise(std::string tag) {
return [this, tag = std::move(tag),
// tag should have static lifetime (i.e. pass a string literal here).
auto OkPromise(absl::string_view tag) {
return [this, tag,
p = std::make_unique<absl::Status>(absl::OkStatus())]() mutable {
order_.push_back(tag);
return std::move(*p);
};
}

// Create a promise that never resolves and carries a memory allocation
auto NeverPromise(std::string tag) {
return [this, tag = std::move(tag),
p = std::make_unique<Pending>()]() -> Poll<absl::Status> {
order_.push_back(tag);
return *p;
};
// tag should have static lifetime (i.e. pass a string literal here).
auto NeverPromise(absl::string_view tag) {
return
[this, tag, p = std::make_unique<Pending>()]() -> Poll<absl::Status> {
order_.push_back(tag);
return *p;
};
}

// Create a promise that fails and carries a memory allocation
auto FailPromise(std::string tag) {
// tag should have static lifetime (i.e. pass a string literal here).
auto FailPromise(absl::string_view tag) {
return [this, p = std::make_unique<absl::Status>(absl::UnknownError(tag)),
tag = std::move(tag)]() mutable {
tag]() mutable {
order_.push_back(tag);
return std::move(*p);
};
}

// Finish one round and return a vector of strings representing which promises
// were polled and in which order.
std::vector<std::string> Finish() { return std::exchange(order_, {}); }
std::vector<absl::string_view> Finish() { return std::exchange(order_, {}); }

private:
std::vector<std::string> order_;
std::vector<absl::string_view> order_;
};

std::ostream& operator<<(std::ostream& out, const Poll<absl::Status>& p) {
Expand All @@ -72,54 +76,54 @@ TEST(TryConcurrentlyTest, Immediate) {
PromiseFactory pf;
auto a = TryConcurrently(pf.OkPromise("1"));
EXPECT_EQ(a(), Poll<absl::Status>(absl::OkStatus()));
EXPECT_EQ(pf.Finish(), std::vector<std::string>({"1"}));
EXPECT_EQ(pf.Finish(), std::vector<absl::string_view>({"1"}));
auto b = TryConcurrently(pf.OkPromise("1")).NecessaryPush(pf.OkPromise("2"));
EXPECT_EQ(b(), Poll<absl::Status>(absl::OkStatus()));
EXPECT_EQ(pf.Finish(), std::vector<std::string>({"2", "1"}));
EXPECT_EQ(pf.Finish(), std::vector<absl::string_view>({"2", "1"}));
auto c = TryConcurrently(pf.OkPromise("1")).NecessaryPull(pf.OkPromise("2"));
EXPECT_EQ(c(), Poll<absl::Status>(absl::OkStatus()));
EXPECT_EQ(pf.Finish(), std::vector<std::string>({"1", "2"}));
EXPECT_EQ(pf.Finish(), std::vector<absl::string_view>({"1", "2"}));
auto d = TryConcurrently(pf.OkPromise("1"))
.NecessaryPull(pf.OkPromise("2"))
.NecessaryPush(pf.OkPromise("3"));
EXPECT_EQ(d(), Poll<absl::Status>(absl::OkStatus()));
EXPECT_EQ(pf.Finish(), std::vector<std::string>({"3", "1", "2"}));
EXPECT_EQ(pf.Finish(), std::vector<absl::string_view>({"3", "1", "2"}));
auto e = TryConcurrently(pf.OkPromise("1")).Push(pf.NeverPromise("2"));
EXPECT_EQ(e(), Poll<absl::Status>(absl::OkStatus()));
EXPECT_EQ(pf.Finish(), std::vector<std::string>({"2", "1"}));
EXPECT_EQ(pf.Finish(), std::vector<absl::string_view>({"2", "1"}));
auto f = TryConcurrently(pf.OkPromise("1")).Pull(pf.NeverPromise("2"));
EXPECT_EQ(f(), Poll<absl::Status>(absl::OkStatus()));
EXPECT_EQ(pf.Finish(), std::vector<std::string>({"1", "2"}));
EXPECT_EQ(pf.Finish(), std::vector<absl::string_view>({"1", "2"}));
}

TEST(TryConcurrentlyTest, Paused) {
PromiseFactory pf;
auto a = TryConcurrently(pf.NeverPromise("1"));
EXPECT_EQ(a(), Poll<absl::Status>(Pending{}));
EXPECT_EQ(pf.Finish(), std::vector<std::string>({"1"}));
EXPECT_EQ(pf.Finish(), std::vector<absl::string_view>({"1"}));
auto b =
TryConcurrently(pf.OkPromise("1")).NecessaryPush(pf.NeverPromise("2"));
EXPECT_EQ(b(), Poll<absl::Status>(Pending{}));
EXPECT_EQ(pf.Finish(), std::vector<std::string>({"2", "1"}));
EXPECT_EQ(pf.Finish(), std::vector<absl::string_view>({"2", "1"}));
auto c =
TryConcurrently(pf.OkPromise("1")).NecessaryPull(pf.NeverPromise("2"));
EXPECT_EQ(c(), Poll<absl::Status>(Pending{}));
EXPECT_EQ(pf.Finish(), std::vector<std::string>({"1", "2"}));
EXPECT_EQ(pf.Finish(), std::vector<absl::string_view>({"1", "2"}));
}

TEST(TryConcurrentlyTest, OneFailed) {
PromiseFactory pf;
auto a = TryConcurrently(pf.FailPromise("bah"));
EXPECT_EQ(a(), Poll<absl::Status>(absl::UnknownError("bah")));
EXPECT_EQ(pf.Finish(), std::vector<std::string>({"bah"}));
EXPECT_EQ(pf.Finish(), std::vector<absl::string_view>({"bah"}));
auto b = TryConcurrently(pf.NeverPromise("1"))
.NecessaryPush(pf.FailPromise("humbug"));
EXPECT_EQ(b(), Poll<absl::Status>(absl::UnknownError("humbug")));
EXPECT_EQ(pf.Finish(), std::vector<std::string>({"humbug"}));
EXPECT_EQ(pf.Finish(), std::vector<absl::string_view>({"humbug"}));
auto c = TryConcurrently(pf.NeverPromise("1"))
.NecessaryPull(pf.FailPromise("wha"));
EXPECT_EQ(c(), Poll<absl::Status>(absl::UnknownError("wha")));
EXPECT_EQ(pf.Finish(), std::vector<std::string>({"1", "wha"}));
EXPECT_EQ(pf.Finish(), std::vector<absl::string_view>({"1", "wha"}));
}

// A pointer to an int designed to cause a double free if it's double destructed
Expand Down

0 comments on commit b19f87d

Please sign in to comment.