From 34f99205ca80275801068756b9a02dc7125deb4e Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Thu, 12 Dec 2024 23:44:51 +0100 Subject: [PATCH] Convert code to use Timer instead of Thread (#120) --- Documentation/ideas.md | 7 +-- Tactility/Source/app/gpio/Gpio.cpp | 58 ++++++------------- .../Source/app/i2cscanner/I2cScanner.cpp | 15 ++++- Tactility/Source/app/i2cscanner/I2cScanner.h | 5 +- .../app/i2cscanner/I2cScannerThread.cpp | 55 +++++++----------- .../Source/app/i2cscanner/I2cScannerThread.h | 2 +- 6 files changed, 57 insertions(+), 85 deletions(-) diff --git a/Documentation/ideas.md b/Documentation/ideas.md index fdd37b89..6ed25fac 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -1,5 +1,7 @@ # TODOs +- Release process should bundle all libs and TactilityC headers and generate an SDK project - Attach ELF data to wrapper app (as app data) (check that app state is "running"!) so you can run more than 1 external apps at a time. + We'll need to keep track of all manifest instances, so that the wrapper can look up the relevant manifest for the relevant callbacks. - T-Deck: Clear screen before turning on blacklight - Single wire audio - Audio recording app @@ -8,11 +10,8 @@ - Logging to disk/etc. - Crash monitoring: Keep track of which system phase the app crashed in (e.g. which app in which state) - AppContext's onResult should pass the app id (or launch request id!) that was started, so we can differentiate between multiple types of apps being launched -- Loader: Use Timer instead of Thread, and move API to `tt::app::` -- Gpio: Use Timer instead of Thread -- I2cScannerThread: Use Timer instead of Thread +- Loader: Use main dispatcher instead of Thread, and move API to `tt::app::` - Bug: I2C Scanner is on M5Stack devices is broken -- WiFi AP Connect app: add "Forget" option. - Make firmwares available via release process - Make firmwares available via web serial website - Bug: When closing a top level app, there's often an error "can't stop root app" diff --git a/Tactility/Source/app/gpio/Gpio.cpp b/Tactility/Source/app/gpio/Gpio.cpp index 8c406683..43fbe713 100644 --- a/Tactility/Source/app/gpio/Gpio.cpp +++ b/Tactility/Source/app/gpio/Gpio.cpp @@ -6,6 +6,7 @@ #include "GpioHal.h" #include "lvgl/LvglSync.h" +#include "Timer.h" namespace tt::app::gpio { @@ -15,9 +16,8 @@ private: lv_obj_t* lvPins[GPIO_NUM_MAX] = {0 }; uint8_t pinStates[GPIO_NUM_MAX] = {0 }; - Thread* thread = nullptr; + std::unique_ptr timer; Mutex mutex; - bool interruptTask = true; public: @@ -32,9 +32,8 @@ public: void onShow(AppContext& app, lv_obj_t* parent); void onHide(AppContext& app); - void startTask(); + void startTask(std::shared_ptr ptr); void stopTask(); - bool shouldInterruptTask() const { return interruptTask; }; void updatePinStates(); void updatePinWidgets(); @@ -86,50 +85,30 @@ static lv_obj_t* createGpioRowWrapper(lv_obj_t* parent) { // region Task -static int32_t taskMain(void* context) { - Gpio* gpio = (Gpio*)context; - bool interrupted = false; +static void onTimer(std::shared_ptr context) { + auto gpio = std::static_pointer_cast(context); - while (!interrupted) { - kernel::delayMillis(100); - - gpio->updatePinStates(); - gpio->updatePinWidgets(); - - gpio->lock(); - interrupted = gpio->shouldInterruptTask(); - gpio->unlock(); - } - - return 0; + gpio->updatePinStates(); + gpio->updatePinWidgets(); } -void Gpio::startTask() { +void Gpio::startTask(std::shared_ptr ptr) { lock(); - tt_assert(thread == nullptr); - thread = new Thread( - "gpio", - 4096, - &taskMain, - this + tt_assert(timer == nullptr); + timer = std::make_unique( + Timer::TypePeriodic, + &onTimer, + ptr ); - interruptTask = false; - thread->start(); + timer->start(100 / portTICK_PERIOD_MS); unlock(); } void Gpio::stopTask() { - tt_assert(thread); - lock(); - interruptTask = true; - unlock(); + tt_assert(timer); - thread->join(); - - lock(); - delete thread; - thread = nullptr; - unlock(); + timer->stop(); + timer = nullptr; } // endregion Task @@ -188,7 +167,7 @@ void Gpio::onShow(AppContext& app, lv_obj_t* parent) { } gpio->unlock(); - gpio->startTask(); + gpio->startTask(gpio); } void Gpio::onHide(AppContext& app) { @@ -196,7 +175,6 @@ void Gpio::onHide(AppContext& app) { gpio->stopTask(); } - // region App lifecycle static void onShow(AppContext& app, lv_obj_t* parent) { diff --git a/Tactility/Source/app/i2cscanner/I2cScanner.cpp b/Tactility/Source/app/i2cscanner/I2cScanner.cpp index 465cae6c..72884168 100644 --- a/Tactility/Source/app/i2cscanner/I2cScanner.cpp +++ b/Tactility/Source/app/i2cscanner/I2cScanner.cpp @@ -102,7 +102,7 @@ static void updateViewsSafely(std::shared_ptr data) { } } -void onThreadFinished(std::shared_ptr data) { +void onScanTimerFinished(std::shared_ptr data) { if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { if (data->scanState == ScanStateScanning) { data->scanState = ScanStateStopped; @@ -110,7 +110,7 @@ void onThreadFinished(std::shared_ptr data) { } tt_check(data->mutex.release() == TtStatusOk); } else { - TT_LOG_W(TAG, "onThreadFinished lock"); + TT_LOG_W(TAG, "onScanTimerFinished lock"); } } @@ -159,7 +159,16 @@ static void onShow(AppContext& app, lv_obj_t* parent) { static void onHide(AppContext& app) { auto data = std::static_pointer_cast(app.getData()); - if (hasScanThread(data)) { + + bool isRunning = false; + if (data->mutex.acquire(250 / portTICK_PERIOD_MS) == TtStatusOk) { + isRunning = data->scanTimer->isRunning(); + data->mutex.release(); + } else { + return; + } + + if (isRunning) { stopScanning(data); } } diff --git a/Tactility/Source/app/i2cscanner/I2cScanner.h b/Tactility/Source/app/i2cscanner/I2cScanner.h index e652561f..54028725 100644 --- a/Tactility/Source/app/i2cscanner/I2cScanner.h +++ b/Tactility/Source/app/i2cscanner/I2cScanner.h @@ -5,6 +5,7 @@ #include #include "lvgl.h" #include "hal/i2c/I2c.h" +#include "Timer.h" #include namespace tt::app::i2cscanner { @@ -20,7 +21,7 @@ enum ScanState { struct Data { // Core Mutex mutex = Mutex(Mutex::TypeRecursive); - Thread* _Nullable scanThread = nullptr; + std::unique_ptr scanTimer = nullptr; // State ScanState scanState; i2c_port_t port = I2C_NUM_0; @@ -31,6 +32,6 @@ struct Data { lv_obj_t* scanListWidget = nullptr; }; -void onThreadFinished(std::shared_ptr data); +void onScanTimerFinished(std::shared_ptr data); } diff --git a/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp b/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp index 64881219..cba72b14 100644 --- a/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp +++ b/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp @@ -6,7 +6,7 @@ namespace tt::app::i2cscanner { std::shared_ptr _Nullable optData(); -static bool shouldStopScanThread(std::shared_ptr data) { +static bool shouldStopScanTimer(std::shared_ptr data) { if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { bool is_scanning = data->scanState == ScanStateScanning; tt_check(data->mutex.release() == TtStatusOk); @@ -38,10 +38,10 @@ static bool addAddressToList(std::shared_ptr data, uint8_t address) { } } -static int32_t scanThread(TT_UNUSED void* context) { +static void onScanTimer(TT_UNUSED std::shared_ptr context) { auto data = optData(); if (data == nullptr) { - return -1; + return; } TT_LOG_I(TAG, "Scan thread started"); @@ -51,39 +51,39 @@ static int32_t scanThread(TT_UNUSED void* context) { if (getPort(data, &port)) { if (hal::i2c::masterCheckAddressForDevice(port, address, 10 / portTICK_PERIOD_MS)) { TT_LOG_I(TAG, "Found device at address %d", address); - if (!shouldStopScanThread(data)) { + if (!shouldStopScanTimer(data)) { addAddressToList(data, address); + } else { + break; } } } else { - TT_LOG_W(TAG, "scanThread lock"); + TT_LOG_W(TAG, "onScanTimer lock"); break; } - if (shouldStopScanThread(data)) { + if (shouldStopScanTimer(data)) { break; } } TT_LOG_I(TAG, "Scan thread finalizing"); - onThreadFinished(data); + onScanTimerFinished(data); - TT_LOG_I(TAG, "Scan thread stopped"); - - return 0; + TT_LOG_I(TAG, "Scan timer done"); } bool hasScanThread(std::shared_ptr data) { bool has_thread; if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { - has_thread = data->scanThread != nullptr; + has_thread = data->scanTimer != nullptr; tt_check(data->mutex.release() == TtStatusOk); return has_thread; } else { // Unsafe way - TT_LOG_W(TAG, "hasScanThread lock"); - return data->scanThread != nullptr; + TT_LOG_W(TAG, "hasScanTimer lock"); + return data->scanTimer != nullptr; } } @@ -98,15 +98,13 @@ void startScanning(std::shared_ptr data) { lv_obj_add_flag(data->scanListWidget, LV_OBJ_FLAG_HIDDEN); lv_obj_clean(data->scanListWidget); - tt_assert(data->scanThread == nullptr); data->scanState = ScanStateScanning; - data->scanThread = new Thread( - "i2c scanner", - 4096, - scanThread, - nullptr + data->scanTimer = std::make_unique( + Timer::TypeOnce, + onScanTimer, + data ); - data->scanThread->start(); + data->scanTimer->start(10); tt_check(data->mutex.release() == TtStatusOk); } else { TT_LOG_W(TAG, "startScanning lock"); @@ -114,25 +112,12 @@ void startScanning(std::shared_ptr data) { } void stopScanning(std::shared_ptr data) { - bool sent_halt; if (data->mutex.acquire(250 / portTICK_PERIOD_MS) == TtStatusOk) { - tt_assert(data->scanThread != nullptr); + tt_assert(data->scanTimer != nullptr); data->scanState = ScanStateStopped; tt_check(data->mutex.release() == TtStatusOk); - sent_halt = true; } else { - sent_halt = false; - } - - if (sent_halt) { - tt_assert(data->scanThread != nullptr); - data->scanThread->join(); - - if (data->mutex.acquire(250 / portTICK_PERIOD_MS) == TtStatusOk) { - delete data->scanThread; - data->scanThread = nullptr; - tt_check(data->mutex.release() == TtStatusOk); - } + TT_LOG_E(TAG, "Acquire mutex failed"); } } diff --git a/Tactility/Source/app/i2cscanner/I2cScannerThread.h b/Tactility/Source/app/i2cscanner/I2cScannerThread.h index 794f75a8..c137e705 100644 --- a/Tactility/Source/app/i2cscanner/I2cScannerThread.h +++ b/Tactility/Source/app/i2cscanner/I2cScannerThread.h @@ -5,7 +5,7 @@ namespace tt::app::i2cscanner { -bool hasScanThread(std::shared_ptr data); +bool hasScanTimer(std::shared_ptr data); void startScanning(std::shared_ptr data); void stopScanning(std::shared_ptr data);