Skip to content

Commit 1a034ea

Browse files
franz-hoepfinger-4diacclaude
authored andcommitted
Use static FreeRTOS task creation for forte threads
Migrates CFreeRTOSThread to xTaskCreateStatic(), explicitly allocating stack memory (StackType_t array via new) and TCB (StaticTask_t via pvPortMalloc). This eliminates reliance on FreeRTOS's internal heap for task creation, improving determinism and avoiding heap fragmentation. On systems with PSRAM (e.g. ESP32-S3), the stack can be placed in PSRAM while the TCB is guaranteed to remain in internal SRAM via pvPortMalloc(), which FreeRTOS requires. Internal SRAM is scarce; future extensions (OPC-UA for example) will require moving more data to the heap — controllers in the field will otherwise stop working once SRAM is exhausted. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
1 parent 50e3dc7 commit 1a034ea

4 files changed

Lines changed: 73 additions & 15 deletions

File tree

core/arch/common/include/forte/arch/threadbase.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ namespace forte::arch {
9090
}
9191

9292
protected:
93+
constexpr long getStackSize() const {
94+
return mStackSize;
95+
}
96+
9397
explicit CThreadBase(long paStackSize);
9498

9599
virtual ~CThreadBase();
@@ -136,6 +140,7 @@ namespace forte::arch {
136140

137141
/*! \brief create the thread and return a handle to it
138142
*
143+
* @param paStackSize size of the stack in bytes to allocate for this thread
139144
* @return handle to the newly created thread
140145
*/
141146
virtual TThreadHandleType createThread(long paStackSize) = 0;

core/arch/freeRTOS/include/forte/arch/forte_thread.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/************************************************************************************
2-
* Copyright (c) 2016 fortiss GmbH
2+
* Copyright (c) 2016 fortiss GmbH, HR Agartechnik GmbH
33
* This program and the accompanying materials are made available under the
44
* terms of the Eclipse Public License 2.0 which is available at
55
* http://www.eclipse.org/legal/epl-2.0.
@@ -15,6 +15,8 @@
1515

1616
#include <freertos/FreeRTOS.h>
1717
#include <freertos/task.h>
18+
#include <memory>
19+
#include <vector>
1820

1921
#include "forte/datatype.h"
2022
#include "forte/util/devlog.h"
@@ -63,7 +65,6 @@ namespace forte::arch {
6365
*
6466
* @param pa_miliSeconds The miliseconds for the thread to sleep
6567
*/
66-
6768
static void sleepThread(unsigned int paMilliSeconds);
6869

6970
protected:
@@ -87,7 +88,23 @@ namespace forte::arch {
8788
*/
8889
virtual TThreadHandleType createThread(long paStackSize);
8990

91+
/*! \brief Convert a stack size in bytes to a FreeRTOS stack depth in words.
92+
*
93+
* FreeRTOS expects the stack depth as the number of StackType_t words, not bytes.
94+
* @param paStackSize stack size in bytes
95+
* @return stack depth in StackType_t words
96+
*/
97+
constexpr size_t getStackDepth() {
98+
return static_cast<size_t>(getStackSize()) / sizeof(StackType_t);
99+
}
100+
101+
void deleteFreeRTOSTask();
102+
90103
static const int scmForteTaskPriority;
104+
105+
std::vector<StackType_t> mFreeRTOSStack;
106+
std::unique_ptr<StaticTask_t, decltype([](StaticTask_t *paTask) { vPortFree(paTask); })> mTask;
107+
TaskHandle_t mFreeRTOSThreadHandle = nullptr;
91108
};
92109

93110
typedef CFreeRTOSThread CThread; // allows that doxygen can generate better documenation

core/arch/freeRTOS/src/forte_thread.cpp

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/************************************************************************************
2-
* Copyright (c) 2016 - 2018 fortiss GmbH
2+
* Copyright (c) 2016 fortiss GmbH, HR Agartechnik GmbH
33
* This program and the accompanying materials are made available under the
44
* terms of the Eclipse Public License 2.0 which is available at
55
* http://www.eclipse.org/legal/epl-2.0.
@@ -20,32 +20,65 @@ namespace forte::arch {
2020
*/
2121
const int CFreeRTOSThread::scmForteTaskPriority = tskIDLE_PRIORITY + 4;
2222

23-
CFreeRTOSThread::CFreeRTOSThread(long paStackSize) : CThreadBase(paStackSize) {
23+
CFreeRTOSThread::CFreeRTOSThread(long paStackSize) :
24+
CThreadBase(paStackSize),
25+
mFreeRTOSStack(getStackDepth()),
26+
/* Allocate the TCB via pvPortMalloc(), not as a member or via new, because
27+
* on systems with PSRAM (e.g. ESP32-S3) the CFreeRTOSThread object itself
28+
* may reside in PSRAM. FreeRTOS requires the TCB to be in internal SRAM;
29+
* pvPortMalloc() guarantees MALLOC_CAP_INTERNAL on ESP-IDF. */
30+
mTask{static_cast<StaticTask_t *>(pvPortMalloc(sizeof(StaticTask_t)))} {
31+
}
32+
33+
void CFreeRTOSThread::deleteFreeRTOSTask() {
34+
if (mFreeRTOSThreadHandle != nullptr) {
35+
/* On SMP (dual-core ESP32) there is a narrow window between runThread() signalling
36+
* mJoinSem and threadFunction() actually reaching vTaskDelay(). Calling vTaskDelete
37+
* on a Running task across cores hangs on ESP-IDF SMP, so we wait. */
38+
while (eTaskGetState(mFreeRTOSThreadHandle) == eRunning) {
39+
vTaskDelay(1);
40+
}
41+
vTaskDelete(mFreeRTOSThreadHandle);
42+
mFreeRTOSThreadHandle = nullptr;
43+
}
2444
}
2545

2646
CFreeRTOSThread::~CFreeRTOSThread() {
47+
end();
48+
/* After end(), run() has returned and join() completed, but the FreeRTOS task is
49+
* still alive and parked in vTaskDelay(). Delete it now that TCB and stack are no
50+
* longer needed. */
51+
deleteFreeRTOSTask();
2752
}
2853

2954
void CFreeRTOSThread::threadFunction(void *paData) {
30-
CThreadBase::runThread(static_cast<CFreeRTOSThread *>(paData));
31-
/* Tasks must not attempt to return from their implementing
32-
function or otherwise exit.
33-
https://www.freertos.org/implementing-a-FreeRTOS-task.html */
34-
vTaskDelete(nullptr);
55+
auto paThreadInst = static_cast<CFreeRTOSThread *>(paData);
56+
CThreadBase::runThread(paThreadInst);
57+
/* join() is notified via mJoinSem.inc() inside runThread(), before we reach here.
58+
* Park the task so the TCB and stack remain valid until ~CFreeRTOSThread()
59+
* calls vTaskDelete() after join() has returned. */
60+
vTaskDelay(portMAX_DELAY);
3561
}
3662

3763
CThreadBase<TaskHandle_t, TaskHandle_t(0), CFreeRTOSThread>::TThreadHandleType
3864
CFreeRTOSThread::createThread(long paStackSize) {
39-
TaskHandle_t handle = 0;
65+
if (mFreeRTOSStack.empty() || !mTask) {
66+
DEVLOG_ERROR("Error: Could not allocate memory for FreeRTOS Task!\n");
67+
return TaskHandle_t(0);
68+
}
4069

41-
if (pdPASS != xTaskCreate(threadFunction, "FORTE", paStackSize, this, scmForteTaskPriority, &handle)) {
70+
/* Clean up any previously parked task before creating a new one (restart case). */
71+
deleteFreeRTOSTask();
72+
mFreeRTOSThreadHandle = xTaskCreateStatic(threadFunction, "FORTE", mFreeRTOSStack.size(), this,
73+
scmForteTaskPriority, mFreeRTOSStack.data(), mTask.get());
74+
if (mFreeRTOSThreadHandle == nullptr) {
4275
DEVLOG_ERROR("Error: Could not create FreeRTOS Task thread!");
4376
}
44-
45-
return handle;
77+
return mFreeRTOSThreadHandle;
4678
}
4779

4880
void CFreeRTOSThread::sleepThread(unsigned int paMilliSeconds) {
4981
vTaskDelay(pdMS_TO_TICKS(paMilliSeconds));
5082
}
83+
5184
} // namespace forte::arch

core/arch/freeRTOS/src/main.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/************************************************************************************
2-
* Copyright (c) 2016, 2024 fortiss GmbH, Jose Cabral
2+
* Copyright (c) 2016, 2024 fortiss GmbH, Jose Cabral, HR Agartechnik GmbH
33
* This program and the accompanying materials are made available under the
44
* terms of the Eclipse Public License 2.0 which is available at
55
* http://www.eclipse.org/legal/epl-2.0.
@@ -14,6 +14,7 @@
1414
#include <freertos/task.h>
1515

1616
#include "../c_interface/forte_c.h"
17+
#include "forte/util/devlog.h"
1718

1819
namespace {
1920
const unsigned forteTaskPriority = tskIDLE_PRIORITY + 1;
@@ -43,7 +44,9 @@ int main() {
4344
return result;
4445
}
4546

46-
xTaskCreate(vForteTask, "forte", stackDepth, nullptr, forteTaskPriority, nullptr);
47+
if (pdPASS != xTaskCreate(vForteTask, "forte", stackDepth, nullptr, forteTaskPriority, nullptr)) {
48+
DEVLOG_ERROR("Failed to create forte task");
49+
}
4750

4851
vTaskStartScheduler();
4952

0 commit comments

Comments
 (0)