Threads: Add initial Actor implementation, rework primitivethread a bit.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
6f7756e1cb
commit
ea474dd915
102
Threads/Inc/threads_actor.hpp
Normal file
102
Threads/Inc/threads_actor.hpp
Normal 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_ */
|
||||||
@ -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,9 +31,8 @@ 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;
|
||||||
ExclusiveSignal(ExclusiveSignal&&) = delete;
|
ExclusiveSignal(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,41 +65,56 @@ 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;
|
||||||
|
|
||||||
ExclusiveSignal()
|
ExclusiveSignal()
|
||||||
: waiting_thread_(std::nullopt)
|
: waiting_thread_(std::nullopt)
|
||||||
{ }
|
{}
|
||||||
|
|
||||||
ExclusiveSignal(const ExclusiveSignal&) = delete;
|
ExclusiveSignal(const ExclusiveSignal&) = delete;
|
||||||
ExclusiveSignal(ExclusiveSignal&&) = delete;
|
ExclusiveSignal(ExclusiveSignal&&) = delete;
|
||||||
@ -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_ */
|
||||||
|
|||||||
@ -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_ */
|
||||||
|
|||||||
54
Threads/Inc/threads_signal.hpp
Normal file
54
Threads/Inc/threads_signal.hpp
Normal 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_ */
|
||||||
@ -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;
|
||||||
|
|||||||
@ -10,17 +10,17 @@
|
|||||||
#include "peripherals_utility.hpp"
|
#include "peripherals_utility.hpp"
|
||||||
|
|
||||||
#ifdef INCLUDE_xTaskGetCurrentTaskHandle
|
#ifdef INCLUDE_xTaskGetCurrentTaskHandle
|
||||||
# define ASSERT_IS_CURRENT() \
|
#define ASSERT_IS_CURRENT() \
|
||||||
assert(Threads::PrimitiveThread::getCurrentThread().taskHandle() == this->taskHandle())
|
assert(Threads::PrimitiveThread::getCurrentThread().taskHandle() == this->taskHandle())
|
||||||
#else
|
#else
|
||||||
# define ASSERT_IS_CURRENT()
|
#define ASSERT_IS_CURRENT()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef INCLUDE_xTaskGetCurrentTaskHandle
|
#ifdef INCLUDE_xTaskGetCurrentTaskHandle
|
||||||
# define ASSERT_IS_NOT_CURRENT() \
|
#define ASSERT_IS_NOT_CURRENT() \
|
||||||
assert(Threads::PrimitiveThread::getCurrentThread().taskHandle() != this->taskHandle())
|
assert(Threads::PrimitiveThread::getCurrentThread().taskHandle() != this->taskHandle())
|
||||||
#else
|
#else
|
||||||
# define ASSERT_IS_NOT_CURRENT()
|
#define ASSERT_IS_NOT_CURRENT()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Threads
|
namespace Threads
|
||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user