Create TactilityFreertos subproject (#440)
This commit is contained in:
committed by
GitHub
parent
a4dc633063
commit
7283920def
@@ -1,9 +1,9 @@
|
||||
#include "Tactility/Check.h"
|
||||
#include <Tactility/Check.h>
|
||||
|
||||
#include "Tactility/Log.h"
|
||||
#include "Tactility/RtosCompatTask.h"
|
||||
#include <Tactility/Log.h>
|
||||
#include <Tactility/freertoscompat/Task.h>
|
||||
|
||||
#define TAG "kernel"
|
||||
constexpr auto TAG = "kernel";
|
||||
|
||||
static void logMemoryInfo() {
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
#include "Tactility/Dispatcher.h"
|
||||
|
||||
#include "Tactility/Check.h"
|
||||
#include "Tactility/kernel/Kernel.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
#define TAG "dispatcher"
|
||||
#define BACKPRESSURE_WARNING_COUNT ((EventBits_t)100)
|
||||
#define WAIT_FLAG ((EventBits_t)1U)
|
||||
|
||||
Dispatcher::~Dispatcher() {
|
||||
// Wait for Mutex usage
|
||||
mutex.lock();
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
bool Dispatcher::dispatch(Function function, TickType_t timeout) {
|
||||
// Mutate
|
||||
if (mutex.lock(timeout)) {
|
||||
queue.push(std::move(function));
|
||||
if (queue.size() == BACKPRESSURE_WARNING_COUNT) {
|
||||
TT_LOG_W(TAG, "Backpressure: You're not consuming fast enough (100 queued)");
|
||||
}
|
||||
tt_check(mutex.unlock());
|
||||
// Signal
|
||||
eventFlag.set(WAIT_FLAG);
|
||||
return true;
|
||||
} else {
|
||||
TT_LOG_E(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Dispatcher::consume(TickType_t timeout) {
|
||||
// Wait for signal
|
||||
uint32_t result = eventFlag.wait(WAIT_FLAG, EventFlag::WaitAny, timeout);
|
||||
if (result & EventFlag::Error) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
eventFlag.clear(WAIT_FLAG);
|
||||
|
||||
// Mutate
|
||||
bool processing = true;
|
||||
uint32_t consumed = 0;
|
||||
do {
|
||||
if (mutex.lock(10)) {
|
||||
if (!queue.empty()) {
|
||||
auto function = queue.front();
|
||||
queue.pop();
|
||||
consumed++;
|
||||
processing = !queue.empty();
|
||||
// Don't keep lock as callback might be slow
|
||||
mutex.unlock();
|
||||
function();
|
||||
} else {
|
||||
processing = false;
|
||||
mutex.unlock();
|
||||
}
|
||||
} else {
|
||||
TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED);
|
||||
}
|
||||
|
||||
} while (processing);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -1,47 +0,0 @@
|
||||
#include "Tactility/DispatcherThread.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
DispatcherThread::DispatcherThread(const std::string& threadName, size_t threadStackSize) {
|
||||
thread = std::make_unique<Thread>(
|
||||
threadName,
|
||||
threadStackSize,
|
||||
[this] {
|
||||
return threadMain();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
DispatcherThread::~DispatcherThread() {
|
||||
if (thread->getState() != Thread::State::Stopped) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
int32_t DispatcherThread::threadMain() {
|
||||
do {
|
||||
/**
|
||||
* If this value is too high (e.g. 1 second) then the dispatcher destroys too slowly when the simulator exits.
|
||||
* This causes the problems with other services doing an update (e.g. Statusbar) and calling into destroyed mutex in the global scope.
|
||||
*/
|
||||
dispatcher.consume(100 / portTICK_PERIOD_MS);
|
||||
} while (!interruptThread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool DispatcherThread::dispatch(Dispatcher::Function function, TickType_t timeout) {
|
||||
return dispatcher.dispatch(function, timeout);
|
||||
}
|
||||
|
||||
void DispatcherThread::start() {
|
||||
interruptThread = false;
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void DispatcherThread::stop() {
|
||||
interruptThread = true;
|
||||
thread->join();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
#include "Tactility/EventFlag.h"
|
||||
|
||||
#include "Tactility/Check.h"
|
||||
#include "Tactility/kernel/Kernel.h"
|
||||
|
||||
#define TT_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U
|
||||
#define TT_EVENT_FLAG_INVALID_BITS (~((1UL << TT_EVENT_FLAG_MAX_BITS_EVENT_GROUPS) - 1U))
|
||||
|
||||
namespace tt {
|
||||
|
||||
EventFlag::EventFlag() :
|
||||
handle(xEventGroupCreate())
|
||||
{
|
||||
assert(!kernel::isIsr());
|
||||
tt_check(handle);
|
||||
}
|
||||
|
||||
EventFlag::~EventFlag() {
|
||||
assert(!kernel::isIsr());
|
||||
}
|
||||
|
||||
uint32_t EventFlag::set(uint32_t flags) const {
|
||||
assert(handle);
|
||||
assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U);
|
||||
|
||||
uint32_t rflags;
|
||||
BaseType_t yield;
|
||||
|
||||
if (kernel::isIsr()) {
|
||||
yield = pdFALSE;
|
||||
if (xEventGroupSetBitsFromISR(handle.get(), (EventBits_t)flags, &yield) == pdFAIL) {
|
||||
rflags = (uint32_t)ErrorResource;
|
||||
} else {
|
||||
rflags = flags;
|
||||
portYIELD_FROM_ISR(yield);
|
||||
}
|
||||
} else {
|
||||
rflags = xEventGroupSetBits(handle.get(), (EventBits_t)flags);
|
||||
}
|
||||
|
||||
/* Return event flags after setting */
|
||||
return rflags;
|
||||
}
|
||||
|
||||
uint32_t EventFlag::clear(uint32_t flags) const {
|
||||
assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U);
|
||||
|
||||
uint32_t rflags;
|
||||
|
||||
if (kernel::isIsr()) {
|
||||
rflags = xEventGroupGetBitsFromISR(handle.get());
|
||||
|
||||
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. */
|
||||
/* See https://github.com/FreeRTOS/FreeRTOS-Kernel/issues/93 for more info. */
|
||||
portYIELD_FROM_ISR(pdTRUE);
|
||||
}
|
||||
} else {
|
||||
rflags = xEventGroupClearBits(handle.get(), (EventBits_t)flags);
|
||||
}
|
||||
|
||||
/* Return event flags before clearing */
|
||||
return rflags;
|
||||
}
|
||||
|
||||
uint32_t EventFlag::get() const {
|
||||
uint32_t rflags;
|
||||
|
||||
if (kernel::isIsr()) {
|
||||
rflags = xEventGroupGetBitsFromISR(handle.get());
|
||||
} else {
|
||||
rflags = xEventGroupGetBits(handle.get());
|
||||
}
|
||||
|
||||
/* Return current event flags */
|
||||
return (rflags);
|
||||
}
|
||||
|
||||
uint32_t EventFlag::wait(
|
||||
uint32_t flags,
|
||||
uint32_t options,
|
||||
uint32_t timeoutTicksw
|
||||
) const {
|
||||
assert(!kernel::isIsr());
|
||||
assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U);
|
||||
|
||||
BaseType_t wait_all;
|
||||
BaseType_t exit_clear;
|
||||
uint32_t rflags;
|
||||
|
||||
if (options & WaitAll) {
|
||||
wait_all = pdTRUE;
|
||||
} else {
|
||||
wait_all = pdFALSE;
|
||||
}
|
||||
|
||||
if (options & NoClear) {
|
||||
exit_clear = pdFALSE;
|
||||
} else {
|
||||
exit_clear = pdTRUE;
|
||||
}
|
||||
|
||||
rflags = xEventGroupWaitBits(
|
||||
handle.get(),
|
||||
(EventBits_t)flags,
|
||||
exit_clear,
|
||||
wait_all,
|
||||
(TickType_t)timeoutTicksw
|
||||
);
|
||||
|
||||
if (options & WaitAll) {
|
||||
if ((flags & rflags) != flags) {
|
||||
if (timeoutTicksw > 0U) {
|
||||
rflags = (uint32_t)ErrorTimeout;
|
||||
} else {
|
||||
rflags = (uint32_t)ErrorResource;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((flags & rflags) == 0U) {
|
||||
if (timeoutTicksw > 0U) {
|
||||
rflags = (uint32_t)ErrorTimeout;
|
||||
} else {
|
||||
rflags = (uint32_t)ErrorResource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rflags;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -1,9 +0,0 @@
|
||||
#include "Tactility/Lock.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
ScopedLock Lock::asScopedLock() const {
|
||||
return ScopedLock(*this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
#include "Tactility/MessageQueue.h"
|
||||
#include "Tactility/Check.h"
|
||||
#include "Tactility/kernel/Kernel.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
static inline QueueHandle_t createQueue(uint32_t capacity, uint32_t messageSize) {
|
||||
assert(!kernel::isIsr() && (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(!kernel::isIsr());
|
||||
}
|
||||
|
||||
bool MessageQueue::put(const void* message, TickType_t timeout) {
|
||||
bool result = true;
|
||||
BaseType_t yield;
|
||||
|
||||
if (kernel::isIsr()) {
|
||||
if ((handle == nullptr) || (message == nullptr) || (timeout != 0U)) {
|
||||
result = false;
|
||||
} else {
|
||||
yield = pdFALSE;
|
||||
|
||||
if (xQueueSendToBackFromISR(handle.get(), message, &yield) != pdTRUE) {
|
||||
result = false;
|
||||
} else {
|
||||
portYIELD_FROM_ISR(yield);
|
||||
}
|
||||
}
|
||||
} else if ((handle == nullptr) || (message == nullptr)) {
|
||||
result = false;
|
||||
} else if (xQueueSendToBack(handle.get(), message, (TickType_t)timeout) != pdPASS) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MessageQueue::get(void* msg_ptr, TickType_t timeout) {
|
||||
bool result = true;
|
||||
BaseType_t yield;
|
||||
|
||||
if (kernel::isIsr()) {
|
||||
if ((handle == nullptr) || (msg_ptr == nullptr) || (timeout != 0U)) {
|
||||
result = false;
|
||||
} else {
|
||||
yield = pdFALSE;
|
||||
|
||||
if (xQueueReceiveFromISR(handle.get(), msg_ptr, &yield) != pdPASS) {
|
||||
result = false;
|
||||
} else {
|
||||
portYIELD_FROM_ISR(yield);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((handle == nullptr) || (msg_ptr == nullptr)) {
|
||||
result = false;
|
||||
} else if (xQueueReceive(handle.get(), msg_ptr, (TickType_t)timeout) != pdPASS) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t MessageQueue::getCapacity() const {
|
||||
auto* mq = (StaticQueue_t*)(handle.get());
|
||||
if (mq == nullptr) {
|
||||
return 0U;
|
||||
} else {
|
||||
return mq->uxDummy4[1];
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t MessageQueue::getMessageSize() const {
|
||||
auto* mq = (StaticQueue_t*)(handle.get());
|
||||
if (mq == nullptr) {
|
||||
return 0U;
|
||||
} else {
|
||||
return mq->uxDummy4[2];
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t MessageQueue::getCount() const {
|
||||
UBaseType_t count;
|
||||
|
||||
if (handle == nullptr) {
|
||||
count = 0U;
|
||||
} else if (kernel::isIsr()) {
|
||||
count = uxQueueMessagesWaitingFromISR(handle.get());
|
||||
} else {
|
||||
count = uxQueueMessagesWaiting(handle.get());
|
||||
}
|
||||
|
||||
/* Return number of queued messages */
|
||||
return (uint32_t)count;
|
||||
}
|
||||
|
||||
uint32_t MessageQueue::getSpace() const {
|
||||
auto* mq = (StaticQueue_t*)(handle.get());
|
||||
uint32_t space;
|
||||
uint32_t isrm;
|
||||
|
||||
if (mq == nullptr) {
|
||||
space = 0U;
|
||||
} else if (kernel::isIsr()) {
|
||||
isrm = taskENTER_CRITICAL_FROM_ISR();
|
||||
|
||||
/* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */
|
||||
space = mq->uxDummy4[1] - mq->uxDummy4[0];
|
||||
|
||||
taskEXIT_CRITICAL_FROM_ISR(isrm);
|
||||
} else {
|
||||
space = (uint32_t)uxQueueSpacesAvailable((QueueHandle_t)mq);
|
||||
}
|
||||
|
||||
return space;
|
||||
}
|
||||
|
||||
bool MessageQueue::reset() {
|
||||
tt_check(!kernel::isIsr());
|
||||
if (handle == nullptr) {
|
||||
return false;
|
||||
} else {
|
||||
xQueueReset(handle.get());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -1,79 +0,0 @@
|
||||
#include "Tactility/Semaphore.h"
|
||||
#include "Tactility/Check.h"
|
||||
#include "Tactility/CoreDefines.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
static inline QueueHandle_t createHandle(uint32_t maxCount, uint32_t initialCount) {
|
||||
assert((maxCount > 0U) && (initialCount <= maxCount));
|
||||
|
||||
if (maxCount == 1U) {
|
||||
auto handle = xSemaphoreCreateBinary();
|
||||
if ((handle != nullptr) && (initialCount != 0U)) {
|
||||
if (xSemaphoreGive(handle) != pdPASS) {
|
||||
vSemaphoreDelete(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
}
|
||||
return handle;
|
||||
} else {
|
||||
return xSemaphoreCreateCounting(maxCount, initialCount);
|
||||
}
|
||||
}
|
||||
|
||||
Semaphore::Semaphore(uint32_t maxAvailable, uint32_t initialAvailable) : handle(createHandle(maxAvailable, initialAvailable)) {
|
||||
assert(!kernel::isIsr());
|
||||
tt_check(handle != nullptr);
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore() {
|
||||
assert(!kernel::isIsr());
|
||||
}
|
||||
|
||||
bool Semaphore::acquire(TickType_t timeout) const {
|
||||
if (kernel::isIsr()) {
|
||||
if (timeout != 0U) {
|
||||
return false;
|
||||
} else {
|
||||
BaseType_t yield = pdFALSE;
|
||||
|
||||
if (xSemaphoreTakeFromISR(handle.get(), &yield) != pdPASS) {
|
||||
return false;
|
||||
} else {
|
||||
portYIELD_FROM_ISR(yield);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return xSemaphoreTake(handle.get(), timeout) == pdPASS;
|
||||
}
|
||||
}
|
||||
|
||||
bool Semaphore::release() const {
|
||||
if (kernel::isIsr()) {
|
||||
BaseType_t yield = pdFALSE;
|
||||
if (xSemaphoreGiveFromISR(handle.get(), &yield) != pdTRUE) {
|
||||
return false;
|
||||
} else {
|
||||
portYIELD_FROM_ISR(yield);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return xSemaphoreGive(handle.get()) == pdPASS;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Semaphore::getAvailable() const {
|
||||
if (kernel::isIsr()) {
|
||||
// TODO: uxSemaphoreGetCountFromISR is not supported on esp-idf 5.1.2 - perhaps later on?
|
||||
#ifdef uxSemaphoreGetCountFromISR
|
||||
return uxSemaphoreGetCountFromISR(handle.get());
|
||||
#else
|
||||
return uxQueueMessagesWaitingFromISR(handle.get());
|
||||
#endif
|
||||
} else {
|
||||
return uxSemaphoreGetCount(handle.get());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -1,71 +0,0 @@
|
||||
#include "Tactility/StreamBuffer.h"
|
||||
|
||||
#include "Tactility/Check.h"
|
||||
#include "Tactility/kernel/Kernel.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
static StreamBufferHandle_t createStreamBuffer(size_t size, size_t triggerLevel) {
|
||||
assert(size != 0);
|
||||
return xStreamBufferCreate(size, triggerLevel);
|
||||
}
|
||||
|
||||
StreamBuffer::StreamBuffer(size_t size, size_t triggerLevel) : handle(createStreamBuffer(size, triggerLevel)) {
|
||||
tt_check(handle);
|
||||
};
|
||||
|
||||
bool StreamBuffer::setTriggerLevel(size_t triggerLevel) const {
|
||||
return xStreamBufferSetTriggerLevel(handle.get(), triggerLevel) == pdTRUE;
|
||||
};
|
||||
|
||||
size_t StreamBuffer::send(
|
||||
const void* data,
|
||||
size_t length,
|
||||
uint32_t timeout
|
||||
) const {
|
||||
if (kernel::isIsr()) {
|
||||
BaseType_t yield;
|
||||
size_t result = xStreamBufferSendFromISR(handle.get(), data, length, &yield);
|
||||
portYIELD_FROM_ISR(yield);
|
||||
return result;
|
||||
} else {
|
||||
return xStreamBufferSend(handle.get(), data, length, timeout);
|
||||
}
|
||||
};
|
||||
|
||||
size_t StreamBuffer::receive(
|
||||
void* data,
|
||||
size_t length,
|
||||
uint32_t timeout
|
||||
) const {
|
||||
if (kernel::isIsr()) {
|
||||
BaseType_t yield;
|
||||
size_t result = xStreamBufferReceiveFromISR(handle.get(), data, length, &yield);
|
||||
portYIELD_FROM_ISR(yield);
|
||||
return result;
|
||||
} else {
|
||||
return xStreamBufferReceive(handle.get(), data, length, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
size_t StreamBuffer::getAvailableReadBytes() const {
|
||||
return xStreamBufferBytesAvailable(handle.get());
|
||||
};
|
||||
|
||||
size_t StreamBuffer::getAvailableWriteBytes() const {
|
||||
return xStreamBufferSpacesAvailable(handle.get());
|
||||
};
|
||||
|
||||
bool StreamBuffer::isFull() const {
|
||||
return xStreamBufferIsFull(handle.get()) == pdTRUE;
|
||||
};
|
||||
|
||||
bool StreamBuffer::isEmpty() const {
|
||||
return xStreamBufferIsEmpty(handle.get()) == pdTRUE;
|
||||
};
|
||||
|
||||
bool StreamBuffer::reset() const {
|
||||
return xStreamBufferReset(handle.get()) == pdPASS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -1,386 +0,0 @@
|
||||
#include "Tactility/Thread.h"
|
||||
|
||||
#include "Tactility/Check.h"
|
||||
#include "Tactility/CoreDefines.h"
|
||||
#include "Tactility/EventFlag.h"
|
||||
#include "Tactility/kernel/Kernel.h"
|
||||
#include "Tactility/Log.h"
|
||||
#include "Tactility/TactilityCoreConfig.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace tt {
|
||||
|
||||
#define TAG "Thread"
|
||||
|
||||
#define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers
|
||||
|
||||
// Limits
|
||||
#define MAX_BITS_TASK_NOTIFY 31U
|
||||
#define MAX_BITS_EVENT_GROUPS 24U
|
||||
|
||||
#define THREAD_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_TASK_NOTIFY) - 1U))
|
||||
#define EVENT_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_EVENT_GROUPS) - 1U))
|
||||
|
||||
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 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 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();
|
||||
}
|
||||
|
||||
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, thread);
|
||||
|
||||
TT_LOG_I(TAG, "Starting %s", thread->name.c_str());
|
||||
assert(thread->state == Thread::State::Starting);
|
||||
thread->setState(Thread::State::Running);
|
||||
thread->callbackResult = thread->mainFunction();
|
||||
assert(thread->state == Thread::State::Running);
|
||||
|
||||
thread->setState(Thread::State::Stopped);
|
||||
TT_LOG_I(TAG, "Stopped %s", thread->name.c_str());
|
||||
|
||||
vTaskSetThreadLocalStoragePointer(nullptr, 0, nullptr);
|
||||
thread->taskHandle = nullptr;
|
||||
|
||||
vTaskDelete(nullptr);
|
||||
threadCatch();
|
||||
}
|
||||
|
||||
Thread::Thread(
|
||||
std::string name,
|
||||
configSTACK_DEPTH_TYPE stackSize,
|
||||
MainFunction function,
|
||||
portBASE_TYPE affinity
|
||||
) :
|
||||
mainFunction(function),
|
||||
name(std::move(name)),
|
||||
stackSize(stackSize),
|
||||
affinity(affinity)
|
||||
{}
|
||||
|
||||
Thread::~Thread() {
|
||||
// Ensure that use join before free
|
||||
assert(state == State::Stopped);
|
||||
assert(taskHandle == nullptr);
|
||||
}
|
||||
|
||||
void Thread::setName(std::string newName) {
|
||||
assert(state == State::Stopped);
|
||||
name = std::move(newName);
|
||||
}
|
||||
|
||||
void Thread::setStackSize(size_t newStackSize) {
|
||||
assert(state == State::Stopped);
|
||||
assert(stackSize % 4 == 0);
|
||||
stackSize = newStackSize;
|
||||
}
|
||||
|
||||
void Thread::setAffinity(portBASE_TYPE newAffinity) {
|
||||
assert(state == State::Stopped);
|
||||
affinity = newAffinity;
|
||||
}
|
||||
|
||||
void Thread::setCallback(Callback callback, _Nullable void* callbackContext) {
|
||||
assert(state == State::Stopped);
|
||||
mainFunction = [callback, callbackContext] {
|
||||
return callback(callbackContext);
|
||||
};
|
||||
}
|
||||
|
||||
void Thread::setMainFunction(MainFunction function) {
|
||||
assert(state == State::Stopped);
|
||||
mainFunction = function;
|
||||
}
|
||||
|
||||
void Thread::setPriority(Priority newPriority) {
|
||||
assert(state == State::Stopped);
|
||||
priority = newPriority;
|
||||
}
|
||||
|
||||
|
||||
void Thread::setStateCallback(StateCallback callback, _Nullable void* callbackContext) {
|
||||
assert(state == State::Stopped);
|
||||
stateCallback = callback;
|
||||
stateCallbackContext = callbackContext;
|
||||
}
|
||||
|
||||
Thread::State Thread::getState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
void Thread::start() {
|
||||
assert(mainFunction);
|
||||
assert(state == State::Stopped);
|
||||
assert(stackSize > 0U && stackSize < (UINT16_MAX * sizeof(StackType_t)));
|
||||
|
||||
setState(State::Starting);
|
||||
|
||||
uint32_t stack_depth = stackSize / sizeof(StackType_t);
|
||||
|
||||
BaseType_t result;
|
||||
if (affinity != -1) {
|
||||
#ifdef ESP_PLATFORM
|
||||
result = xTaskCreatePinnedToCore(
|
||||
mainBody,
|
||||
name.c_str(),
|
||||
stack_depth,
|
||||
this,
|
||||
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(
|
||||
mainBody,
|
||||
name.c_str(),
|
||||
stack_depth,
|
||||
this,
|
||||
static_cast<UBaseType_t>(priority),
|
||||
&taskHandle
|
||||
);
|
||||
#endif
|
||||
} else {
|
||||
result = xTaskCreate(
|
||||
mainBody,
|
||||
name.c_str(),
|
||||
stack_depth,
|
||||
this,
|
||||
static_cast<UBaseType_t>(priority),
|
||||
&taskHandle
|
||||
);
|
||||
}
|
||||
|
||||
tt_check(result == pdPASS);
|
||||
tt_check(state == State::Stopped || taskHandle);
|
||||
}
|
||||
|
||||
bool Thread::join(TickType_t timeout, TickType_t pollInterval) {
|
||||
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 (taskHandle) {
|
||||
kernel::delayTicks(pollInterval);
|
||||
if ((kernel::getTicks() - start_ticks) > timeout) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ThreadId Thread::getId() const {
|
||||
return taskHandle;
|
||||
}
|
||||
|
||||
int32_t Thread::getReturnCode() const {
|
||||
assert(state == State::Stopped);
|
||||
return callbackResult;
|
||||
}
|
||||
|
||||
Thread* Thread::getCurrent() {
|
||||
return static_cast<Thread*>(pvTaskGetThreadLocalStoragePointer(nullptr, 0));
|
||||
}
|
||||
|
||||
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)EventFlag::ErrorParameter;
|
||||
} else {
|
||||
rflags = (uint32_t)EventFlag::Error;
|
||||
|
||||
if (kernel::isIsr()) {
|
||||
yield = pdFALSE;
|
||||
|
||||
(void)xTaskNotifyIndexedFromISR(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits, &yield);
|
||||
(void)xTaskNotifyAndQueryIndexedFromISR(
|
||||
hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags, nullptr
|
||||
);
|
||||
|
||||
portYIELD_FROM_ISR(yield);
|
||||
} else {
|
||||
(void)xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits);
|
||||
(void)xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags);
|
||||
}
|
||||
}
|
||||
/* Return flags after setting */
|
||||
return (rflags);
|
||||
}
|
||||
|
||||
uint32_t Thread::clearFlags(uint32_t flags) {
|
||||
TaskHandle_t hTask;
|
||||
uint32_t rflags, cflags;
|
||||
|
||||
if (kernel::isIsr()) {
|
||||
rflags = (uint32_t)EventFlag::ErrorISR;
|
||||
} else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {
|
||||
rflags = (uint32_t)EventFlag::ErrorParameter;
|
||||
} else {
|
||||
hTask = xTaskGetCurrentTaskHandle();
|
||||
|
||||
if (xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &cflags) ==
|
||||
pdPASS) {
|
||||
rflags = cflags;
|
||||
cflags &= ~flags;
|
||||
|
||||
if (xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, cflags, eSetValueWithOverwrite) !=
|
||||
pdPASS) {
|
||||
rflags = (uint32_t)EventFlag::Error;
|
||||
}
|
||||
} else {
|
||||
rflags = (uint32_t)EventFlag::Error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return flags before clearing */
|
||||
return (rflags);
|
||||
}
|
||||
|
||||
uint32_t Thread::getFlags() {
|
||||
TaskHandle_t hTask;
|
||||
uint32_t rflags;
|
||||
|
||||
if (kernel::isIsr()) {
|
||||
rflags = (uint32_t)EventFlag::ErrorISR;
|
||||
} else {
|
||||
hTask = xTaskGetCurrentTaskHandle();
|
||||
|
||||
if (xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags) !=
|
||||
pdPASS) {
|
||||
rflags = (uint32_t)EventFlag::Error;
|
||||
}
|
||||
}
|
||||
|
||||
return (rflags);
|
||||
}
|
||||
|
||||
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 (kernel::isIsr()) {
|
||||
rflags = (uint32_t)EventFlag::ErrorISR;
|
||||
} else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {
|
||||
rflags = (uint32_t)EventFlag::ErrorParameter;
|
||||
} else {
|
||||
if ((options & EventFlag::NoClear) == EventFlag::NoClear) {
|
||||
clear = 0U;
|
||||
} else {
|
||||
clear = flags;
|
||||
}
|
||||
|
||||
rflags = 0U;
|
||||
tout = timeout;
|
||||
|
||||
t0 = xTaskGetTickCount();
|
||||
do {
|
||||
rval = xTaskNotifyWaitIndexed(THREAD_NOTIFY_INDEX, 0, clear, &nval, tout);
|
||||
|
||||
if (rval == pdPASS) {
|
||||
rflags &= flags;
|
||||
rflags |= nval;
|
||||
|
||||
if ((options & EventFlag::WaitAll) == EventFlag::WaitAll) {
|
||||
if ((flags & rflags) == flags) {
|
||||
break;
|
||||
} else {
|
||||
if (timeout == 0U) {
|
||||
rflags = (uint32_t)EventFlag::ErrorResource;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((flags & rflags) != 0) {
|
||||
break;
|
||||
} else {
|
||||
if (timeout == 0U) {
|
||||
rflags = (uint32_t)EventFlag::ErrorResource;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update timeout */
|
||||
td = xTaskGetTickCount() - t0;
|
||||
|
||||
if (td > tout) {
|
||||
tout = 0;
|
||||
} else {
|
||||
tout -= td;
|
||||
}
|
||||
} else {
|
||||
if (timeout == 0) {
|
||||
rflags = (uint32_t)EventFlag::ErrorResource;
|
||||
} else {
|
||||
rflags = (uint32_t)EventFlag::ErrorTimeout;
|
||||
}
|
||||
}
|
||||
} while (rval != pdFAIL);
|
||||
}
|
||||
|
||||
/* Return flags before clearing */
|
||||
return (rflags);
|
||||
}
|
||||
|
||||
uint32_t Thread::getStackSpace(ThreadId threadId) {
|
||||
auto hTask = (TaskHandle_t)threadId;
|
||||
uint32_t sz;
|
||||
|
||||
if (kernel::isIsr() || (hTask == nullptr)) {
|
||||
sz = 0U;
|
||||
} else {
|
||||
sz = (uint32_t)(uxTaskGetStackHighWaterMark(hTask) * sizeof(StackType_t));
|
||||
}
|
||||
|
||||
return (sz);
|
||||
}
|
||||
|
||||
void Thread::suspend(ThreadId threadId) {
|
||||
auto hTask = (TaskHandle_t)threadId;
|
||||
vTaskSuspend(hTask);
|
||||
}
|
||||
|
||||
void Thread::resume(ThreadId threadId) {
|
||||
auto hTask = (TaskHandle_t)threadId;
|
||||
if (kernel::isIsr()) {
|
||||
xTaskResumeFromISR(hTask);
|
||||
} else {
|
||||
vTaskResume(hTask);
|
||||
}
|
||||
}
|
||||
|
||||
bool Thread::isSuspended(ThreadId threadId) {
|
||||
auto hTask = (TaskHandle_t)threadId;
|
||||
return eTaskGetState(hTask) == eSuspended;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -1,88 +0,0 @@
|
||||
#include "Tactility/Timer.h"
|
||||
|
||||
#include "Tactility/Check.h"
|
||||
#include "Tactility/RtosCompat.h"
|
||||
#include "Tactility/kernel/Kernel.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
void Timer::onCallback(TimerHandle_t hTimer) {
|
||||
auto* timer = static_cast<Timer*>(pvTimerGetTimerID(hTimer));
|
||||
if (timer != nullptr) {
|
||||
timer->callback();
|
||||
}
|
||||
}
|
||||
|
||||
static TimerHandle_t createTimer(Timer::Type type, void* timerId, TimerCallbackFunction_t callback) {
|
||||
assert(timerId != nullptr);
|
||||
assert(callback != nullptr);
|
||||
|
||||
UBaseType_t reload;
|
||||
if (type == Timer::Type::Once) {
|
||||
reload = pdFALSE;
|
||||
} else {
|
||||
reload = pdTRUE;
|
||||
}
|
||||
|
||||
return xTimerCreate(nullptr, portMAX_DELAY, (BaseType_t)reload, timerId, callback);
|
||||
}
|
||||
|
||||
Timer::Timer(Type type, Callback callback) :
|
||||
callback(callback),
|
||||
handle(createTimer(type, this, onCallback))
|
||||
{
|
||||
assert(!kernel::isIsr());
|
||||
assert(handle != nullptr);
|
||||
}
|
||||
|
||||
Timer::~Timer() {
|
||||
assert(!kernel::isIsr());
|
||||
}
|
||||
|
||||
bool Timer::start(TickType_t interval) {
|
||||
assert(!kernel::isIsr());
|
||||
assert(interval < portMAX_DELAY);
|
||||
return xTimerChangePeriod(handle.get(), interval, portMAX_DELAY) == pdPASS;
|
||||
}
|
||||
|
||||
bool Timer::restart(TickType_t interval) {
|
||||
assert(!kernel::isIsr());
|
||||
assert(interval < portMAX_DELAY);
|
||||
return xTimerChangePeriod(handle.get(), interval, portMAX_DELAY) == pdPASS &&
|
||||
xTimerReset(handle.get(), portMAX_DELAY) == pdPASS;
|
||||
}
|
||||
|
||||
bool Timer::stop() {
|
||||
assert(!kernel::isIsr());
|
||||
return xTimerStop(handle.get(), portMAX_DELAY) == pdPASS;
|
||||
}
|
||||
|
||||
bool Timer::isRunning() {
|
||||
assert(!kernel::isIsr());
|
||||
return xTimerIsTimerActive(handle.get()) == pdTRUE;
|
||||
}
|
||||
|
||||
TickType_t Timer::getExpireTime() {
|
||||
assert(!kernel::isIsr());
|
||||
return xTimerGetExpiryTime(handle.get());
|
||||
}
|
||||
|
||||
bool Timer::setPendingCallback(PendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeout) {
|
||||
if (kernel::isIsr()) {
|
||||
assert(timeout == 0);
|
||||
return xTimerPendFunctionCallFromISR(callback, callbackContext, callbackArg, nullptr) == pdPASS;
|
||||
} else {
|
||||
return xTimerPendFunctionCall(callback, callbackContext, callbackArg, timeout) == pdPASS;
|
||||
}
|
||||
}
|
||||
|
||||
void Timer::setThreadPriority(Thread::Priority priority) {
|
||||
assert(!kernel::isIsr());
|
||||
|
||||
TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle();
|
||||
assert(task_handle); // Don't call this method before timer task start
|
||||
|
||||
vTaskPrioritySet(task_handle, static_cast<UBaseType_t>(priority));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -15,7 +15,7 @@ constexpr auto* TAG = "file";
|
||||
|
||||
class NoLock final : public Lock {
|
||||
bool lock(TickType_t timeout) const override { return true; }
|
||||
bool unlock() const override { return true; }
|
||||
void unlock() const override { /* NO-OP */ }
|
||||
};
|
||||
|
||||
static std::shared_ptr<Lock> noLock = std::make_shared<NoLock>();
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
#include "Tactility/kernel/Kernel.h"
|
||||
#include "Tactility/CoreDefines.h"
|
||||
#include "Tactility/RtosCompatTask.h"
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "rom/ets_sys.h"
|
||||
#else
|
||||
#include <cassert>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace tt::kernel {
|
||||
|
||||
bool isRunning() {
|
||||
return xTaskGetSchedulerState() != taskSCHEDULER_RUNNING;
|
||||
}
|
||||
|
||||
bool lock() {
|
||||
assert(!kernel::isIsr());
|
||||
|
||||
int32_t lock;
|
||||
|
||||
switch (xTaskGetSchedulerState()) {
|
||||
// Already suspended
|
||||
case taskSCHEDULER_SUSPENDED:
|
||||
return true;
|
||||
|
||||
case taskSCHEDULER_RUNNING:
|
||||
vTaskSuspendAll();
|
||||
return true;
|
||||
|
||||
case taskSCHEDULER_NOT_STARTED:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return previous lock state */
|
||||
return (lock);
|
||||
}
|
||||
|
||||
bool unlock() {
|
||||
assert(!kernel::isIsr());
|
||||
|
||||
switch (xTaskGetSchedulerState()) {
|
||||
case taskSCHEDULER_SUSPENDED:
|
||||
if (xTaskResumeAll() != pdTRUE) {
|
||||
if (xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case taskSCHEDULER_RUNNING:
|
||||
return true;
|
||||
|
||||
case taskSCHEDULER_NOT_STARTED:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool restoreLock(bool lock) {
|
||||
assert(!kernel::isIsr());
|
||||
|
||||
switch (xTaskGetSchedulerState()) {
|
||||
case taskSCHEDULER_SUSPENDED:
|
||||
case taskSCHEDULER_RUNNING:
|
||||
if (lock) {
|
||||
vTaskSuspendAll();
|
||||
return true;
|
||||
} else {
|
||||
if (xTaskResumeAll() != pdTRUE) {
|
||||
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case taskSCHEDULER_NOT_STARTED:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t getTickFrequency() {
|
||||
/* Return frequency in hertz */
|
||||
return (configTICK_RATE_HZ);
|
||||
}
|
||||
|
||||
void delayTicks(TickType_t ticks) {
|
||||
assert(!kernel::isIsr());
|
||||
if (ticks == 0U) {
|
||||
taskYIELD();
|
||||
} else {
|
||||
vTaskDelay(ticks);
|
||||
}
|
||||
}
|
||||
|
||||
bool delayUntilTick(TickType_t tick) {
|
||||
assert(!kernel::isIsr());
|
||||
|
||||
TickType_t tcnt, delay;
|
||||
|
||||
tcnt = xTaskGetTickCount();
|
||||
|
||||
/* Determine remaining number of tick to delay */
|
||||
delay = (TickType_t)tick - tcnt;
|
||||
|
||||
/* Check if target tick has not expired */
|
||||
if ((delay != 0U) && (0 == (delay >> (8 * sizeof(TickType_t) - 1)))) {
|
||||
if (xTaskDelayUntil(&tcnt, delay) == pdPASS) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TickType_t getTicks() {
|
||||
if (kernel::isIsr() != 0U) {
|
||||
return xTaskGetTickCountFromISR();
|
||||
} else {
|
||||
return xTaskGetTickCount();
|
||||
}
|
||||
}
|
||||
|
||||
TickType_t millisToTicks(uint32_t milliseconds) {
|
||||
#if configTICK_RATE_HZ == 1000
|
||||
return (TickType_t)milliseconds;
|
||||
#else
|
||||
return (TickType_t)((float)configTICK_RATE_HZ) / 1000.0f * (float)milliseconds;
|
||||
#endif
|
||||
}
|
||||
|
||||
void delayMillis(uint32_t milliseconds) {
|
||||
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
|
||||
if (milliseconds > 0 && milliseconds < portMAX_DELAY - 1) {
|
||||
milliseconds += 1;
|
||||
}
|
||||
#if configTICK_RATE_HZ_RAW == 1000
|
||||
tt_delay_tick(milliseconds);
|
||||
#else
|
||||
delayTicks(kernel::millisToTicks(milliseconds));
|
||||
#endif
|
||||
} else if (milliseconds > 0) {
|
||||
kernel::delayMicros(milliseconds * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
void delayMicros(uint32_t microseconds) {
|
||||
#ifdef ESP_PLATFORM
|
||||
ets_delay_us(microseconds);
|
||||
#else
|
||||
usleep(microseconds);
|
||||
#endif
|
||||
}
|
||||
|
||||
Platform getPlatform() {
|
||||
#ifdef ESP_PLATFORM
|
||||
return PlatformEsp;
|
||||
#else
|
||||
return PlatformSimulator;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "Tactility/kernel/Platform.h"
|
||||
|
||||
namespace tt::kernel {
|
||||
|
||||
Platform getPlatform() {
|
||||
#ifdef ESP_PLATFORM
|
||||
return PlatformEsp;
|
||||
#else
|
||||
return PlatformSimulator;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -1,13 +1,15 @@
|
||||
#include "Tactility/kernel/critical/Critical.h"
|
||||
#include <Tactility/kernel/critical/Critical.h>
|
||||
|
||||
#include "Tactility/RtosCompatTask.h"
|
||||
#include "Tactility/kernel/Kernel.h"
|
||||
#include <Tactility/freertoscompat/Task.h>
|
||||
#include <Tactility/kernel/Kernel.h>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
static portMUX_TYPE critical_mutex;
|
||||
#define TT_ENTER_CRITICAL() taskENTER_CRITICAL(&critical_mutex)
|
||||
#define TT_EXIT_CRITICAL() taskEXIT_CRITICAL(&critical_mutex)
|
||||
#else
|
||||
#define TT_ENTER_CRITICAL() taskENTER_CRITICAL()
|
||||
#define TT_EXIT_CRITICAL() taskEXIT_CRITICAL()
|
||||
#endif
|
||||
|
||||
namespace tt::kernel::critical {
|
||||
@@ -15,7 +17,7 @@ namespace tt::kernel::critical {
|
||||
CriticalInfo enter() {
|
||||
CriticalInfo info = {
|
||||
.isrm = 0,
|
||||
.fromIsr = kernel::isIsr(),
|
||||
.fromIsr = (xPortInIsrContext() == pdTRUE),
|
||||
.kernelRunning = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING)
|
||||
};
|
||||
|
||||
@@ -34,7 +36,7 @@ void exit(CriticalInfo info) {
|
||||
if (info.fromIsr) {
|
||||
taskEXIT_CRITICAL_FROM_ISR(info.isrm);
|
||||
} else if (info.kernelRunning) {
|
||||
TT_ENTER_CRITICAL();
|
||||
TT_EXIT_CRITICAL();
|
||||
} else {
|
||||
portENABLE_INTERRUPTS();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user