WIP1
This commit is contained in:
parent
af9db5a1b0
commit
5af059f4c6
@ -16,10 +16,15 @@ option(SKULLC_WITH_TESTS "Enable unit testing." OFF)
|
|||||||
option(SKULLC_WITH_HAL "Enable the compiling and deployment of the HAL dependent sections." OFF)
|
option(SKULLC_WITH_HAL "Enable the compiling and deployment of the HAL dependent sections." OFF)
|
||||||
option(SKULLC_USE_HAL_ST "Enable the ST HAl when SKULLC_WITH_HAL is enabled." OFF)
|
option(SKULLC_USE_HAL_ST "Enable the ST HAl when SKULLC_WITH_HAL is enabled." OFF)
|
||||||
option(SKULLC_USE_HAL_ESP "Enable the ESP HAL when SKULLC_WITH_HAL is enabled." OFF)
|
option(SKULLC_USE_HAL_ESP "Enable the ESP HAL when SKULLC_WITH_HAL is enabled." OFF)
|
||||||
|
option(SKULLC_WITH_CORO "Enable coroutine support library." OFF)
|
||||||
option(SKULLC_WITH_DOCS "Enable documentation building." OFF)
|
option(SKULLC_WITH_DOCS "Enable documentation building." OFF)
|
||||||
|
|
||||||
include(skullc-install)
|
include(skullc-install)
|
||||||
|
|
||||||
|
if(SKULLC_WITH_CORO)
|
||||||
|
add_subdirectory(coro)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(Peripherals)
|
add_subdirectory(Peripherals)
|
||||||
add_subdirectory(Utility)
|
add_subdirectory(Utility)
|
||||||
add_subdirectory(Messaging)
|
add_subdirectory(Messaging)
|
||||||
|
|||||||
@ -31,19 +31,21 @@ add_executable(tests
|
|||||||
enum_helpers.cpp
|
enum_helpers.cpp
|
||||||
bytes.cpp
|
bytes.cpp
|
||||||
filters.cpp
|
filters.cpp
|
||||||
|
coro.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(tests
|
target_link_libraries(tests
|
||||||
PUBLIC
|
PUBLIC
|
||||||
skullc::utility
|
skullc::utility
|
||||||
skullc::messaging
|
skullc::messaging
|
||||||
|
skullc::coro
|
||||||
skullc::peripherals
|
skullc::peripherals
|
||||||
Catch2::Catch2
|
Catch2::Catch2
|
||||||
)
|
)
|
||||||
|
|
||||||
set_target_properties(tests
|
set_target_properties(tests
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
CXX_STANDARD 17
|
CXX_STANDARD 23
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/contrib)
|
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/contrib)
|
||||||
|
|||||||
9
Tests/coro.cpp
Normal file
9
Tests/coro.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 30/01/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
#include "skullc/coro/task.hpp"
|
||||||
|
#include "skullc/coro/scheduler.hpp"
|
||||||
|
|
||||||
14
coro/CMakeLists.txt
Normal file
14
coro/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||||
|
|
||||||
|
add_library(coro INTERFACE)
|
||||||
|
|
||||||
|
add_library(skullc::coro ALIAS coro)
|
||||||
|
|
||||||
|
target_include_directories(coro
|
||||||
|
INTERFACE
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc>
|
||||||
|
$<INSTALL_INTERFACE:include>
|
||||||
|
)
|
||||||
|
|
||||||
|
## INSTALL
|
||||||
|
skullc_install_packages(skullc coro ${SKULLC_VERSION})
|
||||||
2
coro/coro-config.cmake.in
Normal file
2
coro/coro-config.cmake.in
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/skullc-coro-config-version.cmake)
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/skullc-coro-targets.cmake)
|
||||||
142
coro/inc/skullc/coro/scheduler.hpp
Normal file
142
coro/inc/skullc/coro/scheduler.hpp
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 30/01/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <coroutine>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "skullc/coro/task.hpp"
|
||||||
|
|
||||||
|
namespace skullc::coro
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept Task = requires(T task)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
task.get_handle().resume()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TaskPoller {
|
||||||
|
virtual ~TaskPoller() = default;
|
||||||
|
|
||||||
|
virtual bool isReady() = 0;
|
||||||
|
std::coroutine_handle<> stored_coro;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Scheduler
|
||||||
|
{
|
||||||
|
constexpr static auto cmp = [](const auto &a, const auto &b) {
|
||||||
|
return a.first > b.first;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
Scheduler()
|
||||||
|
: Scheduler(16)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Scheduler(const Scheduler&) = delete;
|
||||||
|
Scheduler(Scheduler&&) = delete;
|
||||||
|
Scheduler &operator=(const Scheduler&) = delete;
|
||||||
|
Scheduler &operator=(Scheduler&&) = delete;
|
||||||
|
|
||||||
|
explicit Scheduler(uint num_tasks)
|
||||||
|
{
|
||||||
|
scheduled.reserve(num_tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Start a new task. The task can terminate by calling co_return; . */
|
||||||
|
void start_task(task<> task, uint64_t when = 0)
|
||||||
|
{
|
||||||
|
schedule(task.get_handle(), when);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Task... T>
|
||||||
|
void start_tasks(T... tasks) {
|
||||||
|
start(tasks...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Schedule a coroutine for future restart.
|
||||||
|
* \details This routine should only be called on the same core and from
|
||||||
|
* regular execution code. Do not call this from an interrupt.
|
||||||
|
* @param handle
|
||||||
|
* @param when
|
||||||
|
*/
|
||||||
|
void schedule(std::coroutine_handle<> handle, uint64_t when)
|
||||||
|
{
|
||||||
|
scheduled.push_back({when, handle});
|
||||||
|
std::push_heap(scheduled.begin(), scheduled.end(), cmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(const uint64_t& current_time)
|
||||||
|
{
|
||||||
|
if (!scheduled.empty())
|
||||||
|
{
|
||||||
|
auto min_time = scheduled.front();
|
||||||
|
if (min_time.first < current_time)
|
||||||
|
{
|
||||||
|
std::pop_heap(scheduled.begin(), scheduled.end(), cmp);
|
||||||
|
scheduled.pop_back();
|
||||||
|
if (min_time.second)
|
||||||
|
min_time.second.resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pollers_need_updating = false;
|
||||||
|
for (auto* poller : pollers)
|
||||||
|
{
|
||||||
|
if (poller->isReady())
|
||||||
|
{
|
||||||
|
schedule(poller->stored_coro, current_time);
|
||||||
|
poller->stored_coro = nullptr;
|
||||||
|
pollers_need_updating = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pollers_need_updating)
|
||||||
|
pollers.erase(std::remove_if(pollers.begin(), pollers.end(),
|
||||||
|
[](const auto* poller) { return poller->stored_coro == nullptr; }));
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_poller(TaskPoller* poller)
|
||||||
|
{
|
||||||
|
pollers.push_back(poller);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool remove(std::coroutine_handle<> handle)
|
||||||
|
{
|
||||||
|
for (auto &p : scheduled)
|
||||||
|
if (p.second == handle)
|
||||||
|
{
|
||||||
|
std::swap(p, scheduled.back());
|
||||||
|
scheduled.pop_back();
|
||||||
|
std::make_heap(scheduled.begin(), scheduled.end(), cmp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// \brief Heap of scheduled coroutines. Top of the heap is earliest time
|
||||||
|
std::vector<std::pair<uint64_t, std::coroutine_handle<>>> scheduled;
|
||||||
|
std::vector<TaskPoller*> pollers;
|
||||||
|
|
||||||
|
template <typename T0, typename... R>
|
||||||
|
void start(T0 &t0, R &...r)
|
||||||
|
{
|
||||||
|
/// Start the first task. As soon as it co_await anything, it will re-queue itself.
|
||||||
|
t0.get_handle().resume();
|
||||||
|
if constexpr (sizeof...(r) > 0) {
|
||||||
|
start(r...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
160
coro/inc/skullc/coro/task.hpp
Normal file
160
coro/inc/skullc/coro/task.hpp
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 30/01/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <coroutine>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace skullc::coro
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T = void, bool lazy = true>
|
||||||
|
struct [[nodiscard]] task
|
||||||
|
{
|
||||||
|
using value_type = T;
|
||||||
|
struct task_promise;
|
||||||
|
using promise_type = task_promise;
|
||||||
|
|
||||||
|
task() = delete;
|
||||||
|
|
||||||
|
explicit task(std::coroutine_handle<promise_type> handle)
|
||||||
|
: my_handle(handle)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
task(task&& t) noexcept : my_handle(t.my_handle) { t.my_handle = nullptr; }
|
||||||
|
|
||||||
|
task(const task&) = delete;
|
||||||
|
task& operator=(const task&) = delete;
|
||||||
|
|
||||||
|
~task()
|
||||||
|
{
|
||||||
|
if constexpr (!std::is_same_v<T, void>)
|
||||||
|
{
|
||||||
|
if (my_handle)
|
||||||
|
my_handle.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto get_handle() const { return my_handle; }
|
||||||
|
void detach() { my_handle = nullptr; }
|
||||||
|
|
||||||
|
task& operator=(task&& other) noexcept
|
||||||
|
{
|
||||||
|
if (std::addressof(other) != this)
|
||||||
|
{
|
||||||
|
if (my_handle)
|
||||||
|
{
|
||||||
|
my_handle.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
my_handle = other.my_handle;
|
||||||
|
other.my_handle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator co_await() noexcept
|
||||||
|
{
|
||||||
|
struct awaiter
|
||||||
|
{
|
||||||
|
bool await_ready() const noexcept
|
||||||
|
{
|
||||||
|
return !coro_handle || coro_handle.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::coroutine_handle<>
|
||||||
|
await_suspend(std::coroutine_handle<> awaiting_coroutine) noexcept
|
||||||
|
{
|
||||||
|
coro_handle.promise().set_continuation(awaiting_coroutine);
|
||||||
|
return coro_handle;
|
||||||
|
}
|
||||||
|
T await_resume() noexcept
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<T, void>)
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
return std::move(coro_handle.promise().data.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::coroutine_handle<promise_type> coro_handle;
|
||||||
|
};
|
||||||
|
return awaiter{my_handle};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::coroutine_handle<promise_type> my_handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct promise_setter
|
||||||
|
{
|
||||||
|
template<typename U>
|
||||||
|
void return_value(U&& u)
|
||||||
|
{
|
||||||
|
data.emplace(std::forward<U>(u));
|
||||||
|
}
|
||||||
|
std::optional<T> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct promise_setter<void>
|
||||||
|
{
|
||||||
|
void return_void() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T, bool lazy>
|
||||||
|
struct task<T, lazy>::task_promise : public promise_setter<T>
|
||||||
|
{
|
||||||
|
|
||||||
|
task get_return_object() noexcept
|
||||||
|
{
|
||||||
|
return task{std::coroutine_handle<task_promise>::from_promise(*this)};
|
||||||
|
};
|
||||||
|
|
||||||
|
auto initial_suspend() const noexcept
|
||||||
|
{
|
||||||
|
if constexpr (lazy)
|
||||||
|
return std::suspend_always{};
|
||||||
|
else
|
||||||
|
return std::suspend_never{};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto final_suspend() const noexcept
|
||||||
|
{
|
||||||
|
struct awaiter
|
||||||
|
{
|
||||||
|
bool await_ready() const noexcept { return false; }
|
||||||
|
|
||||||
|
void await_resume() const noexcept {}
|
||||||
|
|
||||||
|
std::coroutine_handle<>
|
||||||
|
await_suspend(std::coroutine_handle<task::task_promise> h) noexcept
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<T, void>)
|
||||||
|
{
|
||||||
|
auto continuation = h.promise().continuation;
|
||||||
|
h.destroy();
|
||||||
|
return continuation;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return h.promise().continuation;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return awaiter{};
|
||||||
|
}
|
||||||
|
|
||||||
|
void unhandled_exception() noexcept { std::terminate(); }
|
||||||
|
|
||||||
|
void set_continuation(std::coroutine_handle<> handle) { continuation = handle; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::coroutine_handle<> continuation = std::noop_coroutine();
|
||||||
|
};
|
||||||
|
|
||||||
|
}// namespace skullc::coro
|
||||||
Loading…
x
Reference in New Issue
Block a user