TactilityCore improvements (#187)
FreeRTOS handles were stored plainly and they were deleted in the destructor of classes. This meant that if a class were to be copied, the destructor would be called twice on the same handles and lead to double-free. Seha on Discord suggested to fix this by using `std::unique_ptr` with a custom deletion function. The changes affect: - Thread - Semaphore - Mutex - StreamBuffer - Timer - MessageQueue - EventFlag Thread changes: - Removal of the hack with the `Data` struct - Thread's main body is now just a private static function inside the class. - The C functions were relocated to static class members PubSub changes: - Refactored pubsub into class - Renamed files to `PubSub` instead of `Pubsub` - `PubSubSubscription` is now a private inner struct and `PubSub` only exposes `SubscriptionHandle` Lockable, ScopedLockable, Mutex: - Added `lock()` method that locks indefinitely - Remove deprecated `acquire()` and `release()` methods - Removed `TtWaitForever` in favour of `portMAX_DELAY`
This commit is contained in:
committed by
GitHub
parent
c2edbad0fb
commit
686f7cce83
@@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "TactilityCoreConfig.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
[[deprecated("Using this is poor software design in most scenarios")]]
|
||||
typedef enum {
|
||||
TtWaitForever = 0xFFFFFFFFU,
|
||||
} TtWait;
|
||||
|
||||
[[deprecated("Define flags as needed")]]
|
||||
typedef enum {
|
||||
TtFlagWaitAny = 0x00000000U, ///< Wait for any flag (default).
|
||||
TtFlagWaitAll = 0x00000001U, ///< Wait for all flags.
|
||||
TtFlagNoClear = 0x00000002U, ///< Do not clear flags which have been specified to wait for.
|
||||
|
||||
TtFlagError = 0x80000000U, ///< Error indicator.
|
||||
TtFlagErrorUnknown = 0xFFFFFFFFU, ///< TtStatusError (-1).
|
||||
TtFlagErrorTimeout = 0xFFFFFFFEU, ///< TtStatusErrorTimeout (-2).
|
||||
TtFlagErrorResource = 0xFFFFFFFDU, ///< TtStatusErrorResource (-3).
|
||||
TtFlagErrorParameter = 0xFFFFFFFCU, ///< TtStatusErrorParameter (-4).
|
||||
TtFlagErrorISR = 0xFFFFFFFAU, ///< TtStatusErrorISR (-6).
|
||||
} TtFlag;
|
||||
|
||||
[[deprecated("Use bool or specific type")]]
|
||||
typedef enum {
|
||||
TtStatusOk = 0, ///< Operation completed successfully.
|
||||
TtStatusError =
|
||||
-1, ///< Unspecified RTOS error: run-time error but no other error message fits.
|
||||
TtStatusErrorTimeout = -2, ///< Operation not completed within the timeout period.
|
||||
TtStatusErrorResource = -3, ///< Resource not available.
|
||||
TtStatusErrorParameter = -4, ///< Parameter error.
|
||||
TtStatusErrorNoMemory =
|
||||
-5, ///< System is out of memory: it was impossible to allocate or reserve memory for the operation.
|
||||
TtStatusErrorISR =
|
||||
-6, ///< Not allowed in ISR context: the function cannot be called from interrupt service routines.
|
||||
TtStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
|
||||
} TtStatus;
|
||||
|
||||
} // namespace
|
||||
@@ -5,17 +5,13 @@
|
||||
namespace tt {
|
||||
|
||||
#define TAG "dispatcher"
|
||||
#define BACKPRESSURE_WARNING_COUNT 100
|
||||
#define WAIT_FLAG 1
|
||||
|
||||
Dispatcher::Dispatcher() :
|
||||
mutex(Mutex::Type::Normal)
|
||||
{}
|
||||
#define BACKPRESSURE_WARNING_COUNT ((EventBits_t)100)
|
||||
#define WAIT_FLAG ((EventBits_t)1)
|
||||
|
||||
Dispatcher::~Dispatcher() {
|
||||
// Wait for Mutex usage
|
||||
mutex.acquire(TtWaitForever);
|
||||
mutex.release();
|
||||
mutex.lock();
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void Dispatcher::dispatch(Function function, std::shared_ptr<void> context) {
|
||||
@@ -37,7 +33,7 @@ void Dispatcher::dispatch(Function function, std::shared_ptr<void> context) {
|
||||
uint32_t Dispatcher::consume(TickType_t timeout) {
|
||||
// Wait for signal and clear
|
||||
TickType_t start_ticks = kernel::getTicks();
|
||||
if (eventFlag.wait(WAIT_FLAG, TtFlagWaitAny, timeout) == WAIT_FLAG) {
|
||||
if (eventFlag.wait(WAIT_FLAG, EventFlag::WaitAny, timeout)) {
|
||||
eventFlag.clear(WAIT_FLAG);
|
||||
} else {
|
||||
return 0;
|
||||
|
||||
@@ -43,7 +43,7 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
explicit Dispatcher();
|
||||
explicit Dispatcher() = default;
|
||||
~Dispatcher();
|
||||
|
||||
/**
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
* @param[in] timeout the ticks to wait for a message
|
||||
* @return the amount of messages that were consumed
|
||||
*/
|
||||
uint32_t consume(TickType_t timeout);
|
||||
uint32_t consume(TickType_t timeout = portMAX_DELAY);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
|
||||
namespace tt {
|
||||
|
||||
EventFlag::EventFlag() {
|
||||
EventFlag::EventFlag() :
|
||||
handle(xEventGroupCreate())
|
||||
{
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
handle = xEventGroupCreate();
|
||||
tt_check(handle);
|
||||
}
|
||||
|
||||
EventFlag::~EventFlag() {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
vEventGroupDelete(handle);
|
||||
}
|
||||
|
||||
uint32_t EventFlag::set(uint32_t flags) const {
|
||||
@@ -28,14 +28,14 @@ uint32_t EventFlag::set(uint32_t flags) const {
|
||||
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
yield = pdFALSE;
|
||||
if (xEventGroupSetBitsFromISR(handle, (EventBits_t)flags, &yield) == pdFAIL) {
|
||||
rflags = (uint32_t)TtFlagErrorResource;
|
||||
if (xEventGroupSetBitsFromISR(handle.get(), (EventBits_t)flags, &yield) == pdFAIL) {
|
||||
rflags = (uint32_t)ErrorResource;
|
||||
} else {
|
||||
rflags = flags;
|
||||
portYIELD_FROM_ISR(yield);
|
||||
}
|
||||
} else {
|
||||
rflags = xEventGroupSetBits(handle, (EventBits_t)flags);
|
||||
rflags = xEventGroupSetBits(handle.get(), (EventBits_t)flags);
|
||||
}
|
||||
|
||||
/* Return event flags after setting */
|
||||
@@ -48,10 +48,10 @@ uint32_t EventFlag::clear(uint32_t flags) const {
|
||||
uint32_t rflags;
|
||||
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
rflags = xEventGroupGetBitsFromISR(handle);
|
||||
rflags = xEventGroupGetBitsFromISR(handle.get());
|
||||
|
||||
if (xEventGroupClearBitsFromISR(handle, (EventBits_t)flags) == pdFAIL) {
|
||||
rflags = (uint32_t)TtStatusErrorResource;
|
||||
if (xEventGroupClearBitsFromISR(handle.get(), (EventBits_t)flags) == pdFAIL) {
|
||||
rflags = (uint32_t)ErrorResource;
|
||||
} else {
|
||||
/* xEventGroupClearBitsFromISR only registers clear operation in the timer command queue. */
|
||||
/* Yield is required here otherwise clear operation might not execute in the right order. */
|
||||
@@ -59,7 +59,7 @@ uint32_t EventFlag::clear(uint32_t flags) const {
|
||||
portYIELD_FROM_ISR(pdTRUE);
|
||||
}
|
||||
} else {
|
||||
rflags = xEventGroupClearBits(handle, (EventBits_t)flags);
|
||||
rflags = xEventGroupClearBits(handle.get(), (EventBits_t)flags);
|
||||
}
|
||||
|
||||
/* Return event flags before clearing */
|
||||
@@ -70,9 +70,9 @@ uint32_t EventFlag::get() const {
|
||||
uint32_t rflags;
|
||||
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
rflags = xEventGroupGetBitsFromISR(handle);
|
||||
rflags = xEventGroupGetBitsFromISR(handle.get());
|
||||
} else {
|
||||
rflags = xEventGroupGetBits(handle);
|
||||
rflags = xEventGroupGetBits(handle.get());
|
||||
}
|
||||
|
||||
/* Return current event flags */
|
||||
@@ -91,40 +91,40 @@ uint32_t EventFlag::wait(
|
||||
BaseType_t exit_clear;
|
||||
uint32_t rflags;
|
||||
|
||||
if (options & TtFlagWaitAll) {
|
||||
if (options & WaitAll) {
|
||||
wait_all = pdTRUE;
|
||||
} else {
|
||||
wait_all = pdFAIL;
|
||||
}
|
||||
|
||||
if (options & TtFlagNoClear) {
|
||||
if (options & NoClear) {
|
||||
exit_clear = pdFAIL;
|
||||
} else {
|
||||
exit_clear = pdTRUE;
|
||||
}
|
||||
|
||||
rflags = xEventGroupWaitBits(
|
||||
handle,
|
||||
handle.get(),
|
||||
(EventBits_t)flags,
|
||||
exit_clear,
|
||||
wait_all,
|
||||
(TickType_t)timeout
|
||||
);
|
||||
|
||||
if (options & TtFlagWaitAll) {
|
||||
if (options & WaitAll) {
|
||||
if ((flags & rflags) != flags) {
|
||||
if (timeout > 0U) {
|
||||
rflags = (uint32_t)TtStatusErrorTimeout;
|
||||
rflags = (uint32_t)ErrorTimeout;
|
||||
} else {
|
||||
rflags = (uint32_t)TtStatusErrorResource;
|
||||
rflags = (uint32_t)ErrorResource;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((flags & rflags) == 0U) {
|
||||
if (timeout > 0U) {
|
||||
rflags = (uint32_t)TtStatusErrorTimeout;
|
||||
rflags = (uint32_t)ErrorTimeout;
|
||||
} else {
|
||||
rflags = (uint32_t)TtStatusErrorResource;
|
||||
rflags = (uint32_t)ErrorResource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "RtosCompatEventGroups.h"
|
||||
#include <memory>
|
||||
|
||||
namespace tt {
|
||||
|
||||
@@ -10,17 +10,40 @@ namespace tt {
|
||||
*/
|
||||
class EventFlag {
|
||||
private:
|
||||
EventGroupHandle_t handle;
|
||||
|
||||
struct EventGroupHandleDeleter {
|
||||
void operator()(EventGroupHandle_t handleToDelete) {
|
||||
vEventGroupDelete(handleToDelete);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<std::remove_pointer_t<EventGroupHandle_t>, EventGroupHandleDeleter> handle;
|
||||
|
||||
public:
|
||||
|
||||
EventFlag();
|
||||
~EventFlag();
|
||||
|
||||
enum Flag {
|
||||
WaitAny = 0x00000000U, ///< Wait for any flag (default).
|
||||
WaitAll = 0x00000001U, ///< Wait for all flags.
|
||||
NoClear = 0x00000002U, ///< Do not clear flags which have been specified to wait for.
|
||||
|
||||
Error = 0x80000000U, ///< Error indicator.
|
||||
ErrorUnknown = 0xFFFFFFFFU, ///< TtStatusError (-1).
|
||||
ErrorTimeout = 0xFFFFFFFEU, ///< TtStatusErrorTimeout (-2).
|
||||
ErrorResource = 0xFFFFFFFDU, ///< TtStatusErrorResource (-3).
|
||||
ErrorParameter = 0xFFFFFFFCU, ///< TtStatusErrorParameter (-4).
|
||||
ErrorISR = 0xFFFFFFFAU, ///< TtStatusErrorISR (-6).
|
||||
};
|
||||
|
||||
uint32_t set(uint32_t flags) const;
|
||||
uint32_t clear(uint32_t flags) const;
|
||||
uint32_t get() const;
|
||||
uint32_t wait(
|
||||
uint32_t flags,
|
||||
uint32_t options = TtFlagWaitAny,
|
||||
uint32_t timeout = TtWaitForever
|
||||
uint32_t options = WaitAny,
|
||||
uint32_t timeout = portMAX_DELAY
|
||||
) const;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include "Check.h"
|
||||
#include "RtosCompat.h"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
|
||||
namespace tt {
|
||||
|
||||
@@ -15,6 +17,8 @@ public:
|
||||
|
||||
virtual bool lock(TickType_t timeoutTicks) const = 0;
|
||||
|
||||
virtual bool lock() const { return lock(portMAX_DELAY); }
|
||||
|
||||
virtual bool unlock() const = 0;
|
||||
|
||||
std::unique_ptr<ScopedLockableUsage> scoped() const;
|
||||
@@ -43,6 +47,8 @@ public:
|
||||
return lockable.lock(timeout);
|
||||
}
|
||||
|
||||
bool lock() const override { return lock(portMAX_DELAY); }
|
||||
|
||||
bool unlock() const override {
|
||||
return lockable.unlock();
|
||||
}
|
||||
|
||||
@@ -4,63 +4,64 @@
|
||||
|
||||
namespace tt {
|
||||
|
||||
MessageQueue::MessageQueue(uint32_t capacity, uint32_t msg_size) {
|
||||
assert(!TT_IS_ISR() && (capacity > 0U) && (msg_size > 0U));
|
||||
queue_handle = xQueueCreate(capacity, msg_size);
|
||||
tt_check(queue_handle);
|
||||
static inline QueueHandle_t createQueue(uint32_t capacity, uint32_t messageSize) {
|
||||
assert(!TT_IS_ISR() && (capacity > 0U) && (messageSize > 0U));
|
||||
return xQueueCreate(capacity, messageSize);
|
||||
}
|
||||
|
||||
MessageQueue::MessageQueue(uint32_t capacity, uint32_t messageSize) : handle(createQueue(capacity, messageSize)) {
|
||||
tt_check(handle != nullptr);
|
||||
}
|
||||
|
||||
MessageQueue::~MessageQueue() {
|
||||
assert(!TT_IS_ISR());
|
||||
vQueueDelete(queue_handle);
|
||||
}
|
||||
|
||||
bool MessageQueue::put(const void* message, uint32_t timeout) {
|
||||
bool MessageQueue::put(const void* message, TickType_t timeout) {
|
||||
bool result = true;
|
||||
BaseType_t yield;
|
||||
|
||||
if (TT_IS_ISR()) {
|
||||
if ((queue_handle == nullptr) || (message == nullptr) || (timeout != 0U)) {
|
||||
if ((handle == nullptr) || (message == nullptr) || (timeout != 0U)) {
|
||||
result = false;
|
||||
} else {
|
||||
yield = pdFALSE;
|
||||
|
||||
if (xQueueSendToBackFromISR(queue_handle, message, &yield) != pdTRUE) {
|
||||
if (xQueueSendToBackFromISR(handle.get(), message, &yield) != pdTRUE) {
|
||||
result = false;
|
||||
} else {
|
||||
portYIELD_FROM_ISR(yield);
|
||||
}
|
||||
}
|
||||
} else if ((queue_handle == nullptr) || (message == nullptr)) {
|
||||
} else if ((handle == nullptr) || (message == nullptr)) {
|
||||
result = false;
|
||||
} else if (xQueueSendToBack(queue_handle, message, (TickType_t)timeout) != pdPASS) {
|
||||
} else if (xQueueSendToBack(handle.get(), message, (TickType_t)timeout) != pdPASS) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MessageQueue::get(void* msg_ptr, uint32_t timeout_ticks) {
|
||||
bool MessageQueue::get(void* msg_ptr, TickType_t timeout) {
|
||||
bool result = true;
|
||||
BaseType_t yield;
|
||||
|
||||
|
||||
if (TT_IS_ISR()) {
|
||||
if ((queue_handle == nullptr) || (msg_ptr == nullptr) || (timeout_ticks != 0U)) {
|
||||
if ((handle == nullptr) || (msg_ptr == nullptr) || (timeout != 0U)) {
|
||||
result = false;
|
||||
} else {
|
||||
yield = pdFALSE;
|
||||
|
||||
if (xQueueReceiveFromISR(queue_handle, msg_ptr, &yield) != pdPASS) {
|
||||
if (xQueueReceiveFromISR(handle.get(), msg_ptr, &yield) != pdPASS) {
|
||||
result = false;
|
||||
} else {
|
||||
portYIELD_FROM_ISR(yield);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((queue_handle == nullptr) || (msg_ptr == nullptr)) {
|
||||
if ((handle == nullptr) || (msg_ptr == nullptr)) {
|
||||
result = false;
|
||||
} else if (xQueueReceive(queue_handle, msg_ptr, (TickType_t)timeout_ticks) != pdPASS) {
|
||||
} else if (xQueueReceive(handle.get(), msg_ptr, (TickType_t)timeout) != pdPASS) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
@@ -69,7 +70,7 @@ bool MessageQueue::get(void* msg_ptr, uint32_t timeout_ticks) {
|
||||
}
|
||||
|
||||
uint32_t MessageQueue::getCapacity() const {
|
||||
auto* mq = (StaticQueue_t*)(queue_handle);
|
||||
auto* mq = (StaticQueue_t*)(handle.get());
|
||||
if (mq == nullptr) {
|
||||
return 0U;
|
||||
} else {
|
||||
@@ -78,7 +79,7 @@ uint32_t MessageQueue::getCapacity() const {
|
||||
}
|
||||
|
||||
uint32_t MessageQueue::getMessageSize() const {
|
||||
auto* mq = (StaticQueue_t*)(queue_handle);
|
||||
auto* mq = (StaticQueue_t*)(handle.get());
|
||||
if (mq == nullptr) {
|
||||
return 0U;
|
||||
} else {
|
||||
@@ -89,12 +90,12 @@ uint32_t MessageQueue::getMessageSize() const {
|
||||
uint32_t MessageQueue::getCount() const {
|
||||
UBaseType_t count;
|
||||
|
||||
if (queue_handle == nullptr) {
|
||||
if (handle == nullptr) {
|
||||
count = 0U;
|
||||
} else if (TT_IS_ISR()) {
|
||||
count = uxQueueMessagesWaitingFromISR(queue_handle);
|
||||
count = uxQueueMessagesWaitingFromISR(handle.get());
|
||||
} else {
|
||||
count = uxQueueMessagesWaiting(queue_handle);
|
||||
count = uxQueueMessagesWaiting(handle.get());
|
||||
}
|
||||
|
||||
/* Return number of queued messages */
|
||||
@@ -102,7 +103,7 @@ uint32_t MessageQueue::getCount() const {
|
||||
}
|
||||
|
||||
uint32_t MessageQueue::getSpace() const {
|
||||
auto* mq = (StaticQueue_t*)(queue_handle);
|
||||
auto* mq = (StaticQueue_t*)(handle.get());
|
||||
uint32_t space;
|
||||
uint32_t isrm;
|
||||
|
||||
@@ -124,10 +125,10 @@ uint32_t MessageQueue::getSpace() const {
|
||||
|
||||
bool MessageQueue::reset() {
|
||||
tt_check(!TT_IS_ISR());
|
||||
if (queue_handle == nullptr) {
|
||||
if (handle == nullptr) {
|
||||
return false;
|
||||
} else {
|
||||
xQueueReset(queue_handle);
|
||||
xQueueReset(handle.get());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include <memory>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -25,7 +25,14 @@ namespace tt {
|
||||
*/
|
||||
class MessageQueue {
|
||||
private:
|
||||
QueueHandle_t queue_handle;
|
||||
|
||||
struct QueueHandleDeleter {
|
||||
void operator()(QueueHandle_t handleToDelete) {
|
||||
vQueueDelete(handleToDelete);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<std::remove_pointer_t<QueueHandle_t>, QueueHandleDeleter> handle;
|
||||
|
||||
public:
|
||||
/** Allocate message queue
|
||||
@@ -39,17 +46,17 @@ public:
|
||||
/** Post a message to the queue.
|
||||
* The message is queued by copy, not by reference.
|
||||
* @param[in] message A pointer to a message. The message will be copied into a buffer.
|
||||
* @param[in] timeoutTicks
|
||||
* @param[in] timeout
|
||||
* @return success result
|
||||
*/
|
||||
bool put(const void* message, uint32_t timeoutTicks);
|
||||
bool put(const void* message, TickType_t timeout);
|
||||
|
||||
/** Get message from queue
|
||||
* @param[out] message A pointer to an already allocated message object
|
||||
* @param[in] timeoutTicks
|
||||
* @param[in] timeout
|
||||
* @return success result
|
||||
*/
|
||||
bool get(void* message, uint32_t timeoutTicks);
|
||||
bool get(void* message, TickType_t timeout);
|
||||
|
||||
/**
|
||||
* @return The maximum amount of messages that can be in the queue at any given time.
|
||||
|
||||
@@ -22,88 +22,56 @@ namespace tt {
|
||||
#define tt_mutex_info(mutex, text)
|
||||
#endif
|
||||
|
||||
Mutex::Mutex(Type type) : type(type) {
|
||||
tt_mutex_info(data, "alloc");
|
||||
static inline SemaphoreHandle_t createSemaphoreHandle(Mutex::Type type) {
|
||||
switch (type) {
|
||||
case Type::Normal:
|
||||
semaphore = xSemaphoreCreateMutex();
|
||||
break;
|
||||
case Type::Recursive:
|
||||
semaphore = xSemaphoreCreateRecursiveMutex();
|
||||
break;
|
||||
case Mutex::Type::Normal:
|
||||
return xSemaphoreCreateMutex();
|
||||
case Mutex::Type::Recursive:
|
||||
return xSemaphoreCreateRecursiveMutex();
|
||||
default:
|
||||
tt_crash("Mutex type unknown/corrupted");
|
||||
}
|
||||
|
||||
assert(semaphore != nullptr);
|
||||
}
|
||||
|
||||
Mutex::~Mutex() {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
vSemaphoreDelete(semaphore);
|
||||
semaphore = nullptr; // If the mutex is used after release, this might help debugging
|
||||
Mutex::Mutex(Type type) : handle(createSemaphoreHandle(type)), type(type) {
|
||||
tt_mutex_info(data, "alloc");
|
||||
assert(handle != nullptr);
|
||||
}
|
||||
|
||||
TtStatus Mutex::acquire(TickType_t timeout) const {
|
||||
bool Mutex::lock(TickType_t timeout) const {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
assert(semaphore != nullptr);
|
||||
|
||||
assert(handle != nullptr);
|
||||
tt_mutex_info(mutex, "acquire");
|
||||
|
||||
switch (type) {
|
||||
case Type::Normal:
|
||||
if (xSemaphoreTake(semaphore, timeout) != pdPASS) {
|
||||
if (timeout != 0U) {
|
||||
return TtStatusErrorTimeout;
|
||||
} else {
|
||||
return TtStatusErrorResource;
|
||||
}
|
||||
} else {
|
||||
return TtStatusOk;
|
||||
}
|
||||
return xSemaphoreTake(handle.get(), timeout) == pdPASS;
|
||||
case Type::Recursive:
|
||||
if (xSemaphoreTakeRecursive(semaphore, timeout) != pdPASS) {
|
||||
if (timeout != 0U) {
|
||||
return TtStatusErrorTimeout;
|
||||
} else {
|
||||
return TtStatusErrorResource;
|
||||
}
|
||||
} else {
|
||||
return TtStatusOk;
|
||||
}
|
||||
return xSemaphoreTakeRecursive(handle.get(), timeout) == pdPASS;
|
||||
default:
|
||||
tt_crash("mutex type unknown/corrupted");
|
||||
tt_crash();
|
||||
}
|
||||
}
|
||||
|
||||
TtStatus Mutex::release() const {
|
||||
bool Mutex::unlock() const {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
assert(semaphore);
|
||||
assert(handle != nullptr);
|
||||
tt_mutex_info(mutex, "release");
|
||||
|
||||
switch (type) {
|
||||
case Type::Normal: {
|
||||
if (xSemaphoreGive(semaphore) != pdPASS) {
|
||||
return TtStatusErrorResource;
|
||||
} else {
|
||||
return TtStatusOk;
|
||||
}
|
||||
}
|
||||
case Type::Normal:
|
||||
return xSemaphoreGive(handle.get()) == pdPASS;
|
||||
case Type::Recursive:
|
||||
if (xSemaphoreGiveRecursive(semaphore) != pdPASS) {
|
||||
return TtStatusErrorResource;
|
||||
} else {
|
||||
return TtStatusOk;
|
||||
}
|
||||
return xSemaphoreGiveRecursive(handle.get()) == pdPASS;
|
||||
default:
|
||||
tt_crash("mutex type unknown/corrupted");
|
||||
tt_crash();
|
||||
}
|
||||
}
|
||||
|
||||
ThreadId Mutex::getOwner() const {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
assert(semaphore);
|
||||
return (ThreadId)xSemaphoreGetMutexHolder(semaphore);
|
||||
assert(handle != nullptr);
|
||||
return (ThreadId)xSemaphoreGetMutexHolder(handle.get());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Thread.h"
|
||||
#include "RtosCompatSemaphore.h"
|
||||
#include "Check.h"
|
||||
@@ -15,9 +14,9 @@ namespace tt {
|
||||
|
||||
/**
|
||||
* Wrapper for FreeRTOS xSemaphoreCreateMutex and xSemaphoreCreateRecursiveMutex
|
||||
* Can be used in IRQ mode (within ISR context)
|
||||
* Cannot be used in IRQ mode (within ISR context)
|
||||
*/
|
||||
class Mutex : public Lockable {
|
||||
class Mutex final : public Lockable {
|
||||
|
||||
public:
|
||||
|
||||
@@ -28,35 +27,36 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
SemaphoreHandle_t semaphore;
|
||||
struct SemaphoreHandleDeleter {
|
||||
void operator()(QueueHandle_t handleToDelete) {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
vSemaphoreDelete(handleToDelete);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<std::remove_pointer_t<QueueHandle_t>, SemaphoreHandleDeleter> handle;
|
||||
Type type;
|
||||
|
||||
public:
|
||||
|
||||
explicit Mutex(Type type = Type::Normal);
|
||||
~Mutex() override;
|
||||
|
||||
/** Attempt to lock the mutex. Blocks until timeout passes or lock is acquired.
|
||||
* @param[in] timeout
|
||||
* @return status result
|
||||
*/
|
||||
TtStatus acquire(TickType_t timeout) const;
|
||||
|
||||
/** Attempt to unlock the mutex.
|
||||
* @return status result
|
||||
*/
|
||||
TtStatus release() const;
|
||||
~Mutex() override = default;
|
||||
|
||||
/** Attempt to lock the mutex. Blocks until timeout passes or lock is acquired.
|
||||
* @param[in] timeout
|
||||
* @return success result
|
||||
*/
|
||||
bool lock(TickType_t timeout) const override { return acquire(timeout) == TtStatusOk; }
|
||||
bool lock(TickType_t timeout) const override;
|
||||
|
||||
/** Attempt to lock the mutex. Blocks until lock is acquired, without timeout.
|
||||
* @return success result
|
||||
*/
|
||||
bool lock() const override { return lock(portMAX_DELAY); }
|
||||
|
||||
/** Attempt to unlock the mutex.
|
||||
* @return success result
|
||||
*/
|
||||
bool unlock() const override { return release() == TtStatusOk; }
|
||||
bool unlock() const override;
|
||||
|
||||
/** @return the owner of the thread */
|
||||
ThreadId getOwner() const;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#include "PubSub.h"
|
||||
#include "Check.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
PubSub::SubscriptionHandle PubSub::subscribe(PubSubCallback callback, void* callbackParameter) {
|
||||
mutex.lock();
|
||||
items.push_back({
|
||||
.id = (++lastId),
|
||||
.callback = callback,
|
||||
.callbackParameter = callbackParameter});
|
||||
|
||||
mutex.unlock();
|
||||
|
||||
return (Subscription*)lastId;
|
||||
}
|
||||
|
||||
void PubSub::unsubscribe(SubscriptionHandle subscription) {
|
||||
assert(subscription);
|
||||
|
||||
mutex.lock();
|
||||
bool result = false;
|
||||
auto id = (uint64_t)subscription;
|
||||
for (auto it = items.begin(); it != items.end(); it++) {
|
||||
if (it->id == id) {
|
||||
items.erase(it);
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex.unlock();
|
||||
tt_check(result);
|
||||
}
|
||||
|
||||
void PubSub::publish(void* message) {
|
||||
mutex.lock();
|
||||
|
||||
// Iterate over subscribers
|
||||
for (auto& it : items) {
|
||||
it.callback(message, it.callbackParameter);
|
||||
}
|
||||
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @file pubsub.h
|
||||
* PubSub
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "Mutex.h"
|
||||
#include <list>
|
||||
|
||||
namespace tt {
|
||||
|
||||
/** PubSub Callback type */
|
||||
typedef void (*PubSubCallback)(const void* message, void* context);
|
||||
|
||||
/** Publish and subscribe to messages in a thread-safe manner. */
|
||||
class PubSub {
|
||||
|
||||
private:
|
||||
|
||||
struct Subscription {
|
||||
uint64_t id;
|
||||
PubSubCallback callback;
|
||||
void* callbackParameter;
|
||||
};
|
||||
|
||||
typedef std::list<Subscription> Subscriptions;
|
||||
uint64_t lastId = 0;
|
||||
Subscriptions items;
|
||||
Mutex mutex;
|
||||
|
||||
public:
|
||||
|
||||
typedef void* SubscriptionHandle;
|
||||
|
||||
PubSub() = default;
|
||||
|
||||
~PubSub() {
|
||||
tt_check(items.empty());
|
||||
}
|
||||
|
||||
/** Start receiving messages at the specified handle (Threadsafe, Re-entrable)
|
||||
* @param[in] callback
|
||||
* @param[in] callbackParameter the data to pass to the callback
|
||||
* @return subscription instance
|
||||
*/
|
||||
SubscriptionHandle subscribe(PubSubCallback callback, void* callbackParameter);
|
||||
|
||||
/** Stop receiving messages at the specified handle (Threadsafe, Re-entrable.)
|
||||
* No use of `tt_pubsub_subscription` allowed after call of this method
|
||||
* @param[in] subscription
|
||||
*/
|
||||
void unsubscribe(SubscriptionHandle subscription);
|
||||
|
||||
/** Publish message to all subscribers (Threadsafe, Re-entrable.)
|
||||
* @param[in] message message pointer to publish - it is passed as-is to the callback
|
||||
*/
|
||||
void publish(void* message);
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
@@ -1,52 +0,0 @@
|
||||
#include "Pubsub.h"
|
||||
#include "Check.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
PubSubSubscription* tt_pubsub_subscribe(std::shared_ptr<PubSub> pubsub, PubSubCallback callback, void* callbackContext) {
|
||||
tt_check(pubsub->mutex.acquire(TtWaitForever) == TtStatusOk);
|
||||
PubSubSubscription subscription = {
|
||||
.id = (++pubsub->last_id),
|
||||
.callback = callback,
|
||||
.callback_context = callbackContext
|
||||
};
|
||||
pubsub->items.push_back(
|
||||
subscription
|
||||
);
|
||||
|
||||
tt_check(pubsub->mutex.release() == TtStatusOk);
|
||||
|
||||
return (PubSubSubscription*)pubsub->last_id;
|
||||
}
|
||||
|
||||
void tt_pubsub_unsubscribe(std::shared_ptr<PubSub> pubsub, PubSubSubscription* pubsub_subscription) {
|
||||
assert(pubsub);
|
||||
assert(pubsub_subscription);
|
||||
|
||||
tt_check(pubsub->mutex.acquire(TtWaitForever) == TtStatusOk);
|
||||
bool result = false;
|
||||
auto id = (uint64_t)pubsub_subscription;
|
||||
for (auto it = pubsub->items.begin(); it != pubsub->items.end(); it++) {
|
||||
if (it->id == id) {
|
||||
pubsub->items.erase(it);
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tt_check(pubsub->mutex.release() == TtStatusOk);
|
||||
tt_check(result);
|
||||
}
|
||||
|
||||
void tt_pubsub_publish(std::shared_ptr<PubSub> pubsub, void* message) {
|
||||
tt_check(pubsub->mutex.acquire(TtWaitForever) == TtStatusOk);
|
||||
|
||||
// Iterate over subscribers
|
||||
for (auto& it : pubsub->items) {
|
||||
it.callback(message, it.callback_context);
|
||||
}
|
||||
|
||||
tt_check(pubsub->mutex.release() == TtStatusOk);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -1,57 +0,0 @@
|
||||
/**
|
||||
* @file pubsub.h
|
||||
* PubSub
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "Mutex.h"
|
||||
#include <list>
|
||||
|
||||
namespace tt {
|
||||
|
||||
/** PubSub Callback type */
|
||||
typedef void (*PubSubCallback)(const void* message, void* context);
|
||||
|
||||
struct PubSubSubscription {
|
||||
uint64_t id;
|
||||
PubSubCallback callback;
|
||||
void* callback_context;
|
||||
};
|
||||
|
||||
struct PubSub {
|
||||
typedef std::list<PubSubSubscription> Subscriptions;
|
||||
uint64_t last_id = 0;
|
||||
Subscriptions items;
|
||||
Mutex mutex;
|
||||
|
||||
~PubSub() {
|
||||
tt_check(items.empty());
|
||||
}
|
||||
};
|
||||
|
||||
/** Subscribe to PubSub
|
||||
* Threadsafe, Reentrable
|
||||
* @param[in] pubsub pointer to PubSub instance
|
||||
* @param[in] callback
|
||||
* @param[in] callbackContext the data to pass to the callback
|
||||
* @return subscription instance
|
||||
*/
|
||||
PubSubSubscription*
|
||||
tt_pubsub_subscribe(std::shared_ptr<PubSub> pubsub, PubSubCallback callback, void* callbackContext);
|
||||
|
||||
/** Unsubscribe from PubSub
|
||||
* No use of `tt_pubsub_subscription` allowed after call of this method
|
||||
* Threadsafe, Re-entrable.
|
||||
* @param[in] pubsub
|
||||
* @param[in] subscription
|
||||
*/
|
||||
void tt_pubsub_unsubscribe(std::shared_ptr<PubSub> pubsub, PubSubSubscription* subscription);
|
||||
|
||||
/** Publish message to PubSub
|
||||
* Threadsafe, Reentrable.
|
||||
* @param[in] pubsub
|
||||
* @param[in] message message pointer to publish - it is passed as-is to the callback
|
||||
*/
|
||||
void tt_pubsub_publish(std::shared_ptr<PubSub> pubsub, void* message);
|
||||
|
||||
} // namespace
|
||||
@@ -4,28 +4,30 @@
|
||||
|
||||
namespace tt {
|
||||
|
||||
Semaphore::Semaphore(uint32_t maxCount, uint32_t initialCount) {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
static inline struct QueueDefinition* createHandle(uint32_t maxCount, uint32_t initialCount) {
|
||||
assert((maxCount > 0U) && (initialCount <= maxCount));
|
||||
|
||||
if (maxCount == 1U) {
|
||||
handle = xSemaphoreCreateBinary();
|
||||
auto handle = xSemaphoreCreateBinary();
|
||||
if ((handle != nullptr) && (initialCount != 0U)) {
|
||||
if (xSemaphoreGive(handle) != pdPASS) {
|
||||
vSemaphoreDelete(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
}
|
||||
return handle;
|
||||
} else {
|
||||
handle = xSemaphoreCreateCounting(maxCount, initialCount);
|
||||
return xSemaphoreCreateCounting(maxCount, initialCount);
|
||||
}
|
||||
}
|
||||
|
||||
tt_check(handle);
|
||||
Semaphore::Semaphore(uint32_t maxCount, uint32_t initialCount) : handle(createHandle(maxCount, initialCount)){
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
tt_check(handle != nullptr);
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore() {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
vSemaphoreDelete(handle);
|
||||
}
|
||||
|
||||
bool Semaphore::acquire(uint32_t timeout) const {
|
||||
@@ -35,7 +37,7 @@ bool Semaphore::acquire(uint32_t timeout) const {
|
||||
} else {
|
||||
BaseType_t yield = pdFALSE;
|
||||
|
||||
if (xSemaphoreTakeFromISR(handle, &yield) != pdPASS) {
|
||||
if (xSemaphoreTakeFromISR(handle.get(), &yield) != pdPASS) {
|
||||
return false;
|
||||
} else {
|
||||
portYIELD_FROM_ISR(yield);
|
||||
@@ -43,21 +45,21 @@ bool Semaphore::acquire(uint32_t timeout) const {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return xSemaphoreTake(handle, (TickType_t)timeout) == pdPASS;
|
||||
return xSemaphoreTake(handle.get(), (TickType_t)timeout) == pdPASS;
|
||||
}
|
||||
}
|
||||
|
||||
bool Semaphore::release() const {
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
BaseType_t yield = pdFALSE;
|
||||
if (xSemaphoreGiveFromISR(handle, &yield) != pdTRUE) {
|
||||
if (xSemaphoreGiveFromISR(handle.get(), &yield) != pdTRUE) {
|
||||
return false;
|
||||
} else {
|
||||
portYIELD_FROM_ISR(yield);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return xSemaphoreGive(handle) == pdPASS;
|
||||
return xSemaphoreGive(handle.get()) == pdPASS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,12 +67,12 @@ uint32_t Semaphore::getCount() const {
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
// TODO: uxSemaphoreGetCountFromISR is not supported on esp-idf 5.1.2 - perhaps later on?
|
||||
#ifdef uxSemaphoreGetCountFromISR
|
||||
return uxSemaphoreGetCountFromISR(handle);
|
||||
return uxSemaphoreGetCountFromISR(handle.get());
|
||||
#else
|
||||
return uxQueueMessagesWaitingFromISR((QueueHandle_t)hSemaphore);
|
||||
#endif
|
||||
} else {
|
||||
return uxSemaphoreGetCount(handle);
|
||||
return uxSemaphoreGetCount(handle.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Thread.h"
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -18,8 +19,18 @@ namespace tt {
|
||||
* Can be used from IRQ/ISR mode, but cannot be created/destroyed from such a context.
|
||||
*/
|
||||
class Semaphore {
|
||||
|
||||
private:
|
||||
SemaphoreHandle_t handle;
|
||||
|
||||
struct SemaphoreHandleDeleter {
|
||||
void operator()(QueueHandle_t handleToDelete) {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
vSemaphoreDelete(handleToDelete);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<std::remove_pointer_t<QueueHandle_t>, SemaphoreHandleDeleter> handle;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Cannot be called from IRQ/ISR mode.
|
||||
|
||||
@@ -2,22 +2,20 @@
|
||||
|
||||
#include "Check.h"
|
||||
#include "CoreDefines.h"
|
||||
#include "CoreTypes.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
StreamBuffer::StreamBuffer(size_t size, size_t triggerLevel) {
|
||||
inline static StreamBufferHandle_t createStreamBuffer(size_t size, size_t triggerLevel) {
|
||||
assert(size != 0);
|
||||
handle = xStreamBufferCreate(size, triggerLevel);
|
||||
return xStreamBufferCreate(size, triggerLevel);
|
||||
}
|
||||
|
||||
StreamBuffer::StreamBuffer(size_t size, size_t triggerLevel) : handle(createStreamBuffer(size, triggerLevel)) {
|
||||
tt_check(handle);
|
||||
};
|
||||
|
||||
StreamBuffer::~StreamBuffer() {
|
||||
vStreamBufferDelete(handle);
|
||||
};
|
||||
|
||||
bool StreamBuffer::setTriggerLevel(size_t triggerLevel) const {
|
||||
return xStreamBufferSetTriggerLevel(handle, triggerLevel) == pdTRUE;
|
||||
return xStreamBufferSetTriggerLevel(handle.get(), triggerLevel) == pdTRUE;
|
||||
};
|
||||
|
||||
size_t StreamBuffer::send(
|
||||
@@ -27,11 +25,11 @@ size_t StreamBuffer::send(
|
||||
) const {
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
BaseType_t yield;
|
||||
size_t result = xStreamBufferSendFromISR(handle, data, length, &yield);
|
||||
size_t result = xStreamBufferSendFromISR(handle.get(), data, length, &yield);
|
||||
portYIELD_FROM_ISR(yield);
|
||||
return result;
|
||||
} else {
|
||||
return xStreamBufferSend(handle, data, length, timeout);
|
||||
return xStreamBufferSend(handle.get(), data, length, timeout);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -42,32 +40,32 @@ size_t StreamBuffer::receive(
|
||||
) const {
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
BaseType_t yield;
|
||||
size_t result = xStreamBufferReceiveFromISR(handle, data, length, &yield);
|
||||
size_t result = xStreamBufferReceiveFromISR(handle.get(), data, length, &yield);
|
||||
portYIELD_FROM_ISR(yield);
|
||||
return result;
|
||||
} else {
|
||||
return xStreamBufferReceive(handle, data, length, timeout);
|
||||
return xStreamBufferReceive(handle.get(), data, length, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
size_t StreamBuffer::getAvailableReadBytes() const {
|
||||
return xStreamBufferBytesAvailable(handle);
|
||||
return xStreamBufferBytesAvailable(handle.get());
|
||||
};
|
||||
|
||||
size_t StreamBuffer::getAvailableWriteBytes() const {
|
||||
return xStreamBufferSpacesAvailable(handle);
|
||||
return xStreamBufferSpacesAvailable(handle.get());
|
||||
};
|
||||
|
||||
bool StreamBuffer::isFull() const {
|
||||
return xStreamBufferIsFull(handle) == pdTRUE;
|
||||
return xStreamBufferIsFull(handle.get()) == pdTRUE;
|
||||
};
|
||||
|
||||
bool StreamBuffer::isEmpty() const {
|
||||
return xStreamBufferIsEmpty(handle) == pdTRUE;
|
||||
return xStreamBufferIsEmpty(handle.get()) == pdTRUE;
|
||||
};
|
||||
|
||||
bool StreamBuffer::reset() const {
|
||||
return xStreamBufferReset(handle) == pdPASS;
|
||||
return xStreamBufferReset(handle.get()) == pdPASS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -25,8 +23,16 @@ namespace tt {
|
||||
* interrupt that will read from the buffer (the reader).
|
||||
*/
|
||||
class StreamBuffer {
|
||||
|
||||
private:
|
||||
StreamBufferHandle_t handle;
|
||||
|
||||
struct StreamBufferHandleDeleter {
|
||||
void operator()(StreamBufferHandle_t handleToDelete) {
|
||||
vStreamBufferDelete(handleToDelete);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<std::remove_pointer_t<StreamBufferHandle_t>, StreamBufferHandleDeleter> handle;
|
||||
|
||||
public:
|
||||
|
||||
@@ -42,7 +48,7 @@ public:
|
||||
*/
|
||||
StreamBuffer(size_t size, size_t triggerLevel);
|
||||
|
||||
~StreamBuffer();
|
||||
~StreamBuffer() = default;
|
||||
|
||||
/**
|
||||
* @brief Set trigger level for stream buffer.
|
||||
@@ -65,7 +71,7 @@ public:
|
||||
* @param[in] timeout The maximum amount of time the task should remain in the
|
||||
* Blocked state to wait for space to become available if the stream buffer is full.
|
||||
* Will return immediately if timeout is zero.
|
||||
* Setting timeout to TtWaitForever will cause the task to wait indefinitely.
|
||||
* Setting timeout to portMAX_DELAY will cause the task to wait indefinitely.
|
||||
* Ignored if called from ISR.
|
||||
* @return The number of bytes actually written to the stream buffer.
|
||||
*/
|
||||
@@ -85,7 +91,7 @@ public:
|
||||
* @param[in] timeout The maximum amount of time the task should remain in the
|
||||
* Blocked state to wait for data to become available if the stream buffer is empty.
|
||||
* Will return immediately if timeout is zero.
|
||||
* Setting timeout to TtWaitForever will cause the task to wait indefinitely.
|
||||
* Setting timeout to portMAX_DELAY will cause the task to wait indefinitely.
|
||||
* Ignored if called from ISR.
|
||||
* @return The number of bytes read from the stream buffer, if any.
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "Check.h"
|
||||
#include "CoreDefines.h"
|
||||
#include "CoreExtraDefines.h"
|
||||
#include "CoreTypes.h"
|
||||
#include "EventFlag.h"
|
||||
#include "kernel/Kernel.h"
|
||||
#include "kernel/critical/Critical.h"
|
||||
|
||||
+105
-158
@@ -1,10 +1,13 @@
|
||||
#include "Thread.h"
|
||||
#include <string>
|
||||
|
||||
#include "Check.h"
|
||||
#include "CoreDefines.h"
|
||||
#include "EventFlag.h"
|
||||
#include "kernel/Kernel.h"
|
||||
#include "Log.h"
|
||||
#include "TactilityCoreConfig.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace tt {
|
||||
|
||||
@@ -22,180 +25,155 @@ namespace tt {
|
||||
static_assert(static_cast<UBaseType_t>(Thread::Priority::Critical) <= TT_CONFIG_THREAD_MAX_PRIORITIES, "highest thread priority is higher than max priority");
|
||||
static_assert(TT_CONFIG_THREAD_MAX_PRIORITIES <= configMAX_PRIORITIES, "highest tactility priority is higher than max FreeRTOS priority");
|
||||
|
||||
void setState(Thread::Data* data, Thread::State state) {
|
||||
data->state = state;
|
||||
if (data->stateCallback) {
|
||||
data->stateCallback(state, data->stateCallbackContext);
|
||||
void Thread::setState(Thread::State newState) {
|
||||
state = newState;
|
||||
if (stateCallback) {
|
||||
stateCallback(state, stateCallbackContext);
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(configSUPPORT_DYNAMIC_ALLOCATION == 1);
|
||||
|
||||
/** Catch threads that are trying to exit wrong way */
|
||||
__attribute__((__noreturn__)) void thread_catch() { //-V1082
|
||||
// If you're here it means you're probably doing something wrong
|
||||
// with critical sections or with scheduler state
|
||||
asm volatile("nop"); // extra magic
|
||||
tt_crash("You are doing it wrong"); //-V779
|
||||
__attribute__((__noreturn__)) void threadCatch() { //-V1082
|
||||
// If you're here it means you're probably doing something wrong with critical sections or with scheduler state
|
||||
asm volatile("nop");
|
||||
tt_crash();
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
|
||||
static void thread_body(void* context) {
|
||||
assert(context);
|
||||
auto* data = static_cast<Thread::Data*>(context);
|
||||
void Thread::mainBody(void* context) {
|
||||
assert(context != nullptr);
|
||||
auto* thread = static_cast<Thread*>(context);
|
||||
|
||||
// Store thread data instance to thread local storage
|
||||
assert(pvTaskGetThreadLocalStoragePointer(nullptr, 0) == nullptr);
|
||||
vTaskSetThreadLocalStoragePointer(nullptr, 0, data->thread);
|
||||
vTaskSetThreadLocalStoragePointer(nullptr, 0, thread);
|
||||
|
||||
assert(data->state == Thread::State::Starting);
|
||||
setState(data, Thread::State::Running);
|
||||
data->callbackResult = data->callback(data->callbackContext);
|
||||
assert(data->state == Thread::State::Running);
|
||||
assert(thread->state == Thread::State::Starting);
|
||||
thread->setState(Thread::State::Running);
|
||||
thread->callbackResult = thread->callback(thread->callbackContext);
|
||||
assert(thread->state == Thread::State::Running);
|
||||
|
||||
setState(data, Thread::State::Stopped);
|
||||
thread->setState(Thread::State::Stopped);
|
||||
|
||||
vTaskSetThreadLocalStoragePointer(nullptr, 0, nullptr);
|
||||
data->taskHandle = nullptr;
|
||||
thread->taskHandle = nullptr;
|
||||
|
||||
vTaskDelete(nullptr);
|
||||
thread_catch();
|
||||
threadCatch();
|
||||
}
|
||||
|
||||
Thread::Thread() :
|
||||
data({
|
||||
.thread = nullptr,
|
||||
.taskHandle = nullptr,
|
||||
.state = State::Stopped,
|
||||
.callback = nullptr,
|
||||
.callbackContext = nullptr,
|
||||
.callbackResult = 0,
|
||||
.stateCallback = nullptr,
|
||||
.stateCallbackContext = nullptr,
|
||||
.name = std::string(),
|
||||
.priority = Priority::Normal,
|
||||
.stackSize = 0,
|
||||
}) { }
|
||||
|
||||
Thread::Thread(
|
||||
const std::string& name,
|
||||
std::string name,
|
||||
configSTACK_DEPTH_TYPE stackSize,
|
||||
Callback callback,
|
||||
_Nullable void* callbackContext,
|
||||
portBASE_TYPE affinity
|
||||
) :
|
||||
data({
|
||||
.thread = nullptr,
|
||||
.taskHandle = nullptr,
|
||||
.state = State::Stopped,
|
||||
.callback = callback,
|
||||
.callbackContext = callbackContext,
|
||||
.callbackResult = 0,
|
||||
.stateCallback = nullptr,
|
||||
.stateCallbackContext = nullptr,
|
||||
.name = name,
|
||||
.priority = Priority::Normal,
|
||||
.stackSize = stackSize,
|
||||
.affinity = affinity
|
||||
}) { }
|
||||
callback(callback),
|
||||
callbackContext(callbackContext),
|
||||
name(std::move(name)),
|
||||
stackSize(stackSize),
|
||||
affinity(affinity)
|
||||
{}
|
||||
|
||||
Thread::~Thread() {
|
||||
// Ensure that use join before free
|
||||
assert(data.state == State::Stopped);
|
||||
assert(data.taskHandle == nullptr);
|
||||
assert(state == State::Stopped);
|
||||
assert(taskHandle == nullptr);
|
||||
}
|
||||
|
||||
void Thread::setName(const std::string& newName) {
|
||||
assert(data.state == State::Stopped);
|
||||
data.name = newName;
|
||||
void Thread::setName(std::string newName) {
|
||||
assert(state == State::Stopped);
|
||||
name = std::move(newName);
|
||||
}
|
||||
|
||||
void Thread::setStackSize(size_t stackSize) {
|
||||
assert(data.state == State::Stopped);
|
||||
void Thread::setStackSize(size_t newStackSize) {
|
||||
assert(state == State::Stopped);
|
||||
assert(stackSize % 4 == 0);
|
||||
data.stackSize = stackSize;
|
||||
stackSize = newStackSize;
|
||||
}
|
||||
|
||||
void Thread::setCallback(Callback callback, _Nullable void* callbackContext) {
|
||||
assert(data.state == State::Stopped);
|
||||
data.callback = callback;
|
||||
data.callbackContext = callbackContext;
|
||||
void Thread::setCallback(Callback newCallback, _Nullable void* newCallbackContext) {
|
||||
assert(state == State::Stopped);
|
||||
callback = newCallback;
|
||||
callbackContext = newCallbackContext;
|
||||
}
|
||||
|
||||
|
||||
void Thread::setPriority(Priority priority) {
|
||||
assert(data.state == State::Stopped);
|
||||
data.priority = priority;
|
||||
void Thread::setPriority(Priority newPriority) {
|
||||
assert(state == State::Stopped);
|
||||
priority = newPriority;
|
||||
}
|
||||
|
||||
|
||||
void Thread::setStateCallback(StateCallback callback, _Nullable void* callbackContext) {
|
||||
assert(data.state == State::Stopped);
|
||||
data.stateCallback = callback;
|
||||
data.stateCallbackContext = callbackContext;
|
||||
assert(state == State::Stopped);
|
||||
stateCallback = callback;
|
||||
stateCallbackContext = callbackContext;
|
||||
}
|
||||
|
||||
Thread::State Thread::getState() const {
|
||||
return data.state;
|
||||
return state;
|
||||
}
|
||||
|
||||
void Thread::start() {
|
||||
assert(data.callback);
|
||||
assert(data.state == State::Stopped);
|
||||
assert(data.stackSize > 0U && data.stackSize < (UINT16_MAX * sizeof(StackType_t)));
|
||||
assert(callback);
|
||||
assert(state == State::Stopped);
|
||||
assert(stackSize > 0U && stackSize < (UINT16_MAX * sizeof(StackType_t)));
|
||||
|
||||
setState(&data, State::Starting);
|
||||
setState(State::Starting);
|
||||
|
||||
uint32_t stack_depth = data.stackSize / sizeof(StackType_t);
|
||||
uint32_t stack_depth = stackSize / sizeof(StackType_t);
|
||||
|
||||
BaseType_t result;
|
||||
if (data.affinity != -1) {
|
||||
if (affinity != -1) {
|
||||
#ifdef ESP_PLATFORM
|
||||
result = xTaskCreatePinnedToCore(
|
||||
thread_body,
|
||||
data.name.c_str(),
|
||||
mainBody,
|
||||
name.c_str(),
|
||||
stack_depth,
|
||||
this,
|
||||
static_cast<UBaseType_t>(data.priority),
|
||||
&(data.taskHandle),
|
||||
data.affinity
|
||||
static_cast<UBaseType_t>(priority),
|
||||
&taskHandle,
|
||||
affinity
|
||||
);
|
||||
#else
|
||||
TT_LOG_W(TAG, "Pinned tasks are not supported by current FreeRTOS platform - creating regular one");
|
||||
result = xTaskCreate(
|
||||
thread_body,
|
||||
data.name.c_str(),
|
||||
mainBody,
|
||||
name.c_str(),
|
||||
stack_depth,
|
||||
this,
|
||||
static_cast<UBaseType_t>(data.priority),
|
||||
&(data.taskHandle)
|
||||
static_cast<UBaseType_t>(priority),
|
||||
&taskHandle
|
||||
);
|
||||
#endif
|
||||
} else {
|
||||
result = xTaskCreate(
|
||||
thread_body,
|
||||
data.name.c_str(),
|
||||
mainBody,
|
||||
name.c_str(),
|
||||
stack_depth,
|
||||
this,
|
||||
static_cast<UBaseType_t>(data.priority),
|
||||
&(data.taskHandle)
|
||||
static_cast<UBaseType_t>(priority),
|
||||
&taskHandle
|
||||
);
|
||||
}
|
||||
|
||||
tt_check(result == pdPASS);
|
||||
tt_check(data.state == State::Stopped || data.taskHandle);
|
||||
tt_check(state == State::Stopped || taskHandle);
|
||||
}
|
||||
|
||||
bool Thread::join(TickType_t timeout, TickType_t pollInterval) {
|
||||
tt_check(thread_get_current() != this);
|
||||
tt_check(getCurrent() != this);
|
||||
|
||||
// !!! IMPORTANT NOTICE !!!
|
||||
//
|
||||
// If your thread exited, but your app stuck here: some other thread uses
|
||||
// all cpu time, which delays kernel from releasing task handle
|
||||
TickType_t start_ticks = kernel::getTicks();
|
||||
while (data.taskHandle) {
|
||||
while (taskHandle) {
|
||||
kernel::delayTicks(pollInterval);
|
||||
if ((kernel::getTicks() - start_ticks) > timeout) {
|
||||
return false;
|
||||
@@ -206,45 +184,27 @@ bool Thread::join(TickType_t timeout, TickType_t pollInterval) {
|
||||
}
|
||||
|
||||
ThreadId Thread::getId() const {
|
||||
return data.taskHandle;
|
||||
return taskHandle;
|
||||
}
|
||||
|
||||
int32_t Thread::getReturnCode() const {
|
||||
assert(data.state == State::Stopped);
|
||||
return data.callbackResult;
|
||||
assert(state == State::Stopped);
|
||||
return callbackResult;
|
||||
}
|
||||
|
||||
ThreadId thread_get_current_id() {
|
||||
return xTaskGetCurrentTaskHandle();
|
||||
Thread* Thread::getCurrent() {
|
||||
return static_cast<Thread*>(pvTaskGetThreadLocalStoragePointer(nullptr, 0));
|
||||
}
|
||||
|
||||
Thread* thread_get_current() {
|
||||
auto* thread = static_cast<Thread*>(pvTaskGetThreadLocalStoragePointer(nullptr, 0));
|
||||
return thread;
|
||||
}
|
||||
|
||||
void thread_set_current_priority(Thread::Priority priority) {
|
||||
vTaskPrioritySet(nullptr, static_cast<UBaseType_t>(priority));
|
||||
}
|
||||
|
||||
Thread::Priority thread_get_current_priority() {
|
||||
return (Thread::Priority)uxTaskPriorityGet(nullptr);
|
||||
}
|
||||
|
||||
void thread_yield() {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
taskYIELD();
|
||||
}
|
||||
|
||||
uint32_t thread_flags_set(ThreadId thread_id, uint32_t flags) {
|
||||
auto hTask = (TaskHandle_t)thread_id;
|
||||
uint32_t Thread::setFlags(ThreadId threadId, uint32_t flags) {
|
||||
auto hTask = (TaskHandle_t)threadId;
|
||||
uint32_t rflags;
|
||||
BaseType_t yield;
|
||||
|
||||
if ((hTask == nullptr) || ((flags & THREAD_FLAGS_INVALID_BITS) != 0U)) {
|
||||
rflags = (uint32_t)TtStatusErrorParameter;
|
||||
rflags = (uint32_t)EventFlag::ErrorParameter;
|
||||
} else {
|
||||
rflags = (uint32_t)TtStatusError;
|
||||
rflags = (uint32_t)EventFlag::Error;
|
||||
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
yield = pdFALSE;
|
||||
@@ -264,14 +224,14 @@ uint32_t thread_flags_set(ThreadId thread_id, uint32_t flags) {
|
||||
return (rflags);
|
||||
}
|
||||
|
||||
uint32_t thread_flags_clear(uint32_t flags) {
|
||||
uint32_t Thread::clearFlags(uint32_t flags) {
|
||||
TaskHandle_t hTask;
|
||||
uint32_t rflags, cflags;
|
||||
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
rflags = (uint32_t)TtStatusErrorISR;
|
||||
rflags = (uint32_t)EventFlag::ErrorISR;
|
||||
} else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {
|
||||
rflags = (uint32_t)TtStatusErrorParameter;
|
||||
rflags = (uint32_t)EventFlag::ErrorParameter;
|
||||
} else {
|
||||
hTask = xTaskGetCurrentTaskHandle();
|
||||
|
||||
@@ -282,10 +242,10 @@ uint32_t thread_flags_clear(uint32_t flags) {
|
||||
|
||||
if (xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, cflags, eSetValueWithOverwrite) !=
|
||||
pdPASS) {
|
||||
rflags = (uint32_t)TtStatusError;
|
||||
rflags = (uint32_t)EventFlag::Error;
|
||||
}
|
||||
} else {
|
||||
rflags = (uint32_t)TtStatusError;
|
||||
rflags = (uint32_t)EventFlag::Error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,36 +253,36 @@ uint32_t thread_flags_clear(uint32_t flags) {
|
||||
return (rflags);
|
||||
}
|
||||
|
||||
uint32_t thread_flags_get() {
|
||||
uint32_t Thread::getFlags() {
|
||||
TaskHandle_t hTask;
|
||||
uint32_t rflags;
|
||||
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
rflags = (uint32_t)TtStatusErrorISR;
|
||||
rflags = (uint32_t)EventFlag::ErrorISR;
|
||||
} else {
|
||||
hTask = xTaskGetCurrentTaskHandle();
|
||||
|
||||
if (xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags) !=
|
||||
pdPASS) {
|
||||
rflags = (uint32_t)TtStatusError;
|
||||
rflags = (uint32_t)EventFlag::Error;
|
||||
}
|
||||
}
|
||||
|
||||
return (rflags);
|
||||
}
|
||||
|
||||
uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
|
||||
uint32_t Thread::awaitFlags(uint32_t flags, uint32_t options, uint32_t timeout) {
|
||||
uint32_t rflags, nval;
|
||||
uint32_t clear;
|
||||
TickType_t t0, td, tout;
|
||||
BaseType_t rval;
|
||||
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
rflags = (uint32_t)TtStatusErrorISR;
|
||||
rflags = (uint32_t)EventFlag::ErrorISR;
|
||||
} else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {
|
||||
rflags = (uint32_t)TtStatusErrorParameter;
|
||||
rflags = (uint32_t)EventFlag::ErrorParameter;
|
||||
} else {
|
||||
if ((options & TtFlagNoClear) == TtFlagNoClear) {
|
||||
if ((options & EventFlag::NoClear) == EventFlag::NoClear) {
|
||||
clear = 0U;
|
||||
} else {
|
||||
clear = flags;
|
||||
@@ -339,12 +299,12 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
|
||||
rflags &= flags;
|
||||
rflags |= nval;
|
||||
|
||||
if ((options & TtFlagWaitAll) == TtFlagWaitAll) {
|
||||
if ((options & EventFlag::WaitAll) == EventFlag::WaitAll) {
|
||||
if ((flags & rflags) == flags) {
|
||||
break;
|
||||
} else {
|
||||
if (timeout == 0U) {
|
||||
rflags = (uint32_t)TtStatusErrorResource;
|
||||
rflags = (uint32_t)EventFlag::ErrorResource;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -353,7 +313,7 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
|
||||
break;
|
||||
} else {
|
||||
if (timeout == 0U) {
|
||||
rflags = (uint32_t)TtStatusErrorResource;
|
||||
rflags = (uint32_t)EventFlag::ErrorResource;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -369,9 +329,9 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
|
||||
}
|
||||
} else {
|
||||
if (timeout == 0) {
|
||||
rflags = (uint32_t)TtStatusErrorResource;
|
||||
rflags = (uint32_t)EventFlag::ErrorResource;
|
||||
} else {
|
||||
rflags = (uint32_t)TtStatusErrorTimeout;
|
||||
rflags = (uint32_t)EventFlag::ErrorTimeout;
|
||||
}
|
||||
}
|
||||
} while (rval != pdFAIL);
|
||||
@@ -381,21 +341,8 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
|
||||
return (rflags);
|
||||
}
|
||||
|
||||
const char* thread_get_name(ThreadId thread_id) {
|
||||
auto hTask = (TaskHandle_t)thread_id;
|
||||
const char* name;
|
||||
|
||||
if (TT_IS_IRQ_MODE() || (hTask == nullptr)) {
|
||||
name = nullptr;
|
||||
} else {
|
||||
name = pcTaskGetName(hTask);
|
||||
}
|
||||
|
||||
return (name);
|
||||
}
|
||||
|
||||
uint32_t thread_get_stack_space(ThreadId thread_id) {
|
||||
auto hTask = (TaskHandle_t)thread_id;
|
||||
uint32_t Thread::getStackSpace(ThreadId threadId) {
|
||||
auto hTask = (TaskHandle_t)threadId;
|
||||
uint32_t sz;
|
||||
|
||||
if (TT_IS_IRQ_MODE() || (hTask == nullptr)) {
|
||||
@@ -407,13 +354,13 @@ uint32_t thread_get_stack_space(ThreadId thread_id) {
|
||||
return (sz);
|
||||
}
|
||||
|
||||
void thread_suspend(ThreadId thread_id) {
|
||||
auto hTask = (TaskHandle_t)thread_id;
|
||||
void Thread::suspend(ThreadId threadId) {
|
||||
auto hTask = (TaskHandle_t)threadId;
|
||||
vTaskSuspend(hTask);
|
||||
}
|
||||
|
||||
void thread_resume(ThreadId thread_id) {
|
||||
auto hTask = (TaskHandle_t)thread_id;
|
||||
void Thread::resume(ThreadId threadId) {
|
||||
auto hTask = (TaskHandle_t)threadId;
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
xTaskResumeFromISR(hTask);
|
||||
} else {
|
||||
@@ -421,8 +368,8 @@ void thread_resume(ThreadId thread_id) {
|
||||
}
|
||||
}
|
||||
|
||||
bool thread_is_suspended(ThreadId thread_id) {
|
||||
auto hTask = (TaskHandle_t)thread_id;
|
||||
bool Thread::isSuspended(ThreadId threadId) {
|
||||
auto hTask = (TaskHandle_t)threadId;
|
||||
return eTaskGetState(hTask) == eSuspended;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreDefines.h"
|
||||
#include "CoreTypes.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "RtosCompatTask.h"
|
||||
|
||||
@@ -14,6 +12,7 @@ namespace tt {
|
||||
typedef TaskHandle_t ThreadId;
|
||||
|
||||
class Thread {
|
||||
|
||||
public:
|
||||
|
||||
enum class State{
|
||||
@@ -34,6 +33,7 @@ public:
|
||||
Critical = 7U
|
||||
};
|
||||
|
||||
|
||||
/** ThreadCallback Your callback to run in new thread
|
||||
* @warning never use osThreadExit in Thread
|
||||
*/
|
||||
@@ -51,22 +51,27 @@ public:
|
||||
*/
|
||||
typedef void (*StateCallback)(State state, void* context);
|
||||
|
||||
typedef struct {
|
||||
Thread* thread;
|
||||
TaskHandle_t taskHandle;
|
||||
State state;
|
||||
Callback callback;
|
||||
void* callbackContext;
|
||||
int32_t callbackResult;
|
||||
StateCallback stateCallback;
|
||||
void* stateCallbackContext;
|
||||
std::string name;
|
||||
Priority priority;
|
||||
configSTACK_DEPTH_TYPE stackSize;
|
||||
portBASE_TYPE affinity;
|
||||
} Data;
|
||||
private:
|
||||
|
||||
Thread();
|
||||
static void mainBody(void* context);
|
||||
|
||||
TaskHandle_t taskHandle = nullptr;
|
||||
State state = State::Stopped;
|
||||
Callback callback = nullptr;
|
||||
void* callbackContext = nullptr;
|
||||
int32_t callbackResult = 0;
|
||||
StateCallback stateCallback = nullptr;
|
||||
void* stateCallbackContext = nullptr;
|
||||
std::string name = {};
|
||||
Priority priority = Priority::Normal;
|
||||
configSTACK_DEPTH_TYPE stackSize = 0;
|
||||
portBASE_TYPE affinity = -1;
|
||||
|
||||
void setState(Thread::State state);
|
||||
|
||||
public:
|
||||
|
||||
Thread() = default;
|
||||
|
||||
/** Allocate Thread, shortcut version
|
||||
* @param[in] name the name of the thread
|
||||
@@ -76,7 +81,7 @@ public:
|
||||
* @param[in] affinity Which CPU core to pin this task to, -1 means unpinned (only works on ESP32)
|
||||
*/
|
||||
Thread(
|
||||
const std::string& name,
|
||||
std::string name,
|
||||
configSTACK_DEPTH_TYPE stackSize,
|
||||
Callback callback,
|
||||
_Nullable void* callbackContext,
|
||||
@@ -88,7 +93,7 @@ public:
|
||||
/** Set Thread name
|
||||
* @param[in] name string
|
||||
*/
|
||||
void setName(const std::string& name);
|
||||
void setName(std::string name);
|
||||
|
||||
/** Set Thread stack size
|
||||
* @param[in] stackSize stack size in bytes
|
||||
@@ -140,9 +145,39 @@ public:
|
||||
*/
|
||||
int32_t getReturnCode() const;
|
||||
|
||||
private:
|
||||
/** Suspend thread
|
||||
* @param[in] threadId thread id
|
||||
*/
|
||||
static void suspend(ThreadId threadId);
|
||||
|
||||
Data data;
|
||||
/** Resume thread
|
||||
* @param[in] threadId thread id
|
||||
*/
|
||||
static void resume(ThreadId threadId);
|
||||
|
||||
/** Get thread suspended state
|
||||
* @param[in] threadId thread id
|
||||
* @return true if thread is suspended
|
||||
*/
|
||||
static bool isSuspended(ThreadId threadId);
|
||||
|
||||
/**
|
||||
* @brief Get thread stack watermark
|
||||
* @param[in] threadId
|
||||
* @return uint32_t
|
||||
*/
|
||||
static uint32_t getStackSpace(ThreadId threadId);
|
||||
|
||||
/** @return pointer to Thread instance or nullptr if this thread doesn't belong to Tactility */
|
||||
static Thread* getCurrent();
|
||||
|
||||
static uint32_t setFlags(ThreadId threadId, uint32_t flags);
|
||||
|
||||
static uint32_t clearFlags(uint32_t flags);
|
||||
|
||||
static uint32_t getFlags();
|
||||
|
||||
static uint32_t awaitFlags(uint32_t flags, uint32_t options, uint32_t timeout);
|
||||
};
|
||||
|
||||
#define THREAD_PRIORITY_APP Thread::PriorityNormal
|
||||
@@ -150,59 +185,4 @@ private:
|
||||
#define THREAD_PRIORITY_RENDER Thread::Priority::Higher
|
||||
#define THREAD_PRIORITY_ISR Thread::Priority::Critical
|
||||
|
||||
/** Set current thread priority
|
||||
* @param[in] priority ThreadPriority value
|
||||
*/
|
||||
void thread_set_current_priority(Thread::Priority priority);
|
||||
|
||||
/** @return ThreadPriority value */
|
||||
Thread::Priority thread_get_current_priority();
|
||||
|
||||
/** @return FreeRTOS ThreadId or NULL */
|
||||
ThreadId thread_get_current_id();
|
||||
|
||||
/** @return pointer to Thread instance or NULL if this thread doesn't belongs to Tactility */
|
||||
Thread* thread_get_current();
|
||||
|
||||
/** Return control to scheduler */
|
||||
void thread_yield();
|
||||
|
||||
uint32_t thread_flags_set(ThreadId thread_id, uint32_t flags);
|
||||
|
||||
uint32_t thread_flags_clear(uint32_t flags);
|
||||
|
||||
uint32_t thread_flags_get();
|
||||
|
||||
uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Get thread name
|
||||
* @param[in] threadId
|
||||
* @return const char* name or NULL
|
||||
*/
|
||||
const char* thread_get_name(ThreadId threadId);
|
||||
|
||||
/**
|
||||
* @brief Get thread stack watermark
|
||||
* @param[in] threadId
|
||||
* @return uint32_t
|
||||
*/
|
||||
uint32_t thread_get_stack_space(ThreadId threadId);
|
||||
|
||||
/** Suspend thread
|
||||
* @param[in] threadId thread id
|
||||
*/
|
||||
void thread_suspend(ThreadId threadId);
|
||||
|
||||
/** Resume thread
|
||||
* @param[in] threadId thread id
|
||||
*/
|
||||
void thread_resume(ThreadId threadId);
|
||||
|
||||
/** Get thread suspended state
|
||||
* @param[in] threadId thread id
|
||||
* @return true if thread is suspended
|
||||
*/
|
||||
bool thread_is_suspended(ThreadId threadId);
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace tt {
|
||||
|
||||
static void timer_callback(TimerHandle_t hTimer) {
|
||||
void Timer::onCallback(TimerHandle_t hTimer) {
|
||||
auto* timer = static_cast<Timer*>(pvTimerGetTimerID(hTimer));
|
||||
|
||||
if (timer != nullptr) {
|
||||
@@ -14,54 +14,59 @@ static void timer_callback(TimerHandle_t hTimer) {
|
||||
}
|
||||
}
|
||||
|
||||
Timer::Timer(Type type, Callback callback, std::shared_ptr<void> callbackContext) {
|
||||
assert((!TT_IS_ISR()) && (callback != nullptr));
|
||||
|
||||
this->callback = callback;
|
||||
this->callbackContext = std::move(callbackContext);
|
||||
static inline TimerHandle_t createTimer(Timer::Type type, void* timerId, TimerCallbackFunction_t callback) {
|
||||
assert(timerId != nullptr);
|
||||
assert(callback != nullptr);
|
||||
|
||||
UBaseType_t reload;
|
||||
if (type == Type::Once) {
|
||||
if (type == Timer::Type::Once) {
|
||||
reload = pdFALSE;
|
||||
} else {
|
||||
reload = pdTRUE;
|
||||
}
|
||||
|
||||
this->timerHandle = xTimerCreate(nullptr, portMAX_DELAY, (BaseType_t)reload, this, timer_callback);
|
||||
assert(this->timerHandle);
|
||||
return xTimerCreate(nullptr, portMAX_DELAY, (BaseType_t)reload, timerId, callback);
|
||||
}
|
||||
|
||||
Timer::Timer(Type type, Callback callback, std::shared_ptr<void> callbackContext) :
|
||||
callback(callback),
|
||||
callbackContext(std::move(callbackContext)),
|
||||
handle(createTimer(type, this, onCallback))
|
||||
{
|
||||
assert(!TT_IS_ISR());
|
||||
assert(handle != nullptr);
|
||||
}
|
||||
|
||||
Timer::~Timer() {
|
||||
assert(!TT_IS_ISR());
|
||||
tt_check(xTimerDelete(timerHandle, portMAX_DELAY) == pdPASS);
|
||||
}
|
||||
|
||||
bool Timer::start(TickType_t interval) {
|
||||
assert(!TT_IS_ISR());
|
||||
assert(interval < portMAX_DELAY);
|
||||
return xTimerChangePeriod(timerHandle, interval, portMAX_DELAY) == pdPASS;
|
||||
return xTimerChangePeriod(handle.get(), interval, portMAX_DELAY) == pdPASS;
|
||||
}
|
||||
|
||||
bool Timer::restart(TickType_t interval) {
|
||||
assert(!TT_IS_ISR());
|
||||
assert(interval < portMAX_DELAY);
|
||||
return xTimerChangePeriod(timerHandle, interval, portMAX_DELAY) == pdPASS &&
|
||||
xTimerReset(timerHandle, portMAX_DELAY) == pdPASS;
|
||||
return xTimerChangePeriod(handle.get(), interval, portMAX_DELAY) == pdPASS &&
|
||||
xTimerReset(handle.get(), portMAX_DELAY) == pdPASS;
|
||||
}
|
||||
|
||||
bool Timer::stop() {
|
||||
assert(!TT_IS_ISR());
|
||||
return xTimerStop(timerHandle, portMAX_DELAY) == pdPASS;
|
||||
return xTimerStop(handle.get(), portMAX_DELAY) == pdPASS;
|
||||
}
|
||||
|
||||
bool Timer::isRunning() {
|
||||
assert(!TT_IS_ISR());
|
||||
return xTimerIsTimerActive(timerHandle) == pdTRUE;
|
||||
return xTimerIsTimerActive(handle.get()) == pdTRUE;
|
||||
}
|
||||
|
||||
TickType_t Timer::getExpireTime() {
|
||||
assert(!TT_IS_ISR());
|
||||
return xTimerGetExpiryTime(timerHandle);
|
||||
return xTimerGetExpiryTime(handle.get());
|
||||
}
|
||||
|
||||
bool Timer::setPendingCallback(PendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeout) {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
#include "RtosCompatTimers.h"
|
||||
#include "Thread.h"
|
||||
#include <memory>
|
||||
@@ -9,15 +7,27 @@
|
||||
namespace tt {
|
||||
|
||||
class Timer {
|
||||
private:
|
||||
TimerHandle_t timerHandle;
|
||||
|
||||
public:
|
||||
|
||||
typedef void (*Callback)(std::shared_ptr<void> context);
|
||||
typedef void (*PendingCallback)(void* context, uint32_t arg);
|
||||
|
||||
private:
|
||||
|
||||
struct TimerHandleDeleter {
|
||||
void operator()(TimerHandle_t handleToDelete) {
|
||||
xTimerDelete(handleToDelete, portMAX_DELAY);
|
||||
}
|
||||
};
|
||||
|
||||
Callback callback;
|
||||
std::shared_ptr<void> callbackContext;
|
||||
std::unique_ptr<std::remove_pointer_t<TimerHandle_t>, TimerHandleDeleter> handle;
|
||||
|
||||
static void onCallback(TimerHandle_t hTimer);
|
||||
|
||||
public:
|
||||
|
||||
enum class Type {
|
||||
Once = 0, ///< One-shot timer.
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#include "kernel/Kernel.h"
|
||||
#include "Check.h"
|
||||
#include "CoreDefines.h"
|
||||
#include "CoreTypes.h"
|
||||
#include "RtosCompatTask.h"
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "rom/ets_sys.h"
|
||||
#else
|
||||
#include <cassert>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
@@ -16,90 +15,72 @@ bool isRunning() {
|
||||
return xTaskGetSchedulerState() != taskSCHEDULER_RUNNING;
|
||||
}
|
||||
|
||||
int32_t lock() {
|
||||
bool lock() {
|
||||
assert(!TT_IS_ISR());
|
||||
|
||||
int32_t lock;
|
||||
|
||||
switch (xTaskGetSchedulerState()) {
|
||||
// Already suspended
|
||||
case taskSCHEDULER_SUSPENDED:
|
||||
lock = 1;
|
||||
break;
|
||||
return true;
|
||||
|
||||
case taskSCHEDULER_RUNNING:
|
||||
vTaskSuspendAll();
|
||||
lock = 0;
|
||||
break;
|
||||
return true;
|
||||
|
||||
case taskSCHEDULER_NOT_STARTED:
|
||||
default:
|
||||
lock = (int32_t)TtStatusError;
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return previous lock state */
|
||||
return (lock);
|
||||
}
|
||||
|
||||
int32_t unlock() {
|
||||
bool unlock() {
|
||||
assert(!TT_IS_ISR());
|
||||
|
||||
int32_t lock;
|
||||
|
||||
switch (xTaskGetSchedulerState()) {
|
||||
case taskSCHEDULER_SUSPENDED:
|
||||
lock = 1;
|
||||
|
||||
if (xTaskResumeAll() != pdTRUE) {
|
||||
if (xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) {
|
||||
lock = (int32_t)TtStatusError;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
|
||||
case taskSCHEDULER_RUNNING:
|
||||
lock = 0;
|
||||
break;
|
||||
return true;
|
||||
|
||||
case taskSCHEDULER_NOT_STARTED:
|
||||
default:
|
||||
lock = (int32_t)TtStatusError;
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return previous lock state */
|
||||
return (lock);
|
||||
}
|
||||
|
||||
int32_t restoreLock(int32_t lock) {
|
||||
bool restoreLock(bool lock) {
|
||||
assert(!TT_IS_ISR());
|
||||
|
||||
switch (xTaskGetSchedulerState()) {
|
||||
case taskSCHEDULER_SUSPENDED:
|
||||
case taskSCHEDULER_RUNNING:
|
||||
if (lock == 1) {
|
||||
if (lock) {
|
||||
vTaskSuspendAll();
|
||||
return true;
|
||||
} else {
|
||||
if (lock != 0) {
|
||||
lock = (int32_t)TtStatusError;
|
||||
} else {
|
||||
if (xTaskResumeAll() != pdTRUE) {
|
||||
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
|
||||
lock = (int32_t)TtStatusError;
|
||||
}
|
||||
if (xTaskResumeAll() != pdTRUE) {
|
||||
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case taskSCHEDULER_NOT_STARTED:
|
||||
default:
|
||||
lock = (int32_t)TtStatusError;
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return new lock state */
|
||||
return (lock);
|
||||
}
|
||||
|
||||
uint32_t getTickFrequency() {
|
||||
@@ -116,13 +97,11 @@ void delayTicks(TickType_t ticks) {
|
||||
}
|
||||
}
|
||||
|
||||
TtStatus delayUntilTick(TickType_t tick) {
|
||||
bool delayUntilTick(TickType_t tick) {
|
||||
assert(!TT_IS_ISR());
|
||||
|
||||
TickType_t tcnt, delay;
|
||||
TtStatus stat;
|
||||
|
||||
stat = TtStatusOk;
|
||||
tcnt = xTaskGetTickCount();
|
||||
|
||||
/* Determine remaining number of tick to delay */
|
||||
@@ -130,29 +109,20 @@ TtStatus delayUntilTick(TickType_t tick) {
|
||||
|
||||
/* Check if target tick has not expired */
|
||||
if ((delay != 0U) && (0 == (delay >> (8 * sizeof(TickType_t) - 1)))) {
|
||||
if (xTaskDelayUntil(&tcnt, delay) == pdFALSE) {
|
||||
/* Did not delay */
|
||||
stat = TtStatusError;
|
||||
if (xTaskDelayUntil(&tcnt, delay) == pdPASS) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
/* No delay or already expired */
|
||||
stat = TtStatusErrorParameter;
|
||||
}
|
||||
|
||||
/* Return execution status */
|
||||
return (stat);
|
||||
return false;
|
||||
}
|
||||
|
||||
TickType_t getTicks() {
|
||||
TickType_t ticks;
|
||||
|
||||
if (TT_IS_ISR() != 0U) {
|
||||
ticks = xTaskGetTickCountFromISR();
|
||||
return xTaskGetTickCountFromISR();
|
||||
} else {
|
||||
ticks = xTaskGetTickCount();
|
||||
return xTaskGetTickCount();
|
||||
}
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
TickType_t millisToTicks(uint32_t milliseconds) {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#else
|
||||
@@ -23,22 +21,22 @@ bool isRunning();
|
||||
|
||||
/** Lock kernel, pause process scheduling
|
||||
* @warning don't call from ISR context
|
||||
* @return previous lock state(0 - unlocked, 1 - locked)
|
||||
* @return true on success
|
||||
*/
|
||||
int32_t lock();
|
||||
bool lock();
|
||||
|
||||
/** Unlock kernel, resume process scheduling
|
||||
* @warning don't call from ISR context
|
||||
* @return previous lock state(0 - unlocked, 1 - locked)
|
||||
* @return true on success
|
||||
*/
|
||||
int32_t unlock();
|
||||
bool unlock();
|
||||
|
||||
/** Restore kernel lock state
|
||||
* @warning don't call from ISR context
|
||||
* @param[in] lock The lock state
|
||||
* @return new lock state or error
|
||||
* @return true on success
|
||||
*/
|
||||
int32_t restoreLock(int32_t lock);
|
||||
bool restoreLock(bool lock);
|
||||
|
||||
/** Get kernel systick frequency
|
||||
* @return systick counts per second
|
||||
@@ -57,9 +55,9 @@ void delayTicks(TickType_t ticks);
|
||||
/** Delay until tick
|
||||
* @warning don't call from ISR context
|
||||
* @param[in] ticks The tick until which kerel should delay task execution
|
||||
* @return the status
|
||||
* @return true on success
|
||||
*/
|
||||
TtStatus delayUntilTick(TickType_t tick);
|
||||
bool delayUntilTick(TickType_t tick);
|
||||
|
||||
/** Convert milliseconds to ticks
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user