WIP4: Add Signal entity
Some checks failed
CI & Unit Tests / Unit-Tests (push) Failing after 18s
CI & Unit Tests / Docs (push) Successful in 12s

This commit is contained in:
Erki 2025-02-21 21:19:42 +02:00
parent 66461af1e4
commit 8d49d2d446
4 changed files with 199 additions and 15 deletions

View File

@ -33,20 +33,26 @@ std::uint32_t HAL::millis = 1;
struct Gpio struct Gpio
{ {
static bool set; static bool is_set;
Gpio() Gpio()
{ {
set = false; is_set = false;
} }
bool read() bool read() const
{ {
return set; return is_set;
} }
void set(const bool value)
{ }
void toggle()
{ }
}; };
bool Gpio::set = false; bool Gpio::is_set = false;
}// namespace }// namespace
@ -75,13 +81,13 @@ TEST_CASE("Button reads presses properly.", "[peripherals],[button]")
Button button{g}; Button button{g};
Gpio::set = true; Gpio::is_set = true;
button.update(); button.update();
SECTION("Too short of a press is debounced.") SECTION("Too short of a press is debounced.")
{ {
HAL::millis = Button::TIMEOUT_SHORT_PRESS - 1; HAL::millis = Button::TIMEOUT_SHORT_PRESS - 1;
Gpio::set = false; Gpio::is_set = false;
button.update(); button.update();
@ -91,7 +97,7 @@ TEST_CASE("Button reads presses properly.", "[peripherals],[button]")
SECTION("Short press is identified properly.") SECTION("Short press is identified properly.")
{ {
HAL::millis = Button::TIMEOUT_SHORT_PRESS + 2; HAL::millis = Button::TIMEOUT_SHORT_PRESS + 2;
Gpio::set = false; Gpio::is_set = false;
button.update(); button.update();
@ -124,7 +130,7 @@ TEST_CASE("Button reads presses properly.", "[peripherals],[button]")
SECTION("Releasing keeps NOT_PRESSED state.") SECTION("Releasing keeps NOT_PRESSED state.")
{ {
HAL::millis += 1; HAL::millis += 1;
Gpio::set = false; Gpio::is_set = false;
button.update(); button.update();

View File

@ -9,6 +9,7 @@
#include "skullc/coro/sleep.hpp" #include "skullc/coro/sleep.hpp"
#include "skullc/coro/task.hpp" #include "skullc/coro/task.hpp"
#include "skullc/coro/this_coro.hpp" #include "skullc/coro/this_coro.hpp"
#include "skullc/coro/signal.hpp"
#include <semaphore> #include <semaphore>
@ -159,7 +160,7 @@ struct TestGpio
} }
}; };
skullc::coro::Task<> await_pin_edge(TestGpio* pin, const Peripherals::Hal::Edge edge) skullc::coro::Task<> await_edge(TestGpio* pin, const Peripherals::Hal::Edge edge)
{ {
co_await skullc::coro::PinEdgeAwaiter(pin, edge); co_await skullc::coro::PinEdgeAwaiter(pin, edge);
test_coro_called = 1; test_coro_called = 1;
@ -180,7 +181,7 @@ TEST_CASE("Peripheral awaiters work.", "[coro],[awaiters]")
SECTION("Pin edge awaiter works with rising edge.") SECTION("Pin edge awaiter works with rising edge.")
{ {
TestGpio gpio{false}; TestGpio gpio{false};
scheduler.start_tasks(await_pin_edge(&gpio, Peripherals::Hal::Edge::Rising)); scheduler.start_tasks(await_edge(&gpio, Peripherals::Hal::Edge::Rising));
scheduler.loop(0); scheduler.loop(0);
REQUIRE(test_coro_called == 0); REQUIRE(test_coro_called == 0);
@ -207,7 +208,7 @@ TEST_CASE("Peripheral awaiters work.", "[coro],[awaiters]")
SECTION("Pin edge awaiter works with falling edge.") SECTION("Pin edge awaiter works with falling edge.")
{ {
TestGpio gpio{true}; TestGpio gpio{true};
scheduler.start_tasks(await_pin_edge(&gpio, Peripherals::Hal::Edge::Falling)); scheduler.start_tasks(await_edge(&gpio, Peripherals::Hal::Edge::Falling));
scheduler.loop(0); scheduler.loop(0);
REQUIRE(test_coro_called == 0); REQUIRE(test_coro_called == 0);
@ -231,3 +232,88 @@ TEST_CASE("Peripheral awaiters work.", "[coro],[awaiters]")
} }
} }
} }
namespace
{
skullc::coro::Task<> await_signal(const int expected, skullc::coro::Signal<int>* signal)
{
const int value = co_await signal->receive();
REQUIRE(value == expected);
test_coro_called = 1;
co_return;
}
skullc::coro::Task<> await_signal_n(const auto& expecteds, skullc::coro::Signal<int>* signal)
{
for (const int e : expecteds)
{
const int value = co_await signal->receive();
REQUIRE(value == e);
test_coro_called++;
}
co_return;
}
skullc::coro::Task<> send_signal(const int value, skullc::coro::Signal<int>* signal)
{
co_await skullc::coro::sleep(0ms, 10ms);
signal->send(value);
co_return;
}
}
TEST_CASE("Signal awaiters work.", "[coro],[signal]")
{
using namespace skullc::coro;
Scheduler<std::vector> scheduler;
skullc::this_coro::scheduler.register_scheduler(scheduler);
test_coro_called = 0;
Signal<int> signal;
SECTION("Sending from main thread.")
{
scheduler.start_tasks(await_signal(10, &signal));
scheduler.loop(0);
scheduler.loop(1);
REQUIRE(test_coro_called == 0);
signal.send(10);
scheduler.loop(2);
REQUIRE(test_coro_called == 1);
}
SECTION("Sending from another coroutine.")
{
scheduler.start_tasks(await_signal(10, &signal), send_signal(10, &signal));
scheduler.loop(0);
scheduler.loop(1);
REQUIRE(test_coro_called == 0);
scheduler.loop(10);
REQUIRE(test_coro_called == 0);
scheduler.loop(10);
REQUIRE(test_coro_called == 1);
}
const std::vector<int> values = { 10, 11, 13 };
SECTION("Sending multiple values.")
{
scheduler.start_tasks(await_signal_n(values, &signal));
scheduler.loop(0);
scheduler.loop(1);
REQUIRE(test_coro_called == 0);
for (int i = 0; i < values.size(); i++)
{
signal.send(values[i]);
scheduler.loop(i + 1);
REQUIRE(test_coro_called == i + 1);
}
}
}

View File

@ -26,9 +26,10 @@ struct PinEdgeAwaiter : TaskPoller
~PinEdgeAwaiter() override ~PinEdgeAwaiter() override
{ {
if (continuation) if (continuation)
{
this_coro::scheduler().remove(continuation); this_coro::scheduler().remove(continuation);
}
if (stored_coro)
this_coro::scheduler().remove(stored_coro);
} }
bool await_ready() { return false; } bool await_ready() { return false; }
@ -36,7 +37,7 @@ struct PinEdgeAwaiter : TaskPoller
{ {
continuation = h; continuation = h;
stored_coro = continuation; stored_coro = continuation;
skullc::this_coro::scheduler().add_poller(this); this_coro::scheduler().add_poller(this);
} }
auto await_resume() auto await_resume()
{ {

View File

@ -0,0 +1,91 @@
//
// Created by erki on 06/02/25.
//
#pragma once
#include <atomic>
#include <optional>
#include "skullc/coro/this_coro.hpp"
namespace skullc::coro
{
template<typename T>
class Signal
{
struct Awaiter
{
Signal* signal;
std::coroutine_handle<> continuation;
Awaiter() = delete;
Awaiter(const Awaiter&) = delete;
Awaiter(Awaiter&&) = delete;
explicit Awaiter(Signal* signal)
: signal(signal)
{}
Awaiter& operator=(const Awaiter&) = delete;
Awaiter& operator=(Awaiter&&) = delete;
~Awaiter()
{
if (continuation)
this_coro::scheduler().remove(continuation);
}
bool await_ready() { return false; }
void await_suspend(std::coroutine_handle<> h)
{
continuation = h;
}
T&& await_resume()
{
continuation = {};
return std::move(*signal->data_);
}
T&& get_return_object() noexcept
{
return std::move(*signal->data_);
}
void trigger_resume()
{
this_coro::scheduler().schedule(continuation, 0);
}
};
public:
void send(const T& value)
{
data_.emplace(value);
if (awaiter_.has_value())
awaiter_->trigger_resume();
}
void send(T&& value)
{
data_.emplace(value);
if (awaiter_.has_value())
awaiter_->trigger_resume();
}
auto& receive()
{
awaiter_.emplace(this);
return *awaiter_;
}
private:
std::optional<T> data_;
std::optional<Awaiter> awaiter_;
};
}