diff --git a/Tests/button.cpp b/Tests/button.cpp index 096f0fc..6871da5 100644 --- a/Tests/button.cpp +++ b/Tests/button.cpp @@ -33,20 +33,26 @@ std::uint32_t HAL::millis = 1; struct Gpio { - static bool set; + static bool is_set; 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 @@ -75,13 +81,13 @@ TEST_CASE("Button reads presses properly.", "[peripherals],[button]") Button button{g}; - Gpio::set = true; + Gpio::is_set = true; button.update(); SECTION("Too short of a press is debounced.") { HAL::millis = Button::TIMEOUT_SHORT_PRESS - 1; - Gpio::set = false; + Gpio::is_set = false; button.update(); @@ -91,7 +97,7 @@ TEST_CASE("Button reads presses properly.", "[peripherals],[button]") SECTION("Short press is identified properly.") { HAL::millis = Button::TIMEOUT_SHORT_PRESS + 2; - Gpio::set = false; + Gpio::is_set = false; button.update(); @@ -124,7 +130,7 @@ TEST_CASE("Button reads presses properly.", "[peripherals],[button]") SECTION("Releasing keeps NOT_PRESSED state.") { HAL::millis += 1; - Gpio::set = false; + Gpio::is_set = false; button.update(); diff --git a/Tests/coro.cpp b/Tests/coro.cpp index f06b166..78d016a 100644 --- a/Tests/coro.cpp +++ b/Tests/coro.cpp @@ -9,6 +9,7 @@ #include "skullc/coro/sleep.hpp" #include "skullc/coro/task.hpp" #include "skullc/coro/this_coro.hpp" +#include "skullc/coro/signal.hpp" #include @@ -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); test_coro_called = 1; @@ -180,7 +181,7 @@ TEST_CASE("Peripheral awaiters work.", "[coro],[awaiters]") SECTION("Pin edge awaiter works with rising edge.") { 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); REQUIRE(test_coro_called == 0); @@ -207,7 +208,7 @@ TEST_CASE("Peripheral awaiters work.", "[coro],[awaiters]") SECTION("Pin edge awaiter works with falling edge.") { 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); 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* 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* 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* 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 scheduler; + skullc::this_coro::scheduler.register_scheduler(scheduler); + + test_coro_called = 0; + + Signal 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 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); + } + } +} diff --git a/coro/inc/skullc/coro/peripheral_awaiters.hpp b/coro/inc/skullc/coro/peripheral_awaiters.hpp index e71d1db..f505d01 100644 --- a/coro/inc/skullc/coro/peripheral_awaiters.hpp +++ b/coro/inc/skullc/coro/peripheral_awaiters.hpp @@ -26,9 +26,10 @@ struct PinEdgeAwaiter : TaskPoller ~PinEdgeAwaiter() override { if (continuation) - { this_coro::scheduler().remove(continuation); - } + + if (stored_coro) + this_coro::scheduler().remove(stored_coro); } bool await_ready() { return false; } @@ -36,7 +37,7 @@ struct PinEdgeAwaiter : TaskPoller { continuation = h; stored_coro = continuation; - skullc::this_coro::scheduler().add_poller(this); + this_coro::scheduler().add_poller(this); } auto await_resume() { diff --git a/coro/inc/skullc/coro/signal.hpp b/coro/inc/skullc/coro/signal.hpp new file mode 100644 index 0000000..55a1355 --- /dev/null +++ b/coro/inc/skullc/coro/signal.hpp @@ -0,0 +1,91 @@ +// +// Created by erki on 06/02/25. +// + +#pragma once + +#include +#include + +#include "skullc/coro/this_coro.hpp" + +namespace skullc::coro +{ + +template +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 data_; + std::optional awaiter_; +}; + +}