add a .clang-format file (#9154)

This commit is contained in:
Jorropo
2026-01-03 21:19:24 +01:00
committed by GitHub
parent abab6ce815
commit 0d11331d18
771 changed files with 77752 additions and 83184 deletions

View File

@@ -4,35 +4,21 @@
#ifdef HAS_FREE_RTOS
namespace concurrency
{
namespace concurrency {
BinarySemaphoreFreeRTOS::BinarySemaphoreFreeRTOS() : semaphore(xSemaphoreCreateBinary())
{
assert(semaphore);
}
BinarySemaphoreFreeRTOS::BinarySemaphoreFreeRTOS() : semaphore(xSemaphoreCreateBinary()) { assert(semaphore); }
BinarySemaphoreFreeRTOS::~BinarySemaphoreFreeRTOS()
{
vSemaphoreDelete(semaphore);
}
BinarySemaphoreFreeRTOS::~BinarySemaphoreFreeRTOS() { vSemaphoreDelete(semaphore); }
/**
* Returns false if we were interrupted
*/
bool BinarySemaphoreFreeRTOS::take(uint32_t msec)
{
return xSemaphoreTake(semaphore, pdMS_TO_TICKS(msec));
}
bool BinarySemaphoreFreeRTOS::take(uint32_t msec) { return xSemaphoreTake(semaphore, pdMS_TO_TICKS(msec)); }
void BinarySemaphoreFreeRTOS::give()
{
xSemaphoreGive(semaphore);
}
void BinarySemaphoreFreeRTOS::give() { xSemaphoreGive(semaphore); }
IRAM_ATTR void BinarySemaphoreFreeRTOS::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken)
{
xSemaphoreGiveFromISR(semaphore, pxHigherPriorityTaskWoken);
IRAM_ATTR void BinarySemaphoreFreeRTOS::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken) {
xSemaphoreGiveFromISR(semaphore, pxHigherPriorityTaskWoken);
}
} // namespace concurrency

View File

@@ -2,27 +2,25 @@
#include "../freertosinc.h"
namespace concurrency
{
namespace concurrency {
#ifdef HAS_FREE_RTOS
class BinarySemaphoreFreeRTOS
{
SemaphoreHandle_t semaphore;
class BinarySemaphoreFreeRTOS {
SemaphoreHandle_t semaphore;
public:
BinarySemaphoreFreeRTOS();
~BinarySemaphoreFreeRTOS();
public:
BinarySemaphoreFreeRTOS();
~BinarySemaphoreFreeRTOS();
/**
* Returns false if we timed out
*/
bool take(uint32_t msec);
/**
* Returns false if we timed out
*/
bool take(uint32_t msec);
void give();
void give();
void giveFromISR(BaseType_t *pxHigherPriorityTaskWoken);
void giveFromISR(BaseType_t *pxHigherPriorityTaskWoken);
};
#endif

View File

@@ -3,8 +3,7 @@
#ifndef HAS_FREE_RTOS
namespace concurrency
{
namespace concurrency {
BinarySemaphorePosix::BinarySemaphorePosix() {}
@@ -13,10 +12,9 @@ BinarySemaphorePosix::~BinarySemaphorePosix() {}
/**
* Returns false if we timed out
*/
bool BinarySemaphorePosix::take(uint32_t msec)
{
delay(msec); // FIXME
return false;
bool BinarySemaphorePosix::take(uint32_t msec) {
delay(msec); // FIXME
return false;
}
void BinarySemaphorePosix::give() {}

View File

@@ -2,27 +2,25 @@
#include "../freertosinc.h"
namespace concurrency
{
namespace concurrency {
#ifndef HAS_FREE_RTOS
class BinarySemaphorePosix
{
// SemaphoreHandle_t semaphore;
class BinarySemaphorePosix {
// SemaphoreHandle_t semaphore;
public:
BinarySemaphorePosix();
~BinarySemaphorePosix();
public:
BinarySemaphorePosix();
~BinarySemaphorePosix();
/**
* Returns false if we timed out
*/
bool take(uint32_t msec);
/**
* Returns false if we timed out
*/
bool take(uint32_t msec);
void give();
void give();
void giveFromISR(BaseType_t *pxHigherPriorityTaskWoken);
void giveFromISR(BaseType_t *pxHigherPriorityTaskWoken);
};
#endif

View File

@@ -1,8 +1,7 @@
#include "concurrency/InterruptableDelay.h"
#include "configuration.h"
namespace concurrency
{
namespace concurrency {
InterruptableDelay::InterruptableDelay() {}
@@ -11,25 +10,18 @@ InterruptableDelay::~InterruptableDelay() {}
/**
* Returns false if we were interrupted
*/
bool InterruptableDelay::delay(uint32_t msec)
{
// LOG_DEBUG("delay %u ", msec);
bool InterruptableDelay::delay(uint32_t msec) {
// LOG_DEBUG("delay %u ", msec);
// sem take will return false if we timed out (i.e. were not interrupted)
bool r = semaphore.take(msec);
// sem take will return false if we timed out (i.e. were not interrupted)
bool r = semaphore.take(msec);
// LOG_DEBUG("interrupt=%d", r);
return !r;
// LOG_DEBUG("interrupt=%d", r);
return !r;
}
void InterruptableDelay::interrupt()
{
semaphore.give();
}
void InterruptableDelay::interrupt() { semaphore.give(); }
IRAM_ATTR void InterruptableDelay::interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken)
{
semaphore.giveFromISR(pxHigherPriorityTaskWoken);
}
IRAM_ATTR void InterruptableDelay::interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken) { semaphore.giveFromISR(pxHigherPriorityTaskWoken); }
} // namespace concurrency

View File

@@ -10,32 +10,31 @@
#define BinarySemaphore BinarySemaphorePosix
#endif
namespace concurrency
{
namespace concurrency {
/**
* An object that provides delay(msec) like functionality, but can be interrupted by calling interrupt().
*
* Useful for they top level loop() delay call to keep the CPU powered down until our next scheduled event or some external event.
* Useful for they top level loop() delay call to keep the CPU powered down until our next scheduled event or some
* external event.
*
* This is implemented for FreeRTOS but should be easy to port to other operating systems.
*/
class InterruptableDelay
{
BinarySemaphore semaphore;
class InterruptableDelay {
BinarySemaphore semaphore;
public:
InterruptableDelay();
~InterruptableDelay();
public:
InterruptableDelay();
~InterruptableDelay();
/**
* Returns false if we were interrupted
*/
bool delay(uint32_t msec);
/**
* Returns false if we were interrupted
*/
bool delay(uint32_t msec);
void interrupt();
void interrupt();
void interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken);
void interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken);
};
} // namespace concurrency

View File

@@ -2,30 +2,26 @@
#include "configuration.h"
#include <cassert>
namespace concurrency
{
namespace concurrency {
#ifdef HAS_FREE_RTOS
Lock::Lock() : handle(xSemaphoreCreateBinary())
{
assert(handle);
if (xSemaphoreGive(handle) == false) {
abort();
}
Lock::Lock() : handle(xSemaphoreCreateBinary()) {
assert(handle);
if (xSemaphoreGive(handle) == false) {
abort();
}
}
void Lock::lock()
{
if (xSemaphoreTake(handle, portMAX_DELAY) == false) {
abort();
}
void Lock::lock() {
if (xSemaphoreTake(handle, portMAX_DELAY) == false) {
abort();
}
}
void Lock::unlock()
{
if (xSemaphoreGive(handle) == false) {
abort();
}
void Lock::unlock() {
if (xSemaphoreGive(handle) == false) {
abort();
}
}
#else
Lock::Lock() {}

View File

@@ -2,33 +2,31 @@
#include "../freertosinc.h"
namespace concurrency
{
namespace concurrency {
/**
* @brief Simple wrapper around FreeRTOS API for implementing a mutex lock
*/
class Lock
{
public:
Lock();
class Lock {
public:
Lock();
Lock(const Lock &) = delete;
Lock &operator=(const Lock &) = delete;
Lock(const Lock &) = delete;
Lock &operator=(const Lock &) = delete;
/// Locks the lock.
//
// Must not be called from an ISR.
void lock();
/// Locks the lock.
//
// Must not be called from an ISR.
void lock();
// Unlocks the lock.
//
// Must not be called from an ISR.
void unlock();
// Unlocks the lock.
//
// Must not be called from an ISR.
void unlock();
private:
private:
#ifdef HAS_FREE_RTOS
SemaphoreHandle_t handle;
SemaphoreHandle_t handle;
#endif
};

View File

@@ -1,17 +1,10 @@
#include "LockGuard.h"
#include "configuration.h"
namespace concurrency
{
namespace concurrency {
LockGuard::LockGuard(Lock *lock) : lock(lock)
{
lock->lock();
}
LockGuard::LockGuard(Lock *lock) : lock(lock) { lock->lock(); }
LockGuard::~LockGuard()
{
lock->unlock();
}
LockGuard::~LockGuard() { lock->unlock(); }
} // namespace concurrency

View File

@@ -2,23 +2,21 @@
#include "Lock.h"
namespace concurrency
{
namespace concurrency {
/**
* @brief RAII lock guard
*/
class LockGuard
{
public:
explicit LockGuard(Lock *lock);
~LockGuard();
class LockGuard {
public:
explicit LockGuard(Lock *lock);
~LockGuard();
LockGuard(const LockGuard &) = delete;
LockGuard &operator=(const LockGuard &) = delete;
LockGuard(const LockGuard &) = delete;
LockGuard &operator=(const LockGuard &) = delete;
private:
Lock *lock;
private:
Lock *lock;
};
} // namespace concurrency

View File

@@ -2,45 +2,42 @@
#include "configuration.h"
#include "main.h"
namespace concurrency
{
namespace concurrency {
static bool debugNotification;
/**
* Notify this thread so it can run
*/
bool NotifiedWorkerThread::notify(uint32_t v, bool overwrite)
{
bool r = notifyCommon(v, overwrite);
bool NotifiedWorkerThread::notify(uint32_t v, bool overwrite) {
bool r = notifyCommon(v, overwrite);
if (r)
mainDelay.interrupt();
if (r)
mainDelay.interrupt();
return r;
return r;
}
/**
* Notify this thread so it can run
*/
IRAM_ATTR bool NotifiedWorkerThread::notifyCommon(uint32_t v, bool overwrite)
{
if (overwrite || notification == 0) {
enabled = true;
setInterval(0); // Run ASAP
runASAP = true;
IRAM_ATTR bool NotifiedWorkerThread::notifyCommon(uint32_t v, bool overwrite) {
if (overwrite || notification == 0) {
enabled = true;
setInterval(0); // Run ASAP
runASAP = true;
notification = v;
if (debugNotification) {
LOG_DEBUG("Set notification %d", v);
}
return true;
} else {
if (debugNotification) {
LOG_DEBUG("Drop notification %d", v);
}
return false;
notification = v;
if (debugNotification) {
LOG_DEBUG("Set notification %d", v);
}
return true;
} else {
if (debugNotification) {
LOG_DEBUG("Drop notification %d", v);
}
return false;
}
}
/**
@@ -48,47 +45,43 @@ IRAM_ATTR bool NotifiedWorkerThread::notifyCommon(uint32_t v, bool overwrite)
*
* This must be inline or IRAM_ATTR on ESP32
*/
IRAM_ATTR bool NotifiedWorkerThread::notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite)
{
bool r = notifyCommon(v, overwrite);
if (r)
mainDelay.interruptFromISR(highPriWoken);
IRAM_ATTR bool NotifiedWorkerThread::notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite) {
bool r = notifyCommon(v, overwrite);
if (r)
mainDelay.interruptFromISR(highPriWoken);
return r;
return r;
}
/**
* Schedule a notification to fire in delay msecs
*/
bool NotifiedWorkerThread::notifyLater(uint32_t delay, uint32_t v, bool overwrite)
{
bool didIt = notify(v, overwrite);
bool NotifiedWorkerThread::notifyLater(uint32_t delay, uint32_t v, bool overwrite) {
bool didIt = notify(v, overwrite);
if (didIt) { // If we didn't already have something queued, override the delay to be larger
setIntervalFromNow(delay); // a new version of setInterval relative to the current time
if (debugNotification) {
LOG_DEBUG("Delay notification %u", delay);
}
if (didIt) { // If we didn't already have something queued, override the delay to be larger
setIntervalFromNow(delay); // a new version of setInterval relative to the current time
if (debugNotification) {
LOG_DEBUG("Delay notification %u", delay);
}
}
return didIt;
return didIt;
}
void NotifiedWorkerThread::checkNotification()
{
auto n = notification;
notification = 0; // clear notification
if (n) {
onNotify(n);
}
void NotifiedWorkerThread::checkNotification() {
auto n = notification;
notification = 0; // clear notification
if (n) {
onNotify(n);
}
}
int32_t NotifiedWorkerThread::runOnce()
{
enabled = false; // Only run once per notification
checkNotification();
int32_t NotifiedWorkerThread::runOnce() {
enabled = false; // Only run once per notification
checkNotification();
return RUN_SAME;
return RUN_SAME;
}
} // namespace concurrency

View File

@@ -2,55 +2,53 @@
#include "OSThread.h"
namespace concurrency
{
namespace concurrency {
/**
* @brief A worker thread that waits on a freertos notification
*/
class NotifiedWorkerThread : public OSThread
{
/**
* The notification that was most recently used to wake the thread. Read from runOnce()
*/
uint32_t notification = 0;
class NotifiedWorkerThread : public OSThread {
/**
* The notification that was most recently used to wake the thread. Read from runOnce()
*/
uint32_t notification = 0;
public:
NotifiedWorkerThread(const char *name) : OSThread(name) {}
public:
NotifiedWorkerThread(const char *name) : OSThread(name) {}
/**
* Notify this thread so it can run
*/
bool notify(uint32_t v, bool overwrite);
/**
* Notify this thread so it can run
*/
bool notify(uint32_t v, bool overwrite);
/**
* Notify from an ISR
*
* This must be inline or IRAM_ATTR on ESP32
*/
bool notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite);
/**
* Notify from an ISR
*
* This must be inline or IRAM_ATTR on ESP32
*/
bool notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite);
/**
* Schedule a notification to fire in delay msecs
*/
bool notifyLater(uint32_t delay, uint32_t v, bool overwrite);
/**
* Schedule a notification to fire in delay msecs
*/
bool notifyLater(uint32_t delay, uint32_t v, bool overwrite);
protected:
virtual void onNotify(uint32_t notification) = 0;
protected:
virtual void onNotify(uint32_t notification) = 0;
/// just calls checkNotification()
virtual int32_t runOnce() override;
/// just calls checkNotification()
virtual int32_t runOnce() override;
/// Sometimes we might want to check notifications independently of when our thread was getting woken up (i.e. if we are about
/// to change radio transmit/receive modes we want to handle any pending interrupts first). You can call this method and if
/// any notifications are currently pending they will be handled immediately.
void checkNotification();
/// Sometimes we might want to check notifications independently of when our thread was getting woken up (i.e. if we
/// are about to change radio transmit/receive modes we want to handle any pending interrupts first). You can call
/// this method and if any notifications are currently pending they will be handled immediately.
void checkNotification();
private:
/**
* Notify this thread so it can run
*/
bool notifyCommon(uint32_t v, bool overwrite);
private:
/**
* Notify this thread so it can run
*/
bool notifyCommon(uint32_t v, bool overwrite);
};
} // namespace concurrency

View File

@@ -3,8 +3,7 @@
#include "memGet.h"
#include <assert.h>
namespace concurrency
{
namespace concurrency {
/// Show debugging info for disabled threads
bool OSThread::showDisabled;
@@ -20,93 +19,85 @@ const OSThread *OSThread::currentThread;
ThreadController mainController, timerController;
InterruptableDelay mainDelay;
void OSThread::setup()
{
mainController.ThreadName = "mainController";
timerController.ThreadName = "timerController";
void OSThread::setup() {
mainController.ThreadName = "mainController";
timerController.ThreadName = "timerController";
}
OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_controller)
: Thread(NULL, period), controller(_controller)
{
assertIsSetup();
OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_controller) : Thread(NULL, period), controller(_controller) {
assertIsSetup();
ThreadName = _name;
ThreadName = _name;
if (controller) {
bool added = controller->add(this);
assert(added);
}
if (controller) {
bool added = controller->add(this);
assert(added);
}
}
OSThread::~OSThread()
{
if (controller)
controller->remove(this);
OSThread::~OSThread() {
if (controller)
controller->remove(this);
}
/**
* Wait a specified number msecs starting from the current time (rather than the last time we were run)
*/
void OSThread::setIntervalFromNow(unsigned long _interval)
{
// Save interval
interval = _interval;
void OSThread::setIntervalFromNow(unsigned long _interval) {
// Save interval
interval = _interval;
// Cache the next run based on the last_run
_cached_next_run = millis() + interval;
// Cache the next run based on the last_run
_cached_next_run = millis() + interval;
}
bool OSThread::shouldRun(unsigned long time)
{
bool r = Thread::shouldRun(time);
bool OSThread::shouldRun(unsigned long time) {
bool r = Thread::shouldRun(time);
if (showRun && r) {
LOG_DEBUG("Thread %s: run", ThreadName.c_str());
}
if (showRun && r) {
LOG_DEBUG("Thread %s: run", ThreadName.c_str());
}
if (showWaiting && enabled && !r) {
LOG_DEBUG("Thread %s: wait %lu", ThreadName.c_str(), interval);
}
if (showWaiting && enabled && !r) {
LOG_DEBUG("Thread %s: wait %lu", ThreadName.c_str(), interval);
}
if (showDisabled && !enabled) {
LOG_DEBUG("Thread %s: disabled", ThreadName.c_str());
}
if (showDisabled && !enabled) {
LOG_DEBUG("Thread %s: disabled", ThreadName.c_str());
}
return r;
return r;
}
void OSThread::run()
{
void OSThread::run() {
#ifdef DEBUG_HEAP
auto heap = memGet.getFreeHeap();
auto heap = memGet.getFreeHeap();
#endif
currentThread = this;
auto newDelay = runOnce();
currentThread = this;
auto newDelay = runOnce();
#ifdef DEBUG_HEAP
auto newHeap = memGet.getFreeHeap();
if (newHeap < heap)
LOG_HEAP("------ Thread %s leaked heap %d -> %d (%d) ------", ThreadName.c_str(), heap, newHeap, newHeap - heap);
if (heap < newHeap)
LOG_HEAP("++++++ Thread %s freed heap %d -> %d (%d) ++++++", ThreadName.c_str(), heap, newHeap, newHeap - heap);
auto newHeap = memGet.getFreeHeap();
if (newHeap < heap)
LOG_HEAP("------ Thread %s leaked heap %d -> %d (%d) ------", ThreadName.c_str(), heap, newHeap, newHeap - heap);
if (heap < newHeap)
LOG_HEAP("++++++ Thread %s freed heap %d -> %d (%d) ++++++", ThreadName.c_str(), heap, newHeap, newHeap - heap);
#endif
#ifdef DEBUG_LOOP_TIMING
LOG_DEBUG("====== Thread next run in: %d", newDelay);
LOG_DEBUG("====== Thread next run in: %d", newDelay);
#endif
runned();
runned();
if (newDelay >= 0)
setInterval(newDelay);
if (newDelay >= 0)
setInterval(newDelay);
currentThread = NULL;
currentThread = NULL;
}
int32_t OSThread::disable()
{
enabled = false;
setInterval(INT32_MAX);
int32_t OSThread::disable() {
enabled = false;
setInterval(INT32_MAX);
return INT32_MAX;
return INT32_MAX;
}
/**
@@ -122,23 +113,22 @@ int32_t OSThread::disable()
*/
bool hasBeenSetup;
void assertIsSetup()
{
void assertIsSetup() {
/**
* Dear developer comrade - If this assert fails() that means you need to fix the following:
*
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
*
* it is super important to never allocate those object statically. instead, you should explicitly
* new them at a point where you are guaranteed that other objects that this instance
* depends on have already been created.
*
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
* this makes it guaranteed that the global mainController is fully constructed first.
*/
assert(hasBeenSetup);
/**
* Dear developer comrade - If this assert fails() that means you need to fix the following:
*
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor
* calls. Call assertIsSetup() to force a crash if someone tries to create an instance too early.
*
* it is super important to never allocate those object statically. instead, you should explicitly
* new them at a point where you are guaranteed that other objects that this instance
* depends on have already been created.
*
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
* this makes it guaranteed that the global mainController is fully constructed first.
*/
assert(hasBeenSetup);
}
} // namespace concurrency

View File

@@ -7,8 +7,7 @@
#include "ThreadController.h"
#include "concurrency/InterruptableDelay.h"
namespace concurrency
{
namespace concurrency {
extern ThreadController mainController, timerController;
extern InterruptableDelay mainDelay;
@@ -18,7 +17,8 @@ extern InterruptableDelay mainDelay;
/**
* @brief Base threading
*
* This is a pseudo threading layer that is super easy to port, well suited to our slow network and very ram & power efficient.
* This is a pseudo threading layer that is super easy to port, well suited to our slow network and very ram & power
* efficient.
*
* TODO FIXME @geeksville
*
@@ -28,49 +28,48 @@ extern InterruptableDelay mainDelay;
* move typedQueue into concurrency
* remove freertos from typedqueue
*/
class OSThread : public Thread
{
ThreadController *controller;
class OSThread : public Thread {
ThreadController *controller;
/// Show debugging info for disabled threads
static bool showDisabled;
/// Show debugging info for disabled threads
static bool showDisabled;
/// Show debugging info for threads when we run them
static bool showRun;
/// Show debugging info for threads when we run them
static bool showRun;
/// Show debugging info for threads we decide not to run;
static bool showWaiting;
/// Show debugging info for threads we decide not to run;
static bool showWaiting;
public:
/// For debug printing only (might be null)
static const OSThread *currentThread;
public:
/// For debug printing only (might be null)
static const OSThread *currentThread;
OSThread(const char *name, uint32_t period = 0, ThreadController *controller = &mainController);
OSThread(const char *name, uint32_t period = 0, ThreadController *controller = &mainController);
virtual ~OSThread();
virtual ~OSThread();
virtual bool shouldRun(unsigned long time);
virtual bool shouldRun(unsigned long time);
static void setup();
static void setup();
virtual int32_t disable();
virtual int32_t disable();
/**
* Wait a specified number msecs starting from the current time (rather than the last time we were run)
*/
void setIntervalFromNow(unsigned long _interval);
/**
* Wait a specified number msecs starting from the current time (rather than the last time we were run)
*/
void setIntervalFromNow(unsigned long _interval);
protected:
/**
* The method that will be called each time our thread gets a chance to run
*
* Returns desired period for next invocation (or RUN_SAME for no change)
*/
virtual int32_t runOnce() = 0;
bool sleepOnNextExecution = false;
protected:
/**
* The method that will be called each time our thread gets a chance to run
*
* Returns desired period for next invocation (or RUN_SAME for no change)
*/
virtual int32_t runOnce() = 0;
bool sleepOnNextExecution = false;
// Do not override this
virtual void run();
// Do not override this
virtual void run();
};
/**

View File

@@ -2,23 +2,21 @@
#include "concurrency/OSThread.h"
namespace concurrency
{
namespace concurrency {
/**
* @brief Periodically invoke a callback. This just provides C-style callback conventions
* rather than a virtual function - FIXME, remove?
*/
class Periodic : public OSThread
{
int32_t (*callback)();
class Periodic : public OSThread {
int32_t (*callback)();
public:
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
Periodic(const char *name, int32_t (*_callback)()) : OSThread(name), callback(_callback) {}
public:
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
Periodic(const char *name, int32_t (*_callback)()) : OSThread(name), callback(_callback) {}
protected:
int32_t runOnce() override { return callback(); }
protected:
int32_t runOnce() override { return callback(); }
};
} // namespace concurrency