Paul Mathieu
f274749050
All host tests currently pass Some async refactors may not work well on device, will try later
281 lines
6.1 KiB
C++
281 lines
6.1 KiB
C++
#include "async.h"
|
|
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <cstdio>
|
|
#include <mutex>
|
|
#include <semaphore>
|
|
#include <thread>
|
|
|
|
#include "trace.h"
|
|
|
|
using namespace ::testing;
|
|
|
|
using async::AwaitableType;
|
|
using namespace std::literals::chrono_literals;
|
|
|
|
int uptime() {
|
|
static std::chrono::system_clock::time_point start =
|
|
std::chrono::system_clock::now();
|
|
|
|
return std::chrono::duration_cast<std::chrono::microseconds>(
|
|
std::chrono::system_clock::now() - start)
|
|
.count();
|
|
}
|
|
|
|
template <typename... Args>
|
|
void log(int i, const char* fmt, Args... args) {
|
|
if (i != 3) {
|
|
return;
|
|
}
|
|
printf("[%10i] ", uptime());
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wformat-security"
|
|
printf(fmt, args...);
|
|
#pragma clang diagnostic pop
|
|
printf("\n");
|
|
}
|
|
|
|
namespace tracing {
|
|
void trace(TraceEvent) {}
|
|
} // namespace tracing
|
|
|
|
struct SimpleAsyncTest : public Test {
|
|
SimpleAsyncTest() : done(0) {}
|
|
|
|
std::binary_semaphore done;
|
|
|
|
async::task<> test_task() {
|
|
co_await async::await(AwaitableType::kUartRx);
|
|
done.release();
|
|
}
|
|
|
|
void SetUp() { terminate = false; }
|
|
|
|
static std::atomic<bool> terminate;
|
|
};
|
|
|
|
std::atomic<bool> SimpleAsyncTest::terminate;
|
|
|
|
TEST_F(SimpleAsyncTest, EnqueueResume) {
|
|
async::schedule(test_task().h);
|
|
std::thread t(
|
|
[]() { async::main_loop([]() -> bool { return terminate; }); });
|
|
|
|
async::resume(AwaitableType::kUartRx);
|
|
EXPECT_TRUE(done.try_acquire_for(1s));
|
|
terminate = true;
|
|
|
|
t.join();
|
|
}
|
|
|
|
struct LoopAsyncTest : public Test {
|
|
int in_isr = 0;
|
|
std::mutex m;
|
|
std::counting_semaphore<123456> processed;
|
|
|
|
LoopAsyncTest() : processed(0) {}
|
|
|
|
async::task<> loop_task() {
|
|
while (!terminate) {
|
|
co_await async::await(AwaitableType::kUartRx);
|
|
{
|
|
std::scoped_lock lock(m);
|
|
while (in_isr > 0) {
|
|
processed.release();
|
|
log(1, "processed.release() in_isr: %i", in_isr);
|
|
in_isr--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetUp() { terminate = false; }
|
|
|
|
static std::atomic<bool> terminate;
|
|
};
|
|
std::atomic<bool> LoopAsyncTest::terminate;
|
|
|
|
TEST_F(LoopAsyncTest, ManyEnqueueResume) {
|
|
constexpr int kLoops = 100;
|
|
|
|
async::schedule(loop_task().h);
|
|
std::thread t(
|
|
[]() { async::main_loop([]() -> bool { return terminate; }); });
|
|
|
|
for (int i = 0; i < kLoops; i++) {
|
|
std::scoped_lock lock(m);
|
|
in_isr++;
|
|
async::resume(AwaitableType::kUartRx);
|
|
log(1, "async::resume() in_isr: %i", in_isr);
|
|
}
|
|
int done = 0;
|
|
for (int i = 0; i < kLoops; i++) {
|
|
if (processed.try_acquire_for(2s)) {
|
|
{
|
|
std::scoped_lock lock(m);
|
|
log(1, "processed acquired");
|
|
}
|
|
done++;
|
|
} else {
|
|
log(1, "skipping %i", i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
EXPECT_EQ(done, kLoops);
|
|
log(3, "got all of em, done");
|
|
|
|
terminate = true;
|
|
t.join();
|
|
}
|
|
|
|
struct NestedAsync : SimpleAsyncTest {
|
|
async::task<int> interior() { co_return 42; }
|
|
async::task<int> interior_yield() {
|
|
co_await async::delay(0);
|
|
co_return 42;
|
|
}
|
|
|
|
async::task<int> generator() {
|
|
int i = 41;
|
|
while (true) {
|
|
co_yield i++;
|
|
}
|
|
}
|
|
|
|
std::atomic<bool> voiddone = false;
|
|
std::atomic<int> result = 0;
|
|
|
|
async::task<> voidstuff() {
|
|
voiddone = true;
|
|
log(3, "voidstuff()");
|
|
co_return;
|
|
}
|
|
|
|
async::task<> nested() {
|
|
log(3, "nested()");
|
|
result = co_await interior();
|
|
log(3, "got result");
|
|
co_await voidstuff();
|
|
log(3, "voidstuff returned");
|
|
done.release();
|
|
}
|
|
|
|
async::task<> othernested() {
|
|
log(3, "othernested()");
|
|
result = co_await interior_yield();
|
|
log(3, "got result");
|
|
co_await voidstuff();
|
|
log(3, "voidstuff returned");
|
|
done.release();
|
|
}
|
|
|
|
async::task<> call_generator() {
|
|
log(4, "call_generator()");
|
|
auto g = generator();
|
|
log(4, "generator()");
|
|
|
|
int a0 = co_await g;
|
|
log(4, "a0: %d", a0);
|
|
|
|
int a1 = co_await g;
|
|
log(4, "a1: %d", a1);
|
|
|
|
result = a1;
|
|
|
|
g.h.destroy();
|
|
|
|
done.release();
|
|
}
|
|
|
|
static std::binary_semaphore next;
|
|
static std::binary_semaphore ready;
|
|
};
|
|
|
|
std::binary_semaphore NestedAsync::next(0);
|
|
std::binary_semaphore NestedAsync::ready(0);
|
|
|
|
TEST_F(NestedAsync, SimpleNestedTest) {
|
|
while (next.try_acquire());
|
|
while (ready.try_acquire());
|
|
|
|
async::schedule(nested().h);
|
|
std::thread t([]() {
|
|
async::main_loop([]() -> bool {
|
|
// for some reason the bare `acquire` fails sometimes :/
|
|
while (!next.try_acquire_for(10ms));
|
|
return terminate;
|
|
});
|
|
});
|
|
|
|
ASSERT_FALSE(voiddone);
|
|
ASSERT_EQ(result, 0);
|
|
|
|
next.release();
|
|
|
|
EXPECT_TRUE(done.try_acquire_for(1s));
|
|
|
|
ASSERT_TRUE(voiddone);
|
|
ASSERT_EQ(result, 42);
|
|
|
|
terminate = true;
|
|
next.release();
|
|
|
|
t.join();
|
|
}
|
|
|
|
TEST_F(NestedAsync, LessSimpleNestedTest) {
|
|
async::schedule(othernested().h);
|
|
|
|
ASSERT_FALSE(voiddone);
|
|
ASSERT_EQ(result, 0);
|
|
|
|
log(3, "step");
|
|
async::step(); // should proceed until yield
|
|
|
|
ASSERT_FALSE(voiddone);
|
|
ASSERT_EQ(result, 0);
|
|
|
|
log(3, "step");
|
|
async::step(); // should proceed until end
|
|
|
|
EXPECT_EQ(voiddone, true);
|
|
ASSERT_EQ(result, 42);
|
|
|
|
EXPECT_TRUE(done.try_acquire_for(1s));
|
|
}
|
|
|
|
TEST_F(NestedAsync, GeneratorTest) {
|
|
async::schedule(call_generator().h);
|
|
std::thread t([]() {
|
|
async::main_loop([]() -> bool {
|
|
ready.release();
|
|
log(3, "loop ready");
|
|
next.acquire();
|
|
return terminate;
|
|
});
|
|
});
|
|
|
|
ASSERT_EQ(result, 0);
|
|
|
|
log(4, "starting stuff");
|
|
|
|
ASSERT_TRUE(ready.try_acquire_for(1s));
|
|
next.release();
|
|
ASSERT_TRUE(ready.try_acquire_for(1s));
|
|
|
|
ASSERT_EQ(result, 42);
|
|
|
|
EXPECT_TRUE(done.try_acquire_for(1s));
|
|
|
|
terminate = true;
|
|
next.release();
|
|
|
|
t.join();
|
|
}
|