Threads: Add initial Actor implementation, rework primitivethread a bit.
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Erki 2021-06-20 21:14:30 +03:00
parent 6f7756e1cb
commit ea474dd915
6 changed files with 240 additions and 55 deletions

View File

@ -0,0 +1,102 @@
/*
* threads_actor.hpp
*
* Created on: Jun 11, 2021
* Author: erki
*/
#ifndef SKULLC_THREADS_ACTOR_HPP_
#define SKULLC_THREADS_ACTOR_HPP_
#include <queue.h>
#include <type_traits>
#include "threads_primitivethread.hpp"
#include "threads_signal.hpp"
namespace Threads
{
template<typename T>
class Actor
{
public:
using value_type = T;
static_assert(std::is_trivially_constructible_v<value_type>, "T must be trivially constructible.");
static_assert(std::is_trivially_copyable_v<value_type>, "T must be trivially copyable.");
Actor(const std::uint32_t queue_size, const char* name, const osPriority_t priority, const std::uint32_t stack_size)
: thread_([](void* d) {
auto* dd = static_cast<Actor<T>*>(d);
(*dd)();
},
this, name, priority, stack_size),
msg_queue_(xQueueCreate(queue_size, sizeof(value_type))), signal_(*this)
{
assert(msg_queue_);
}
Signallable<T>* getSignal()
{
return &signal_;
}
protected:
virtual void init() = 0;
virtual void onSignal(const T& data) = 0;
private:
PrimitiveThread thread_;
QueueHandle_t msg_queue_;
void operator()()
{
init();
value_type data;
while (true)
{
const BaseType_t got = xQueueReceive(msg_queue_, &data, portMAX_DELAY);
if (got)
{
onSignal(data);
}
}
}
friend struct Signal_;
struct Signal_ : Signallable<value_type>
{
using parent = Actor<T>;
parent& q;
explicit Signal_(parent& q)
: q(q)
{}
bool emit(const value_type& data) override
{
const BaseType_t success = xQueueSend(q.msg_queue_, &data, 0);
return success == pdTRUE;
}
std::pair<bool, BaseType_t> emitFromIsr(const value_type& data) override
{
BaseType_t was_awoken = pdFALSE;
const BaseType_t success = xQueueSendFromISR(q.msg_queue_, &data, &was_awoken);
return {success == pdTRUE, was_awoken};
}
};
Signal_ signal_;
};
}// namespace Threads
#endif /* SKULLC_THREADS_ACTOR_HPP_ */

View File

@ -14,12 +14,14 @@
#include <type_traits> #include <type_traits>
#include "threads_primitivethread.hpp" #include "threads_primitivethread.hpp"
#include "threads_signal.hpp"
namespace Threads namespace Threads
{ {
template<typename T> template<typename T>
class ExclusiveSignal class ExclusiveSignal : public Signallable<T>
, public Awaitable<std::optional<T>>
{ {
public: public:
using value_type = T; using value_type = T;
@ -29,8 +31,7 @@ public:
std::is_trivially_copyable_v<value_type>, "Signal's value must be trivially copyable."); std::is_trivially_copyable_v<value_type>, "Signal's value must be trivially copyable.");
ExclusiveSignal() ExclusiveSignal()
: current_value_(std::nullopt) : current_value_(std::nullopt), waiting_thread_(std::nullopt)
, waiting_thread_(std::nullopt)
{} {}
ExclusiveSignal(const ExclusiveSignal&) = delete; ExclusiveSignal(const ExclusiveSignal&) = delete;
@ -39,23 +40,23 @@ public:
ExclusiveSignal& operator=(ExclusiveSignal&&) = delete; ExclusiveSignal& operator=(ExclusiveSignal&&) = delete;
#ifdef INCLUDE_xTaskGetCurrentTaskHandle #ifdef INCLUDE_xTaskGetCurrentTaskHandle
result_type wait() result_type await() override
{ {
return wait(PrimitiveThread::getCurrentThread()); return await(PrimitiveThread::getCurrentThread());
} }
result_type wait(const long timeout) result_type await(const long timeout) override
{ {
return wait(PrimitiveThread::getCurrentThread(), timeout); return await(PrimitiveThread::getCurrentThread(), timeout);
} }
#endif #endif
result_type wait(const PrimitiveThread& currentThread) result_type await(const PrimitiveThread& currentThread) override
{ {
return wait(currentThread, portMAX_DELAY); return await(currentThread, portMAX_DELAY);
} }
result_type wait(const PrimitiveThread& currentThread, const long timeout) result_type await(const PrimitiveThread& currentThread, const long timeout) override
{ {
waiting_thread_ = currentThread; waiting_thread_ = currentThread;
@ -64,34 +65,49 @@ public:
if (!notified) if (!notified)
{ {
return std::nullopt; return std::nullopt;
} } else
else
{ {
return current_value_; return current_value_;
} }
} }
bool emit(const result_type& v) bool emit(const value_type& v) override
{ {
current_value_ = v; current_value_ = v;
if (!waiting_thread_) if (!waiting_thread_)
{ {
return false; return false;
} } else
else
{ {
return waiting_thread_->notify(0, eNoAction); return waiting_thread_->notify(0, eNoAction);
} }
} }
std::pair<bool, BaseType_t> emitFromIsr(const value_type& v) override
{
current_value_ = v;
if (!waiting_thread_)
{
return {false, pdFALSE};
} else
{
auto const [discard, was_notified] = waiting_thread_->notifyFromIsr(0, eNoAction);
(void) discard;
return {true, was_notified};
}
}
private: private:
result_type current_value_; result_type current_value_;
std::optional<PrimitiveThread> waiting_thread_; std::optional<PrimitiveThread> waiting_thread_;
}; };
template<> template<>
struct ExclusiveSignal<void> struct ExclusiveSignal<void> : public Signallable<void>
, public Awaitable<bool>
{ {
using value_type = void; using value_type = void;
using result_type = bool; using result_type = bool;
@ -106,45 +122,58 @@ struct ExclusiveSignal<void>
ExclusiveSignal& operator=(ExclusiveSignal&&) = delete; ExclusiveSignal& operator=(ExclusiveSignal&&) = delete;
#ifdef INCLUDE_xTaskGetCurrentTaskHandle #ifdef INCLUDE_xTaskGetCurrentTaskHandle
result_type wait() result_type await() override
{ {
return wait(PrimitiveThread::getCurrentThread()); return await(PrimitiveThread::getCurrentThread());
} }
result_type wait(const long timeout) result_type await(const long timeout) override
{ {
return wait(PrimitiveThread::getCurrentThread(), timeout); return await(PrimitiveThread::getCurrentThread(), timeout);
} }
#endif #endif
result_type wait(PrimitiveThread currentThread) result_type await(const PrimitiveThread& currentThread) override
{ {
return wait(currentThread, portMAX_DELAY); return await(currentThread, portMAX_DELAY);
} }
result_type wait(PrimitiveThread currentThread, const long timeout) result_type await(const PrimitiveThread& currentThread, const long timeout) override
{ {
waiting_thread_ = currentThread; waiting_thread_ = currentThread;
return currentThread.notifyWait(timeout); return currentThread.notifyWait(timeout);
} }
bool emit() bool emit() override
{ {
if (!waiting_thread_) if (!waiting_thread_)
{ {
return false; return false;
} } else
else
{ {
return waiting_thread_->notify(0, eNoAction); return waiting_thread_->notify(0, eNoAction);
} }
} }
std::pair<bool, BaseType_t> emitFromIsr() override
{
if (!waiting_thread_)
{
return {false, pdFALSE};
} else
{
auto const [discard, was_notified] = waiting_thread_->notifyFromIsr(0, eNoAction);
(void) discard;
return {true, was_notified};
}
}
private: private:
std::optional<PrimitiveThread> waiting_thread_; std::optional<PrimitiveThread> waiting_thread_;
}; };
} }// namespace Threads
#endif /* SKULLC_THREADS_EXCLUSIVESIGNAL_HPP_ */ #endif /* SKULLC_THREADS_EXCLUSIVESIGNAL_HPP_ */

View File

@ -11,8 +11,8 @@
#include <cmsis_os.h> #include <cmsis_os.h>
#include <freertos_os2.h> #include <freertos_os2.h>
#include <type_traits>
#include <tuple> #include <tuple>
#include <type_traits>
namespace Threads namespace Threads
{ {
@ -26,6 +26,15 @@ public:
PrimitiveThread& operator=(const PrimitiveThread&) = default; PrimitiveThread& operator=(const PrimitiveThread&) = default;
PrimitiveThread& operator=(PrimitiveThread&&) = default; PrimitiveThread& operator=(PrimitiveThread&&) = default;
explicit PrimitiveThread(osThreadFunc_t function, void* runner_data, const osThreadAttr_t& attrs);
template<typename F>
PrimitiveThread(F runner, void* runner_data, const char* name, const osPriority_t priority, const std::uint32_t stack_size)
: PrimitiveThread(runner, runner_data, constructThreadAttrs_(name, priority, stack_size))
{
static_assert(std::is_convertible_v<F, osThreadFunc_t>, "Run function F is not convertible to osThreadFunc_t (void (void*)).");
}
osThreadId_t thread_id; osThreadId_t thread_id;
osThreadAttr_t attributes; osThreadAttr_t attributes;
@ -55,17 +64,9 @@ public:
[[nodiscard]] bool notifyWait(const TickType_t delay) const; [[nodiscard]] bool notifyWait(const TickType_t delay) const;
[[nodiscard]] std::pair<bool, unsigned long> notifyWait(const TickType_t delay, const unsigned long set_on_entry, const unsigned long set_on_exit) const; [[nodiscard]] std::pair<bool, unsigned long> notifyWait(const TickType_t delay, const unsigned long set_on_entry, const unsigned long set_on_exit) const;
protected:
explicit PrimitiveThread(osThreadFunc_t function, const osThreadAttr_t& attrs);
template<typename F>
PrimitiveThread(F runner, const char* name, const osPriority_t priority, const std::uint32_t stack_size)
: PrimitiveThread(runner, constructThreadAttrs_(name, priority, stack_size))
{
static_assert(std::is_convertible_v<F, osThreadFunc_t>, "Run function F is not convertible to osThreadFunc_t (void (void*)).");
}
private: private:
void* runner_data_ = nullptr;
explicit PrimitiveThread(TaskHandle_t threadHandle); explicit PrimitiveThread(TaskHandle_t threadHandle);
osThreadAttr_t constructThreadAttrs_(const char* name, const osPriority_t priority, const std::uint32_t stack_size); osThreadAttr_t constructThreadAttrs_(const char* name, const osPriority_t priority, const std::uint32_t stack_size);
}; };
@ -78,7 +79,7 @@ inline bool operator==(const PrimitiveThread& lhs, const PrimitiveThread& rhs)
return lhs.thread_id == rhs.thread_id; return lhs.thread_id == rhs.thread_id;
} }
} }// namespace Threads
#endif /* SKULLC_THREADS_PRIMITIVETHREAD_HPP_ */ #endif /* SKULLC_THREADS_PRIMITIVETHREAD_HPP_ */

View File

@ -0,0 +1,54 @@
/*
* threads_signal.hpp
*
* Created on: Jun 20, 2021
* Author: erki
*/
#ifndef THREADS_INC_THREADS_SIGNAL_HPP_
#define THREADS_INC_THREADS_SIGNAL_HPP_
#include <FreeRTOSConfig.h>
namespace Threads
{
class PrimitiveThread;
template<typename T>
struct Signallable
{
using value_type = T;
static_assert(std::is_trivially_constructible_v<value_type>, "T must be trivially constructible.");
virtual bool emit(const T& t) = 0;
virtual std::pair<bool, BaseType_t> emitFromIsr(const T& t) = 0;
};
template<>
struct Signallable<void>
{
using value_type = void;
virtual bool emit() = 0;
virtual std::pair<bool, BaseType_t> emitFromIsr() = 0;
};
template<typename R>
struct Awaitable
{
using result_type = R;
#ifdef INCLUDE_xTaskGetCurrentTaskHandle
virtual result_type await() = 0;
virtual result_type await(const long timeout) = 0;
#endif
virtual result_type await(const PrimitiveThread& currentThread) = 0;
virtual result_type await(const PrimitiveThread& currentThread, const long timeout) = 0;
};
}// namespace Threads
#endif /* THREADS_INC_THREADS_SIGNAL_HPP_ */

View File

@ -21,7 +21,8 @@ public:
: PrimitiveThread([](void* d) { : PrimitiveThread([](void* d) {
CRTP* dd = static_cast<CRTP*>(d); CRTP* dd = static_cast<CRTP*>(d);
(*dd)(); (*dd)();
}, name, priority, stack_size) },
this, name, priority, stack_size)
{} {}
Thread() = delete; Thread() = delete;

View File

@ -88,20 +88,18 @@ void PrimitiveThread::sleepUntil(const unsigned long deadline)
} }
PrimitiveThread::PrimitiveThread(osThreadFunc_t function, const osThreadAttr_t& attrs) PrimitiveThread::PrimitiveThread(osThreadFunc_t function, void* runner_data, const osThreadAttr_t& attrs)
: thread_id(nullptr) : thread_id(nullptr), attributes(attrs), runner_data_(runner_data)
, attributes(attrs)
{ {
assert(function); assert(function);
thread_id = osThreadNew(function, this, &attributes); thread_id = osThreadNew(function, runner_data_, &attributes);
assert(thread_id != nullptr); assert(thread_id != nullptr);
} }
PrimitiveThread::PrimitiveThread(TaskHandle_t threadHandle) PrimitiveThread::PrimitiveThread(TaskHandle_t threadHandle)
: thread_id(static_cast<osThreadId_t>(threadHandle)) : thread_id(static_cast<osThreadId_t>(threadHandle)), attributes(Peripherals::zeroInitialized<osThreadAttr_t>())
, attributes(Peripherals::zeroInitialized<osThreadAttr_t>())
{ {
assert(thread_id != nullptr); assert(thread_id != nullptr);
} }
@ -117,4 +115,4 @@ osThreadAttr_t PrimitiveThread::constructThreadAttrs_(const char* name, const os
return attrs; return attrs;
} }
} }// namespace Threads