From 686f7cce83e842d86c532352eb0e6f7481c60fa1 Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Sat, 25 Jan 2025 17:29:11 +0100 Subject: [PATCH] TactilityCore improvements (#187) FreeRTOS handles were stored plainly and they were deleted in the destructor of classes. This meant that if a class were to be copied, the destructor would be called twice on the same handles and lead to double-free. Seha on Discord suggested to fix this by using `std::unique_ptr` with a custom deletion function. The changes affect: - Thread - Semaphore - Mutex - StreamBuffer - Timer - MessageQueue - EventFlag Thread changes: - Removal of the hack with the `Data` struct - Thread's main body is now just a private static function inside the class. - The C functions were relocated to static class members PubSub changes: - Refactored pubsub into class - Renamed files to `PubSub` instead of `Pubsub` - `PubSubSubscription` is now a private inner struct and `PubSub` only exposes `SubscriptionHandle` Lockable, ScopedLockable, Mutex: - Added `lock()` method that locks indefinitely - Remove deprecated `acquire()` and `release()` methods - Removed `TtWaitForever` in favour of `portMAX_DELAY` --- .../pull_request_template.md | 8 + .github/workflows/build-firmware.yml | 2 + .github/workflows/build-sdk.yml | 2 + .github/workflows/build-simulator.yml | 2 + .github/workflows/tests.yml | 2 + Boards/Simulator/Source/LvglTask.cpp | 12 +- Documentation/ideas.md | 19 +- Tactility/Private/app/files/State.h | 2 +- .../app/wificonnect/WifiConnectPrivate.h | 2 +- .../app/wifimanage/WifiManagePrivate.h | 2 +- Tactility/Private/service/gui/Gui_i.h | 4 +- Tactility/Private/service/loader/Loader_i.h | 12 +- Tactility/Source/Tactility.cpp | 2 +- Tactility/Source/app/AppInstance.cpp | 20 +- Tactility/Source/app/ManifestRegistry.cpp | 12 +- Tactility/Source/app/gpio/Gpio.cpp | 4 +- .../Source/app/i2cscanner/I2cScanner.cpp | 40 +-- Tactility/Source/app/wificonnect/State.cpp | 25 +- .../Source/app/wificonnect/WifiConnect.cpp | 10 +- Tactility/Source/app/wifimanage/State.cpp | 32 +-- .../Source/app/wifimanage/WifiManage.cpp | 10 +- Tactility/Source/lvgl/LvglSync.cpp | 4 +- Tactility/Source/lvgl/Statusbar.cpp | 26 +- Tactility/Source/service/gui/Gui.cpp | 16 +- Tactility/Source/service/loader/Loader.cpp | 8 +- Tactility/Source/service/loader/Loader.h | 4 +- .../service/screenshot/ScreenshotTask.cpp | 2 +- .../Source/service/statusbar/Statusbar.cpp | 4 +- TactilityCore/Source/CoreTypes.h | 42 --- TactilityCore/Source/Dispatcher.cpp | 14 +- TactilityCore/Source/Dispatcher.h | 4 +- TactilityCore/Source/EventFlag.cpp | 40 +-- TactilityCore/Source/EventFlag.h | 31 ++- TactilityCore/Source/Lockable.h | 6 + TactilityCore/Source/MessageQueue.cpp | 49 ++-- TactilityCore/Source/MessageQueue.h | 19 +- TactilityCore/Source/Mutex.cpp | 74 ++--- TactilityCore/Source/Mutex.h | 36 +-- TactilityCore/Source/PubSub.cpp | 47 ++++ TactilityCore/Source/PubSub.h | 61 ++++ TactilityCore/Source/Pubsub.cpp | 52 ---- TactilityCore/Source/Pubsub.h | 57 ---- TactilityCore/Source/Semaphore.cpp | 26 +- TactilityCore/Source/Semaphore.h | 15 +- TactilityCore/Source/StreamBuffer.cpp | 32 +-- TactilityCore/Source/StreamBuffer.h | 20 +- TactilityCore/Source/TactilityCore.h | 1 - TactilityCore/Source/Thread.cpp | 263 +++++++----------- TactilityCore/Source/Thread.h | 134 ++++----- TactilityCore/Source/Timer.cpp | 37 +-- TactilityCore/Source/Timer.h | 18 +- TactilityCore/Source/kernel/Kernel.cpp | 80 ++---- TactilityCore/Source/kernel/Kernel.h | 18 +- TactilityHeadless/Source/hal/i2c/I2c.h | 9 +- .../Source/hal/i2c/I2cDevice.cpp | 1 + .../Source/service/ServiceRegistry.cpp | 18 +- .../Source/service/sdcard/Sdcard.cpp | 4 +- TactilityHeadless/Source/service/wifi/Wifi.h | 2 +- .../Source/service/wifi/WifiEsp.cpp | 40 +-- .../Source/service/wifi/WifiMock.cpp | 4 +- 60 files changed, 711 insertions(+), 831 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE/pull_request_template.md delete mode 100644 TactilityCore/Source/CoreTypes.h create mode 100644 TactilityCore/Source/PubSub.cpp create mode 100644 TactilityCore/Source/PubSub.h delete mode 100644 TactilityCore/Source/Pubsub.cpp delete mode 100644 TactilityCore/Source/Pubsub.h diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 100644 index 00000000..3580d31f --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1,8 @@ +### Checklist + +- [ ] I read [contribution guidelines](https://github.com/ByteWelder/Tactility/blob/main/CONTRIBUTING.md) +- [ ] Code adheres to the [coding style](https://github.com/ByteWelder/Tactility/blob/main/CODING_STYLE.md) + +### Description + +(describe what your changes are) diff --git a/.github/workflows/build-firmware.yml b/.github/workflows/build-firmware.yml index 2630d709..c7d3f1a6 100644 --- a/.github/workflows/build-firmware.yml +++ b/.github/workflows/build-firmware.yml @@ -1,6 +1,8 @@ name: Build Firmware on: push: + branches: + - main pull_request: types: [opened, synchronize, reopened] diff --git a/.github/workflows/build-sdk.yml b/.github/workflows/build-sdk.yml index 6e9b2f36..f6919baf 100644 --- a/.github/workflows/build-sdk.yml +++ b/.github/workflows/build-sdk.yml @@ -1,6 +1,8 @@ name: Build SDK on: push: + branches: + - main pull_request: types: [opened, synchronize, reopened] diff --git a/.github/workflows/build-simulator.yml b/.github/workflows/build-simulator.yml index aa4dcbc0..1a28f6e6 100644 --- a/.github/workflows/build-simulator.yml +++ b/.github/workflows/build-simulator.yml @@ -2,6 +2,8 @@ name: Build Simulator on: push: + branches: + - main pull_request: types: [opened, synchronize, reopened] jobs: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b33814e4..dc54967d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,6 +1,8 @@ name: Tests on: push: + branches: + - main pull_request: types: [opened, synchronize, reopened] jobs: diff --git a/Boards/Simulator/Source/LvglTask.cpp b/Boards/Simulator/Source/LvglTask.cpp index 579578d2..acd41cf3 100644 --- a/Boards/Simulator/Source/LvglTask.cpp +++ b/Boards/Simulator/Source/LvglTask.cpp @@ -19,12 +19,12 @@ static bool task_running = false; static void lvgl_task(void* arg); -static bool task_lock(int timeout_ticks) { - return task_mutex.acquire(timeout_ticks) == tt::TtStatusOk; +static bool task_lock(TickType_t timeout) { + return task_mutex.lock(timeout); } static void task_unlock() { - task_mutex.release(); + task_mutex.unlock(); } static void task_set_running(bool running) { @@ -41,15 +41,15 @@ bool lvgl_task_is_running() { } static bool lvgl_lock(uint32_t timeoutMillis) { - return lvgl_mutex.acquire(pdMS_TO_TICKS(timeoutMillis)) == tt::TtStatusOk; + return lvgl_mutex.lock(pdMS_TO_TICKS(timeoutMillis)); } static void lvgl_unlock() { - lvgl_mutex.release(); + lvgl_mutex.unlock(); } void lvgl_task_interrupt() { - tt_check(task_lock(tt::TtWaitForever)); + tt_check(task_lock(portMAX_DELAY)); task_set_running(false); // interrupt task with boolean as flag task_unlock(); } diff --git a/Documentation/ideas.md b/Documentation/ideas.md index 3e7ce560..e7eade65 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -1,22 +1,9 @@ -# Issues -- WiFi bug: when pressing disconnect while between `WIFI_EVENT_STA_START` and `IP_EVENT_STA_GOT_IP`, then auto-connect becomes active again. -- ESP32 (CYD) memory issues (or any device without PSRAM): - - Boot app doesn't show logo - - WiFi is on and navigating back to Desktop makes desktop icons disappear - - WiFi might fail quietly when trying to enable it: this shows no feedback (force it by increasing LVGL buffers to 100kB) - Possible mitigations: - - When no PSRAM is available, use simplified desktop buttons - - Add statusbar icon for memory pressure. - - Show error in WiFi screen (e.g. AlertDialog when SPI is not enabled and available memory is below a certain amount) -- Clean up static_cast when casting to base class. -- EventFlag: Fix return value of set/get/wait (the errors are weirdly mixed in) -- Fix bug in T-Deck/etc: esp_lvgl_port settings has a large stack size (~9kB) to fix an issue where the T-Deck would get a stackoverflow. This sometimes happens when WiFi is auto-enabled and you open the app while it is still connecting. -- M5Stack Core only shows 4MB of SPIRAM in use - # TODOs +- Fix bug in T-Deck/etc: esp_lvgl_port settings has a large stack size (~9kB) to fix an issue where the T-Deck would get a stackoverflow. This sometimes happens when WiFi is auto-enabled and you open the app while it is still connecting. +- Clean up static_cast when casting to base class. +- Mutex: Implement give/take from ISR support (works only for non-recursive ones) - Extend unPhone power driver: add charging status, usb connection status, etc. - Expose app::Paths to TactilityC -- Refactor ServiceManifest into C++ class-based design like the App class - Experiment with what happens when using C++ code in an external app (without using standard library!) - Boards' CMakeLists.txt manually declare each source folder. Update them all to do a recursive search of all folders. - We currently build all boards for a given platform (e.g. ESP32S3), but it's better to filter all irrelevant ones based on the Kconfig board settings: diff --git a/Tactility/Private/app/files/State.h b/Tactility/Private/app/files/State.h index ffefb92a..fcd065d4 100644 --- a/Tactility/Private/app/files/State.h +++ b/Tactility/Private/app/files/State.h @@ -42,7 +42,7 @@ public: bool setEntriesForPath(const std::string& path); const std::vector& lockEntries() const { - mutex.lock(TtWaitForever); + mutex.lock(); return dir_entries; } diff --git a/Tactility/Private/app/wificonnect/WifiConnectPrivate.h b/Tactility/Private/app/wificonnect/WifiConnectPrivate.h index 88a2ce61..a4666919 100644 --- a/Tactility/Private/app/wificonnect/WifiConnectPrivate.h +++ b/Tactility/Private/app/wificonnect/WifiConnectPrivate.h @@ -21,7 +21,7 @@ private: .onConnectSsidContext = nullptr }; View view = View(&bindings, &state); - PubSubSubscription* wifiSubscription; + PubSub::SubscriptionHandle wifiSubscription; bool view_enabled = false; public: diff --git a/Tactility/Private/app/wifimanage/WifiManagePrivate.h b/Tactility/Private/app/wifimanage/WifiManagePrivate.h index c91d0f34..3b529050 100644 --- a/Tactility/Private/app/wifimanage/WifiManagePrivate.h +++ b/Tactility/Private/app/wifimanage/WifiManagePrivate.h @@ -12,7 +12,7 @@ class WifiManage : public App { private: - PubSubSubscription* wifiSubscription = nullptr; + PubSub::SubscriptionHandle wifiSubscription = nullptr; Mutex mutex; Bindings bindings = { }; State state; diff --git a/Tactility/Private/service/gui/Gui_i.h b/Tactility/Private/service/gui/Gui_i.h index c047e587..97a958c3 100644 --- a/Tactility/Private/service/gui/Gui_i.h +++ b/Tactility/Private/service/gui/Gui_i.h @@ -2,7 +2,7 @@ #include "MessageQueue.h" #include "Mutex.h" -#include "Pubsub.h" +#include "PubSub.h" #include "service/gui/Gui.h" #include #include @@ -19,7 +19,7 @@ struct Gui { // Thread and lock Thread* thread = nullptr; Mutex mutex = Mutex(Mutex::Type::Recursive); - PubSubSubscription* loader_pubsub_subscription = nullptr; + PubSub::SubscriptionHandle loader_pubsub_subscription = nullptr; // Layers and Canvas lv_obj_t* appRootWidget = nullptr; diff --git a/Tactility/Private/service/loader/Loader_i.h b/Tactility/Private/service/loader/Loader_i.h index 35bb4405..5703c077 100644 --- a/Tactility/Private/service/loader/Loader_i.h +++ b/Tactility/Private/service/loader/Loader_i.h @@ -1,16 +1,16 @@ #pragma once -#include "app/AppManifest.h" -#include "app/AppInstance.h" #include "EventFlag.h" #include "MessageQueue.h" -#include "Pubsub.h" -#include "Thread.h" -#include "service/loader/Loader.h" +#include "PubSub.h" #include "RtosCompatSemaphore.h" +#include "Thread.h" +#include "app/AppInstance.h" +#include "app/AppManifest.h" +#include "service/loader/Loader.h" +#include #include #include -#include namespace tt::service::loader { diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 0eff769b..3315c701 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -163,7 +163,7 @@ void run(const Configuration& config) { TT_LOG_I(TAG, "Processing main dispatcher"); while (true) { - getMainDispatcher().consume(TtWaitForever); + getMainDispatcher().consume(); } } diff --git a/Tactility/Source/app/AppInstance.cpp b/Tactility/Source/app/AppInstance.cpp index 86c2eb70..8b6fba2b 100644 --- a/Tactility/Source/app/AppInstance.cpp +++ b/Tactility/Source/app/AppInstance.cpp @@ -6,15 +6,15 @@ namespace tt::app { #define TAG "app" void AppInstance::setState(State newState) { - mutex.acquire(TtWaitForever); + mutex.lock(); state = newState; - mutex.release(); + mutex.unlock(); } State AppInstance::getState() const { - mutex.acquire(TtWaitForever); + mutex.lock(); auto result = state; - mutex.release(); + mutex.unlock(); return result; } @@ -30,22 +30,22 @@ const AppManifest& AppInstance::getManifest() const { } Flags AppInstance::getFlags() const { - mutex.acquire(TtWaitForever); + mutex.lock(); auto result = flags; - mutex.release(); + mutex.unlock(); return result; } void AppInstance::setFlags(Flags newFlags) { - mutex.acquire(TtWaitForever); + mutex.lock(); flags = newFlags; - mutex.release(); + mutex.unlock(); } std::shared_ptr AppInstance::getParameters() const { - mutex.acquire(TtWaitForever); + mutex.lock(); std::shared_ptr result = parameters; - mutex.release(); + mutex.unlock(); return result; } diff --git a/Tactility/Source/app/ManifestRegistry.cpp b/Tactility/Source/app/ManifestRegistry.cpp index 298e2d03..8835e45d 100644 --- a/Tactility/Source/app/ManifestRegistry.cpp +++ b/Tactility/Source/app/ManifestRegistry.cpp @@ -15,7 +15,7 @@ static Mutex hash_mutex(Mutex::Type::Normal); void addApp(const AppManifest& manifest) { TT_LOG_I(TAG, "Registering manifest %s", manifest.id.c_str()); - hash_mutex.acquire(TtWaitForever); + hash_mutex.lock(); if (!app_manifest_map.contains(manifest.id)) { app_manifest_map[manifest.id] = std::make_shared(manifest); @@ -23,13 +23,13 @@ void addApp(const AppManifest& manifest) { TT_LOG_E(TAG, "App id in use: %s", manifest.id.c_str()); } - hash_mutex.release(); + hash_mutex.unlock(); } _Nullable std::shared_ptr findAppById(const std::string& id) { - hash_mutex.acquire(TtWaitForever); + hash_mutex.lock(); auto result = app_manifest_map.find(id); - hash_mutex.release(); + hash_mutex.unlock(); if (result != app_manifest_map.end()) { return result->second; } else { @@ -39,11 +39,11 @@ _Nullable std::shared_ptr findAppById(const std::string& id) { std::vector> getApps() { std::vector> manifests; - hash_mutex.acquire(TtWaitForever); + hash_mutex.lock(); for (const auto& item: app_manifest_map) { manifests.push_back(item.second); } - hash_mutex.release(); + hash_mutex.unlock(); return manifests; } diff --git a/Tactility/Source/app/gpio/Gpio.cpp b/Tactility/Source/app/gpio/Gpio.cpp index cebde4a8..c6de598e 100644 --- a/Tactility/Source/app/gpio/Gpio.cpp +++ b/Tactility/Source/app/gpio/Gpio.cpp @@ -26,11 +26,11 @@ private: public: void lock() const { - tt_check(mutex.acquire(1000) == TtStatusOk); + tt_check(mutex.lock(1000)); } void unlock() const { - tt_check(mutex.release() == TtStatusOk); + tt_check(mutex.unlock()); } void onShow(AppContext& app, lv_obj_t* parent) override; diff --git a/Tactility/Source/app/i2cscanner/I2cScanner.cpp b/Tactility/Source/app/i2cscanner/I2cScanner.cpp index 44f2682f..30db64e5 100644 --- a/Tactility/Source/app/i2cscanner/I2cScanner.cpp +++ b/Tactility/Source/app/i2cscanner/I2cScanner.cpp @@ -115,12 +115,12 @@ void I2cScannerApp::onShow(AppContext& app, lv_obj_t* parent) { void I2cScannerApp::onHide(AppContext& app) { bool isRunning = false; - if (mutex.acquire(250 / portTICK_PERIOD_MS) == TtStatusOk) { + if (mutex.lock(250 / portTICK_PERIOD_MS)) { auto* timer = scanTimer.get(); if (timer != nullptr) { isRunning = timer->isRunning(); } - mutex.release(); + mutex.unlock(); } else { return; } @@ -158,9 +158,9 @@ void I2cScannerApp::onScanTimerCallback(TT_UNUSED std::shared_ptr context) // endregion Callbacks bool I2cScannerApp::getPort(i2c_port_t* outPort) { - if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + if (mutex.lock(100 / portTICK_PERIOD_MS)) { *outPort = this->port; - assert(mutex.release() == TtStatusOk); + mutex.unlock(); return true; } else { TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "getPort"); @@ -169,9 +169,9 @@ bool I2cScannerApp::getPort(i2c_port_t* outPort) { } bool I2cScannerApp::addAddressToList(uint8_t address) { - if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + if (mutex.lock(100 / portTICK_PERIOD_MS)) { scannedAddresses.push_back(address); - assert(mutex.release() == TtStatusOk); + mutex.unlock(); return true; } else { TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "addAddressToList"); @@ -180,9 +180,9 @@ bool I2cScannerApp::addAddressToList(uint8_t address) { } bool I2cScannerApp::shouldStopScanTimer() { - if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + if (mutex.lock(100 / portTICK_PERIOD_MS)) { bool is_scanning = scanState == ScanStateScanning; - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); return !is_scanning; } else { return true; @@ -222,9 +222,9 @@ void I2cScannerApp::onScanTimer() { bool I2cScannerApp::hasScanThread() { bool has_thread; - if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + if (mutex.lock(100 / portTICK_PERIOD_MS)) { has_thread = scanTimer != nullptr; - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); return has_thread; } else { // Unsafe way @@ -238,7 +238,7 @@ void I2cScannerApp::startScanning() { stopScanning(); } - if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + if (mutex.lock(100 / portTICK_PERIOD_MS)) { scannedAddresses.clear(); lv_obj_add_flag(scanListWidget, LV_OBJ_FLAG_HIDDEN); @@ -250,16 +250,16 @@ void I2cScannerApp::startScanning() { onScanTimerCallback ); scanTimer->start(10); - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); } else { TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "startScanning"); } } void I2cScannerApp::stopScanning() { - if (mutex.acquire(250 / portTICK_PERIOD_MS) == TtStatusOk) { + if (mutex.lock(250 / portTICK_PERIOD_MS)) { assert(scanTimer != nullptr); scanState = ScanStateStopped; - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); } else { TT_LOG_E(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED); } @@ -271,11 +271,11 @@ void I2cScannerApp::onSelectBus(lv_event_t* event) { auto i2c_devices = tt::getConfiguration()->hardware->i2c; assert(selected < i2c_devices.size()); - if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + if (mutex.lock(100 / portTICK_PERIOD_MS)) { scannedAddresses.clear(); port = i2c_devices[selected].port; scanState = ScanStateInitial; - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); updateViews(); } @@ -293,7 +293,7 @@ void I2cScannerApp::onPressScan(TT_UNUSED lv_event_t* event) { } void I2cScannerApp::updateViews() { - if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + if (mutex.lock(100 / portTICK_PERIOD_MS)) { if (scanState == ScanStateScanning) { lv_label_set_text(scanButtonLabelWidget, STOP_SCAN_TEXT); lv_obj_remove_flag(portDropdownWidget, LV_OBJ_FLAG_CLICKABLE); @@ -317,7 +317,7 @@ void I2cScannerApp::updateViews() { lv_obj_add_flag(scanListWidget, LV_OBJ_FLAG_HIDDEN); } - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); } else { TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "updateViews"); } @@ -333,12 +333,12 @@ void I2cScannerApp::updateViewsSafely() { } void I2cScannerApp::onScanTimerFinished() { - if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + if (mutex.lock(100 / portTICK_PERIOD_MS)) { if (scanState == ScanStateScanning) { scanState = ScanStateStopped; updateViewsSafely(); } - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); } else { TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "onScanTimerFinished"); } diff --git a/Tactility/Source/app/wificonnect/State.cpp b/Tactility/Source/app/wificonnect/State.cpp index 47e11e41..f179eaf2 100644 --- a/Tactility/Source/app/wificonnect/State.cpp +++ b/Tactility/Source/app/wificonnect/State.cpp @@ -1,47 +1,46 @@ #include "app/wificonnect/State.h" -#include "Check.h" #include namespace tt::app::wificonnect { void State::setConnectionError(bool error) { - tt_check(lock.acquire(TtWaitForever) == TtStatusOk); + lock.lock(); connectionError = error; - tt_check(lock.release() == TtStatusOk); + lock.unlock(); } bool State::hasConnectionError() const { - tt_check(lock.acquire(TtWaitForever) == TtStatusOk); + lock.lock(); auto result = connectionError; - tt_check(lock.release() == TtStatusOk); + lock.unlock(); return result; } void State::setApSettings(const service::wifi::settings::WifiApSettings* newSettings) { - tt_check(lock.acquire(TtWaitForever) == TtStatusOk); + lock.lock(); memcpy(&this->apSettings, newSettings, sizeof(service::wifi::settings::WifiApSettings)); - tt_check(lock.release() == TtStatusOk); + lock.unlock(); } const service::wifi::settings::WifiApSettings& State::lockApSettings() { - tt_check(lock.acquire(TtWaitForever) == TtStatusOk); + lock.lock(); return apSettings; } void State::unlockApSettings() { - tt_check(lock.release() == TtStatusOk); + lock.unlock(); } void State::setConnecting(bool isConnecting) { - tt_check(lock.acquire(TtWaitForever) == TtStatusOk); + lock.lock(); connecting = isConnecting; - tt_check(lock.release() == TtStatusOk); + lock.unlock(); } bool State::isConnecting() const { - tt_check(lock.acquire(TtWaitForever) == TtStatusOk); + lock.lock(); auto result = connecting; - tt_check(lock.release() == TtStatusOk); + lock.unlock(); return result; } diff --git a/Tactility/Source/app/wificonnect/WifiConnect.cpp b/Tactility/Source/app/wificonnect/WifiConnect.cpp index f2587796..ee729bdc 100644 --- a/Tactility/Source/app/wificonnect/WifiConnect.cpp +++ b/Tactility/Source/app/wificonnect/WifiConnect.cpp @@ -56,8 +56,7 @@ static void onConnect(const service::wifi::settings::WifiApSettings* ap_settings } WifiConnect::WifiConnect() { - auto wifi_pubsub = service::wifi::getPubsub(); - wifiSubscription = tt_pubsub_subscribe(wifi_pubsub, &eventCallback, this); + wifiSubscription = service::wifi::getPubsub()->subscribe(&eventCallback, this); bindings = (Bindings) { .onConnectSsid = onConnect, .onConnectSsidContext = this, @@ -65,16 +64,15 @@ WifiConnect::WifiConnect() { } WifiConnect::~WifiConnect() { - auto pubsub = service::wifi::getPubsub(); - tt_pubsub_unsubscribe(pubsub, wifiSubscription); + service::wifi::getPubsub()->unsubscribe(wifiSubscription); } void WifiConnect::lock() { - tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); + mutex.lock(); } void WifiConnect::unlock() { - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); } void WifiConnect::requestViewUpdate() { diff --git a/Tactility/Source/app/wifimanage/State.cpp b/Tactility/Source/app/wifimanage/State.cpp index 083141ce..b5b3d11c 100644 --- a/Tactility/Source/app/wifimanage/State.cpp +++ b/Tactility/Source/app/wifimanage/State.cpp @@ -1,63 +1,61 @@ -#include #include "app/wifimanage/WifiManagePrivate.h" namespace tt::app::wifimanage { void State::setScanning(bool isScanning) { - tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); + mutex.lock(); scanning = isScanning; scannedAfterRadioOn |= isScanning; - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); } void State::setRadioState(service::wifi::RadioState state) { - tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); + mutex.lock(); radioState = state; if (radioState == service::wifi::RadioState::Off) { scannedAfterRadioOn = false; } - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); } service::wifi::RadioState State::getRadioState() const { - tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); + mutex.lock(); auto result = radioState; - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); return result; } bool State::isScanning() const { - tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); + mutex.lock(); bool result = scanning; - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); return result; } const std::vector& State::lockApRecords() const { - tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); + mutex.lock(); return apRecords; } void State::unlockApRecords() const { - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); } void State::updateApRecords() { - tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); + mutex.lock(); apRecords = service::wifi::getScanResults(); - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); } void State::setConnectSsid(const std::string& ssid) { - tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); + mutex.lock(); connectSsid = ssid; - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); } std::string State::getConnectSsid() const { - tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); + mutex.lock(); auto result = connectSsid; - tt_check(mutex.release() == TtStatusOk); return result; } diff --git a/Tactility/Source/app/wifimanage/WifiManage.cpp b/Tactility/Source/app/wifimanage/WifiManage.cpp index 0966cb58..50066d17 100644 --- a/Tactility/Source/app/wifimanage/WifiManage.cpp +++ b/Tactility/Source/app/wifimanage/WifiManage.cpp @@ -64,11 +64,11 @@ WifiManage::WifiManage() { } void WifiManage::lock() { - tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); + mutex.lock(); } void WifiManage::unlock() { - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); } void WifiManage::requestViewUpdate() { @@ -111,8 +111,7 @@ static void wifiManageEventCallback(const void* message, void* context) { } void WifiManage::onShow(AppContext& app, lv_obj_t* parent) { - auto wifi_pubsub = service::wifi::getPubsub(); - wifiSubscription = tt_pubsub_subscribe(wifi_pubsub, &wifiManageEventCallback, this); + wifiSubscription = service::wifi::getPubsub()->subscribe(&wifiManageEventCallback, this); // State update (it has its own locking) state.setRadioState(service::wifi::getRadioState()); @@ -139,8 +138,7 @@ void WifiManage::onShow(AppContext& app, lv_obj_t* parent) { void WifiManage::onHide(TT_UNUSED AppContext& app) { lock(); - auto wifi_pubsub = service::wifi::getPubsub(); - tt_pubsub_unsubscribe(wifi_pubsub, wifiSubscription); + service::wifi::getPubsub()->unsubscribe(wifiSubscription); wifiSubscription = nullptr; isViewEnabled = false; unlock(); diff --git a/Tactility/Source/lvgl/LvglSync.cpp b/Tactility/Source/lvgl/LvglSync.cpp index 55dc197a..5822e88f 100644 --- a/Tactility/Source/lvgl/LvglSync.cpp +++ b/Tactility/Source/lvgl/LvglSync.cpp @@ -6,11 +6,11 @@ namespace tt::lvgl { static Mutex lockMutex; static bool defaultLock(uint32_t timeoutMillis) { - return lockMutex.acquire(timeoutMillis) == TtStatusOk; + return lockMutex.lock(timeoutMillis); } static void defaultUnlock() { - lockMutex.release(); + lockMutex.unlock(); } static LvglLock lock_singleton = defaultLock; diff --git a/Tactility/Source/lvgl/Statusbar.cpp b/Tactility/Source/lvgl/Statusbar.cpp index 8b789af8..30c26013 100644 --- a/Tactility/Source/lvgl/Statusbar.cpp +++ b/Tactility/Source/lvgl/Statusbar.cpp @@ -4,7 +4,7 @@ #include "Statusbar.h" #include "Mutex.h" -#include "Pubsub.h" +#include "PubSub.h" #include "TactilityCore.h" #include "lvgl/Style.h" @@ -43,10 +43,10 @@ typedef struct { lv_obj_t* time; lv_obj_t* icons[STATUSBAR_ICON_LIMIT]; lv_obj_t* battery_icon; - PubSubSubscription* pubsub_subscription; + PubSub::SubscriptionHandle pubsub_subscription; } Statusbar; -static bool statusbar_lock(uint32_t timeoutTicks) { +static bool statusbar_lock(uint32_t timeoutTicks = portMAX_DELAY) { return statusbar_data.mutex.lock(timeoutTicks); } @@ -83,7 +83,7 @@ static void onUpdateTime(TT_UNUSED std::shared_ptr context) { statusbar_data.time_update_timer->start(getNextUpdateTime()); // Notify widget - tt_pubsub_publish(statusbar_data.pubsub, nullptr); + statusbar_data.pubsub->publish(nullptr); } else { statusbar_data.time_update_timer->start(pdMS_TO_TICKS(60000U)); } @@ -132,7 +132,7 @@ static void statusbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLLABLE); LV_TRACE_OBJ_CREATE("finished"); auto* statusbar = (Statusbar*)obj; - statusbar->pubsub_subscription = tt_pubsub_subscribe(statusbar_data.pubsub, &statusbar_pubsub_event, statusbar); + statusbar->pubsub_subscription = statusbar_data.pubsub->subscribe(&statusbar_pubsub_event, statusbar); if (!statusbar_data.time_update_timer->isRunning()) { statusbar_data.time_update_timer->start(50 / portTICK_PERIOD_MS); @@ -145,7 +145,7 @@ static void statusbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) static void statusbar_destructor(TT_UNUSED const lv_obj_class_t* class_p, lv_obj_t* obj) { auto* statusbar = (Statusbar*)obj; - tt_pubsub_unsubscribe(statusbar_data.pubsub, statusbar->pubsub_subscription); + statusbar_data.pubsub->unsubscribe(statusbar->pubsub_subscription); } static void update_icon(lv_obj_t* image, const StatusbarIcon* icon) { @@ -181,7 +181,7 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) { obj_set_style_bg_invisible(left_spacer); lv_obj_set_flex_grow(left_spacer, 1); - statusbar_lock(TtWaitForever); + statusbar_lock(portMAX_DELAY); for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) { auto* image = lv_image_create(obj); lv_obj_set_size(image, STATUSBAR_ICON_SIZE, STATUSBAR_ICON_SIZE); @@ -233,7 +233,7 @@ static void statusbar_event(TT_UNUSED const lv_obj_class_t* class_p, lv_event_t* } int8_t statusbar_icon_add(const std::string& image) { - statusbar_lock(TtWaitForever); + statusbar_lock(); int8_t result = -1; for (int8_t i = 0; i < STATUSBAR_ICON_LIMIT; ++i) { if (!statusbar_data.icons[i].claimed) { @@ -245,7 +245,7 @@ int8_t statusbar_icon_add(const std::string& image) { break; } } - tt_pubsub_publish(statusbar_data.pubsub, nullptr); + statusbar_data.pubsub->publish(nullptr); statusbar_unlock(); return result; } @@ -257,12 +257,12 @@ int8_t statusbar_icon_add() { void statusbar_icon_remove(int8_t id) { TT_LOG_I(TAG, "id %d: remove", id); tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT); - statusbar_lock(TtWaitForever); + statusbar_lock(); StatusbarIcon* icon = &statusbar_data.icons[id]; icon->claimed = false; icon->visible = false; icon->image = ""; - tt_pubsub_publish(statusbar_data.pubsub, nullptr); + statusbar_data.pubsub->publish(nullptr); statusbar_unlock(); } @@ -273,7 +273,7 @@ void statusbar_icon_set_image(int8_t id, const std::string& image) { StatusbarIcon* icon = &statusbar_data.icons[id]; tt_check(icon->claimed); icon->image = image; - tt_pubsub_publish(statusbar_data.pubsub, nullptr); + statusbar_data.pubsub->publish(nullptr); statusbar_unlock(); } } @@ -285,7 +285,7 @@ void statusbar_icon_set_visibility(int8_t id, bool visible) { StatusbarIcon* icon = &statusbar_data.icons[id]; tt_check(icon->claimed); icon->visible = visible; - tt_pubsub_publish(statusbar_data.pubsub, nullptr); + statusbar_data.pubsub->publish(nullptr); statusbar_unlock(); } } diff --git a/Tactility/Source/service/gui/Gui.cpp b/Tactility/Source/service/gui/Gui.cpp index ab3ba0ab..49938ee8 100644 --- a/Tactility/Source/service/gui/Gui.cpp +++ b/Tactility/Source/service/gui/Gui.cpp @@ -35,7 +35,7 @@ Gui* gui_alloc() { &guiMain, nullptr ); - instance->loader_pubsub_subscription = tt_pubsub_subscribe(loader::getPubsub(), &onLoaderMessage, instance); + instance->loader_pubsub_subscription = loader::getPubsub()->subscribe(&onLoaderMessage, instance); tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS)); instance->keyboardGroup = lv_group_create(); auto* screen_root = lv_scr_act(); @@ -90,7 +90,7 @@ void unlock() { void requestDraw() { assert(gui); ThreadId thread_id = gui->thread->getId(); - thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW); + Thread::setFlags(thread_id, GUI_THREAD_FLAG_DRAW); } void showApp(std::shared_ptr app) { @@ -120,19 +120,19 @@ static int32_t guiMain(TT_UNUSED void* p) { Gui* local_gui = gui; while (true) { - uint32_t flags = thread_flags_wait( + uint32_t flags = Thread::awaitFlags( GUI_THREAD_FLAG_ALL, - TtFlagWaitAny, - TtWaitForever + EventFlag::WaitAny, + portMAX_DELAY ); // Process and dispatch draw call if (flags & GUI_THREAD_FLAG_DRAW) { - thread_flags_clear(GUI_THREAD_FLAG_DRAW); + Thread::clearFlags(GUI_THREAD_FLAG_DRAW); redraw(local_gui); } if (flags & GUI_THREAD_FLAG_EXIT) { - thread_flags_clear(GUI_THREAD_FLAG_EXIT); + Thread::clearFlags(GUI_THREAD_FLAG_EXIT); break; } } @@ -159,7 +159,7 @@ public: lock(); ThreadId thread_id = gui->thread->getId(); - thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT); + Thread::setFlags(thread_id, GUI_THREAD_FLAG_EXIT); gui->thread->join(); delete gui->thread; diff --git a/Tactility/Source/service/loader/Loader.cpp b/Tactility/Source/service/loader/Loader.cpp index c3cec50a..527014c0 100644 --- a/Tactility/Source/service/loader/Loader.cpp +++ b/Tactility/Source/service/loader/Loader.cpp @@ -125,13 +125,13 @@ static void transitionAppToState(std::shared_ptr app, app::Sta break; case app::StateShowing: { LoaderEvent event_showing = { .type = LoaderEventTypeApplicationShowing }; - tt_pubsub_publish(loader_singleton->pubsubExternal, &event_showing); + loader_singleton->pubsubExternal->publish(&event_showing); app->setState(app::StateShowing); break; } case app::StateHiding: { LoaderEvent event_hiding = { .type = LoaderEventTypeApplicationHiding }; - tt_pubsub_publish(loader_singleton->pubsubExternal, &event_hiding); + loader_singleton->pubsubExternal->publish(&event_hiding); app->setState(app::StateHiding); break; } @@ -174,7 +174,7 @@ static LoaderStatus startAppWithManifestInternal( transitionAppToState(new_app, app::StateShowing); LoaderEvent event_external = { .type = LoaderEventTypeApplicationStarted }; - tt_pubsub_publish(loader_singleton->pubsubExternal, &event_external); + loader_singleton->pubsubExternal->publish(&event_external); return LoaderStatus::Ok; } @@ -265,7 +265,7 @@ static void stopAppInternal() { // WARNING: After this point we cannot change the app states from this method directly anymore as we don't have a lock! LoaderEvent event_external = { .type = LoaderEventTypeApplicationStopped }; - tt_pubsub_publish(loader_singleton->pubsubExternal, &event_external); + loader_singleton->pubsubExternal->publish(&event_external); if (instance_to_resume != nullptr) { if (result_set) { diff --git a/Tactility/Source/service/loader/Loader.h b/Tactility/Source/service/loader/Loader.h index 5e1dba69..072897da 100644 --- a/Tactility/Source/service/loader/Loader.h +++ b/Tactility/Source/service/loader/Loader.h @@ -1,8 +1,8 @@ #pragma once -#include "app/AppManifest.h" #include "Bundle.h" -#include "Pubsub.h" +#include "PubSub.h" +#include "app/AppManifest.h" #include "service/ServiceManifest.h" #include diff --git a/Tactility/Source/service/screenshot/ScreenshotTask.cpp b/Tactility/Source/service/screenshot/ScreenshotTask.cpp index 4258e8e1..92665075 100644 --- a/Tactility/Source/service/screenshot/ScreenshotTask.cpp +++ b/Tactility/Source/service/screenshot/ScreenshotTask.cpp @@ -41,7 +41,7 @@ bool ScreenshotTask::isFinished() { void ScreenshotTask::setFinished() { auto scoped_lockable = mutex.scoped(); - scoped_lockable->lock(TtWaitForever); + scoped_lockable->lock(); finished = true; } diff --git a/Tactility/Source/service/statusbar/Statusbar.cpp b/Tactility/Source/service/statusbar/Statusbar.cpp index 2abfb4c9..adb21f68 100644 --- a/Tactility/Source/service/statusbar/Statusbar.cpp +++ b/Tactility/Source/service/statusbar/Statusbar.cpp @@ -138,11 +138,11 @@ private: std::unique_ptr paths; void lock() const { - tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); + mutex.lock(); } void unlock() const { - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); } void updateWifiIcon() { diff --git a/TactilityCore/Source/CoreTypes.h b/TactilityCore/Source/CoreTypes.h deleted file mode 100644 index d265dcb1..00000000 --- a/TactilityCore/Source/CoreTypes.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include -#include "TactilityCoreConfig.h" - -namespace tt { - -[[deprecated("Using this is poor software design in most scenarios")]] -typedef enum { - TtWaitForever = 0xFFFFFFFFU, -} TtWait; - -[[deprecated("Define flags as needed")]] -typedef enum { - TtFlagWaitAny = 0x00000000U, ///< Wait for any flag (default). - TtFlagWaitAll = 0x00000001U, ///< Wait for all flags. - TtFlagNoClear = 0x00000002U, ///< Do not clear flags which have been specified to wait for. - - TtFlagError = 0x80000000U, ///< Error indicator. - TtFlagErrorUnknown = 0xFFFFFFFFU, ///< TtStatusError (-1). - TtFlagErrorTimeout = 0xFFFFFFFEU, ///< TtStatusErrorTimeout (-2). - TtFlagErrorResource = 0xFFFFFFFDU, ///< TtStatusErrorResource (-3). - TtFlagErrorParameter = 0xFFFFFFFCU, ///< TtStatusErrorParameter (-4). - TtFlagErrorISR = 0xFFFFFFFAU, ///< TtStatusErrorISR (-6). -} TtFlag; - -[[deprecated("Use bool or specific type")]] -typedef enum { - TtStatusOk = 0, ///< Operation completed successfully. - TtStatusError = - -1, ///< Unspecified RTOS error: run-time error but no other error message fits. - TtStatusErrorTimeout = -2, ///< Operation not completed within the timeout period. - TtStatusErrorResource = -3, ///< Resource not available. - TtStatusErrorParameter = -4, ///< Parameter error. - TtStatusErrorNoMemory = - -5, ///< System is out of memory: it was impossible to allocate or reserve memory for the operation. - TtStatusErrorISR = - -6, ///< Not allowed in ISR context: the function cannot be called from interrupt service routines. - TtStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization. -} TtStatus; - -} // namespace diff --git a/TactilityCore/Source/Dispatcher.cpp b/TactilityCore/Source/Dispatcher.cpp index 57f0dee0..9b555d1d 100644 --- a/TactilityCore/Source/Dispatcher.cpp +++ b/TactilityCore/Source/Dispatcher.cpp @@ -5,17 +5,13 @@ namespace tt { #define TAG "dispatcher" -#define BACKPRESSURE_WARNING_COUNT 100 -#define WAIT_FLAG 1 - -Dispatcher::Dispatcher() : - mutex(Mutex::Type::Normal) -{} +#define BACKPRESSURE_WARNING_COUNT ((EventBits_t)100) +#define WAIT_FLAG ((EventBits_t)1) Dispatcher::~Dispatcher() { // Wait for Mutex usage - mutex.acquire(TtWaitForever); - mutex.release(); + mutex.lock(); + mutex.unlock(); } void Dispatcher::dispatch(Function function, std::shared_ptr context) { @@ -37,7 +33,7 @@ void Dispatcher::dispatch(Function function, std::shared_ptr context) { uint32_t Dispatcher::consume(TickType_t timeout) { // Wait for signal and clear TickType_t start_ticks = kernel::getTicks(); - if (eventFlag.wait(WAIT_FLAG, TtFlagWaitAny, timeout) == WAIT_FLAG) { + if (eventFlag.wait(WAIT_FLAG, EventFlag::WaitAny, timeout)) { eventFlag.clear(WAIT_FLAG); } else { return 0; diff --git a/TactilityCore/Source/Dispatcher.h b/TactilityCore/Source/Dispatcher.h index b83920b6..0ee31713 100644 --- a/TactilityCore/Source/Dispatcher.h +++ b/TactilityCore/Source/Dispatcher.h @@ -43,7 +43,7 @@ private: public: - explicit Dispatcher(); + explicit Dispatcher() = default; ~Dispatcher(); /** @@ -58,7 +58,7 @@ public: * @param[in] timeout the ticks to wait for a message * @return the amount of messages that were consumed */ - uint32_t consume(TickType_t timeout); + uint32_t consume(TickType_t timeout = portMAX_DELAY); }; } // namespace diff --git a/TactilityCore/Source/EventFlag.cpp b/TactilityCore/Source/EventFlag.cpp index dc991d6b..3dbcdd00 100644 --- a/TactilityCore/Source/EventFlag.cpp +++ b/TactilityCore/Source/EventFlag.cpp @@ -8,15 +8,15 @@ namespace tt { -EventFlag::EventFlag() { +EventFlag::EventFlag() : + handle(xEventGroupCreate()) +{ assert(!TT_IS_IRQ_MODE()); - handle = xEventGroupCreate(); tt_check(handle); } EventFlag::~EventFlag() { assert(!TT_IS_IRQ_MODE()); - vEventGroupDelete(handle); } uint32_t EventFlag::set(uint32_t flags) const { @@ -28,14 +28,14 @@ uint32_t EventFlag::set(uint32_t flags) const { if (TT_IS_IRQ_MODE()) { yield = pdFALSE; - if (xEventGroupSetBitsFromISR(handle, (EventBits_t)flags, &yield) == pdFAIL) { - rflags = (uint32_t)TtFlagErrorResource; + if (xEventGroupSetBitsFromISR(handle.get(), (EventBits_t)flags, &yield) == pdFAIL) { + rflags = (uint32_t)ErrorResource; } else { rflags = flags; portYIELD_FROM_ISR(yield); } } else { - rflags = xEventGroupSetBits(handle, (EventBits_t)flags); + rflags = xEventGroupSetBits(handle.get(), (EventBits_t)flags); } /* Return event flags after setting */ @@ -48,10 +48,10 @@ uint32_t EventFlag::clear(uint32_t flags) const { uint32_t rflags; if (TT_IS_IRQ_MODE()) { - rflags = xEventGroupGetBitsFromISR(handle); + rflags = xEventGroupGetBitsFromISR(handle.get()); - if (xEventGroupClearBitsFromISR(handle, (EventBits_t)flags) == pdFAIL) { - rflags = (uint32_t)TtStatusErrorResource; + 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. */ @@ -59,7 +59,7 @@ uint32_t EventFlag::clear(uint32_t flags) const { portYIELD_FROM_ISR(pdTRUE); } } else { - rflags = xEventGroupClearBits(handle, (EventBits_t)flags); + rflags = xEventGroupClearBits(handle.get(), (EventBits_t)flags); } /* Return event flags before clearing */ @@ -70,9 +70,9 @@ uint32_t EventFlag::get() const { uint32_t rflags; if (TT_IS_IRQ_MODE()) { - rflags = xEventGroupGetBitsFromISR(handle); + rflags = xEventGroupGetBitsFromISR(handle.get()); } else { - rflags = xEventGroupGetBits(handle); + rflags = xEventGroupGetBits(handle.get()); } /* Return current event flags */ @@ -91,40 +91,40 @@ uint32_t EventFlag::wait( BaseType_t exit_clear; uint32_t rflags; - if (options & TtFlagWaitAll) { + if (options & WaitAll) { wait_all = pdTRUE; } else { wait_all = pdFAIL; } - if (options & TtFlagNoClear) { + if (options & NoClear) { exit_clear = pdFAIL; } else { exit_clear = pdTRUE; } rflags = xEventGroupWaitBits( - handle, + handle.get(), (EventBits_t)flags, exit_clear, wait_all, (TickType_t)timeout ); - if (options & TtFlagWaitAll) { + if (options & WaitAll) { if ((flags & rflags) != flags) { if (timeout > 0U) { - rflags = (uint32_t)TtStatusErrorTimeout; + rflags = (uint32_t)ErrorTimeout; } else { - rflags = (uint32_t)TtStatusErrorResource; + rflags = (uint32_t)ErrorResource; } } } else { if ((flags & rflags) == 0U) { if (timeout > 0U) { - rflags = (uint32_t)TtStatusErrorTimeout; + rflags = (uint32_t)ErrorTimeout; } else { - rflags = (uint32_t)TtStatusErrorResource; + rflags = (uint32_t)ErrorResource; } } } diff --git a/TactilityCore/Source/EventFlag.h b/TactilityCore/Source/EventFlag.h index 3538381c..8b7dfdda 100644 --- a/TactilityCore/Source/EventFlag.h +++ b/TactilityCore/Source/EventFlag.h @@ -1,7 +1,7 @@ #pragma once -#include "CoreTypes.h" #include "RtosCompatEventGroups.h" +#include namespace tt { @@ -10,17 +10,40 @@ namespace tt { */ class EventFlag { private: - EventGroupHandle_t handle; + + struct EventGroupHandleDeleter { + void operator()(EventGroupHandle_t handleToDelete) { + vEventGroupDelete(handleToDelete); + } + }; + + std::unique_ptr, EventGroupHandleDeleter> handle; + public: + EventFlag(); ~EventFlag(); + + enum Flag { + WaitAny = 0x00000000U, ///< Wait for any flag (default). + WaitAll = 0x00000001U, ///< Wait for all flags. + NoClear = 0x00000002U, ///< Do not clear flags which have been specified to wait for. + + Error = 0x80000000U, ///< Error indicator. + ErrorUnknown = 0xFFFFFFFFU, ///< TtStatusError (-1). + ErrorTimeout = 0xFFFFFFFEU, ///< TtStatusErrorTimeout (-2). + ErrorResource = 0xFFFFFFFDU, ///< TtStatusErrorResource (-3). + ErrorParameter = 0xFFFFFFFCU, ///< TtStatusErrorParameter (-4). + ErrorISR = 0xFFFFFFFAU, ///< TtStatusErrorISR (-6). + }; + uint32_t set(uint32_t flags) const; uint32_t clear(uint32_t flags) const; uint32_t get() const; uint32_t wait( uint32_t flags, - uint32_t options = TtFlagWaitAny, - uint32_t timeout = TtWaitForever + uint32_t options = WaitAny, + uint32_t timeout = portMAX_DELAY ) const; }; diff --git a/TactilityCore/Source/Lockable.h b/TactilityCore/Source/Lockable.h index f5d73f6e..d43a2325 100644 --- a/TactilityCore/Source/Lockable.h +++ b/TactilityCore/Source/Lockable.h @@ -3,6 +3,8 @@ #include "Check.h" #include "RtosCompat.h" #include +#include +#include namespace tt { @@ -15,6 +17,8 @@ public: virtual bool lock(TickType_t timeoutTicks) const = 0; + virtual bool lock() const { return lock(portMAX_DELAY); } + virtual bool unlock() const = 0; std::unique_ptr scoped() const; @@ -43,6 +47,8 @@ public: return lockable.lock(timeout); } + bool lock() const override { return lock(portMAX_DELAY); } + bool unlock() const override { return lockable.unlock(); } diff --git a/TactilityCore/Source/MessageQueue.cpp b/TactilityCore/Source/MessageQueue.cpp index 369a3a48..07aee189 100644 --- a/TactilityCore/Source/MessageQueue.cpp +++ b/TactilityCore/Source/MessageQueue.cpp @@ -4,63 +4,64 @@ namespace tt { -MessageQueue::MessageQueue(uint32_t capacity, uint32_t msg_size) { - assert(!TT_IS_ISR() && (capacity > 0U) && (msg_size > 0U)); - queue_handle = xQueueCreate(capacity, msg_size); - tt_check(queue_handle); +static inline QueueHandle_t createQueue(uint32_t capacity, uint32_t messageSize) { + assert(!TT_IS_ISR() && (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(!TT_IS_ISR()); - vQueueDelete(queue_handle); } -bool MessageQueue::put(const void* message, uint32_t timeout) { +bool MessageQueue::put(const void* message, TickType_t timeout) { bool result = true; BaseType_t yield; if (TT_IS_ISR()) { - if ((queue_handle == nullptr) || (message == nullptr) || (timeout != 0U)) { + if ((handle == nullptr) || (message == nullptr) || (timeout != 0U)) { result = false; } else { yield = pdFALSE; - if (xQueueSendToBackFromISR(queue_handle, message, &yield) != pdTRUE) { + if (xQueueSendToBackFromISR(handle.get(), message, &yield) != pdTRUE) { result = false; } else { portYIELD_FROM_ISR(yield); } } - } else if ((queue_handle == nullptr) || (message == nullptr)) { + } else if ((handle == nullptr) || (message == nullptr)) { result = false; - } else if (xQueueSendToBack(queue_handle, message, (TickType_t)timeout) != pdPASS) { + } else if (xQueueSendToBack(handle.get(), message, (TickType_t)timeout) != pdPASS) { result = false; } return result; } -bool MessageQueue::get(void* msg_ptr, uint32_t timeout_ticks) { +bool MessageQueue::get(void* msg_ptr, TickType_t timeout) { bool result = true; BaseType_t yield; - if (TT_IS_ISR()) { - if ((queue_handle == nullptr) || (msg_ptr == nullptr) || (timeout_ticks != 0U)) { + if ((handle == nullptr) || (msg_ptr == nullptr) || (timeout != 0U)) { result = false; } else { yield = pdFALSE; - if (xQueueReceiveFromISR(queue_handle, msg_ptr, &yield) != pdPASS) { + if (xQueueReceiveFromISR(handle.get(), msg_ptr, &yield) != pdPASS) { result = false; } else { portYIELD_FROM_ISR(yield); } } } else { - if ((queue_handle == nullptr) || (msg_ptr == nullptr)) { + if ((handle == nullptr) || (msg_ptr == nullptr)) { result = false; - } else if (xQueueReceive(queue_handle, msg_ptr, (TickType_t)timeout_ticks) != pdPASS) { + } else if (xQueueReceive(handle.get(), msg_ptr, (TickType_t)timeout) != pdPASS) { result = false; } } @@ -69,7 +70,7 @@ bool MessageQueue::get(void* msg_ptr, uint32_t timeout_ticks) { } uint32_t MessageQueue::getCapacity() const { - auto* mq = (StaticQueue_t*)(queue_handle); + auto* mq = (StaticQueue_t*)(handle.get()); if (mq == nullptr) { return 0U; } else { @@ -78,7 +79,7 @@ uint32_t MessageQueue::getCapacity() const { } uint32_t MessageQueue::getMessageSize() const { - auto* mq = (StaticQueue_t*)(queue_handle); + auto* mq = (StaticQueue_t*)(handle.get()); if (mq == nullptr) { return 0U; } else { @@ -89,12 +90,12 @@ uint32_t MessageQueue::getMessageSize() const { uint32_t MessageQueue::getCount() const { UBaseType_t count; - if (queue_handle == nullptr) { + if (handle == nullptr) { count = 0U; } else if (TT_IS_ISR()) { - count = uxQueueMessagesWaitingFromISR(queue_handle); + count = uxQueueMessagesWaitingFromISR(handle.get()); } else { - count = uxQueueMessagesWaiting(queue_handle); + count = uxQueueMessagesWaiting(handle.get()); } /* Return number of queued messages */ @@ -102,7 +103,7 @@ uint32_t MessageQueue::getCount() const { } uint32_t MessageQueue::getSpace() const { - auto* mq = (StaticQueue_t*)(queue_handle); + auto* mq = (StaticQueue_t*)(handle.get()); uint32_t space; uint32_t isrm; @@ -124,10 +125,10 @@ uint32_t MessageQueue::getSpace() const { bool MessageQueue::reset() { tt_check(!TT_IS_ISR()); - if (queue_handle == nullptr) { + if (handle == nullptr) { return false; } else { - xQueueReset(queue_handle); + xQueueReset(handle.get()); return true; } } diff --git a/TactilityCore/Source/MessageQueue.h b/TactilityCore/Source/MessageQueue.h index 2018f1c7..b5a90ce9 100644 --- a/TactilityCore/Source/MessageQueue.h +++ b/TactilityCore/Source/MessageQueue.h @@ -7,7 +7,7 @@ */ #pragma once -#include "CoreTypes.h" +#include #ifdef ESP_PLATFORM #include "freertos/FreeRTOS.h" @@ -25,7 +25,14 @@ namespace tt { */ class MessageQueue { private: - QueueHandle_t queue_handle; + + struct QueueHandleDeleter { + void operator()(QueueHandle_t handleToDelete) { + vQueueDelete(handleToDelete); + } + }; + + std::unique_ptr, QueueHandleDeleter> handle; public: /** Allocate message queue @@ -39,17 +46,17 @@ public: /** Post a message to the queue. * The message is queued by copy, not by reference. * @param[in] message A pointer to a message. The message will be copied into a buffer. - * @param[in] timeoutTicks + * @param[in] timeout * @return success result */ - bool put(const void* message, uint32_t timeoutTicks); + bool put(const void* message, TickType_t timeout); /** Get message from queue * @param[out] message A pointer to an already allocated message object - * @param[in] timeoutTicks + * @param[in] timeout * @return success result */ - bool get(void* message, uint32_t timeoutTicks); + bool get(void* message, TickType_t timeout); /** * @return The maximum amount of messages that can be in the queue at any given time. diff --git a/TactilityCore/Source/Mutex.cpp b/TactilityCore/Source/Mutex.cpp index d2c7b191..8c0e22f7 100644 --- a/TactilityCore/Source/Mutex.cpp +++ b/TactilityCore/Source/Mutex.cpp @@ -22,88 +22,56 @@ namespace tt { #define tt_mutex_info(mutex, text) #endif -Mutex::Mutex(Type type) : type(type) { - tt_mutex_info(data, "alloc"); +static inline SemaphoreHandle_t createSemaphoreHandle(Mutex::Type type) { switch (type) { - case Type::Normal: - semaphore = xSemaphoreCreateMutex(); - break; - case Type::Recursive: - semaphore = xSemaphoreCreateRecursiveMutex(); - break; + case Mutex::Type::Normal: + return xSemaphoreCreateMutex(); + case Mutex::Type::Recursive: + return xSemaphoreCreateRecursiveMutex(); default: tt_crash("Mutex type unknown/corrupted"); } - - assert(semaphore != nullptr); } -Mutex::~Mutex() { - assert(!TT_IS_IRQ_MODE()); - vSemaphoreDelete(semaphore); - semaphore = nullptr; // If the mutex is used after release, this might help debugging +Mutex::Mutex(Type type) : handle(createSemaphoreHandle(type)), type(type) { + tt_mutex_info(data, "alloc"); + assert(handle != nullptr); } -TtStatus Mutex::acquire(TickType_t timeout) const { +bool Mutex::lock(TickType_t timeout) const { assert(!TT_IS_IRQ_MODE()); - assert(semaphore != nullptr); - + assert(handle != nullptr); tt_mutex_info(mutex, "acquire"); switch (type) { case Type::Normal: - if (xSemaphoreTake(semaphore, timeout) != pdPASS) { - if (timeout != 0U) { - return TtStatusErrorTimeout; - } else { - return TtStatusErrorResource; - } - } else { - return TtStatusOk; - } + return xSemaphoreTake(handle.get(), timeout) == pdPASS; case Type::Recursive: - if (xSemaphoreTakeRecursive(semaphore, timeout) != pdPASS) { - if (timeout != 0U) { - return TtStatusErrorTimeout; - } else { - return TtStatusErrorResource; - } - } else { - return TtStatusOk; - } + return xSemaphoreTakeRecursive(handle.get(), timeout) == pdPASS; default: - tt_crash("mutex type unknown/corrupted"); + tt_crash(); } } -TtStatus Mutex::release() const { +bool Mutex::unlock() const { assert(!TT_IS_IRQ_MODE()); - assert(semaphore); + assert(handle != nullptr); tt_mutex_info(mutex, "release"); switch (type) { - case Type::Normal: { - if (xSemaphoreGive(semaphore) != pdPASS) { - return TtStatusErrorResource; - } else { - return TtStatusOk; - } - } + case Type::Normal: + return xSemaphoreGive(handle.get()) == pdPASS; case Type::Recursive: - if (xSemaphoreGiveRecursive(semaphore) != pdPASS) { - return TtStatusErrorResource; - } else { - return TtStatusOk; - } + return xSemaphoreGiveRecursive(handle.get()) == pdPASS; default: - tt_crash("mutex type unknown/corrupted"); + tt_crash(); } } ThreadId Mutex::getOwner() const { assert(!TT_IS_IRQ_MODE()); - assert(semaphore); - return (ThreadId)xSemaphoreGetMutexHolder(semaphore); + assert(handle != nullptr); + return (ThreadId)xSemaphoreGetMutexHolder(handle.get()); } } // namespace diff --git a/TactilityCore/Source/Mutex.h b/TactilityCore/Source/Mutex.h index 1c0fd8ef..c3306b80 100644 --- a/TactilityCore/Source/Mutex.h +++ b/TactilityCore/Source/Mutex.h @@ -4,7 +4,6 @@ */ #pragma once -#include "CoreTypes.h" #include "Thread.h" #include "RtosCompatSemaphore.h" #include "Check.h" @@ -15,9 +14,9 @@ namespace tt { /** * Wrapper for FreeRTOS xSemaphoreCreateMutex and xSemaphoreCreateRecursiveMutex - * Can be used in IRQ mode (within ISR context) + * Cannot be used in IRQ mode (within ISR context) */ -class Mutex : public Lockable { +class Mutex final : public Lockable { public: @@ -28,35 +27,36 @@ public: private: - SemaphoreHandle_t semaphore; + struct SemaphoreHandleDeleter { + void operator()(QueueHandle_t handleToDelete) { + assert(!TT_IS_IRQ_MODE()); + vSemaphoreDelete(handleToDelete); + } + }; + + std::unique_ptr, SemaphoreHandleDeleter> handle; Type type; public: explicit Mutex(Type type = Type::Normal); - ~Mutex() override; - - /** Attempt to lock the mutex. Blocks until timeout passes or lock is acquired. - * @param[in] timeout - * @return status result - */ - TtStatus acquire(TickType_t timeout) const; - - /** Attempt to unlock the mutex. - * @return status result - */ - TtStatus release() const; + ~Mutex() override = default; /** Attempt to lock the mutex. Blocks until timeout passes or lock is acquired. * @param[in] timeout * @return success result */ - bool lock(TickType_t timeout) const override { return acquire(timeout) == TtStatusOk; } + bool lock(TickType_t timeout) const override; + + /** Attempt to lock the mutex. Blocks until lock is acquired, without timeout. + * @return success result + */ + bool lock() const override { return lock(portMAX_DELAY); } /** Attempt to unlock the mutex. * @return success result */ - bool unlock() const override { return release() == TtStatusOk; } + bool unlock() const override; /** @return the owner of the thread */ ThreadId getOwner() const; diff --git a/TactilityCore/Source/PubSub.cpp b/TactilityCore/Source/PubSub.cpp new file mode 100644 index 00000000..7f026527 --- /dev/null +++ b/TactilityCore/Source/PubSub.cpp @@ -0,0 +1,47 @@ +#include "PubSub.h" +#include "Check.h" + +namespace tt { + +PubSub::SubscriptionHandle PubSub::subscribe(PubSubCallback callback, void* callbackParameter) { + mutex.lock(); + items.push_back({ + .id = (++lastId), + .callback = callback, + .callbackParameter = callbackParameter}); + + mutex.unlock(); + + return (Subscription*)lastId; +} + +void PubSub::unsubscribe(SubscriptionHandle subscription) { + assert(subscription); + + mutex.lock(); + bool result = false; + auto id = (uint64_t)subscription; + for (auto it = items.begin(); it != items.end(); it++) { + if (it->id == id) { + items.erase(it); + result = true; + break; + } + } + + mutex.unlock(); + tt_check(result); +} + +void PubSub::publish(void* message) { + mutex.lock(); + + // Iterate over subscribers + for (auto& it : items) { + it.callback(message, it.callbackParameter); + } + + mutex.unlock(); +} + +} // namespace diff --git a/TactilityCore/Source/PubSub.h b/TactilityCore/Source/PubSub.h new file mode 100644 index 00000000..19b2ce83 --- /dev/null +++ b/TactilityCore/Source/PubSub.h @@ -0,0 +1,61 @@ +/** + * @file pubsub.h + * PubSub + */ +#pragma once + +#include "Mutex.h" +#include + +namespace tt { + +/** PubSub Callback type */ +typedef void (*PubSubCallback)(const void* message, void* context); + +/** Publish and subscribe to messages in a thread-safe manner. */ +class PubSub { + +private: + + struct Subscription { + uint64_t id; + PubSubCallback callback; + void* callbackParameter; + }; + + typedef std::list Subscriptions; + uint64_t lastId = 0; + Subscriptions items; + Mutex mutex; + +public: + + typedef void* SubscriptionHandle; + + PubSub() = default; + + ~PubSub() { + tt_check(items.empty()); + } + + /** Start receiving messages at the specified handle (Threadsafe, Re-entrable) + * @param[in] callback + * @param[in] callbackParameter the data to pass to the callback + * @return subscription instance + */ + SubscriptionHandle subscribe(PubSubCallback callback, void* callbackParameter); + + /** Stop receiving messages at the specified handle (Threadsafe, Re-entrable.) + * No use of `tt_pubsub_subscription` allowed after call of this method + * @param[in] subscription + */ + void unsubscribe(SubscriptionHandle subscription); + + /** Publish message to all subscribers (Threadsafe, Re-entrable.) + * @param[in] message message pointer to publish - it is passed as-is to the callback + */ + void publish(void* message); +}; + + +} // namespace diff --git a/TactilityCore/Source/Pubsub.cpp b/TactilityCore/Source/Pubsub.cpp deleted file mode 100644 index 31869eeb..00000000 --- a/TactilityCore/Source/Pubsub.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "Pubsub.h" -#include "Check.h" - -namespace tt { - -PubSubSubscription* tt_pubsub_subscribe(std::shared_ptr pubsub, PubSubCallback callback, void* callbackContext) { - tt_check(pubsub->mutex.acquire(TtWaitForever) == TtStatusOk); - PubSubSubscription subscription = { - .id = (++pubsub->last_id), - .callback = callback, - .callback_context = callbackContext - }; - pubsub->items.push_back( - subscription - ); - - tt_check(pubsub->mutex.release() == TtStatusOk); - - return (PubSubSubscription*)pubsub->last_id; -} - -void tt_pubsub_unsubscribe(std::shared_ptr pubsub, PubSubSubscription* pubsub_subscription) { - assert(pubsub); - assert(pubsub_subscription); - - tt_check(pubsub->mutex.acquire(TtWaitForever) == TtStatusOk); - bool result = false; - auto id = (uint64_t)pubsub_subscription; - for (auto it = pubsub->items.begin(); it != pubsub->items.end(); it++) { - if (it->id == id) { - pubsub->items.erase(it); - result = true; - break; - } - } - - tt_check(pubsub->mutex.release() == TtStatusOk); - tt_check(result); -} - -void tt_pubsub_publish(std::shared_ptr pubsub, void* message) { - tt_check(pubsub->mutex.acquire(TtWaitForever) == TtStatusOk); - - // Iterate over subscribers - for (auto& it : pubsub->items) { - it.callback(message, it.callback_context); - } - - tt_check(pubsub->mutex.release() == TtStatusOk); -} - -} // namespace diff --git a/TactilityCore/Source/Pubsub.h b/TactilityCore/Source/Pubsub.h deleted file mode 100644 index 80314beb..00000000 --- a/TactilityCore/Source/Pubsub.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @file pubsub.h - * PubSub - */ -#pragma once - -#include "Mutex.h" -#include - -namespace tt { - -/** PubSub Callback type */ -typedef void (*PubSubCallback)(const void* message, void* context); - -struct PubSubSubscription { - uint64_t id; - PubSubCallback callback; - void* callback_context; -}; - -struct PubSub { - typedef std::list Subscriptions; - uint64_t last_id = 0; - Subscriptions items; - Mutex mutex; - - ~PubSub() { - tt_check(items.empty()); - } -}; - -/** Subscribe to PubSub - * Threadsafe, Reentrable - * @param[in] pubsub pointer to PubSub instance - * @param[in] callback - * @param[in] callbackContext the data to pass to the callback - * @return subscription instance - */ -PubSubSubscription* -tt_pubsub_subscribe(std::shared_ptr pubsub, PubSubCallback callback, void* callbackContext); - -/** Unsubscribe from PubSub - * No use of `tt_pubsub_subscription` allowed after call of this method - * Threadsafe, Re-entrable. - * @param[in] pubsub - * @param[in] subscription - */ -void tt_pubsub_unsubscribe(std::shared_ptr pubsub, PubSubSubscription* subscription); - -/** Publish message to PubSub - * Threadsafe, Reentrable. - * @param[in] pubsub - * @param[in] message message pointer to publish - it is passed as-is to the callback - */ -void tt_pubsub_publish(std::shared_ptr pubsub, void* message); - -} // namespace diff --git a/TactilityCore/Source/Semaphore.cpp b/TactilityCore/Source/Semaphore.cpp index 2c306585..4b0d2b23 100644 --- a/TactilityCore/Source/Semaphore.cpp +++ b/TactilityCore/Source/Semaphore.cpp @@ -4,28 +4,30 @@ namespace tt { -Semaphore::Semaphore(uint32_t maxCount, uint32_t initialCount) { - assert(!TT_IS_IRQ_MODE()); +static inline struct QueueDefinition* createHandle(uint32_t maxCount, uint32_t initialCount) { assert((maxCount > 0U) && (initialCount <= maxCount)); if (maxCount == 1U) { - handle = xSemaphoreCreateBinary(); + auto handle = xSemaphoreCreateBinary(); if ((handle != nullptr) && (initialCount != 0U)) { if (xSemaphoreGive(handle) != pdPASS) { vSemaphoreDelete(handle); handle = nullptr; } } + return handle; } else { - handle = xSemaphoreCreateCounting(maxCount, initialCount); + return xSemaphoreCreateCounting(maxCount, initialCount); } +} - tt_check(handle); +Semaphore::Semaphore(uint32_t maxCount, uint32_t initialCount) : handle(createHandle(maxCount, initialCount)){ + assert(!TT_IS_IRQ_MODE()); + tt_check(handle != nullptr); } Semaphore::~Semaphore() { assert(!TT_IS_IRQ_MODE()); - vSemaphoreDelete(handle); } bool Semaphore::acquire(uint32_t timeout) const { @@ -35,7 +37,7 @@ bool Semaphore::acquire(uint32_t timeout) const { } else { BaseType_t yield = pdFALSE; - if (xSemaphoreTakeFromISR(handle, &yield) != pdPASS) { + if (xSemaphoreTakeFromISR(handle.get(), &yield) != pdPASS) { return false; } else { portYIELD_FROM_ISR(yield); @@ -43,21 +45,21 @@ bool Semaphore::acquire(uint32_t timeout) const { } } } else { - return xSemaphoreTake(handle, (TickType_t)timeout) == pdPASS; + return xSemaphoreTake(handle.get(), (TickType_t)timeout) == pdPASS; } } bool Semaphore::release() const { if (TT_IS_IRQ_MODE()) { BaseType_t yield = pdFALSE; - if (xSemaphoreGiveFromISR(handle, &yield) != pdTRUE) { + if (xSemaphoreGiveFromISR(handle.get(), &yield) != pdTRUE) { return false; } else { portYIELD_FROM_ISR(yield); return true; } } else { - return xSemaphoreGive(handle) == pdPASS; + return xSemaphoreGive(handle.get()) == pdPASS; } } @@ -65,12 +67,12 @@ uint32_t Semaphore::getCount() const { if (TT_IS_IRQ_MODE()) { // TODO: uxSemaphoreGetCountFromISR is not supported on esp-idf 5.1.2 - perhaps later on? #ifdef uxSemaphoreGetCountFromISR - return uxSemaphoreGetCountFromISR(handle); + return uxSemaphoreGetCountFromISR(handle.get()); #else return uxQueueMessagesWaitingFromISR((QueueHandle_t)hSemaphore); #endif } else { - return uxSemaphoreGetCount(handle); + return uxSemaphoreGetCount(handle.get()); } } diff --git a/TactilityCore/Source/Semaphore.h b/TactilityCore/Source/Semaphore.h index a947ef62..ee249492 100644 --- a/TactilityCore/Source/Semaphore.h +++ b/TactilityCore/Source/Semaphore.h @@ -1,7 +1,8 @@ #pragma once -#include "CoreTypes.h" #include "Thread.h" +#include +#include #ifdef ESP_PLATFORM #include "freertos/FreeRTOS.h" @@ -18,8 +19,18 @@ namespace tt { * Can be used from IRQ/ISR mode, but cannot be created/destroyed from such a context. */ class Semaphore { + private: - SemaphoreHandle_t handle; + + struct SemaphoreHandleDeleter { + void operator()(QueueHandle_t handleToDelete) { + assert(!TT_IS_IRQ_MODE()); + vSemaphoreDelete(handleToDelete); + } + }; + + std::unique_ptr, SemaphoreHandleDeleter> handle; + public: /** * Cannot be called from IRQ/ISR mode. diff --git a/TactilityCore/Source/StreamBuffer.cpp b/TactilityCore/Source/StreamBuffer.cpp index 66fec595..20f89b80 100644 --- a/TactilityCore/Source/StreamBuffer.cpp +++ b/TactilityCore/Source/StreamBuffer.cpp @@ -2,22 +2,20 @@ #include "Check.h" #include "CoreDefines.h" -#include "CoreTypes.h" namespace tt { -StreamBuffer::StreamBuffer(size_t size, size_t triggerLevel) { +inline static StreamBufferHandle_t createStreamBuffer(size_t size, size_t triggerLevel) { assert(size != 0); - handle = xStreamBufferCreate(size, triggerLevel); + return xStreamBufferCreate(size, triggerLevel); +} + +StreamBuffer::StreamBuffer(size_t size, size_t triggerLevel) : handle(createStreamBuffer(size, triggerLevel)) { tt_check(handle); }; -StreamBuffer::~StreamBuffer() { - vStreamBufferDelete(handle); -}; - bool StreamBuffer::setTriggerLevel(size_t triggerLevel) const { - return xStreamBufferSetTriggerLevel(handle, triggerLevel) == pdTRUE; + return xStreamBufferSetTriggerLevel(handle.get(), triggerLevel) == pdTRUE; }; size_t StreamBuffer::send( @@ -27,11 +25,11 @@ size_t StreamBuffer::send( ) const { if (TT_IS_IRQ_MODE()) { BaseType_t yield; - size_t result = xStreamBufferSendFromISR(handle, data, length, &yield); + size_t result = xStreamBufferSendFromISR(handle.get(), data, length, &yield); portYIELD_FROM_ISR(yield); return result; } else { - return xStreamBufferSend(handle, data, length, timeout); + return xStreamBufferSend(handle.get(), data, length, timeout); } }; @@ -42,32 +40,32 @@ size_t StreamBuffer::receive( ) const { if (TT_IS_IRQ_MODE()) { BaseType_t yield; - size_t result = xStreamBufferReceiveFromISR(handle, data, length, &yield); + size_t result = xStreamBufferReceiveFromISR(handle.get(), data, length, &yield); portYIELD_FROM_ISR(yield); return result; } else { - return xStreamBufferReceive(handle, data, length, timeout); + return xStreamBufferReceive(handle.get(), data, length, timeout); } } size_t StreamBuffer::getAvailableReadBytes() const { - return xStreamBufferBytesAvailable(handle); + return xStreamBufferBytesAvailable(handle.get()); }; size_t StreamBuffer::getAvailableWriteBytes() const { - return xStreamBufferSpacesAvailable(handle); + return xStreamBufferSpacesAvailable(handle.get()); }; bool StreamBuffer::isFull() const { - return xStreamBufferIsFull(handle) == pdTRUE; + return xStreamBufferIsFull(handle.get()) == pdTRUE; }; bool StreamBuffer::isEmpty() const { - return xStreamBufferIsEmpty(handle) == pdTRUE; + return xStreamBufferIsEmpty(handle.get()) == pdTRUE; }; bool StreamBuffer::reset() const { - return xStreamBufferReset(handle) == pdPASS; + return xStreamBufferReset(handle.get()) == pdPASS; } } // namespace diff --git a/TactilityCore/Source/StreamBuffer.h b/TactilityCore/Source/StreamBuffer.h index 3d2db7ae..94c6fb82 100644 --- a/TactilityCore/Source/StreamBuffer.h +++ b/TactilityCore/Source/StreamBuffer.h @@ -1,8 +1,6 @@ #pragma once -#include "CoreTypes.h" -#include -#include +#include #ifdef ESP_PLATFORM #include "freertos/FreeRTOS.h" @@ -25,8 +23,16 @@ namespace tt { * interrupt that will read from the buffer (the reader). */ class StreamBuffer { + private: - StreamBufferHandle_t handle; + + struct StreamBufferHandleDeleter { + void operator()(StreamBufferHandle_t handleToDelete) { + vStreamBufferDelete(handleToDelete); + } + }; + + std::unique_ptr, StreamBufferHandleDeleter> handle; public: @@ -42,7 +48,7 @@ public: */ StreamBuffer(size_t size, size_t triggerLevel); - ~StreamBuffer(); + ~StreamBuffer() = default; /** * @brief Set trigger level for stream buffer. @@ -65,7 +71,7 @@ public: * @param[in] timeout The maximum amount of time the task should remain in the * Blocked state to wait for space to become available if the stream buffer is full. * Will return immediately if timeout is zero. - * Setting timeout to TtWaitForever will cause the task to wait indefinitely. + * Setting timeout to portMAX_DELAY will cause the task to wait indefinitely. * Ignored if called from ISR. * @return The number of bytes actually written to the stream buffer. */ @@ -85,7 +91,7 @@ public: * @param[in] timeout The maximum amount of time the task should remain in the * Blocked state to wait for data to become available if the stream buffer is empty. * Will return immediately if timeout is zero. - * Setting timeout to TtWaitForever will cause the task to wait indefinitely. + * Setting timeout to portMAX_DELAY will cause the task to wait indefinitely. * Ignored if called from ISR. * @return The number of bytes read from the stream buffer, if any. */ diff --git a/TactilityCore/Source/TactilityCore.h b/TactilityCore/Source/TactilityCore.h index 87324adb..8dff9d89 100644 --- a/TactilityCore/Source/TactilityCore.h +++ b/TactilityCore/Source/TactilityCore.h @@ -5,7 +5,6 @@ #include "Check.h" #include "CoreDefines.h" #include "CoreExtraDefines.h" -#include "CoreTypes.h" #include "EventFlag.h" #include "kernel/Kernel.h" #include "kernel/critical/Critical.h" diff --git a/TactilityCore/Source/Thread.cpp b/TactilityCore/Source/Thread.cpp index 1f766a70..e30ac32b 100644 --- a/TactilityCore/Source/Thread.cpp +++ b/TactilityCore/Source/Thread.cpp @@ -1,10 +1,13 @@ #include "Thread.h" -#include #include "Check.h" #include "CoreDefines.h" +#include "EventFlag.h" #include "kernel/Kernel.h" #include "Log.h" +#include "TactilityCoreConfig.h" + +#include namespace tt { @@ -22,180 +25,155 @@ namespace tt { static_assert(static_cast(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 setState(Thread::Data* data, Thread::State state) { - data->state = state; - if (data->stateCallback) { - data->stateCallback(state, data->stateCallbackContext); +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 thread_catch() { //-V1082 - // If you're here it means you're probably doing something wrong - // with critical sections or with scheduler state - asm volatile("nop"); // extra magic - tt_crash("You are doing it wrong"); //-V779 +__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(); } - -static void thread_body(void* context) { - assert(context); - auto* data = static_cast(context); +void Thread::mainBody(void* context) { + assert(context != nullptr); + auto* thread = static_cast(context); // Store thread data instance to thread local storage assert(pvTaskGetThreadLocalStoragePointer(nullptr, 0) == nullptr); - vTaskSetThreadLocalStoragePointer(nullptr, 0, data->thread); + vTaskSetThreadLocalStoragePointer(nullptr, 0, thread); - assert(data->state == Thread::State::Starting); - setState(data, Thread::State::Running); - data->callbackResult = data->callback(data->callbackContext); - assert(data->state == Thread::State::Running); + assert(thread->state == Thread::State::Starting); + thread->setState(Thread::State::Running); + thread->callbackResult = thread->callback(thread->callbackContext); + assert(thread->state == Thread::State::Running); - setState(data, Thread::State::Stopped); + thread->setState(Thread::State::Stopped); vTaskSetThreadLocalStoragePointer(nullptr, 0, nullptr); - data->taskHandle = nullptr; + thread->taskHandle = nullptr; vTaskDelete(nullptr); - thread_catch(); + threadCatch(); } -Thread::Thread() : - data({ - .thread = nullptr, - .taskHandle = nullptr, - .state = State::Stopped, - .callback = nullptr, - .callbackContext = nullptr, - .callbackResult = 0, - .stateCallback = nullptr, - .stateCallbackContext = nullptr, - .name = std::string(), - .priority = Priority::Normal, - .stackSize = 0, - }) { } - Thread::Thread( - const std::string& name, + std::string name, configSTACK_DEPTH_TYPE stackSize, Callback callback, _Nullable void* callbackContext, portBASE_TYPE affinity ) : - data({ - .thread = nullptr, - .taskHandle = nullptr, - .state = State::Stopped, - .callback = callback, - .callbackContext = callbackContext, - .callbackResult = 0, - .stateCallback = nullptr, - .stateCallbackContext = nullptr, - .name = name, - .priority = Priority::Normal, - .stackSize = stackSize, - .affinity = affinity - }) { } + callback(callback), + callbackContext(callbackContext), + name(std::move(name)), + stackSize(stackSize), + affinity(affinity) +{} Thread::~Thread() { // Ensure that use join before free - assert(data.state == State::Stopped); - assert(data.taskHandle == nullptr); + assert(state == State::Stopped); + assert(taskHandle == nullptr); } -void Thread::setName(const std::string& newName) { - assert(data.state == State::Stopped); - data.name = newName; +void Thread::setName(std::string newName) { + assert(state == State::Stopped); + name = std::move(newName); } -void Thread::setStackSize(size_t stackSize) { - assert(data.state == State::Stopped); +void Thread::setStackSize(size_t newStackSize) { + assert(state == State::Stopped); assert(stackSize % 4 == 0); - data.stackSize = stackSize; + stackSize = newStackSize; } -void Thread::setCallback(Callback callback, _Nullable void* callbackContext) { - assert(data.state == State::Stopped); - data.callback = callback; - data.callbackContext = callbackContext; +void Thread::setCallback(Callback newCallback, _Nullable void* newCallbackContext) { + assert(state == State::Stopped); + callback = newCallback; + callbackContext = newCallbackContext; } -void Thread::setPriority(Priority priority) { - assert(data.state == State::Stopped); - data.priority = priority; +void Thread::setPriority(Priority newPriority) { + assert(state == State::Stopped); + priority = newPriority; } void Thread::setStateCallback(StateCallback callback, _Nullable void* callbackContext) { - assert(data.state == State::Stopped); - data.stateCallback = callback; - data.stateCallbackContext = callbackContext; + assert(state == State::Stopped); + stateCallback = callback; + stateCallbackContext = callbackContext; } Thread::State Thread::getState() const { - return data.state; + return state; } void Thread::start() { - assert(data.callback); - assert(data.state == State::Stopped); - assert(data.stackSize > 0U && data.stackSize < (UINT16_MAX * sizeof(StackType_t))); + assert(callback); + assert(state == State::Stopped); + assert(stackSize > 0U && stackSize < (UINT16_MAX * sizeof(StackType_t))); - setState(&data, State::Starting); + setState(State::Starting); - uint32_t stack_depth = data.stackSize / sizeof(StackType_t); + uint32_t stack_depth = stackSize / sizeof(StackType_t); BaseType_t result; - if (data.affinity != -1) { + if (affinity != -1) { #ifdef ESP_PLATFORM result = xTaskCreatePinnedToCore( - thread_body, - data.name.c_str(), + mainBody, + name.c_str(), stack_depth, this, - static_cast(data.priority), - &(data.taskHandle), - data.affinity + static_cast(priority), + &taskHandle, + affinity ); #else TT_LOG_W(TAG, "Pinned tasks are not supported by current FreeRTOS platform - creating regular one"); result = xTaskCreate( - thread_body, - data.name.c_str(), + mainBody, + name.c_str(), stack_depth, this, - static_cast(data.priority), - &(data.taskHandle) + static_cast(priority), + &taskHandle ); #endif } else { result = xTaskCreate( - thread_body, - data.name.c_str(), + mainBody, + name.c_str(), stack_depth, this, - static_cast(data.priority), - &(data.taskHandle) + static_cast(priority), + &taskHandle ); } tt_check(result == pdPASS); - tt_check(data.state == State::Stopped || data.taskHandle); + tt_check(state == State::Stopped || taskHandle); } bool Thread::join(TickType_t timeout, TickType_t pollInterval) { - tt_check(thread_get_current() != this); + 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 (data.taskHandle) { + while (taskHandle) { kernel::delayTicks(pollInterval); if ((kernel::getTicks() - start_ticks) > timeout) { return false; @@ -206,45 +184,27 @@ bool Thread::join(TickType_t timeout, TickType_t pollInterval) { } ThreadId Thread::getId() const { - return data.taskHandle; + return taskHandle; } int32_t Thread::getReturnCode() const { - assert(data.state == State::Stopped); - return data.callbackResult; + assert(state == State::Stopped); + return callbackResult; } -ThreadId thread_get_current_id() { - return xTaskGetCurrentTaskHandle(); +Thread* Thread::getCurrent() { + return static_cast(pvTaskGetThreadLocalStoragePointer(nullptr, 0)); } -Thread* thread_get_current() { - auto* thread = static_cast(pvTaskGetThreadLocalStoragePointer(nullptr, 0)); - return thread; -} - -void thread_set_current_priority(Thread::Priority priority) { - vTaskPrioritySet(nullptr, static_cast(priority)); -} - -Thread::Priority thread_get_current_priority() { - return (Thread::Priority)uxTaskPriorityGet(nullptr); -} - -void thread_yield() { - assert(!TT_IS_IRQ_MODE()); - taskYIELD(); -} - -uint32_t thread_flags_set(ThreadId thread_id, uint32_t flags) { - auto hTask = (TaskHandle_t)thread_id; +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)TtStatusErrorParameter; + rflags = (uint32_t)EventFlag::ErrorParameter; } else { - rflags = (uint32_t)TtStatusError; + rflags = (uint32_t)EventFlag::Error; if (TT_IS_IRQ_MODE()) { yield = pdFALSE; @@ -264,14 +224,14 @@ uint32_t thread_flags_set(ThreadId thread_id, uint32_t flags) { return (rflags); } -uint32_t thread_flags_clear(uint32_t flags) { +uint32_t Thread::clearFlags(uint32_t flags) { TaskHandle_t hTask; uint32_t rflags, cflags; if (TT_IS_IRQ_MODE()) { - rflags = (uint32_t)TtStatusErrorISR; + rflags = (uint32_t)EventFlag::ErrorISR; } else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) { - rflags = (uint32_t)TtStatusErrorParameter; + rflags = (uint32_t)EventFlag::ErrorParameter; } else { hTask = xTaskGetCurrentTaskHandle(); @@ -282,10 +242,10 @@ uint32_t thread_flags_clear(uint32_t flags) { if (xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, cflags, eSetValueWithOverwrite) != pdPASS) { - rflags = (uint32_t)TtStatusError; + rflags = (uint32_t)EventFlag::Error; } } else { - rflags = (uint32_t)TtStatusError; + rflags = (uint32_t)EventFlag::Error; } } @@ -293,36 +253,36 @@ uint32_t thread_flags_clear(uint32_t flags) { return (rflags); } -uint32_t thread_flags_get() { +uint32_t Thread::getFlags() { TaskHandle_t hTask; uint32_t rflags; if (TT_IS_IRQ_MODE()) { - rflags = (uint32_t)TtStatusErrorISR; + rflags = (uint32_t)EventFlag::ErrorISR; } else { hTask = xTaskGetCurrentTaskHandle(); if (xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags) != pdPASS) { - rflags = (uint32_t)TtStatusError; + rflags = (uint32_t)EventFlag::Error; } } return (rflags); } -uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) { +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 (TT_IS_IRQ_MODE()) { - rflags = (uint32_t)TtStatusErrorISR; + rflags = (uint32_t)EventFlag::ErrorISR; } else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) { - rflags = (uint32_t)TtStatusErrorParameter; + rflags = (uint32_t)EventFlag::ErrorParameter; } else { - if ((options & TtFlagNoClear) == TtFlagNoClear) { + if ((options & EventFlag::NoClear) == EventFlag::NoClear) { clear = 0U; } else { clear = flags; @@ -339,12 +299,12 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) { rflags &= flags; rflags |= nval; - if ((options & TtFlagWaitAll) == TtFlagWaitAll) { + if ((options & EventFlag::WaitAll) == EventFlag::WaitAll) { if ((flags & rflags) == flags) { break; } else { if (timeout == 0U) { - rflags = (uint32_t)TtStatusErrorResource; + rflags = (uint32_t)EventFlag::ErrorResource; break; } } @@ -353,7 +313,7 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) { break; } else { if (timeout == 0U) { - rflags = (uint32_t)TtStatusErrorResource; + rflags = (uint32_t)EventFlag::ErrorResource; break; } } @@ -369,9 +329,9 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) { } } else { if (timeout == 0) { - rflags = (uint32_t)TtStatusErrorResource; + rflags = (uint32_t)EventFlag::ErrorResource; } else { - rflags = (uint32_t)TtStatusErrorTimeout; + rflags = (uint32_t)EventFlag::ErrorTimeout; } } } while (rval != pdFAIL); @@ -381,21 +341,8 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) { return (rflags); } -const char* thread_get_name(ThreadId thread_id) { - auto hTask = (TaskHandle_t)thread_id; - const char* name; - - if (TT_IS_IRQ_MODE() || (hTask == nullptr)) { - name = nullptr; - } else { - name = pcTaskGetName(hTask); - } - - return (name); -} - -uint32_t thread_get_stack_space(ThreadId thread_id) { - auto hTask = (TaskHandle_t)thread_id; +uint32_t Thread::getStackSpace(ThreadId threadId) { + auto hTask = (TaskHandle_t)threadId; uint32_t sz; if (TT_IS_IRQ_MODE() || (hTask == nullptr)) { @@ -407,13 +354,13 @@ uint32_t thread_get_stack_space(ThreadId thread_id) { return (sz); } -void thread_suspend(ThreadId thread_id) { - auto hTask = (TaskHandle_t)thread_id; +void Thread::suspend(ThreadId threadId) { + auto hTask = (TaskHandle_t)threadId; vTaskSuspend(hTask); } -void thread_resume(ThreadId thread_id) { - auto hTask = (TaskHandle_t)thread_id; +void Thread::resume(ThreadId threadId) { + auto hTask = (TaskHandle_t)threadId; if (TT_IS_IRQ_MODE()) { xTaskResumeFromISR(hTask); } else { @@ -421,8 +368,8 @@ void thread_resume(ThreadId thread_id) { } } -bool thread_is_suspended(ThreadId thread_id) { - auto hTask = (TaskHandle_t)thread_id; +bool Thread::isSuspended(ThreadId threadId) { + auto hTask = (TaskHandle_t)threadId; return eTaskGetState(hTask) == eSuspended; } diff --git a/TactilityCore/Source/Thread.h b/TactilityCore/Source/Thread.h index aa07fa3c..5473614c 100644 --- a/TactilityCore/Source/Thread.h +++ b/TactilityCore/Source/Thread.h @@ -1,11 +1,9 @@ #pragma once #include "CoreDefines.h" -#include "CoreTypes.h" -#include -#include #include +#include #include "RtosCompatTask.h" @@ -14,6 +12,7 @@ namespace tt { typedef TaskHandle_t ThreadId; class Thread { + public: enum class State{ @@ -34,6 +33,7 @@ public: Critical = 7U }; + /** ThreadCallback Your callback to run in new thread * @warning never use osThreadExit in Thread */ @@ -51,22 +51,27 @@ public: */ typedef void (*StateCallback)(State state, void* context); - typedef struct { - Thread* thread; - TaskHandle_t taskHandle; - State state; - Callback callback; - void* callbackContext; - int32_t callbackResult; - StateCallback stateCallback; - void* stateCallbackContext; - std::string name; - Priority priority; - configSTACK_DEPTH_TYPE stackSize; - portBASE_TYPE affinity; - } Data; +private: - Thread(); + static void mainBody(void* context); + + TaskHandle_t taskHandle = nullptr; + State state = State::Stopped; + Callback callback = nullptr; + void* callbackContext = nullptr; + int32_t callbackResult = 0; + StateCallback stateCallback = nullptr; + void* stateCallbackContext = nullptr; + std::string name = {}; + Priority priority = Priority::Normal; + configSTACK_DEPTH_TYPE stackSize = 0; + portBASE_TYPE affinity = -1; + + void setState(Thread::State state); + +public: + + Thread() = default; /** Allocate Thread, shortcut version * @param[in] name the name of the thread @@ -76,7 +81,7 @@ public: * @param[in] affinity Which CPU core to pin this task to, -1 means unpinned (only works on ESP32) */ Thread( - const std::string& name, + std::string name, configSTACK_DEPTH_TYPE stackSize, Callback callback, _Nullable void* callbackContext, @@ -88,7 +93,7 @@ public: /** Set Thread name * @param[in] name string */ - void setName(const std::string& name); + void setName(std::string name); /** Set Thread stack size * @param[in] stackSize stack size in bytes @@ -140,9 +145,39 @@ public: */ int32_t getReturnCode() const; -private: + /** Suspend thread + * @param[in] threadId thread id + */ + static void suspend(ThreadId threadId); - Data data; + /** Resume thread + * @param[in] threadId thread id + */ + static void resume(ThreadId threadId); + + /** Get thread suspended state + * @param[in] threadId thread id + * @return true if thread is suspended + */ + static bool isSuspended(ThreadId threadId); + + /** + * @brief Get thread stack watermark + * @param[in] threadId + * @return uint32_t + */ + static uint32_t getStackSpace(ThreadId threadId); + + /** @return pointer to Thread instance or nullptr if this thread doesn't belong to Tactility */ + static Thread* getCurrent(); + + static uint32_t setFlags(ThreadId threadId, uint32_t flags); + + static uint32_t clearFlags(uint32_t flags); + + static uint32_t getFlags(); + + static uint32_t awaitFlags(uint32_t flags, uint32_t options, uint32_t timeout); }; #define THREAD_PRIORITY_APP Thread::PriorityNormal @@ -150,59 +185,4 @@ private: #define THREAD_PRIORITY_RENDER Thread::Priority::Higher #define THREAD_PRIORITY_ISR Thread::Priority::Critical -/** Set current thread priority - * @param[in] priority ThreadPriority value - */ -void thread_set_current_priority(Thread::Priority priority); - -/** @return ThreadPriority value */ -Thread::Priority thread_get_current_priority(); - -/** @return FreeRTOS ThreadId or NULL */ -ThreadId thread_get_current_id(); - -/** @return pointer to Thread instance or NULL if this thread doesn't belongs to Tactility */ -Thread* thread_get_current(); - -/** Return control to scheduler */ -void thread_yield(); - -uint32_t thread_flags_set(ThreadId thread_id, uint32_t flags); - -uint32_t thread_flags_clear(uint32_t flags); - -uint32_t thread_flags_get(); - -uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout); - -/** - * @brief Get thread name - * @param[in] threadId - * @return const char* name or NULL - */ -const char* thread_get_name(ThreadId threadId); - -/** - * @brief Get thread stack watermark - * @param[in] threadId - * @return uint32_t - */ -uint32_t thread_get_stack_space(ThreadId threadId); - -/** Suspend thread - * @param[in] threadId thread id - */ -void thread_suspend(ThreadId threadId); - -/** Resume thread - * @param[in] threadId thread id - */ -void thread_resume(ThreadId threadId); - -/** Get thread suspended state - * @param[in] threadId thread id - * @return true if thread is suspended - */ -bool thread_is_suspended(ThreadId threadId); - } // namespace diff --git a/TactilityCore/Source/Timer.cpp b/TactilityCore/Source/Timer.cpp index 997a8b2e..1a6dba2d 100644 --- a/TactilityCore/Source/Timer.cpp +++ b/TactilityCore/Source/Timer.cpp @@ -6,7 +6,7 @@ namespace tt { -static void timer_callback(TimerHandle_t hTimer) { +void Timer::onCallback(TimerHandle_t hTimer) { auto* timer = static_cast(pvTimerGetTimerID(hTimer)); if (timer != nullptr) { @@ -14,54 +14,59 @@ static void timer_callback(TimerHandle_t hTimer) { } } -Timer::Timer(Type type, Callback callback, std::shared_ptr callbackContext) { - assert((!TT_IS_ISR()) && (callback != nullptr)); - - this->callback = callback; - this->callbackContext = std::move(callbackContext); +static inline TimerHandle_t createTimer(Timer::Type type, void* timerId, TimerCallbackFunction_t callback) { + assert(timerId != nullptr); + assert(callback != nullptr); UBaseType_t reload; - if (type == Type::Once) { + if (type == Timer::Type::Once) { reload = pdFALSE; } else { reload = pdTRUE; } - this->timerHandle = xTimerCreate(nullptr, portMAX_DELAY, (BaseType_t)reload, this, timer_callback); - assert(this->timerHandle); + return xTimerCreate(nullptr, portMAX_DELAY, (BaseType_t)reload, timerId, callback); +} + +Timer::Timer(Type type, Callback callback, std::shared_ptr callbackContext) : + callback(callback), + callbackContext(std::move(callbackContext)), + handle(createTimer(type, this, onCallback)) +{ + assert(!TT_IS_ISR()); + assert(handle != nullptr); } Timer::~Timer() { assert(!TT_IS_ISR()); - tt_check(xTimerDelete(timerHandle, portMAX_DELAY) == pdPASS); } bool Timer::start(TickType_t interval) { assert(!TT_IS_ISR()); assert(interval < portMAX_DELAY); - return xTimerChangePeriod(timerHandle, interval, portMAX_DELAY) == pdPASS; + return xTimerChangePeriod(handle.get(), interval, portMAX_DELAY) == pdPASS; } bool Timer::restart(TickType_t interval) { assert(!TT_IS_ISR()); assert(interval < portMAX_DELAY); - return xTimerChangePeriod(timerHandle, interval, portMAX_DELAY) == pdPASS && - xTimerReset(timerHandle, portMAX_DELAY) == pdPASS; + return xTimerChangePeriod(handle.get(), interval, portMAX_DELAY) == pdPASS && + xTimerReset(handle.get(), portMAX_DELAY) == pdPASS; } bool Timer::stop() { assert(!TT_IS_ISR()); - return xTimerStop(timerHandle, portMAX_DELAY) == pdPASS; + return xTimerStop(handle.get(), portMAX_DELAY) == pdPASS; } bool Timer::isRunning() { assert(!TT_IS_ISR()); - return xTimerIsTimerActive(timerHandle) == pdTRUE; + return xTimerIsTimerActive(handle.get()) == pdTRUE; } TickType_t Timer::getExpireTime() { assert(!TT_IS_ISR()); - return xTimerGetExpiryTime(timerHandle); + return xTimerGetExpiryTime(handle.get()); } bool Timer::setPendingCallback(PendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeout) { diff --git a/TactilityCore/Source/Timer.h b/TactilityCore/Source/Timer.h index e1429a47..fddd1866 100644 --- a/TactilityCore/Source/Timer.h +++ b/TactilityCore/Source/Timer.h @@ -1,7 +1,5 @@ #pragma once -#include "CoreTypes.h" - #include "RtosCompatTimers.h" #include "Thread.h" #include @@ -9,15 +7,27 @@ namespace tt { class Timer { -private: - TimerHandle_t timerHandle; + public: typedef void (*Callback)(std::shared_ptr context); typedef void (*PendingCallback)(void* context, uint32_t arg); +private: + + struct TimerHandleDeleter { + void operator()(TimerHandle_t handleToDelete) { + xTimerDelete(handleToDelete, portMAX_DELAY); + } + }; + Callback callback; std::shared_ptr callbackContext; + std::unique_ptr, TimerHandleDeleter> handle; + + static void onCallback(TimerHandle_t hTimer); + +public: enum class Type { Once = 0, ///< One-shot timer. diff --git a/TactilityCore/Source/kernel/Kernel.cpp b/TactilityCore/Source/kernel/Kernel.cpp index 75180237..b6a244bd 100644 --- a/TactilityCore/Source/kernel/Kernel.cpp +++ b/TactilityCore/Source/kernel/Kernel.cpp @@ -1,12 +1,11 @@ #include "kernel/Kernel.h" -#include "Check.h" #include "CoreDefines.h" -#include "CoreTypes.h" #include "RtosCompatTask.h" #ifdef ESP_PLATFORM #include "rom/ets_sys.h" #else +#include #include #endif @@ -16,90 +15,72 @@ bool isRunning() { return xTaskGetSchedulerState() != taskSCHEDULER_RUNNING; } -int32_t lock() { +bool lock() { assert(!TT_IS_ISR()); int32_t lock; switch (xTaskGetSchedulerState()) { + // Already suspended case taskSCHEDULER_SUSPENDED: - lock = 1; - break; + return true; case taskSCHEDULER_RUNNING: vTaskSuspendAll(); - lock = 0; - break; + return true; case taskSCHEDULER_NOT_STARTED: default: - lock = (int32_t)TtStatusError; - break; + return false; } /* Return previous lock state */ return (lock); } -int32_t unlock() { +bool unlock() { assert(!TT_IS_ISR()); - int32_t lock; - switch (xTaskGetSchedulerState()) { case taskSCHEDULER_SUSPENDED: - lock = 1; - if (xTaskResumeAll() != pdTRUE) { if (xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) { - lock = (int32_t)TtStatusError; + return false; } } - break; + return true; case taskSCHEDULER_RUNNING: - lock = 0; - break; + return true; case taskSCHEDULER_NOT_STARTED: default: - lock = (int32_t)TtStatusError; - break; + return false; } - - /* Return previous lock state */ - return (lock); } -int32_t restoreLock(int32_t lock) { +bool restoreLock(bool lock) { assert(!TT_IS_ISR()); switch (xTaskGetSchedulerState()) { case taskSCHEDULER_SUSPENDED: case taskSCHEDULER_RUNNING: - if (lock == 1) { + if (lock) { vTaskSuspendAll(); + return true; } else { - if (lock != 0) { - lock = (int32_t)TtStatusError; - } else { - if (xTaskResumeAll() != pdTRUE) { - if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) { - lock = (int32_t)TtStatusError; - } + if (xTaskResumeAll() != pdTRUE) { + if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) { + return false; } } + return true; } - break; case taskSCHEDULER_NOT_STARTED: default: - lock = (int32_t)TtStatusError; - break; + return false; } - - /* Return new lock state */ - return (lock); } uint32_t getTickFrequency() { @@ -116,13 +97,11 @@ void delayTicks(TickType_t ticks) { } } -TtStatus delayUntilTick(TickType_t tick) { +bool delayUntilTick(TickType_t tick) { assert(!TT_IS_ISR()); TickType_t tcnt, delay; - TtStatus stat; - stat = TtStatusOk; tcnt = xTaskGetTickCount(); /* Determine remaining number of tick to delay */ @@ -130,29 +109,20 @@ TtStatus delayUntilTick(TickType_t tick) { /* Check if target tick has not expired */ if ((delay != 0U) && (0 == (delay >> (8 * sizeof(TickType_t) - 1)))) { - if (xTaskDelayUntil(&tcnt, delay) == pdFALSE) { - /* Did not delay */ - stat = TtStatusError; + if (xTaskDelayUntil(&tcnt, delay) == pdPASS) { + return true; } - } else { - /* No delay or already expired */ - stat = TtStatusErrorParameter; } - /* Return execution status */ - return (stat); + return false; } TickType_t getTicks() { - TickType_t ticks; - if (TT_IS_ISR() != 0U) { - ticks = xTaskGetTickCountFromISR(); + return xTaskGetTickCountFromISR(); } else { - ticks = xTaskGetTickCount(); + return xTaskGetTickCount(); } - - return ticks; } TickType_t millisToTicks(uint32_t milliseconds) { diff --git a/TactilityCore/Source/kernel/Kernel.h b/TactilityCore/Source/kernel/Kernel.h index 016435e6..35e61319 100644 --- a/TactilityCore/Source/kernel/Kernel.h +++ b/TactilityCore/Source/kernel/Kernel.h @@ -1,7 +1,5 @@ #pragma once -#include "CoreTypes.h" - #ifdef ESP_PLATFORM #include "freertos/FreeRTOS.h" #else @@ -23,22 +21,22 @@ bool isRunning(); /** Lock kernel, pause process scheduling * @warning don't call from ISR context - * @return previous lock state(0 - unlocked, 1 - locked) + * @return true on success */ -int32_t lock(); +bool lock(); /** Unlock kernel, resume process scheduling * @warning don't call from ISR context - * @return previous lock state(0 - unlocked, 1 - locked) + * @return true on success */ -int32_t unlock(); +bool unlock(); /** Restore kernel lock state * @warning don't call from ISR context * @param[in] lock The lock state - * @return new lock state or error + * @return true on success */ -int32_t restoreLock(int32_t lock); +bool restoreLock(bool lock); /** Get kernel systick frequency * @return systick counts per second @@ -57,9 +55,9 @@ void delayTicks(TickType_t ticks); /** Delay until tick * @warning don't call from ISR context * @param[in] ticks The tick until which kerel should delay task execution - * @return the status + * @return true on success */ -TtStatus delayUntilTick(TickType_t tick); +bool delayUntilTick(TickType_t tick); /** Convert milliseconds to ticks * diff --git a/TactilityHeadless/Source/hal/i2c/I2c.h b/TactilityHeadless/Source/hal/i2c/I2c.h index 097a7c65..5b59f54e 100644 --- a/TactilityHeadless/Source/hal/i2c/I2c.h +++ b/TactilityHeadless/Source/hal/i2c/I2c.h @@ -1,7 +1,6 @@ #pragma once #include "I2cCompat.h" -#include "CoreTypes.h" #include "RtosCompat.h" #include #include @@ -29,10 +28,10 @@ struct Configuration { i2c_config_t config; }; -enum Status { - STARTED, - STOPPED, - UNKNOWN +enum class Status { + Started, + Stopped, + Unknown }; bool init(const std::vector& configurations); diff --git a/TactilityHeadless/Source/hal/i2c/I2cDevice.cpp b/TactilityHeadless/Source/hal/i2c/I2cDevice.cpp index 36351f30..df4fab74 100644 --- a/TactilityHeadless/Source/hal/i2c/I2cDevice.cpp +++ b/TactilityHeadless/Source/hal/i2c/I2cDevice.cpp @@ -1,4 +1,5 @@ #include "I2cDevice.h" +#include bool I2cDevice::readRegister12(uint8_t reg, float& out) const { std::uint8_t data[2] = {0}; diff --git a/TactilityHeadless/Source/service/ServiceRegistry.cpp b/TactilityHeadless/Source/service/ServiceRegistry.cpp index 93d28efe..0e613630 100644 --- a/TactilityHeadless/Source/service/ServiceRegistry.cpp +++ b/TactilityHeadless/Source/service/ServiceRegistry.cpp @@ -25,7 +25,7 @@ void addService(std::shared_ptr manifest, bool autoStart) TT_LOG_I(TAG, "Adding %s", id.c_str()); - manifest_mutex.lock(TtWaitForever); + manifest_mutex.lock(); if (service_manifest_map[id] == nullptr) { service_manifest_map[id] = std::move(manifest); } else { @@ -43,18 +43,18 @@ void addService(const ServiceManifest& manifest, bool autoStart) { } std::shared_ptr _Nullable findManifestId(const std::string& id) { - manifest_mutex.acquire(TtWaitForever); + manifest_mutex.lock(); auto iterator = service_manifest_map.find(id); auto manifest = iterator != service_manifest_map.end() ? iterator->second : nullptr; - manifest_mutex.release(); + manifest_mutex.unlock(); return manifest; } static std::shared_ptr _Nullable findServiceInstanceById(const std::string& id) { - manifest_mutex.acquire(TtWaitForever); + manifest_mutex.lock(); auto iterator = service_instance_map.find(id); auto service = iterator != service_instance_map.end() ? iterator->second : nullptr; - manifest_mutex.release(); + manifest_mutex.unlock(); return service; } @@ -70,9 +70,9 @@ bool startService(const std::string& id) { auto service_instance = std::make_shared(manifest); // Register first, so that a service can retrieve itself during onStart() - instance_mutex.acquire(TtWaitForever); + instance_mutex.lock(); service_instance_map[manifest->id] = service_instance; - instance_mutex.release(); + instance_mutex.unlock(); service_instance->getService()->onStart(*service_instance); @@ -100,9 +100,9 @@ bool stopService(const std::string& id) { service_instance->getService()->onStop(*service_instance); - instance_mutex.acquire(TtWaitForever); + instance_mutex.lock(); service_instance_map.erase(id); - instance_mutex.release(); + instance_mutex.unlock(); if (service_instance.use_count() > 1) { TT_LOG_W(TAG, "Possible memory leak: service %s still has %ld references", service_instance->getManifest().id.c_str(), service_instance.use_count() - 1); diff --git a/TactilityHeadless/Source/service/sdcard/Sdcard.cpp b/TactilityHeadless/Source/service/sdcard/Sdcard.cpp index 59c6e72f..26a34ce0 100644 --- a/TactilityHeadless/Source/service/sdcard/Sdcard.cpp +++ b/TactilityHeadless/Source/service/sdcard/Sdcard.cpp @@ -20,11 +20,11 @@ private: hal::SdCard::State lastState = hal::SdCard::State::Unmounted; bool lock(TickType_t timeout) const { - return mutex.acquire(timeout) == TtStatusOk; + return mutex.lock(timeout); } void unlock() const { - tt_check(mutex.release() == TtStatusOk); + mutex.unlock(); } void update() { diff --git a/TactilityHeadless/Source/service/wifi/Wifi.h b/TactilityHeadless/Source/service/wifi/Wifi.h index 2ffa65b4..8b4831af 100644 --- a/TactilityHeadless/Source/service/wifi/Wifi.h +++ b/TactilityHeadless/Source/service/wifi/Wifi.h @@ -1,6 +1,6 @@ #pragma once -#include "Pubsub.h" +#include "PubSub.h" #include "WifiGlobals.h" #include "WifiSettings.h" #include diff --git a/TactilityHeadless/Source/service/wifi/WifiEsp.cpp b/TactilityHeadless/Source/service/wifi/WifiEsp.cpp index a5f79dcf..3b5615d2 100644 --- a/TactilityHeadless/Source/service/wifi/WifiEsp.cpp +++ b/TactilityHeadless/Source/service/wifi/WifiEsp.cpp @@ -73,53 +73,53 @@ public: RadioState getRadioState() const { auto lockable = dataMutex.scoped(); - lockable->lock(TtWaitForever); + lockable->lock(); // TODO: Handle lock failure return radio_state; } void setRadioState(RadioState newState) { auto lockable = dataMutex.scoped(); - lockable->lock(TtWaitForever); + lockable->lock(); // TODO: Handle lock failure radio_state = newState; } bool isScanning() const { auto lockable = dataMutex.scoped(); - lockable->lock(TtWaitForever); + lockable->lock(); // TODO: Handle lock failure return scan_active; } void setScanning(bool newState) { auto lockable = dataMutex.scoped(); - lockable->lock(TtWaitForever); + lockable->lock(); // TODO: Handle lock failure scan_active = newState; } bool isScanActive() const { auto lcokable = dataMutex.scoped(); - lcokable->lock(TtWaitForever); + lcokable->lock(); return scan_active; } void setScanActive(bool newState) { auto lockable = dataMutex.scoped(); - lockable->lock(TtWaitForever); + lockable->lock(); scan_active = newState; } bool isSecureConnection() const { auto lockable = dataMutex.scoped(); - lockable->lock(TtWaitForever); + lockable->lock(); return secure_connection; } void setSecureConnection(bool newState) { auto lockable = dataMutex.scoped(); - lockable->lock(TtWaitForever); + lockable->lock(); secure_connection = newState; } }; @@ -325,7 +325,7 @@ int getRssi() { static void scan_list_alloc(std::shared_ptr wifi) { auto lockable = wifi->dataMutex.scoped(); - if (lockable->lock(TtWaitForever)) { + if (lockable->lock()) { assert(wifi->scan_list == nullptr); wifi->scan_list = static_cast(malloc(sizeof(wifi_ap_record_t) * wifi->scan_list_limit)); wifi->scan_list_count = 0; @@ -334,7 +334,7 @@ static void scan_list_alloc(std::shared_ptr wifi) { static void scan_list_alloc_safely(std::shared_ptr wifi) { auto lockable = wifi->dataMutex.scoped(); - if (lockable->lock(TtWaitForever)) { + if (lockable->lock()) { if (wifi->scan_list == nullptr) { scan_list_alloc(wifi); } @@ -343,7 +343,7 @@ static void scan_list_alloc_safely(std::shared_ptr wifi) { static void scan_list_free(std::shared_ptr wifi) { auto lockable = wifi->dataMutex.scoped(); - if (lockable->lock(TtWaitForever)) { + if (lockable->lock()) { assert(wifi->scan_list != nullptr); free(wifi->scan_list); wifi->scan_list = nullptr; @@ -353,7 +353,7 @@ static void scan_list_free(std::shared_ptr wifi) { static void scan_list_free_safely(std::shared_ptr wifi) { auto lockable = wifi->dataMutex.scoped(); - if (lockable->lock(TtWaitForever)) { + if (lockable->lock()) { if (wifi->scan_list != nullptr) { scan_list_free(wifi); } @@ -362,9 +362,9 @@ static void scan_list_free_safely(std::shared_ptr wifi) { static void publish_event_simple(std::shared_ptr wifi, EventType type) { auto lockable = wifi->dataMutex.scoped(); - if (lockable->lock(TtWaitForever)) { + if (lockable->lock()) { Event turning_on_event = {.type = type}; - tt_pubsub_publish(wifi->pubsub, &turning_on_event); + wifi->pubsub->publish(&turning_on_event); } } @@ -379,7 +379,7 @@ static bool copy_scan_list(std::shared_ptr wifi) { } auto lockable = wifi->dataMutex.scoped(); - if (!lockable->lock(TtWaitForever)) { + if (!lockable->lock()) { return false; } @@ -449,7 +449,7 @@ static void eventHandler(TT_UNUSED void* arg, esp_event_base_t event_base, int32 if (event_base == WIFI_EVENT) { TT_LOG_I(TAG, "eventHandler: WIFI_EVENT (%ld)", event_id); } else if (event_base == IP_EVENT) { - TT_LOG_W(TAG, "eventHandler: IP_EVENT (%ld)", event_id); + TT_LOG_I(TAG, "eventHandler: IP_EVENT (%ld)", event_id); } if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { @@ -967,15 +967,15 @@ public: wifi->autoConnectTimer = nullptr; // Must release as it holds a reference to this Wifi instance // Acquire all mutexes - wifi->dataMutex.acquire(TtWaitForever); - wifi->radioMutex.acquire(TtWaitForever); + wifi->dataMutex.lock(); + wifi->radioMutex.lock(); // Detach wifi_singleton = nullptr; // Release mutexes - wifi->dataMutex.release(); - wifi->radioMutex.release(); + wifi->dataMutex.unlock(); + wifi->radioMutex.unlock(); // Release (hopefully) last Wifi instance by scope } diff --git a/TactilityHeadless/Source/service/wifi/WifiMock.cpp b/TactilityHeadless/Source/service/wifi/WifiMock.cpp index 0a3edc0a..60badbf0 100644 --- a/TactilityHeadless/Source/service/wifi/WifiMock.cpp +++ b/TactilityHeadless/Source/service/wifi/WifiMock.cpp @@ -6,7 +6,7 @@ #include "Log.h" #include "MessageQueue.h" #include "Mutex.h" -#include "Pubsub.h" +#include "PubSub.h" #include "service/ServiceContext.h" namespace tt::service::wifi { @@ -36,7 +36,7 @@ static Wifi* wifi = nullptr; static void publish_event_simple(Wifi* wifi, EventType type) { Event turning_on_event = { .type = type }; - tt_pubsub_publish(wifi->pubsub, &turning_on_event); + wifi->pubsub->publish(&turning_on_event); } // endregion Static