BaseThread¶
A worker task with a fixed lifecycle, Setup(), Step() in a loop, Cleanup(), that can be started and stopped
many times and tells you whether a start or stop happened.
Purpose¶
Every HardFOC service loop has the same shape: prepare, repeat a step at some rate, tidy up when told to stop.
BaseThread owns that loop, the start semaphore and the state flags; you write the five methods that differ.
Lifecycle¶
stateDiagram-v2
[*] --> Created: EnsureInitialized() calls Initialize() calls CreateBaseThread()
Created --> Waiting
Waiting --> Running: Start() / StartThreadAndWaitToVerify()
Running --> Running: Step() returns N, sleep N ms
Running --> Stopping: Stop() / StopThreadAndWaitToVerify()
Stopping --> Waiting: Cleanup()
On every start the task calls ResetVariables(), then Setup() (once per start), then Step() until a stop is
requested. After the last Step() it calls Cleanup() and waits for the next start.
Example¶
#include "BaseThread.h"
class BlinkThread : public BaseThread {
public:
BlinkThread() : BaseThread("Blink") {}
protected:
bool Initialize() noexcept override { // creates the task once
return CreateBaseThread(stack_, sizeof(stack_), /*priority=*/5,
/*preempt_threshold=*/5, /*timeSliceAllowed=*/0,
OS_AUTO_START);
}
bool Setup() noexcept override { on_ = false; return true; } // on a start
uint32_t Step() noexcept override { // the loop body
on_ = !on_; // toggle an LED here
return 500; // ms until the next Step()
}
bool Cleanup() noexcept override { on_ = false; return true; } // after a stop
bool ResetVariables() noexcept override { on_ = false; return true; }
private:
bool on_ = false;
uint8_t stack_[4096]; // no heap: the stack lives here
};
static BlinkThread blink;
bool StartBlink() {
if (!blink.EnsureInitialized()) { // Initialize(): create the task
return false;
}
return blink.StartThreadAndWaitToVerify(/*startTimeoutMsec=*/1000);
}
bool StopBlink() {
return blink.StopThreadAndWaitToVerify(/*stopTimeoutMsec=*/1000);
}
Methods you implement¶
| Method | Access | Called | Return |
|---|---|---|---|
Initialize() |
protected | Once, from EnsureInitialized() |
True when the task was created; call CreateBaseThread() here |
ResetVariables() |
protected | At the start of every run | True on success |
Setup() |
public | Once per start, before the first Step() |
True when setup ran |
Step() |
public | Repeatedly while running | Milliseconds to sleep before the next Step() |
Cleanup() |
public | Once per stop, after the last Step() |
True when cleanup ran |
StartAction() |
public, optional | From Start(), before the task is signalled |
False refuses the start |
Methods you call¶
| Method | Blocks | Does |
|---|---|---|
EnsureInitialized() |
No | Runs Initialize() the first time; returns whether the thread is initialised |
Start() / Stop() |
No | Signal a start, or request a stop |
StartThreadAndWaitToVerify(ms) |
Up to ms (default 1000) |
Start and wait until the task reports running |
StopThreadAndWaitToVerify(ms) |
Up to ms |
Stop and wait until the task finished Cleanup() |
Suspend() / Resume() / IsSuspended() |
No | Kernel suspend and resume of the task |
IsThreadRunning(), IsSetupComplete(), IsCleanupComplete() |
No | State flags |
GetStackHighWaterMark() |
No | Minimum free stack seen by the kernel |
ChangePriority(p) |
No | Change the task priority |
CreateBaseThread() has a second overload with a core_id argument that pins the task to a core on SMP kernels
(0 or 1 on the ESP32-S3; a negative value means no affinity). Single-core kernels ignore it.
Memory and timing¶
- No heap in the class. The stack is a buffer you pass to
CreateBaseThread(), usually a member array. The object also holds aSignalSemaphore(with a 40-byte name buffer) and a few flags. - The sleep after
Step()is anos_delay_msec(), which takes auint16_t: return at most 65 535 ms. The delay counts from the end ofStep(), so the period is the step time plus the returned value. - A stop takes effect after the current
Step()and its sleep. Size the stop timeout to the longest step plus sleep. - Not copyable. One object is one kernel task.
On a host build¶
With HF_RTOS_NONE=1, compile src/BaseThreadHostStub.cpp as well. There is no task:
StartThreadAndWaitToVerify() runs Setup() inline and marks the thread running, and your test calls Step()
and applies the returned delay itself. hf-core's host tests do exactly this.
Next: Synchronization.