Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 44 additions & 78 deletions tests/cancel.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,65 +14,53 @@ boost::ut::suite<"cancellation_tests"> cancellation_tests = []() {
// Setup
test_context ctx;

std::pair<int, int> count{ 0, 0 };
counter_pair count{};
int ends_reached = 0;

auto get_counter = [&count](std::string_view p_label = "") -> auto {
return raii_counter(
std::make_pair<int*, int*>(&count.first, &count.second), p_label);
};

auto a = [get_counter,
&ends_reached](async::context& p_ctx) -> async::future<void> {
auto a = [&](async::context& p_ctx) -> async::future<void> {
std::println("[future cancel] entering a");
raii_counter counter = get_counter("a");
raii_counter counter(count, "a");
co_await std::suspend_always{};
std::println("[future cancel] a exited");
ends_reached++;
co_return;
};

auto b = [a, get_counter, &ends_reached](
async::context& p_ctx) -> async::future<void> {
auto b = [&](async::context& p_ctx) -> async::future<void> {
std::println("[future cancel] entering b");
raii_counter counter = get_counter("b");
raii_counter counter(count, "b");
co_await a(p_ctx);
std::println("[future cancel] b exited");
ends_reached++;
co_return;
};

auto c = [b, get_counter, &ends_reached](
async::context& p_ctx) -> async::future<void> {
auto c = [&](async::context& p_ctx) -> async::future<void> {
std::println("[future cancel] entering c");
raii_counter counter = get_counter("c");
raii_counter counter(count, "c");
co_await b(p_ctx);
std::println("[future cancel] c exited");
ends_reached++;
co_return;
};

{
expect(count == std::make_pair<int, int>(0, 0))
<< "count is {" << count.first << ", " << count.second << "}\n";
expect(that % count == counter_pair{ 0, 0 });
expect(that % ends_reached == 0);

auto future = c(ctx);

expect(count == std::make_pair<int, int>(0, 0))
<< "count is {" << count.first << ", " << count.second << "}\n";
expect(that % count == counter_pair{ 0, 0 });
expect(that % ends_reached == 0);

future.resume();

expect(count == std::make_pair<int, int>(3, 0))
<< "count is {" << count.first << ", " << count.second << "}\n";
expect(that % count == counter_pair{ 3, 0 });
expect(that % ends_reached == 0);
expect(that % 0 < ctx.memory_used());
} // destroy future

expect(count == std::make_pair<int, int>(3, 3))
<< "count is {" << count.first << ", " << count.second << "}\n";
expect(that % count == counter_pair{ 3, 3 });
expect(that % ends_reached == 0);
expect(that % 0 == ctx.memory_used());
};
Expand All @@ -81,29 +69,22 @@ boost::ut::suite<"cancellation_tests"> cancellation_tests = []() {
// Setup
test_context ctx;

std::pair<int, int> count{ 0, 0 };
counter_pair count{};
int ends_reached = 0;

auto get_counter = [&count](std::string_view p_label = "") -> auto {
return raii_counter(
std::make_pair<int*, int*>(&count.first, &count.second), p_label);
};

auto a = [get_counter,
&ends_reached](async::context& p_ctx) -> async::future<void> {
auto a = [&](async::context& p_ctx) -> async::future<void> {
std::println("[future cancel direct] entering a");
raii_counter counter = get_counter("future direct A");
raii_counter counter(count, "future direct A");
std::println("[context cancel] suspend a");
co_await std::suspend_always{};
std::println("[future cancel direct] a exited");
ends_reached++;
co_return;
};

auto b = [a, get_counter, &ends_reached](
async::context& p_ctx) -> async::future<void> {
auto b = [&](async::context& p_ctx) -> async::future<void> {
std::println("[future cancel direct] entering b");
raii_counter counter = get_counter("future direct B");
raii_counter counter(count, "future direct B");
co_await a(p_ctx);
std::println("[context cancel] suspend b"); // should never show up
co_await std::suspend_always{};
Expand All @@ -112,10 +93,9 @@ boost::ut::suite<"cancellation_tests"> cancellation_tests = []() {
co_return;
};

auto c = [b, get_counter, &ends_reached](
async::context& p_ctx) -> async::future<void> {
auto c = [&](async::context& p_ctx) -> async::future<void> {
std::println("[future cancel direct] entering c");
raii_counter counter = get_counter("future direct C");
raii_counter counter(count, "future direct C");
co_await b(p_ctx);
std::println("[context cancel] suspend c"); // should never show up
co_await std::suspend_always{};
Expand All @@ -124,27 +104,23 @@ boost::ut::suite<"cancellation_tests"> cancellation_tests = []() {
co_return;
};

expect(count == std::make_pair<int, int>(0, 0))
<< "count is {" << count.first << ", " << count.second << "}\n";
expect(that % count == counter_pair{ 0, 0 });
expect(that % ends_reached == 0);

auto future = c(ctx);

expect(count == std::make_pair<int, int>(0, 0))
<< "count is {" << count.first << ", " << count.second << "}\n";
expect(that % count == counter_pair{ 0, 0 });
expect(that % ends_reached == 0);

future.resume();

expect(count == std::make_pair<int, int>(3, 0))
<< "count is {" << count.first << ", " << count.second << "}\n";
expect(that % count == counter_pair{ 3, 0 });
expect(that % ends_reached == 0);
expect(that % 0 < ctx.memory_used());

future.cancel();

expect(count == std::make_pair<int, int>(3, 3))
<< "count is {" << count.first << ", " << count.second << "}\n";
expect(that % count == counter_pair{ 3, 3 });
expect(that % ends_reached == 0);
expect(that % 0 == ctx.memory_used());
};
Expand All @@ -153,39 +129,33 @@ boost::ut::suite<"cancellation_tests"> cancellation_tests = []() {
// Setup
test_context ctx;

std::pair<int, int> count{ 0, 0 };
counter_pair count{};
int ends_reached = 0;

auto get_counter = [&count](std::string_view p_label) -> auto {
return raii_counter(
std::make_pair<int*, int*>(&count.first, &count.second), p_label);
};

auto a = [get_counter,
&ends_reached](async::context& p_ctx) -> async::future<void> {
auto a = [&](async::context& p_ctx) -> async::future<void> {
std::println("[context cancel] entering a");
raii_counter counter = get_counter("context A");
raii_counter counter(count, "context A");
std::println("[context cancel] suspend a");
co_await std::suspend_always{};
std::println("[context cancel] a exited");
ends_reached++;
co_return;
};
auto b = [a, get_counter, &ends_reached](
async::context& p_ctx) -> async::future<void> {

auto b = [&](async::context& p_ctx) -> async::future<void> {
std::println("[context cancel] entering b");
raii_counter counter = get_counter("context B");
raii_counter counter(count, "context B");
co_await a(p_ctx);
std::println("[context cancel] suspend b"); // should never show up
co_await std::suspend_always{};
std::println("[context cancel] b exited");
ends_reached++;
co_return;
};
auto c = [b, get_counter, &ends_reached](
async::context& p_ctx) -> async::future<void> {

auto c = [&](async::context& p_ctx) -> async::future<void> {
std::println("[context cancel] entering c");
raii_counter counter = get_counter("context C");
raii_counter counter(count, "context C");
co_await b(p_ctx);
std::println("[context cancel] c suspended"); // should never show up
co_await std::suspend_always{};
Expand All @@ -194,25 +164,25 @@ boost::ut::suite<"cancellation_tests"> cancellation_tests = []() {
co_return;
};

expect(count == std::make_pair<int, int>(0, 0));
expect(that % count == counter_pair{ 0, 0 });
expect(that % ends_reached == 0);

auto future = c(ctx);

expect(count == std::make_pair<int, int>(0, 0));
expect(that % count == counter_pair{ 0, 0 });
expect(that % ends_reached == 0);

future.resume();

expect(count == std::make_pair<int, int>(3, 0));
expect(that % count == counter_pair{ 3, 0 });
expect(that % ends_reached == 0);
expect(that % 0 < ctx.memory_used());
expect(that % false == future.has_value());
expect(that % false == future.done());

ctx.cancel();

expect(count == std::make_pair<int, int>(3, 3));
expect(that % count == counter_pair{ 3, 3 });
expect(that % ends_reached == 0);
expect(that % 0 == ctx.memory_used());
expect(that % false == future.has_value());
Expand All @@ -222,20 +192,15 @@ boost::ut::suite<"cancellation_tests"> cancellation_tests = []() {
// Setup
test_context ctx;

std::pair<int, int> count{ 0, 0 };
counter_pair count{};
int ends_reached = 0;

auto get_counter = [&count]() -> auto {
return raii_counter(
std::make_pair<int*, int*>(&count.first, &count.second));
};

bool should_throw = true;
int step = 0;
auto a = [&](async::context& p_ctx) -> async::future<void> {
step = 3;
std::println("[exception] entering a");
raii_counter counter = get_counter();
raii_counter counter(count, "exception A");
co_await std::suspend_always{};
step = 4;
if (should_throw) {
Expand All @@ -246,31 +211,33 @@ boost::ut::suite<"cancellation_tests"> cancellation_tests = []() {
ends_reached++;
co_return;
};

auto b = [&](async::context& p_ctx) -> async::future<void> {
step = 2;
std::println("[exception] entering b");
raii_counter counter = get_counter();
raii_counter counter(count, "exception B");
co_await a(p_ctx);
std::println("[exception] b exited");
ends_reached++;
co_return;
};

auto c = [&](async::context& p_ctx) -> async::future<void> {
step = 1;
std::println("[exception] entering c");
raii_counter counter = get_counter();
raii_counter counter(count, "exception C");
co_await b(p_ctx);
std::println("[exception] c exited");
ends_reached++;
co_return;
};

expect(count == std::make_pair<int, int>(0, 0));
expect(that % count == counter_pair{ 0, 0 });
expect(that % ends_reached == 0);

auto future = c(ctx);

expect(count == std::make_pair<int, int>(0, 0));
expect(that % count == counter_pair{ 0, 0 });
expect(that % ends_reached == 0);

std::println("Resume until future reaches suspension @ coroutine A");
Expand All @@ -280,8 +247,7 @@ boost::ut::suite<"cancellation_tests"> cancellation_tests = []() {
<< "runtime_error exception was not thrown!";
expect(that % future.done());
expect(that % not future.has_value());
expect(count == std::make_pair<int, int>(3, 3))
<< "count is {" << count.first << ", " << count.second << "}\n";
expect(that % count == counter_pair{ 3, 3 });
expect(that % ends_reached == 0);
expect(that % 0 == ctx.memory_used());
};
Expand Down
24 changes: 19 additions & 5 deletions tests/util.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -115,22 +115,36 @@ export {
}
};

struct counter_pair
{
int constructed = 0;
int destructed = 0;

bool operator==(counter_pair const&) const = default;

friend std::ostream& operator<<(std::ostream& out, counter_pair const& c)
{
return out << "{ constructed: " << c.constructed
<< ", destructed: " << c.destructed << " }";
}
};

struct raii_counter
{
raii_counter(std::pair<int*, int*> p_counts, std::string_view p_label = "X")
: counts(p_counts)
raii_counter(counter_pair& p_counts, std::string_view p_label = "X")
: m_counts(&p_counts)
, m_label(p_label)
{
std::println("🟢 CTOR: {}", m_label);
(*counts.first)++;
m_counts->constructed++;
}

~raii_counter() // NOLINT(bugprone-exception-escape)
{
std::println("🔵 DTOR: {}", m_label);
(*counts.second)++;
m_counts->destructed++;
}
std::pair<int*, int*> counts;
counter_pair* m_counts;
std::string_view m_label;
};
}