Threads refactor
All checks were successful
continuous-integration/drone/push Build is passing

Split thread into two different entities.
Add exclusive signal.
This commit is contained in:
Erki 2021-06-19 19:34:28 +03:00
parent 869fe6e7d2
commit 6f7756e1cb
4 changed files with 363 additions and 92 deletions

View File

@ -0,0 +1,150 @@
/*
* threads_signal.hpp
*
* Created on: Jun 11, 2021
* Author: erki
*/
#ifndef SKULLC_THREADS_EXCLUSIVESIGNAL_HPP_
#define SKULLC_THREADS_EXCLUSIVESIGNAL_HPP_
#include <array>
#include <atomic>
#include <optional>
#include <type_traits>
#include "threads_primitivethread.hpp"
namespace Threads
{
template<typename T>
class ExclusiveSignal
{
public:
using value_type = T;
using result_type = std::optional<T>;
static_assert(
std::is_trivially_copyable_v<value_type>, "Signal's value must be trivially copyable.");
ExclusiveSignal()
: current_value_(std::nullopt)
, waiting_thread_(std::nullopt)
{ }
ExclusiveSignal(const ExclusiveSignal&) = delete;
ExclusiveSignal(ExclusiveSignal&&) = delete;
ExclusiveSignal& operator=(const ExclusiveSignal&) = delete;
ExclusiveSignal& operator=(ExclusiveSignal&&) = delete;
#ifdef INCLUDE_xTaskGetCurrentTaskHandle
result_type wait()
{
return wait(PrimitiveThread::getCurrentThread());
}
result_type wait(const long timeout)
{
return wait(PrimitiveThread::getCurrentThread(), timeout);
}
#endif
result_type wait(const PrimitiveThread& currentThread)
{
return wait(currentThread, portMAX_DELAY);
}
result_type wait(const PrimitiveThread& currentThread, const long timeout)
{
waiting_thread_ = currentThread;
const bool notified = currentThread.notifyWait(timeout);
if (!notified)
{
return std::nullopt;
}
else
{
return current_value_;
}
}
bool emit(const result_type& v)
{
current_value_ = v;
if (!waiting_thread_)
{
return false;
}
else
{
return waiting_thread_->notify(0, eNoAction);
}
}
private:
result_type current_value_;
std::optional<PrimitiveThread> waiting_thread_;
};
template<>
struct ExclusiveSignal<void>
{
using value_type = void;
using result_type = bool;
ExclusiveSignal()
: waiting_thread_(std::nullopt)
{ }
ExclusiveSignal(const ExclusiveSignal&) = delete;
ExclusiveSignal(ExclusiveSignal&&) = delete;
ExclusiveSignal& operator=(const ExclusiveSignal&) = delete;
ExclusiveSignal& operator=(ExclusiveSignal&&) = delete;
#ifdef INCLUDE_xTaskGetCurrentTaskHandle
result_type wait()
{
return wait(PrimitiveThread::getCurrentThread());
}
result_type wait(const long timeout)
{
return wait(PrimitiveThread::getCurrentThread(), timeout);
}
#endif
result_type wait(PrimitiveThread currentThread)
{
return wait(currentThread, portMAX_DELAY);
}
result_type wait(PrimitiveThread currentThread, const long timeout)
{
waiting_thread_ = currentThread;
return currentThread.notifyWait(timeout);
}
bool emit()
{
if (!waiting_thread_)
{
return false;
}
else
{
return waiting_thread_->notify(0, eNoAction);
}
}
private:
std::optional<PrimitiveThread> waiting_thread_;
};
}
#endif /* SKULLC_THREADS_EXCLUSIVESIGNAL_HPP_ */

View File

@ -0,0 +1,84 @@
/*
* threads_basethread.hpp
*
* Created on: Jun 11, 2021
* Author: erki
*/
#ifndef SKULLC_THREADS_PRIMITIVETHREAD_HPP_
#define SKULLC_THREADS_PRIMITIVETHREAD_HPP_
#include <cmsis_os.h>
#include <freertos_os2.h>
#include <type_traits>
#include <tuple>
namespace Threads
{
class PrimitiveThread
{
public:
PrimitiveThread() = delete;
PrimitiveThread(const PrimitiveThread&) = default;
PrimitiveThread(PrimitiveThread&&) = default;
PrimitiveThread& operator=(const PrimitiveThread&) = default;
PrimitiveThread& operator=(PrimitiveThread&&) = default;
osThreadId_t thread_id;
osThreadAttr_t attributes;
#ifdef INCLUDE_xTaskGetCurrentTaskHandle
static PrimitiveThread getCurrentThread();
#endif
[[nodiscard]] constexpr TaskHandle_t taskHandle() const
{
return static_cast<TaskHandle_t>(thread_id);
}
[[nodiscard]] BaseType_t notify(const long value, const eNotifyAction action);
/**
*
* @returns Tuple. First member indicating the return value of the underlying notify
* function, the second member indicating whether a higher priority task was awoken.
*
*/
[[nodiscard]] std::pair<BaseType_t, BaseType_t> notifyFromIsr(const long value, const eNotifyAction action);
void yield();
void sleep(const unsigned long delay);
void sleepUntil(const unsigned long deadline);
[[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;
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:
explicit PrimitiveThread(TaskHandle_t threadHandle);
osThreadAttr_t constructThreadAttrs_(const char* name, const osPriority_t priority, const std::uint32_t stack_size);
};
inline bool operator==(const PrimitiveThread& lhs, const PrimitiveThread& rhs)
{
if (!lhs.thread_id || !rhs.thread_id)
return false;
return lhs.thread_id == rhs.thread_id;
}
}
#endif /* SKULLC_THREADS_PRIMITIVETHREAD_HPP_ */

View File

@ -8,110 +8,27 @@
#ifndef SKULLC_THREADS_THREAD_HPP_ #ifndef SKULLC_THREADS_THREAD_HPP_
#define SKULLC_THREADS_THREAD_HPP_ #define SKULLC_THREADS_THREAD_HPP_
#include <cmsis_os.h> #include "threads_primitivethread.hpp"
#include <freertos_os2.h>
#include <cstdint>
#include <cstring>
#include <tuple>
#include <peripherals_utility.hpp>
namespace Threads namespace Threads
{ {
template<typename CRTP> template<typename CRTP>
class Thread class Thread : public PrimitiveThread
{ {
public: public:
Thread(const osThreadAttr_t attrs)
: attributes(attrs)
{
auto f = [](void* d) {
CRTP* dd = static_cast<CRTP*>(d);
(*dd)();
};
thread_id = osThreadNew(f, this, &attributes);
assert(thread_id != nullptr);
}
Thread(const char* name, const osPriority_t priority, const std::uint32_t stack_size) Thread(const char* name, const osPriority_t priority, const std::uint32_t stack_size)
: Thread(constructThreadAttrs_(name, priority, stack_size)) : PrimitiveThread([](void* d) {
CRTP* dd = static_cast<CRTP*>(d);
(*dd)();
}, name, priority, stack_size)
{} {}
Thread() = delete;
Thread(const Thread&) = delete; Thread(const Thread&) = delete;
Thread(Thread&&) = delete; Thread(Thread&&) = delete;
Thread& operator=(const Thread&) = delete;
osThreadId_t thread_id; Thread& operator=(Thread&&) = delete;
osThreadAttr_t attributes;
constexpr TaskHandle_t taskHandle() const
{
return static_cast<TaskHandle_t>(thread_id);
}
[[nodiscard]] BaseType_t notify(const long value, const eNotifyAction action)
{
return xTaskNotify(taskHandle(), value, action);
}
/**
*
* @returns Tuple. First member indicating the return value of the underlying notify
* function, the second member indicating whether a higher priority task was awoken.
*
*/
[[nodiscard]] std::pair<BaseType_t, BaseType_t> notifyFromIsr(const long value, const eNotifyAction action)
{
BaseType_t was_notified = pdFALSE;
const BaseType_t rval = xTaskNotifyFromISR(taskHandle(), value, action, &was_notified);
return {rval, was_notified};
}
void yield()
{
osThreadYield();
}
void sleep(const unsigned long delay)
{
osDelay(delay);
}
void sleepUntil(const unsigned long deadline)
{
osDelayUntil(deadline);
}
protected:
bool notifyWait(const TickType_t delay) const
{
return xTaskNotifyWait(0, std::numeric_limits<unsigned long>::max(), nullptr, delay);
}
std::pair<bool, unsigned long> notifyWait(const TickType_t delay, const unsigned long set_on_entry, const unsigned long set_on_exit) const
{
unsigned long notification_value = 0;
const bool rval = xTaskNotifyWait(set_on_entry, set_on_exit, &notification_value, delay);
return {rval, notification_value};
}
private:
osThreadAttr_t constructThreadAttrs_(const char* name, const osPriority_t priority, const std::uint32_t stack_size)
{
auto attrs = Peripherals::zeroInitialized<osThreadAttr_t>();
attrs.name = name;
attrs.priority = priority;
attrs.stack_size = stack_size;
return attrs;
}
}; };
}// namespace Threads }// namespace Threads

View File

@ -0,0 +1,120 @@
/*
* threads_primitivethread.cpp
*
* Created on: Jun 11, 2021
* Author: erki
*/
#include "threads_primitivethread.hpp"
#include "peripherals_utility.hpp"
#ifdef INCLUDE_xTaskGetCurrentTaskHandle
# define ASSERT_IS_CURRENT() \
assert(Threads::PrimitiveThread::getCurrentThread().taskHandle() == this->taskHandle())
#else
# define ASSERT_IS_CURRENT()
#endif
#ifdef INCLUDE_xTaskGetCurrentTaskHandle
# define ASSERT_IS_NOT_CURRENT() \
assert(Threads::PrimitiveThread::getCurrentThread().taskHandle() != this->taskHandle())
#else
# define ASSERT_IS_NOT_CURRENT()
#endif
namespace Threads
{
#ifdef INCLUDE_xTaskGetCurrentTaskHandle
PrimitiveThread PrimitiveThread::getCurrentThread()
{
return PrimitiveThread(xTaskGetCurrentTaskHandle());
}
#endif
BaseType_t PrimitiveThread::notify(const long value, const eNotifyAction action)
{
ASSERT_IS_NOT_CURRENT();
return xTaskNotify(taskHandle(), value, action);
}
std::pair<BaseType_t, BaseType_t> PrimitiveThread::notifyFromIsr(const long value, const eNotifyAction action)
{
BaseType_t was_notified = pdFALSE;
const BaseType_t rval = xTaskNotifyFromISR(taskHandle(), value, action, &was_notified);
return {rval, was_notified};
}
void PrimitiveThread::yield()
{
ASSERT_IS_CURRENT();
osThreadYield();
}
void PrimitiveThread::sleep(const unsigned long delay)
{
ASSERT_IS_CURRENT();
osDelay(delay);
}
void PrimitiveThread::sleepUntil(const unsigned long deadline)
{
ASSERT_IS_CURRENT();
osDelayUntil(deadline);
}
[[nodiscard]] bool PrimitiveThread::notifyWait(const TickType_t delay) const
{
ASSERT_IS_CURRENT();
return xTaskNotifyWait(0, std::numeric_limits<unsigned long>::max(), nullptr, delay);
}
[[nodiscard]] std::pair<bool, unsigned long> PrimitiveThread::notifyWait(const TickType_t delay, const unsigned long set_on_entry, const unsigned long set_on_exit) const
{
ASSERT_IS_CURRENT();
unsigned long notification_value = 0;
const bool rval = xTaskNotifyWait(set_on_entry, set_on_exit, &notification_value, delay);
return {rval, notification_value};
}
PrimitiveThread::PrimitiveThread(osThreadFunc_t function, const osThreadAttr_t& attrs)
: thread_id(nullptr)
, attributes(attrs)
{
assert(function);
thread_id = osThreadNew(function, this, &attributes);
assert(thread_id != nullptr);
}
PrimitiveThread::PrimitiveThread(TaskHandle_t threadHandle)
: thread_id(static_cast<osThreadId_t>(threadHandle))
, attributes(Peripherals::zeroInitialized<osThreadAttr_t>())
{
assert(thread_id != nullptr);
}
osThreadAttr_t PrimitiveThread::constructThreadAttrs_(const char* name, const osPriority_t priority, const std::uint32_t stack_size)
{
auto attrs = Peripherals::zeroInitialized<osThreadAttr_t>();
attrs.name = name;
attrs.priority = priority;
attrs.stack_size = stack_size;
return attrs;
}
}