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`
This commit is contained in:
Ken Van Hoeylandt 2025-01-25 17:29:11 +01:00 committed by GitHub
parent c2edbad0fb
commit 686f7cce83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
60 changed files with 711 additions and 831 deletions

View File

@ -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)

View File

@ -1,6 +1,8 @@
name: Build Firmware name: Build Firmware
on: on:
push: push:
branches:
- main
pull_request: pull_request:
types: [opened, synchronize, reopened] types: [opened, synchronize, reopened]

View File

@ -1,6 +1,8 @@
name: Build SDK name: Build SDK
on: on:
push: push:
branches:
- main
pull_request: pull_request:
types: [opened, synchronize, reopened] types: [opened, synchronize, reopened]

View File

@ -2,6 +2,8 @@
name: Build Simulator name: Build Simulator
on: on:
push: push:
branches:
- main
pull_request: pull_request:
types: [opened, synchronize, reopened] types: [opened, synchronize, reopened]
jobs: jobs:

View File

@ -1,6 +1,8 @@
name: Tests name: Tests
on: on:
push: push:
branches:
- main
pull_request: pull_request:
types: [opened, synchronize, reopened] types: [opened, synchronize, reopened]
jobs: jobs:

View File

@ -19,12 +19,12 @@ static bool task_running = false;
static void lvgl_task(void* arg); static void lvgl_task(void* arg);
static bool task_lock(int timeout_ticks) { static bool task_lock(TickType_t timeout) {
return task_mutex.acquire(timeout_ticks) == tt::TtStatusOk; return task_mutex.lock(timeout);
} }
static void task_unlock() { static void task_unlock() {
task_mutex.release(); task_mutex.unlock();
} }
static void task_set_running(bool running) { static void task_set_running(bool running) {
@ -41,15 +41,15 @@ bool lvgl_task_is_running() {
} }
static bool lvgl_lock(uint32_t timeoutMillis) { 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() { static void lvgl_unlock() {
lvgl_mutex.release(); lvgl_mutex.unlock();
} }
void lvgl_task_interrupt() { 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_set_running(false); // interrupt task with boolean as flag
task_unlock(); task_unlock();
} }

View File

@ -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 # 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. - Extend unPhone power driver: add charging status, usb connection status, etc.
- Expose app::Paths to TactilityC - 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!) - 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. - 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: - 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:

View File

@ -42,7 +42,7 @@ public:
bool setEntriesForPath(const std::string& path); bool setEntriesForPath(const std::string& path);
const std::vector<dirent>& lockEntries() const { const std::vector<dirent>& lockEntries() const {
mutex.lock(TtWaitForever); mutex.lock();
return dir_entries; return dir_entries;
} }

View File

@ -21,7 +21,7 @@ private:
.onConnectSsidContext = nullptr .onConnectSsidContext = nullptr
}; };
View view = View(&bindings, &state); View view = View(&bindings, &state);
PubSubSubscription* wifiSubscription; PubSub::SubscriptionHandle wifiSubscription;
bool view_enabled = false; bool view_enabled = false;
public: public:

View File

@ -12,7 +12,7 @@ class WifiManage : public App {
private: private:
PubSubSubscription* wifiSubscription = nullptr; PubSub::SubscriptionHandle wifiSubscription = nullptr;
Mutex mutex; Mutex mutex;
Bindings bindings = { }; Bindings bindings = { };
State state; State state;

View File

@ -2,7 +2,7 @@
#include "MessageQueue.h" #include "MessageQueue.h"
#include "Mutex.h" #include "Mutex.h"
#include "Pubsub.h" #include "PubSub.h"
#include "service/gui/Gui.h" #include "service/gui/Gui.h"
#include <cstdio> #include <cstdio>
#include <lvgl.h> #include <lvgl.h>
@ -19,7 +19,7 @@ struct Gui {
// Thread and lock // Thread and lock
Thread* thread = nullptr; Thread* thread = nullptr;
Mutex mutex = Mutex(Mutex::Type::Recursive); Mutex mutex = Mutex(Mutex::Type::Recursive);
PubSubSubscription* loader_pubsub_subscription = nullptr; PubSub::SubscriptionHandle loader_pubsub_subscription = nullptr;
// Layers and Canvas // Layers and Canvas
lv_obj_t* appRootWidget = nullptr; lv_obj_t* appRootWidget = nullptr;

View File

@ -1,16 +1,16 @@
#pragma once #pragma once
#include "app/AppManifest.h"
#include "app/AppInstance.h"
#include "EventFlag.h" #include "EventFlag.h"
#include "MessageQueue.h" #include "MessageQueue.h"
#include "Pubsub.h" #include "PubSub.h"
#include "Thread.h"
#include "service/loader/Loader.h"
#include "RtosCompatSemaphore.h" #include "RtosCompatSemaphore.h"
#include "Thread.h"
#include "app/AppInstance.h"
#include "app/AppManifest.h"
#include "service/loader/Loader.h"
#include <DispatcherThread.h>
#include <stack> #include <stack>
#include <utility> #include <utility>
#include <DispatcherThread.h>
namespace tt::service::loader { namespace tt::service::loader {

View File

@ -163,7 +163,7 @@ void run(const Configuration& config) {
TT_LOG_I(TAG, "Processing main dispatcher"); TT_LOG_I(TAG, "Processing main dispatcher");
while (true) { while (true) {
getMainDispatcher().consume(TtWaitForever); getMainDispatcher().consume();
} }
} }

View File

@ -6,15 +6,15 @@ namespace tt::app {
#define TAG "app" #define TAG "app"
void AppInstance::setState(State newState) { void AppInstance::setState(State newState) {
mutex.acquire(TtWaitForever); mutex.lock();
state = newState; state = newState;
mutex.release(); mutex.unlock();
} }
State AppInstance::getState() const { State AppInstance::getState() const {
mutex.acquire(TtWaitForever); mutex.lock();
auto result = state; auto result = state;
mutex.release(); mutex.unlock();
return result; return result;
} }
@ -30,22 +30,22 @@ const AppManifest& AppInstance::getManifest() const {
} }
Flags AppInstance::getFlags() const { Flags AppInstance::getFlags() const {
mutex.acquire(TtWaitForever); mutex.lock();
auto result = flags; auto result = flags;
mutex.release(); mutex.unlock();
return result; return result;
} }
void AppInstance::setFlags(Flags newFlags) { void AppInstance::setFlags(Flags newFlags) {
mutex.acquire(TtWaitForever); mutex.lock();
flags = newFlags; flags = newFlags;
mutex.release(); mutex.unlock();
} }
std::shared_ptr<const Bundle> AppInstance::getParameters() const { std::shared_ptr<const Bundle> AppInstance::getParameters() const {
mutex.acquire(TtWaitForever); mutex.lock();
std::shared_ptr<const Bundle> result = parameters; std::shared_ptr<const Bundle> result = parameters;
mutex.release(); mutex.unlock();
return result; return result;
} }

View File

@ -15,7 +15,7 @@ static Mutex hash_mutex(Mutex::Type::Normal);
void addApp(const AppManifest& manifest) { void addApp(const AppManifest& manifest) {
TT_LOG_I(TAG, "Registering manifest %s", manifest.id.c_str()); 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)) { if (!app_manifest_map.contains(manifest.id)) {
app_manifest_map[manifest.id] = std::make_shared<AppManifest>(manifest); app_manifest_map[manifest.id] = std::make_shared<AppManifest>(manifest);
@ -23,13 +23,13 @@ void addApp(const AppManifest& manifest) {
TT_LOG_E(TAG, "App id in use: %s", manifest.id.c_str()); TT_LOG_E(TAG, "App id in use: %s", manifest.id.c_str());
} }
hash_mutex.release(); hash_mutex.unlock();
} }
_Nullable std::shared_ptr<AppManifest> findAppById(const std::string& id) { _Nullable std::shared_ptr<AppManifest> findAppById(const std::string& id) {
hash_mutex.acquire(TtWaitForever); hash_mutex.lock();
auto result = app_manifest_map.find(id); auto result = app_manifest_map.find(id);
hash_mutex.release(); hash_mutex.unlock();
if (result != app_manifest_map.end()) { if (result != app_manifest_map.end()) {
return result->second; return result->second;
} else { } else {
@ -39,11 +39,11 @@ _Nullable std::shared_ptr<AppManifest> findAppById(const std::string& id) {
std::vector<std::shared_ptr<AppManifest>> getApps() { std::vector<std::shared_ptr<AppManifest>> getApps() {
std::vector<std::shared_ptr<AppManifest>> manifests; std::vector<std::shared_ptr<AppManifest>> manifests;
hash_mutex.acquire(TtWaitForever); hash_mutex.lock();
for (const auto& item: app_manifest_map) { for (const auto& item: app_manifest_map) {
manifests.push_back(item.second); manifests.push_back(item.second);
} }
hash_mutex.release(); hash_mutex.unlock();
return manifests; return manifests;
} }

View File

@ -26,11 +26,11 @@ private:
public: public:
void lock() const { void lock() const {
tt_check(mutex.acquire(1000) == TtStatusOk); tt_check(mutex.lock(1000));
} }
void unlock() const { void unlock() const {
tt_check(mutex.release() == TtStatusOk); tt_check(mutex.unlock());
} }
void onShow(AppContext& app, lv_obj_t* parent) override; void onShow(AppContext& app, lv_obj_t* parent) override;

View File

@ -115,12 +115,12 @@ void I2cScannerApp::onShow(AppContext& app, lv_obj_t* parent) {
void I2cScannerApp::onHide(AppContext& app) { void I2cScannerApp::onHide(AppContext& app) {
bool isRunning = false; bool isRunning = false;
if (mutex.acquire(250 / portTICK_PERIOD_MS) == TtStatusOk) { if (mutex.lock(250 / portTICK_PERIOD_MS)) {
auto* timer = scanTimer.get(); auto* timer = scanTimer.get();
if (timer != nullptr) { if (timer != nullptr) {
isRunning = timer->isRunning(); isRunning = timer->isRunning();
} }
mutex.release(); mutex.unlock();
} else { } else {
return; return;
} }
@ -158,9 +158,9 @@ void I2cScannerApp::onScanTimerCallback(TT_UNUSED std::shared_ptr<void> context)
// endregion Callbacks // endregion Callbacks
bool I2cScannerApp::getPort(i2c_port_t* outPort) { 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; *outPort = this->port;
assert(mutex.release() == TtStatusOk); mutex.unlock();
return true; return true;
} else { } else {
TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "getPort"); 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) { 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); scannedAddresses.push_back(address);
assert(mutex.release() == TtStatusOk); mutex.unlock();
return true; return true;
} else { } else {
TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "addAddressToList"); TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "addAddressToList");
@ -180,9 +180,9 @@ bool I2cScannerApp::addAddressToList(uint8_t address) {
} }
bool I2cScannerApp::shouldStopScanTimer() { bool I2cScannerApp::shouldStopScanTimer() {
if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { if (mutex.lock(100 / portTICK_PERIOD_MS)) {
bool is_scanning = scanState == ScanStateScanning; bool is_scanning = scanState == ScanStateScanning;
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
return !is_scanning; return !is_scanning;
} else { } else {
return true; return true;
@ -222,9 +222,9 @@ void I2cScannerApp::onScanTimer() {
bool I2cScannerApp::hasScanThread() { bool I2cScannerApp::hasScanThread() {
bool has_thread; bool has_thread;
if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { if (mutex.lock(100 / portTICK_PERIOD_MS)) {
has_thread = scanTimer != nullptr; has_thread = scanTimer != nullptr;
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
return has_thread; return has_thread;
} else { } else {
// Unsafe way // Unsafe way
@ -238,7 +238,7 @@ void I2cScannerApp::startScanning() {
stopScanning(); stopScanning();
} }
if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { if (mutex.lock(100 / portTICK_PERIOD_MS)) {
scannedAddresses.clear(); scannedAddresses.clear();
lv_obj_add_flag(scanListWidget, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(scanListWidget, LV_OBJ_FLAG_HIDDEN);
@ -250,16 +250,16 @@ void I2cScannerApp::startScanning() {
onScanTimerCallback onScanTimerCallback
); );
scanTimer->start(10); scanTimer->start(10);
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
} else { } else {
TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "startScanning"); TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "startScanning");
} }
} }
void I2cScannerApp::stopScanning() { void I2cScannerApp::stopScanning() {
if (mutex.acquire(250 / portTICK_PERIOD_MS) == TtStatusOk) { if (mutex.lock(250 / portTICK_PERIOD_MS)) {
assert(scanTimer != nullptr); assert(scanTimer != nullptr);
scanState = ScanStateStopped; scanState = ScanStateStopped;
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
} else { } else {
TT_LOG_E(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED); 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; auto i2c_devices = tt::getConfiguration()->hardware->i2c;
assert(selected < i2c_devices.size()); assert(selected < i2c_devices.size());
if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { if (mutex.lock(100 / portTICK_PERIOD_MS)) {
scannedAddresses.clear(); scannedAddresses.clear();
port = i2c_devices[selected].port; port = i2c_devices[selected].port;
scanState = ScanStateInitial; scanState = ScanStateInitial;
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
updateViews(); updateViews();
} }
@ -293,7 +293,7 @@ void I2cScannerApp::onPressScan(TT_UNUSED lv_event_t* event) {
} }
void I2cScannerApp::updateViews() { void I2cScannerApp::updateViews() {
if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { if (mutex.lock(100 / portTICK_PERIOD_MS)) {
if (scanState == ScanStateScanning) { if (scanState == ScanStateScanning) {
lv_label_set_text(scanButtonLabelWidget, STOP_SCAN_TEXT); lv_label_set_text(scanButtonLabelWidget, STOP_SCAN_TEXT);
lv_obj_remove_flag(portDropdownWidget, LV_OBJ_FLAG_CLICKABLE); 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); lv_obj_add_flag(scanListWidget, LV_OBJ_FLAG_HIDDEN);
} }
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
} else { } else {
TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "updateViews"); TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "updateViews");
} }
@ -333,12 +333,12 @@ void I2cScannerApp::updateViewsSafely() {
} }
void I2cScannerApp::onScanTimerFinished() { void I2cScannerApp::onScanTimerFinished() {
if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { if (mutex.lock(100 / portTICK_PERIOD_MS)) {
if (scanState == ScanStateScanning) { if (scanState == ScanStateScanning) {
scanState = ScanStateStopped; scanState = ScanStateStopped;
updateViewsSafely(); updateViewsSafely();
} }
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
} else { } else {
TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "onScanTimerFinished"); TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "onScanTimerFinished");
} }

View File

@ -1,47 +1,46 @@
#include "app/wificonnect/State.h" #include "app/wificonnect/State.h"
#include "Check.h"
#include <cstring> #include <cstring>
namespace tt::app::wificonnect { namespace tt::app::wificonnect {
void State::setConnectionError(bool error) { void State::setConnectionError(bool error) {
tt_check(lock.acquire(TtWaitForever) == TtStatusOk); lock.lock();
connectionError = error; connectionError = error;
tt_check(lock.release() == TtStatusOk); lock.unlock();
} }
bool State::hasConnectionError() const { bool State::hasConnectionError() const {
tt_check(lock.acquire(TtWaitForever) == TtStatusOk); lock.lock();
auto result = connectionError; auto result = connectionError;
tt_check(lock.release() == TtStatusOk); lock.unlock();
return result; return result;
} }
void State::setApSettings(const service::wifi::settings::WifiApSettings* newSettings) { 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)); memcpy(&this->apSettings, newSettings, sizeof(service::wifi::settings::WifiApSettings));
tt_check(lock.release() == TtStatusOk); lock.unlock();
} }
const service::wifi::settings::WifiApSettings& State::lockApSettings() { const service::wifi::settings::WifiApSettings& State::lockApSettings() {
tt_check(lock.acquire(TtWaitForever) == TtStatusOk); lock.lock();
return apSettings; return apSettings;
} }
void State::unlockApSettings() { void State::unlockApSettings() {
tt_check(lock.release() == TtStatusOk); lock.unlock();
} }
void State::setConnecting(bool isConnecting) { void State::setConnecting(bool isConnecting) {
tt_check(lock.acquire(TtWaitForever) == TtStatusOk); lock.lock();
connecting = isConnecting; connecting = isConnecting;
tt_check(lock.release() == TtStatusOk); lock.unlock();
} }
bool State::isConnecting() const { bool State::isConnecting() const {
tt_check(lock.acquire(TtWaitForever) == TtStatusOk); lock.lock();
auto result = connecting; auto result = connecting;
tt_check(lock.release() == TtStatusOk); lock.unlock();
return result; return result;
} }

View File

@ -56,8 +56,7 @@ static void onConnect(const service::wifi::settings::WifiApSettings* ap_settings
} }
WifiConnect::WifiConnect() { WifiConnect::WifiConnect() {
auto wifi_pubsub = service::wifi::getPubsub(); wifiSubscription = service::wifi::getPubsub()->subscribe(&eventCallback, this);
wifiSubscription = tt_pubsub_subscribe(wifi_pubsub, &eventCallback, this);
bindings = (Bindings) { bindings = (Bindings) {
.onConnectSsid = onConnect, .onConnectSsid = onConnect,
.onConnectSsidContext = this, .onConnectSsidContext = this,
@ -65,16 +64,15 @@ WifiConnect::WifiConnect() {
} }
WifiConnect::~WifiConnect() { WifiConnect::~WifiConnect() {
auto pubsub = service::wifi::getPubsub(); service::wifi::getPubsub()->unsubscribe(wifiSubscription);
tt_pubsub_unsubscribe(pubsub, wifiSubscription);
} }
void WifiConnect::lock() { void WifiConnect::lock() {
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); mutex.lock();
} }
void WifiConnect::unlock() { void WifiConnect::unlock() {
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
} }
void WifiConnect::requestViewUpdate() { void WifiConnect::requestViewUpdate() {

View File

@ -1,63 +1,61 @@
#include <Check.h>
#include "app/wifimanage/WifiManagePrivate.h" #include "app/wifimanage/WifiManagePrivate.h"
namespace tt::app::wifimanage { namespace tt::app::wifimanage {
void State::setScanning(bool isScanning) { void State::setScanning(bool isScanning) {
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); mutex.lock();
scanning = isScanning; scanning = isScanning;
scannedAfterRadioOn |= isScanning; scannedAfterRadioOn |= isScanning;
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
} }
void State::setRadioState(service::wifi::RadioState state) { void State::setRadioState(service::wifi::RadioState state) {
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); mutex.lock();
radioState = state; radioState = state;
if (radioState == service::wifi::RadioState::Off) { if (radioState == service::wifi::RadioState::Off) {
scannedAfterRadioOn = false; scannedAfterRadioOn = false;
} }
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
} }
service::wifi::RadioState State::getRadioState() const { service::wifi::RadioState State::getRadioState() const {
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); mutex.lock();
auto result = radioState; auto result = radioState;
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
return result; return result;
} }
bool State::isScanning() const { bool State::isScanning() const {
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); mutex.lock();
bool result = scanning; bool result = scanning;
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
return result; return result;
} }
const std::vector<service::wifi::ApRecord>& State::lockApRecords() const { const std::vector<service::wifi::ApRecord>& State::lockApRecords() const {
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); mutex.lock();
return apRecords; return apRecords;
} }
void State::unlockApRecords() const { void State::unlockApRecords() const {
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
} }
void State::updateApRecords() { void State::updateApRecords() {
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); mutex.lock();
apRecords = service::wifi::getScanResults(); apRecords = service::wifi::getScanResults();
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
} }
void State::setConnectSsid(const std::string& ssid) { void State::setConnectSsid(const std::string& ssid) {
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); mutex.lock();
connectSsid = ssid; connectSsid = ssid;
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
} }
std::string State::getConnectSsid() const { std::string State::getConnectSsid() const {
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); mutex.lock();
auto result = connectSsid; auto result = connectSsid;
tt_check(mutex.release() == TtStatusOk);
return result; return result;
} }

View File

@ -64,11 +64,11 @@ WifiManage::WifiManage() {
} }
void WifiManage::lock() { void WifiManage::lock() {
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); mutex.lock();
} }
void WifiManage::unlock() { void WifiManage::unlock() {
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
} }
void WifiManage::requestViewUpdate() { void WifiManage::requestViewUpdate() {
@ -111,8 +111,7 @@ static void wifiManageEventCallback(const void* message, void* context) {
} }
void WifiManage::onShow(AppContext& app, lv_obj_t* parent) { void WifiManage::onShow(AppContext& app, lv_obj_t* parent) {
auto wifi_pubsub = service::wifi::getPubsub(); wifiSubscription = service::wifi::getPubsub()->subscribe(&wifiManageEventCallback, this);
wifiSubscription = tt_pubsub_subscribe(wifi_pubsub, &wifiManageEventCallback, this);
// State update (it has its own locking) // State update (it has its own locking)
state.setRadioState(service::wifi::getRadioState()); 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) { void WifiManage::onHide(TT_UNUSED AppContext& app) {
lock(); lock();
auto wifi_pubsub = service::wifi::getPubsub(); service::wifi::getPubsub()->unsubscribe(wifiSubscription);
tt_pubsub_unsubscribe(wifi_pubsub, wifiSubscription);
wifiSubscription = nullptr; wifiSubscription = nullptr;
isViewEnabled = false; isViewEnabled = false;
unlock(); unlock();

View File

@ -6,11 +6,11 @@ namespace tt::lvgl {
static Mutex lockMutex; static Mutex lockMutex;
static bool defaultLock(uint32_t timeoutMillis) { static bool defaultLock(uint32_t timeoutMillis) {
return lockMutex.acquire(timeoutMillis) == TtStatusOk; return lockMutex.lock(timeoutMillis);
} }
static void defaultUnlock() { static void defaultUnlock() {
lockMutex.release(); lockMutex.unlock();
} }
static LvglLock lock_singleton = defaultLock; static LvglLock lock_singleton = defaultLock;

View File

@ -4,7 +4,7 @@
#include "Statusbar.h" #include "Statusbar.h"
#include "Mutex.h" #include "Mutex.h"
#include "Pubsub.h" #include "PubSub.h"
#include "TactilityCore.h" #include "TactilityCore.h"
#include "lvgl/Style.h" #include "lvgl/Style.h"
@ -43,10 +43,10 @@ typedef struct {
lv_obj_t* time; lv_obj_t* time;
lv_obj_t* icons[STATUSBAR_ICON_LIMIT]; lv_obj_t* icons[STATUSBAR_ICON_LIMIT];
lv_obj_t* battery_icon; lv_obj_t* battery_icon;
PubSubSubscription* pubsub_subscription; PubSub::SubscriptionHandle pubsub_subscription;
} Statusbar; } Statusbar;
static bool statusbar_lock(uint32_t timeoutTicks) { static bool statusbar_lock(uint32_t timeoutTicks = portMAX_DELAY) {
return statusbar_data.mutex.lock(timeoutTicks); return statusbar_data.mutex.lock(timeoutTicks);
} }
@ -83,7 +83,7 @@ static void onUpdateTime(TT_UNUSED std::shared_ptr<void> context) {
statusbar_data.time_update_timer->start(getNextUpdateTime()); statusbar_data.time_update_timer->start(getNextUpdateTime());
// Notify widget // Notify widget
tt_pubsub_publish(statusbar_data.pubsub, nullptr); statusbar_data.pubsub->publish(nullptr);
} else { } else {
statusbar_data.time_update_timer->start(pdMS_TO_TICKS(60000U)); 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_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
LV_TRACE_OBJ_CREATE("finished"); LV_TRACE_OBJ_CREATE("finished");
auto* statusbar = (Statusbar*)obj; 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()) { if (!statusbar_data.time_update_timer->isRunning()) {
statusbar_data.time_update_timer->start(50 / portTICK_PERIOD_MS); 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) { static void statusbar_destructor(TT_UNUSED const lv_obj_class_t* class_p, lv_obj_t* obj) {
auto* statusbar = (Statusbar*)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) { 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); obj_set_style_bg_invisible(left_spacer);
lv_obj_set_flex_grow(left_spacer, 1); lv_obj_set_flex_grow(left_spacer, 1);
statusbar_lock(TtWaitForever); statusbar_lock(portMAX_DELAY);
for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) { for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) {
auto* image = lv_image_create(obj); auto* image = lv_image_create(obj);
lv_obj_set_size(image, STATUSBAR_ICON_SIZE, STATUSBAR_ICON_SIZE); 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) { int8_t statusbar_icon_add(const std::string& image) {
statusbar_lock(TtWaitForever); statusbar_lock();
int8_t result = -1; int8_t result = -1;
for (int8_t i = 0; i < STATUSBAR_ICON_LIMIT; ++i) { for (int8_t i = 0; i < STATUSBAR_ICON_LIMIT; ++i) {
if (!statusbar_data.icons[i].claimed) { if (!statusbar_data.icons[i].claimed) {
@ -245,7 +245,7 @@ int8_t statusbar_icon_add(const std::string& image) {
break; break;
} }
} }
tt_pubsub_publish(statusbar_data.pubsub, nullptr); statusbar_data.pubsub->publish(nullptr);
statusbar_unlock(); statusbar_unlock();
return result; return result;
} }
@ -257,12 +257,12 @@ int8_t statusbar_icon_add() {
void statusbar_icon_remove(int8_t id) { void statusbar_icon_remove(int8_t id) {
TT_LOG_I(TAG, "id %d: remove", id); TT_LOG_I(TAG, "id %d: remove", id);
tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT); tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT);
statusbar_lock(TtWaitForever); statusbar_lock();
StatusbarIcon* icon = &statusbar_data.icons[id]; StatusbarIcon* icon = &statusbar_data.icons[id];
icon->claimed = false; icon->claimed = false;
icon->visible = false; icon->visible = false;
icon->image = ""; icon->image = "";
tt_pubsub_publish(statusbar_data.pubsub, nullptr); statusbar_data.pubsub->publish(nullptr);
statusbar_unlock(); statusbar_unlock();
} }
@ -273,7 +273,7 @@ void statusbar_icon_set_image(int8_t id, const std::string& image) {
StatusbarIcon* icon = &statusbar_data.icons[id]; StatusbarIcon* icon = &statusbar_data.icons[id];
tt_check(icon->claimed); tt_check(icon->claimed);
icon->image = image; icon->image = image;
tt_pubsub_publish(statusbar_data.pubsub, nullptr); statusbar_data.pubsub->publish(nullptr);
statusbar_unlock(); statusbar_unlock();
} }
} }
@ -285,7 +285,7 @@ void statusbar_icon_set_visibility(int8_t id, bool visible) {
StatusbarIcon* icon = &statusbar_data.icons[id]; StatusbarIcon* icon = &statusbar_data.icons[id];
tt_check(icon->claimed); tt_check(icon->claimed);
icon->visible = visible; icon->visible = visible;
tt_pubsub_publish(statusbar_data.pubsub, nullptr); statusbar_data.pubsub->publish(nullptr);
statusbar_unlock(); statusbar_unlock();
} }
} }

View File

@ -35,7 +35,7 @@ Gui* gui_alloc() {
&guiMain, &guiMain,
nullptr 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)); tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS));
instance->keyboardGroup = lv_group_create(); instance->keyboardGroup = lv_group_create();
auto* screen_root = lv_scr_act(); auto* screen_root = lv_scr_act();
@ -90,7 +90,7 @@ void unlock() {
void requestDraw() { void requestDraw() {
assert(gui); assert(gui);
ThreadId thread_id = gui->thread->getId(); 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::AppContext> app) { void showApp(std::shared_ptr<app::AppContext> app) {
@ -120,19 +120,19 @@ static int32_t guiMain(TT_UNUSED void* p) {
Gui* local_gui = gui; Gui* local_gui = gui;
while (true) { while (true) {
uint32_t flags = thread_flags_wait( uint32_t flags = Thread::awaitFlags(
GUI_THREAD_FLAG_ALL, GUI_THREAD_FLAG_ALL,
TtFlagWaitAny, EventFlag::WaitAny,
TtWaitForever portMAX_DELAY
); );
// Process and dispatch draw call // Process and dispatch draw call
if (flags & GUI_THREAD_FLAG_DRAW) { if (flags & GUI_THREAD_FLAG_DRAW) {
thread_flags_clear(GUI_THREAD_FLAG_DRAW); Thread::clearFlags(GUI_THREAD_FLAG_DRAW);
redraw(local_gui); redraw(local_gui);
} }
if (flags & GUI_THREAD_FLAG_EXIT) { if (flags & GUI_THREAD_FLAG_EXIT) {
thread_flags_clear(GUI_THREAD_FLAG_EXIT); Thread::clearFlags(GUI_THREAD_FLAG_EXIT);
break; break;
} }
} }
@ -159,7 +159,7 @@ public:
lock(); lock();
ThreadId thread_id = gui->thread->getId(); 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(); gui->thread->join();
delete gui->thread; delete gui->thread;

View File

@ -125,13 +125,13 @@ static void transitionAppToState(std::shared_ptr<app::AppInstance> app, app::Sta
break; break;
case app::StateShowing: { case app::StateShowing: {
LoaderEvent event_showing = { .type = LoaderEventTypeApplicationShowing }; LoaderEvent event_showing = { .type = LoaderEventTypeApplicationShowing };
tt_pubsub_publish(loader_singleton->pubsubExternal, &event_showing); loader_singleton->pubsubExternal->publish(&event_showing);
app->setState(app::StateShowing); app->setState(app::StateShowing);
break; break;
} }
case app::StateHiding: { case app::StateHiding: {
LoaderEvent event_hiding = { .type = LoaderEventTypeApplicationHiding }; LoaderEvent event_hiding = { .type = LoaderEventTypeApplicationHiding };
tt_pubsub_publish(loader_singleton->pubsubExternal, &event_hiding); loader_singleton->pubsubExternal->publish(&event_hiding);
app->setState(app::StateHiding); app->setState(app::StateHiding);
break; break;
} }
@ -174,7 +174,7 @@ static LoaderStatus startAppWithManifestInternal(
transitionAppToState(new_app, app::StateShowing); transitionAppToState(new_app, app::StateShowing);
LoaderEvent event_external = { .type = LoaderEventTypeApplicationStarted }; LoaderEvent event_external = { .type = LoaderEventTypeApplicationStarted };
tt_pubsub_publish(loader_singleton->pubsubExternal, &event_external); loader_singleton->pubsubExternal->publish(&event_external);
return LoaderStatus::Ok; 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! // 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 }; 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 (instance_to_resume != nullptr) {
if (result_set) { if (result_set) {

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "app/AppManifest.h"
#include "Bundle.h" #include "Bundle.h"
#include "Pubsub.h" #include "PubSub.h"
#include "app/AppManifest.h"
#include "service/ServiceManifest.h" #include "service/ServiceManifest.h"
#include <memory> #include <memory>

View File

@ -41,7 +41,7 @@ bool ScreenshotTask::isFinished() {
void ScreenshotTask::setFinished() { void ScreenshotTask::setFinished() {
auto scoped_lockable = mutex.scoped(); auto scoped_lockable = mutex.scoped();
scoped_lockable->lock(TtWaitForever); scoped_lockable->lock();
finished = true; finished = true;
} }

View File

@ -138,11 +138,11 @@ private:
std::unique_ptr<service::Paths> paths; std::unique_ptr<service::Paths> paths;
void lock() const { void lock() const {
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk); mutex.lock();
} }
void unlock() const { void unlock() const {
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
} }
void updateWifiIcon() { void updateWifiIcon() {

View File

@ -1,42 +0,0 @@
#pragma once
#include <cstdint>
#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

View File

@ -5,17 +5,13 @@
namespace tt { namespace tt {
#define TAG "dispatcher" #define TAG "dispatcher"
#define BACKPRESSURE_WARNING_COUNT 100 #define BACKPRESSURE_WARNING_COUNT ((EventBits_t)100)
#define WAIT_FLAG 1 #define WAIT_FLAG ((EventBits_t)1)
Dispatcher::Dispatcher() :
mutex(Mutex::Type::Normal)
{}
Dispatcher::~Dispatcher() { Dispatcher::~Dispatcher() {
// Wait for Mutex usage // Wait for Mutex usage
mutex.acquire(TtWaitForever); mutex.lock();
mutex.release(); mutex.unlock();
} }
void Dispatcher::dispatch(Function function, std::shared_ptr<void> context) { void Dispatcher::dispatch(Function function, std::shared_ptr<void> context) {
@ -37,7 +33,7 @@ void Dispatcher::dispatch(Function function, std::shared_ptr<void> context) {
uint32_t Dispatcher::consume(TickType_t timeout) { uint32_t Dispatcher::consume(TickType_t timeout) {
// Wait for signal and clear // Wait for signal and clear
TickType_t start_ticks = kernel::getTicks(); 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); eventFlag.clear(WAIT_FLAG);
} else { } else {
return 0; return 0;

View File

@ -43,7 +43,7 @@ private:
public: public:
explicit Dispatcher(); explicit Dispatcher() = default;
~Dispatcher(); ~Dispatcher();
/** /**
@ -58,7 +58,7 @@ public:
* @param[in] timeout the ticks to wait for a message * @param[in] timeout the ticks to wait for a message
* @return the amount of messages that were consumed * @return the amount of messages that were consumed
*/ */
uint32_t consume(TickType_t timeout); uint32_t consume(TickType_t timeout = portMAX_DELAY);
}; };
} // namespace } // namespace

View File

@ -8,15 +8,15 @@
namespace tt { namespace tt {
EventFlag::EventFlag() { EventFlag::EventFlag() :
handle(xEventGroupCreate())
{
assert(!TT_IS_IRQ_MODE()); assert(!TT_IS_IRQ_MODE());
handle = xEventGroupCreate();
tt_check(handle); tt_check(handle);
} }
EventFlag::~EventFlag() { EventFlag::~EventFlag() {
assert(!TT_IS_IRQ_MODE()); assert(!TT_IS_IRQ_MODE());
vEventGroupDelete(handle);
} }
uint32_t EventFlag::set(uint32_t flags) const { 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()) { if (TT_IS_IRQ_MODE()) {
yield = pdFALSE; yield = pdFALSE;
if (xEventGroupSetBitsFromISR(handle, (EventBits_t)flags, &yield) == pdFAIL) { if (xEventGroupSetBitsFromISR(handle.get(), (EventBits_t)flags, &yield) == pdFAIL) {
rflags = (uint32_t)TtFlagErrorResource; rflags = (uint32_t)ErrorResource;
} else { } else {
rflags = flags; rflags = flags;
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
} }
} else { } else {
rflags = xEventGroupSetBits(handle, (EventBits_t)flags); rflags = xEventGroupSetBits(handle.get(), (EventBits_t)flags);
} }
/* Return event flags after setting */ /* Return event flags after setting */
@ -48,10 +48,10 @@ uint32_t EventFlag::clear(uint32_t flags) const {
uint32_t rflags; uint32_t rflags;
if (TT_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
rflags = xEventGroupGetBitsFromISR(handle); rflags = xEventGroupGetBitsFromISR(handle.get());
if (xEventGroupClearBitsFromISR(handle, (EventBits_t)flags) == pdFAIL) { if (xEventGroupClearBitsFromISR(handle.get(), (EventBits_t)flags) == pdFAIL) {
rflags = (uint32_t)TtStatusErrorResource; rflags = (uint32_t)ErrorResource;
} else { } else {
/* xEventGroupClearBitsFromISR only registers clear operation in the timer command queue. */ /* xEventGroupClearBitsFromISR only registers clear operation in the timer command queue. */
/* Yield is required here otherwise clear operation might not execute in the right order. */ /* 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); portYIELD_FROM_ISR(pdTRUE);
} }
} else { } else {
rflags = xEventGroupClearBits(handle, (EventBits_t)flags); rflags = xEventGroupClearBits(handle.get(), (EventBits_t)flags);
} }
/* Return event flags before clearing */ /* Return event flags before clearing */
@ -70,9 +70,9 @@ uint32_t EventFlag::get() const {
uint32_t rflags; uint32_t rflags;
if (TT_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
rflags = xEventGroupGetBitsFromISR(handle); rflags = xEventGroupGetBitsFromISR(handle.get());
} else { } else {
rflags = xEventGroupGetBits(handle); rflags = xEventGroupGetBits(handle.get());
} }
/* Return current event flags */ /* Return current event flags */
@ -91,40 +91,40 @@ uint32_t EventFlag::wait(
BaseType_t exit_clear; BaseType_t exit_clear;
uint32_t rflags; uint32_t rflags;
if (options & TtFlagWaitAll) { if (options & WaitAll) {
wait_all = pdTRUE; wait_all = pdTRUE;
} else { } else {
wait_all = pdFAIL; wait_all = pdFAIL;
} }
if (options & TtFlagNoClear) { if (options & NoClear) {
exit_clear = pdFAIL; exit_clear = pdFAIL;
} else { } else {
exit_clear = pdTRUE; exit_clear = pdTRUE;
} }
rflags = xEventGroupWaitBits( rflags = xEventGroupWaitBits(
handle, handle.get(),
(EventBits_t)flags, (EventBits_t)flags,
exit_clear, exit_clear,
wait_all, wait_all,
(TickType_t)timeout (TickType_t)timeout
); );
if (options & TtFlagWaitAll) { if (options & WaitAll) {
if ((flags & rflags) != flags) { if ((flags & rflags) != flags) {
if (timeout > 0U) { if (timeout > 0U) {
rflags = (uint32_t)TtStatusErrorTimeout; rflags = (uint32_t)ErrorTimeout;
} else { } else {
rflags = (uint32_t)TtStatusErrorResource; rflags = (uint32_t)ErrorResource;
} }
} }
} else { } else {
if ((flags & rflags) == 0U) { if ((flags & rflags) == 0U) {
if (timeout > 0U) { if (timeout > 0U) {
rflags = (uint32_t)TtStatusErrorTimeout; rflags = (uint32_t)ErrorTimeout;
} else { } else {
rflags = (uint32_t)TtStatusErrorResource; rflags = (uint32_t)ErrorResource;
} }
} }
} }

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "CoreTypes.h"
#include "RtosCompatEventGroups.h" #include "RtosCompatEventGroups.h"
#include <memory>
namespace tt { namespace tt {
@ -10,17 +10,40 @@ namespace tt {
*/ */
class EventFlag { class EventFlag {
private: private:
EventGroupHandle_t handle;
struct EventGroupHandleDeleter {
void operator()(EventGroupHandle_t handleToDelete) {
vEventGroupDelete(handleToDelete);
}
};
std::unique_ptr<std::remove_pointer_t<EventGroupHandle_t>, EventGroupHandleDeleter> handle;
public: public:
EventFlag(); EventFlag();
~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 set(uint32_t flags) const;
uint32_t clear(uint32_t flags) const; uint32_t clear(uint32_t flags) const;
uint32_t get() const; uint32_t get() const;
uint32_t wait( uint32_t wait(
uint32_t flags, uint32_t flags,
uint32_t options = TtFlagWaitAny, uint32_t options = WaitAny,
uint32_t timeout = TtWaitForever uint32_t timeout = portMAX_DELAY
) const; ) const;
}; };

View File

@ -3,6 +3,8 @@
#include "Check.h" #include "Check.h"
#include "RtosCompat.h" #include "RtosCompat.h"
#include <memory> #include <memory>
#include <functional>
#include <algorithm>
namespace tt { namespace tt {
@ -15,6 +17,8 @@ public:
virtual bool lock(TickType_t timeoutTicks) const = 0; virtual bool lock(TickType_t timeoutTicks) const = 0;
virtual bool lock() const { return lock(portMAX_DELAY); }
virtual bool unlock() const = 0; virtual bool unlock() const = 0;
std::unique_ptr<ScopedLockableUsage> scoped() const; std::unique_ptr<ScopedLockableUsage> scoped() const;
@ -43,6 +47,8 @@ public:
return lockable.lock(timeout); return lockable.lock(timeout);
} }
bool lock() const override { return lock(portMAX_DELAY); }
bool unlock() const override { bool unlock() const override {
return lockable.unlock(); return lockable.unlock();
} }

View File

@ -4,63 +4,64 @@
namespace tt { namespace tt {
MessageQueue::MessageQueue(uint32_t capacity, uint32_t msg_size) { static inline QueueHandle_t createQueue(uint32_t capacity, uint32_t messageSize) {
assert(!TT_IS_ISR() && (capacity > 0U) && (msg_size > 0U)); assert(!TT_IS_ISR() && (capacity > 0U) && (messageSize > 0U));
queue_handle = xQueueCreate(capacity, msg_size); return xQueueCreate(capacity, messageSize);
tt_check(queue_handle); }
MessageQueue::MessageQueue(uint32_t capacity, uint32_t messageSize) : handle(createQueue(capacity, messageSize)) {
tt_check(handle != nullptr);
} }
MessageQueue::~MessageQueue() { MessageQueue::~MessageQueue() {
assert(!TT_IS_ISR()); 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; bool result = true;
BaseType_t yield; BaseType_t yield;
if (TT_IS_ISR()) { if (TT_IS_ISR()) {
if ((queue_handle == nullptr) || (message == nullptr) || (timeout != 0U)) { if ((handle == nullptr) || (message == nullptr) || (timeout != 0U)) {
result = false; result = false;
} else { } else {
yield = pdFALSE; yield = pdFALSE;
if (xQueueSendToBackFromISR(queue_handle, message, &yield) != pdTRUE) { if (xQueueSendToBackFromISR(handle.get(), message, &yield) != pdTRUE) {
result = false; result = false;
} else { } else {
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
} }
} }
} else if ((queue_handle == nullptr) || (message == nullptr)) { } else if ((handle == nullptr) || (message == nullptr)) {
result = false; result = false;
} else if (xQueueSendToBack(queue_handle, message, (TickType_t)timeout) != pdPASS) { } else if (xQueueSendToBack(handle.get(), message, (TickType_t)timeout) != pdPASS) {
result = false; result = false;
} }
return result; return result;
} }
bool MessageQueue::get(void* msg_ptr, uint32_t timeout_ticks) { bool MessageQueue::get(void* msg_ptr, TickType_t timeout) {
bool result = true; bool result = true;
BaseType_t yield; BaseType_t yield;
if (TT_IS_ISR()) { if (TT_IS_ISR()) {
if ((queue_handle == nullptr) || (msg_ptr == nullptr) || (timeout_ticks != 0U)) { if ((handle == nullptr) || (msg_ptr == nullptr) || (timeout != 0U)) {
result = false; result = false;
} else { } else {
yield = pdFALSE; yield = pdFALSE;
if (xQueueReceiveFromISR(queue_handle, msg_ptr, &yield) != pdPASS) { if (xQueueReceiveFromISR(handle.get(), msg_ptr, &yield) != pdPASS) {
result = false; result = false;
} else { } else {
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
} }
} }
} else { } else {
if ((queue_handle == nullptr) || (msg_ptr == nullptr)) { if ((handle == nullptr) || (msg_ptr == nullptr)) {
result = false; 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; result = false;
} }
} }
@ -69,7 +70,7 @@ bool MessageQueue::get(void* msg_ptr, uint32_t timeout_ticks) {
} }
uint32_t MessageQueue::getCapacity() const { uint32_t MessageQueue::getCapacity() const {
auto* mq = (StaticQueue_t*)(queue_handle); auto* mq = (StaticQueue_t*)(handle.get());
if (mq == nullptr) { if (mq == nullptr) {
return 0U; return 0U;
} else { } else {
@ -78,7 +79,7 @@ uint32_t MessageQueue::getCapacity() const {
} }
uint32_t MessageQueue::getMessageSize() const { uint32_t MessageQueue::getMessageSize() const {
auto* mq = (StaticQueue_t*)(queue_handle); auto* mq = (StaticQueue_t*)(handle.get());
if (mq == nullptr) { if (mq == nullptr) {
return 0U; return 0U;
} else { } else {
@ -89,12 +90,12 @@ uint32_t MessageQueue::getMessageSize() const {
uint32_t MessageQueue::getCount() const { uint32_t MessageQueue::getCount() const {
UBaseType_t count; UBaseType_t count;
if (queue_handle == nullptr) { if (handle == nullptr) {
count = 0U; count = 0U;
} else if (TT_IS_ISR()) { } else if (TT_IS_ISR()) {
count = uxQueueMessagesWaitingFromISR(queue_handle); count = uxQueueMessagesWaitingFromISR(handle.get());
} else { } else {
count = uxQueueMessagesWaiting(queue_handle); count = uxQueueMessagesWaiting(handle.get());
} }
/* Return number of queued messages */ /* Return number of queued messages */
@ -102,7 +103,7 @@ uint32_t MessageQueue::getCount() const {
} }
uint32_t MessageQueue::getSpace() const { uint32_t MessageQueue::getSpace() const {
auto* mq = (StaticQueue_t*)(queue_handle); auto* mq = (StaticQueue_t*)(handle.get());
uint32_t space; uint32_t space;
uint32_t isrm; uint32_t isrm;
@ -124,10 +125,10 @@ uint32_t MessageQueue::getSpace() const {
bool MessageQueue::reset() { bool MessageQueue::reset() {
tt_check(!TT_IS_ISR()); tt_check(!TT_IS_ISR());
if (queue_handle == nullptr) { if (handle == nullptr) {
return false; return false;
} else { } else {
xQueueReset(queue_handle); xQueueReset(handle.get());
return true; return true;
} }
} }

View File

@ -7,7 +7,7 @@
*/ */
#pragma once #pragma once
#include "CoreTypes.h" #include <memory>
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
@ -25,7 +25,14 @@ namespace tt {
*/ */
class MessageQueue { class MessageQueue {
private: private:
QueueHandle_t queue_handle;
struct QueueHandleDeleter {
void operator()(QueueHandle_t handleToDelete) {
vQueueDelete(handleToDelete);
}
};
std::unique_ptr<std::remove_pointer_t<QueueHandle_t>, QueueHandleDeleter> handle;
public: public:
/** Allocate message queue /** Allocate message queue
@ -39,17 +46,17 @@ public:
/** Post a message to the queue. /** Post a message to the queue.
* The message is queued by copy, not by reference. * 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] message A pointer to a message. The message will be copied into a buffer.
* @param[in] timeoutTicks * @param[in] timeout
* @return success result * @return success result
*/ */
bool put(const void* message, uint32_t timeoutTicks); bool put(const void* message, TickType_t timeout);
/** Get message from queue /** Get message from queue
* @param[out] message A pointer to an already allocated message object * @param[out] message A pointer to an already allocated message object
* @param[in] timeoutTicks * @param[in] timeout
* @return success result * @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. * @return The maximum amount of messages that can be in the queue at any given time.

View File

@ -22,88 +22,56 @@ namespace tt {
#define tt_mutex_info(mutex, text) #define tt_mutex_info(mutex, text)
#endif #endif
Mutex::Mutex(Type type) : type(type) { static inline SemaphoreHandle_t createSemaphoreHandle(Mutex::Type type) {
tt_mutex_info(data, "alloc");
switch (type) { switch (type) {
case Type::Normal: case Mutex::Type::Normal:
semaphore = xSemaphoreCreateMutex(); return xSemaphoreCreateMutex();
break; case Mutex::Type::Recursive:
case Type::Recursive: return xSemaphoreCreateRecursiveMutex();
semaphore = xSemaphoreCreateRecursiveMutex();
break;
default: default:
tt_crash("Mutex type unknown/corrupted"); tt_crash("Mutex type unknown/corrupted");
} }
assert(semaphore != nullptr);
} }
Mutex::~Mutex() { Mutex::Mutex(Type type) : handle(createSemaphoreHandle(type)), type(type) {
assert(!TT_IS_IRQ_MODE()); tt_mutex_info(data, "alloc");
vSemaphoreDelete(semaphore); assert(handle != nullptr);
semaphore = nullptr; // If the mutex is used after release, this might help debugging
} }
TtStatus Mutex::acquire(TickType_t timeout) const { bool Mutex::lock(TickType_t timeout) const {
assert(!TT_IS_IRQ_MODE()); assert(!TT_IS_IRQ_MODE());
assert(semaphore != nullptr); assert(handle != nullptr);
tt_mutex_info(mutex, "acquire"); tt_mutex_info(mutex, "acquire");
switch (type) { switch (type) {
case Type::Normal: case Type::Normal:
if (xSemaphoreTake(semaphore, timeout) != pdPASS) { return xSemaphoreTake(handle.get(), timeout) == pdPASS;
if (timeout != 0U) {
return TtStatusErrorTimeout;
} else {
return TtStatusErrorResource;
}
} else {
return TtStatusOk;
}
case Type::Recursive: case Type::Recursive:
if (xSemaphoreTakeRecursive(semaphore, timeout) != pdPASS) { return xSemaphoreTakeRecursive(handle.get(), timeout) == pdPASS;
if (timeout != 0U) {
return TtStatusErrorTimeout;
} else {
return TtStatusErrorResource;
}
} else {
return TtStatusOk;
}
default: default:
tt_crash("mutex type unknown/corrupted"); tt_crash();
} }
} }
TtStatus Mutex::release() const { bool Mutex::unlock() const {
assert(!TT_IS_IRQ_MODE()); assert(!TT_IS_IRQ_MODE());
assert(semaphore); assert(handle != nullptr);
tt_mutex_info(mutex, "release"); tt_mutex_info(mutex, "release");
switch (type) { switch (type) {
case Type::Normal: { case Type::Normal:
if (xSemaphoreGive(semaphore) != pdPASS) { return xSemaphoreGive(handle.get()) == pdPASS;
return TtStatusErrorResource;
} else {
return TtStatusOk;
}
}
case Type::Recursive: case Type::Recursive:
if (xSemaphoreGiveRecursive(semaphore) != pdPASS) { return xSemaphoreGiveRecursive(handle.get()) == pdPASS;
return TtStatusErrorResource;
} else {
return TtStatusOk;
}
default: default:
tt_crash("mutex type unknown/corrupted"); tt_crash();
} }
} }
ThreadId Mutex::getOwner() const { ThreadId Mutex::getOwner() const {
assert(!TT_IS_IRQ_MODE()); assert(!TT_IS_IRQ_MODE());
assert(semaphore); assert(handle != nullptr);
return (ThreadId)xSemaphoreGetMutexHolder(semaphore); return (ThreadId)xSemaphoreGetMutexHolder(handle.get());
} }
} // namespace } // namespace

View File

@ -4,7 +4,6 @@
*/ */
#pragma once #pragma once
#include "CoreTypes.h"
#include "Thread.h" #include "Thread.h"
#include "RtosCompatSemaphore.h" #include "RtosCompatSemaphore.h"
#include "Check.h" #include "Check.h"
@ -15,9 +14,9 @@ namespace tt {
/** /**
* Wrapper for FreeRTOS xSemaphoreCreateMutex and xSemaphoreCreateRecursiveMutex * 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: public:
@ -28,35 +27,36 @@ public:
private: private:
SemaphoreHandle_t semaphore; struct SemaphoreHandleDeleter {
void operator()(QueueHandle_t handleToDelete) {
assert(!TT_IS_IRQ_MODE());
vSemaphoreDelete(handleToDelete);
}
};
std::unique_ptr<std::remove_pointer_t<QueueHandle_t>, SemaphoreHandleDeleter> handle;
Type type; Type type;
public: public:
explicit Mutex(Type type = Type::Normal); explicit Mutex(Type type = Type::Normal);
~Mutex() override; ~Mutex() override = default;
/** 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;
/** Attempt to lock the mutex. Blocks until timeout passes or lock is acquired. /** Attempt to lock the mutex. Blocks until timeout passes or lock is acquired.
* @param[in] timeout * @param[in] timeout
* @return success result * @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. /** Attempt to unlock the mutex.
* @return success result * @return success result
*/ */
bool unlock() const override { return release() == TtStatusOk; } bool unlock() const override;
/** @return the owner of the thread */ /** @return the owner of the thread */
ThreadId getOwner() const; ThreadId getOwner() const;

View File

@ -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

View File

@ -0,0 +1,61 @@
/**
* @file pubsub.h
* PubSub
*/
#pragma once
#include "Mutex.h"
#include <list>
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<Subscription> 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

View File

@ -1,52 +0,0 @@
#include "Pubsub.h"
#include "Check.h"
namespace tt {
PubSubSubscription* tt_pubsub_subscribe(std::shared_ptr<PubSub> 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> 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> 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

View File

@ -1,57 +0,0 @@
/**
* @file pubsub.h
* PubSub
*/
#pragma once
#include "Mutex.h"
#include <list>
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<PubSubSubscription> 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> 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> 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> pubsub, void* message);
} // namespace

View File

@ -4,28 +4,30 @@
namespace tt { namespace tt {
Semaphore::Semaphore(uint32_t maxCount, uint32_t initialCount) { static inline struct QueueDefinition* createHandle(uint32_t maxCount, uint32_t initialCount) {
assert(!TT_IS_IRQ_MODE());
assert((maxCount > 0U) && (initialCount <= maxCount)); assert((maxCount > 0U) && (initialCount <= maxCount));
if (maxCount == 1U) { if (maxCount == 1U) {
handle = xSemaphoreCreateBinary(); auto handle = xSemaphoreCreateBinary();
if ((handle != nullptr) && (initialCount != 0U)) { if ((handle != nullptr) && (initialCount != 0U)) {
if (xSemaphoreGive(handle) != pdPASS) { if (xSemaphoreGive(handle) != pdPASS) {
vSemaphoreDelete(handle); vSemaphoreDelete(handle);
handle = nullptr; handle = nullptr;
} }
} }
return handle;
} else { } 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() { Semaphore::~Semaphore() {
assert(!TT_IS_IRQ_MODE()); assert(!TT_IS_IRQ_MODE());
vSemaphoreDelete(handle);
} }
bool Semaphore::acquire(uint32_t timeout) const { bool Semaphore::acquire(uint32_t timeout) const {
@ -35,7 +37,7 @@ bool Semaphore::acquire(uint32_t timeout) const {
} else { } else {
BaseType_t yield = pdFALSE; BaseType_t yield = pdFALSE;
if (xSemaphoreTakeFromISR(handle, &yield) != pdPASS) { if (xSemaphoreTakeFromISR(handle.get(), &yield) != pdPASS) {
return false; return false;
} else { } else {
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
@ -43,21 +45,21 @@ bool Semaphore::acquire(uint32_t timeout) const {
} }
} }
} else { } else {
return xSemaphoreTake(handle, (TickType_t)timeout) == pdPASS; return xSemaphoreTake(handle.get(), (TickType_t)timeout) == pdPASS;
} }
} }
bool Semaphore::release() const { bool Semaphore::release() const {
if (TT_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
BaseType_t yield = pdFALSE; BaseType_t yield = pdFALSE;
if (xSemaphoreGiveFromISR(handle, &yield) != pdTRUE) { if (xSemaphoreGiveFromISR(handle.get(), &yield) != pdTRUE) {
return false; return false;
} else { } else {
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
return true; return true;
} }
} else { } else {
return xSemaphoreGive(handle) == pdPASS; return xSemaphoreGive(handle.get()) == pdPASS;
} }
} }
@ -65,12 +67,12 @@ uint32_t Semaphore::getCount() const {
if (TT_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
// TODO: uxSemaphoreGetCountFromISR is not supported on esp-idf 5.1.2 - perhaps later on? // TODO: uxSemaphoreGetCountFromISR is not supported on esp-idf 5.1.2 - perhaps later on?
#ifdef uxSemaphoreGetCountFromISR #ifdef uxSemaphoreGetCountFromISR
return uxSemaphoreGetCountFromISR(handle); return uxSemaphoreGetCountFromISR(handle.get());
#else #else
return uxQueueMessagesWaitingFromISR((QueueHandle_t)hSemaphore); return uxQueueMessagesWaitingFromISR((QueueHandle_t)hSemaphore);
#endif #endif
} else { } else {
return uxSemaphoreGetCount(handle); return uxSemaphoreGetCount(handle.get());
} }
} }

View File

@ -1,7 +1,8 @@
#pragma once #pragma once
#include "CoreTypes.h"
#include "Thread.h" #include "Thread.h"
#include <cassert>
#include <memory>
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h" #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. * Can be used from IRQ/ISR mode, but cannot be created/destroyed from such a context.
*/ */
class Semaphore { class Semaphore {
private: private:
SemaphoreHandle_t handle;
struct SemaphoreHandleDeleter {
void operator()(QueueHandle_t handleToDelete) {
assert(!TT_IS_IRQ_MODE());
vSemaphoreDelete(handleToDelete);
}
};
std::unique_ptr<std::remove_pointer_t<QueueHandle_t>, SemaphoreHandleDeleter> handle;
public: public:
/** /**
* Cannot be called from IRQ/ISR mode. * Cannot be called from IRQ/ISR mode.

View File

@ -2,22 +2,20 @@
#include "Check.h" #include "Check.h"
#include "CoreDefines.h" #include "CoreDefines.h"
#include "CoreTypes.h"
namespace tt { namespace tt {
StreamBuffer::StreamBuffer(size_t size, size_t triggerLevel) { inline static StreamBufferHandle_t createStreamBuffer(size_t size, size_t triggerLevel) {
assert(size != 0); 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); tt_check(handle);
}; };
StreamBuffer::~StreamBuffer() {
vStreamBufferDelete(handle);
};
bool StreamBuffer::setTriggerLevel(size_t triggerLevel) const { bool StreamBuffer::setTriggerLevel(size_t triggerLevel) const {
return xStreamBufferSetTriggerLevel(handle, triggerLevel) == pdTRUE; return xStreamBufferSetTriggerLevel(handle.get(), triggerLevel) == pdTRUE;
}; };
size_t StreamBuffer::send( size_t StreamBuffer::send(
@ -27,11 +25,11 @@ size_t StreamBuffer::send(
) const { ) const {
if (TT_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
BaseType_t yield; BaseType_t yield;
size_t result = xStreamBufferSendFromISR(handle, data, length, &yield); size_t result = xStreamBufferSendFromISR(handle.get(), data, length, &yield);
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
return result; return result;
} else { } else {
return xStreamBufferSend(handle, data, length, timeout); return xStreamBufferSend(handle.get(), data, length, timeout);
} }
}; };
@ -42,32 +40,32 @@ size_t StreamBuffer::receive(
) const { ) const {
if (TT_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
BaseType_t yield; BaseType_t yield;
size_t result = xStreamBufferReceiveFromISR(handle, data, length, &yield); size_t result = xStreamBufferReceiveFromISR(handle.get(), data, length, &yield);
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
return result; return result;
} else { } else {
return xStreamBufferReceive(handle, data, length, timeout); return xStreamBufferReceive(handle.get(), data, length, timeout);
} }
} }
size_t StreamBuffer::getAvailableReadBytes() const { size_t StreamBuffer::getAvailableReadBytes() const {
return xStreamBufferBytesAvailable(handle); return xStreamBufferBytesAvailable(handle.get());
}; };
size_t StreamBuffer::getAvailableWriteBytes() const { size_t StreamBuffer::getAvailableWriteBytes() const {
return xStreamBufferSpacesAvailable(handle); return xStreamBufferSpacesAvailable(handle.get());
}; };
bool StreamBuffer::isFull() const { bool StreamBuffer::isFull() const {
return xStreamBufferIsFull(handle) == pdTRUE; return xStreamBufferIsFull(handle.get()) == pdTRUE;
}; };
bool StreamBuffer::isEmpty() const { bool StreamBuffer::isEmpty() const {
return xStreamBufferIsEmpty(handle) == pdTRUE; return xStreamBufferIsEmpty(handle.get()) == pdTRUE;
}; };
bool StreamBuffer::reset() const { bool StreamBuffer::reset() const {
return xStreamBufferReset(handle) == pdPASS; return xStreamBufferReset(handle.get()) == pdPASS;
} }
} // namespace } // namespace

View File

@ -1,8 +1,6 @@
#pragma once #pragma once
#include "CoreTypes.h" #include <memory>
#include <cstddef>
#include <cstdint>
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
@ -25,8 +23,16 @@ namespace tt {
* interrupt that will read from the buffer (the reader). * interrupt that will read from the buffer (the reader).
*/ */
class StreamBuffer { class StreamBuffer {
private: private:
StreamBufferHandle_t handle;
struct StreamBufferHandleDeleter {
void operator()(StreamBufferHandle_t handleToDelete) {
vStreamBufferDelete(handleToDelete);
}
};
std::unique_ptr<std::remove_pointer_t<StreamBufferHandle_t>, StreamBufferHandleDeleter> handle;
public: public:
@ -42,7 +48,7 @@ public:
*/ */
StreamBuffer(size_t size, size_t triggerLevel); StreamBuffer(size_t size, size_t triggerLevel);
~StreamBuffer(); ~StreamBuffer() = default;
/** /**
* @brief Set trigger level for stream buffer. * @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 * @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. * Blocked state to wait for space to become available if the stream buffer is full.
* Will return immediately if timeout is zero. * 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. * Ignored if called from ISR.
* @return The number of bytes actually written to the stream buffer. * @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 * @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. * Blocked state to wait for data to become available if the stream buffer is empty.
* Will return immediately if timeout is zero. * 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. * Ignored if called from ISR.
* @return The number of bytes read from the stream buffer, if any. * @return The number of bytes read from the stream buffer, if any.
*/ */

View File

@ -5,7 +5,6 @@
#include "Check.h" #include "Check.h"
#include "CoreDefines.h" #include "CoreDefines.h"
#include "CoreExtraDefines.h" #include "CoreExtraDefines.h"
#include "CoreTypes.h"
#include "EventFlag.h" #include "EventFlag.h"
#include "kernel/Kernel.h" #include "kernel/Kernel.h"
#include "kernel/critical/Critical.h" #include "kernel/critical/Critical.h"

View File

@ -1,10 +1,13 @@
#include "Thread.h" #include "Thread.h"
#include <string>
#include "Check.h" #include "Check.h"
#include "CoreDefines.h" #include "CoreDefines.h"
#include "EventFlag.h"
#include "kernel/Kernel.h" #include "kernel/Kernel.h"
#include "Log.h" #include "Log.h"
#include "TactilityCoreConfig.h"
#include <string>
namespace tt { namespace tt {
@ -22,180 +25,155 @@ namespace tt {
static_assert(static_cast<UBaseType_t>(Thread::Priority::Critical) <= TT_CONFIG_THREAD_MAX_PRIORITIES, "highest thread priority is higher than max priority"); static_assert(static_cast<UBaseType_t>(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"); 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) { void Thread::setState(Thread::State newState) {
data->state = state; state = newState;
if (data->stateCallback) { if (stateCallback) {
data->stateCallback(state, data->stateCallbackContext); stateCallback(state, stateCallbackContext);
} }
} }
static_assert(configSUPPORT_DYNAMIC_ALLOCATION == 1); static_assert(configSUPPORT_DYNAMIC_ALLOCATION == 1);
/** Catch threads that are trying to exit wrong way */ /** Catch threads that are trying to exit wrong way */
__attribute__((__noreturn__)) void thread_catch() { //-V1082 __attribute__((__noreturn__)) void threadCatch() { //-V1082
// If you're here it means you're probably doing something wrong // If you're here it means you're probably doing something wrong with critical sections or with scheduler state
// with critical sections or with scheduler state asm volatile("nop");
asm volatile("nop"); // extra magic tt_crash();
tt_crash("You are doing it wrong"); //-V779
__builtin_unreachable(); __builtin_unreachable();
} }
void Thread::mainBody(void* context) {
static void thread_body(void* context) { assert(context != nullptr);
assert(context); auto* thread = static_cast<Thread*>(context);
auto* data = static_cast<Thread::Data*>(context);
// Store thread data instance to thread local storage // Store thread data instance to thread local storage
assert(pvTaskGetThreadLocalStoragePointer(nullptr, 0) == nullptr); assert(pvTaskGetThreadLocalStoragePointer(nullptr, 0) == nullptr);
vTaskSetThreadLocalStoragePointer(nullptr, 0, data->thread); vTaskSetThreadLocalStoragePointer(nullptr, 0, thread);
assert(data->state == Thread::State::Starting); assert(thread->state == Thread::State::Starting);
setState(data, Thread::State::Running); thread->setState(Thread::State::Running);
data->callbackResult = data->callback(data->callbackContext); thread->callbackResult = thread->callback(thread->callbackContext);
assert(data->state == Thread::State::Running); assert(thread->state == Thread::State::Running);
setState(data, Thread::State::Stopped); thread->setState(Thread::State::Stopped);
vTaskSetThreadLocalStoragePointer(nullptr, 0, nullptr); vTaskSetThreadLocalStoragePointer(nullptr, 0, nullptr);
data->taskHandle = nullptr; thread->taskHandle = nullptr;
vTaskDelete(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( Thread::Thread(
const std::string& name, std::string name,
configSTACK_DEPTH_TYPE stackSize, configSTACK_DEPTH_TYPE stackSize,
Callback callback, Callback callback,
_Nullable void* callbackContext, _Nullable void* callbackContext,
portBASE_TYPE affinity portBASE_TYPE affinity
) : ) :
data({ callback(callback),
.thread = nullptr, callbackContext(callbackContext),
.taskHandle = nullptr, name(std::move(name)),
.state = State::Stopped, stackSize(stackSize),
.callback = callback, affinity(affinity)
.callbackContext = callbackContext, {}
.callbackResult = 0,
.stateCallback = nullptr,
.stateCallbackContext = nullptr,
.name = name,
.priority = Priority::Normal,
.stackSize = stackSize,
.affinity = affinity
}) { }
Thread::~Thread() { Thread::~Thread() {
// Ensure that use join before free // Ensure that use join before free
assert(data.state == State::Stopped); assert(state == State::Stopped);
assert(data.taskHandle == nullptr); assert(taskHandle == nullptr);
} }
void Thread::setName(const std::string& newName) { void Thread::setName(std::string newName) {
assert(data.state == State::Stopped); assert(state == State::Stopped);
data.name = newName; name = std::move(newName);
} }
void Thread::setStackSize(size_t stackSize) { void Thread::setStackSize(size_t newStackSize) {
assert(data.state == State::Stopped); assert(state == State::Stopped);
assert(stackSize % 4 == 0); assert(stackSize % 4 == 0);
data.stackSize = stackSize; stackSize = newStackSize;
} }
void Thread::setCallback(Callback callback, _Nullable void* callbackContext) { void Thread::setCallback(Callback newCallback, _Nullable void* newCallbackContext) {
assert(data.state == State::Stopped); assert(state == State::Stopped);
data.callback = callback; callback = newCallback;
data.callbackContext = callbackContext; callbackContext = newCallbackContext;
} }
void Thread::setPriority(Priority priority) { void Thread::setPriority(Priority newPriority) {
assert(data.state == State::Stopped); assert(state == State::Stopped);
data.priority = priority; priority = newPriority;
} }
void Thread::setStateCallback(StateCallback callback, _Nullable void* callbackContext) { void Thread::setStateCallback(StateCallback callback, _Nullable void* callbackContext) {
assert(data.state == State::Stopped); assert(state == State::Stopped);
data.stateCallback = callback; stateCallback = callback;
data.stateCallbackContext = callbackContext; stateCallbackContext = callbackContext;
} }
Thread::State Thread::getState() const { Thread::State Thread::getState() const {
return data.state; return state;
} }
void Thread::start() { void Thread::start() {
assert(data.callback); assert(callback);
assert(data.state == State::Stopped); assert(state == State::Stopped);
assert(data.stackSize > 0U && data.stackSize < (UINT16_MAX * sizeof(StackType_t))); 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; BaseType_t result;
if (data.affinity != -1) { if (affinity != -1) {
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
result = xTaskCreatePinnedToCore( result = xTaskCreatePinnedToCore(
thread_body, mainBody,
data.name.c_str(), name.c_str(),
stack_depth, stack_depth,
this, this,
static_cast<UBaseType_t>(data.priority), static_cast<UBaseType_t>(priority),
&(data.taskHandle), &taskHandle,
data.affinity affinity
); );
#else #else
TT_LOG_W(TAG, "Pinned tasks are not supported by current FreeRTOS platform - creating regular one"); TT_LOG_W(TAG, "Pinned tasks are not supported by current FreeRTOS platform - creating regular one");
result = xTaskCreate( result = xTaskCreate(
thread_body, mainBody,
data.name.c_str(), name.c_str(),
stack_depth, stack_depth,
this, this,
static_cast<UBaseType_t>(data.priority), static_cast<UBaseType_t>(priority),
&(data.taskHandle) &taskHandle
); );
#endif #endif
} else { } else {
result = xTaskCreate( result = xTaskCreate(
thread_body, mainBody,
data.name.c_str(), name.c_str(),
stack_depth, stack_depth,
this, this,
static_cast<UBaseType_t>(data.priority), static_cast<UBaseType_t>(priority),
&(data.taskHandle) &taskHandle
); );
} }
tt_check(result == pdPASS); 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) { bool Thread::join(TickType_t timeout, TickType_t pollInterval) {
tt_check(thread_get_current() != this); tt_check(getCurrent() != this);
// !!! IMPORTANT NOTICE !!! // !!! IMPORTANT NOTICE !!!
// //
// If your thread exited, but your app stuck here: some other thread uses // If your thread exited, but your app stuck here: some other thread uses
// all cpu time, which delays kernel from releasing task handle // all cpu time, which delays kernel from releasing task handle
TickType_t start_ticks = kernel::getTicks(); TickType_t start_ticks = kernel::getTicks();
while (data.taskHandle) { while (taskHandle) {
kernel::delayTicks(pollInterval); kernel::delayTicks(pollInterval);
if ((kernel::getTicks() - start_ticks) > timeout) { if ((kernel::getTicks() - start_ticks) > timeout) {
return false; return false;
@ -206,45 +184,27 @@ bool Thread::join(TickType_t timeout, TickType_t pollInterval) {
} }
ThreadId Thread::getId() const { ThreadId Thread::getId() const {
return data.taskHandle; return taskHandle;
} }
int32_t Thread::getReturnCode() const { int32_t Thread::getReturnCode() const {
assert(data.state == State::Stopped); assert(state == State::Stopped);
return data.callbackResult; return callbackResult;
} }
ThreadId thread_get_current_id() { Thread* Thread::getCurrent() {
return xTaskGetCurrentTaskHandle(); return static_cast<Thread*>(pvTaskGetThreadLocalStoragePointer(nullptr, 0));
} }
Thread* thread_get_current() { uint32_t Thread::setFlags(ThreadId threadId, uint32_t flags) {
auto* thread = static_cast<Thread*>(pvTaskGetThreadLocalStoragePointer(nullptr, 0)); auto hTask = (TaskHandle_t)threadId;
return thread;
}
void thread_set_current_priority(Thread::Priority priority) {
vTaskPrioritySet(nullptr, static_cast<UBaseType_t>(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 rflags; uint32_t rflags;
BaseType_t yield; BaseType_t yield;
if ((hTask == nullptr) || ((flags & THREAD_FLAGS_INVALID_BITS) != 0U)) { if ((hTask == nullptr) || ((flags & THREAD_FLAGS_INVALID_BITS) != 0U)) {
rflags = (uint32_t)TtStatusErrorParameter; rflags = (uint32_t)EventFlag::ErrorParameter;
} else { } else {
rflags = (uint32_t)TtStatusError; rflags = (uint32_t)EventFlag::Error;
if (TT_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
yield = pdFALSE; yield = pdFALSE;
@ -264,14 +224,14 @@ uint32_t thread_flags_set(ThreadId thread_id, uint32_t flags) {
return (rflags); return (rflags);
} }
uint32_t thread_flags_clear(uint32_t flags) { uint32_t Thread::clearFlags(uint32_t flags) {
TaskHandle_t hTask; TaskHandle_t hTask;
uint32_t rflags, cflags; uint32_t rflags, cflags;
if (TT_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
rflags = (uint32_t)TtStatusErrorISR; rflags = (uint32_t)EventFlag::ErrorISR;
} else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) { } else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {
rflags = (uint32_t)TtStatusErrorParameter; rflags = (uint32_t)EventFlag::ErrorParameter;
} else { } else {
hTask = xTaskGetCurrentTaskHandle(); hTask = xTaskGetCurrentTaskHandle();
@ -282,10 +242,10 @@ uint32_t thread_flags_clear(uint32_t flags) {
if (xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, cflags, eSetValueWithOverwrite) != if (xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, cflags, eSetValueWithOverwrite) !=
pdPASS) { pdPASS) {
rflags = (uint32_t)TtStatusError; rflags = (uint32_t)EventFlag::Error;
} }
} else { } else {
rflags = (uint32_t)TtStatusError; rflags = (uint32_t)EventFlag::Error;
} }
} }
@ -293,36 +253,36 @@ uint32_t thread_flags_clear(uint32_t flags) {
return (rflags); return (rflags);
} }
uint32_t thread_flags_get() { uint32_t Thread::getFlags() {
TaskHandle_t hTask; TaskHandle_t hTask;
uint32_t rflags; uint32_t rflags;
if (TT_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
rflags = (uint32_t)TtStatusErrorISR; rflags = (uint32_t)EventFlag::ErrorISR;
} else { } else {
hTask = xTaskGetCurrentTaskHandle(); hTask = xTaskGetCurrentTaskHandle();
if (xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags) != if (xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags) !=
pdPASS) { pdPASS) {
rflags = (uint32_t)TtStatusError; rflags = (uint32_t)EventFlag::Error;
} }
} }
return (rflags); 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 rflags, nval;
uint32_t clear; uint32_t clear;
TickType_t t0, td, tout; TickType_t t0, td, tout;
BaseType_t rval; BaseType_t rval;
if (TT_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
rflags = (uint32_t)TtStatusErrorISR; rflags = (uint32_t)EventFlag::ErrorISR;
} else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) { } else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {
rflags = (uint32_t)TtStatusErrorParameter; rflags = (uint32_t)EventFlag::ErrorParameter;
} else { } else {
if ((options & TtFlagNoClear) == TtFlagNoClear) { if ((options & EventFlag::NoClear) == EventFlag::NoClear) {
clear = 0U; clear = 0U;
} else { } else {
clear = flags; clear = flags;
@ -339,12 +299,12 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
rflags &= flags; rflags &= flags;
rflags |= nval; rflags |= nval;
if ((options & TtFlagWaitAll) == TtFlagWaitAll) { if ((options & EventFlag::WaitAll) == EventFlag::WaitAll) {
if ((flags & rflags) == flags) { if ((flags & rflags) == flags) {
break; break;
} else { } else {
if (timeout == 0U) { if (timeout == 0U) {
rflags = (uint32_t)TtStatusErrorResource; rflags = (uint32_t)EventFlag::ErrorResource;
break; break;
} }
} }
@ -353,7 +313,7 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
break; break;
} else { } else {
if (timeout == 0U) { if (timeout == 0U) {
rflags = (uint32_t)TtStatusErrorResource; rflags = (uint32_t)EventFlag::ErrorResource;
break; break;
} }
} }
@ -369,9 +329,9 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
} }
} else { } else {
if (timeout == 0) { if (timeout == 0) {
rflags = (uint32_t)TtStatusErrorResource; rflags = (uint32_t)EventFlag::ErrorResource;
} else { } else {
rflags = (uint32_t)TtStatusErrorTimeout; rflags = (uint32_t)EventFlag::ErrorTimeout;
} }
} }
} while (rval != pdFAIL); } while (rval != pdFAIL);
@ -381,21 +341,8 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
return (rflags); return (rflags);
} }
const char* thread_get_name(ThreadId thread_id) { uint32_t Thread::getStackSpace(ThreadId threadId) {
auto hTask = (TaskHandle_t)thread_id; auto hTask = (TaskHandle_t)threadId;
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 sz; uint32_t sz;
if (TT_IS_IRQ_MODE() || (hTask == nullptr)) { if (TT_IS_IRQ_MODE() || (hTask == nullptr)) {
@ -407,13 +354,13 @@ uint32_t thread_get_stack_space(ThreadId thread_id) {
return (sz); return (sz);
} }
void thread_suspend(ThreadId thread_id) { void Thread::suspend(ThreadId threadId) {
auto hTask = (TaskHandle_t)thread_id; auto hTask = (TaskHandle_t)threadId;
vTaskSuspend(hTask); vTaskSuspend(hTask);
} }
void thread_resume(ThreadId thread_id) { void Thread::resume(ThreadId threadId) {
auto hTask = (TaskHandle_t)thread_id; auto hTask = (TaskHandle_t)threadId;
if (TT_IS_IRQ_MODE()) { if (TT_IS_IRQ_MODE()) {
xTaskResumeFromISR(hTask); xTaskResumeFromISR(hTask);
} else { } else {
@ -421,8 +368,8 @@ void thread_resume(ThreadId thread_id) {
} }
} }
bool thread_is_suspended(ThreadId thread_id) { bool Thread::isSuspended(ThreadId threadId) {
auto hTask = (TaskHandle_t)thread_id; auto hTask = (TaskHandle_t)threadId;
return eTaskGetState(hTask) == eSuspended; return eTaskGetState(hTask) == eSuspended;
} }

View File

@ -1,11 +1,9 @@
#pragma once #pragma once
#include "CoreDefines.h" #include "CoreDefines.h"
#include "CoreTypes.h"
#include <cstddef>
#include <cstdint>
#include <string> #include <string>
#include <memory>
#include "RtosCompatTask.h" #include "RtosCompatTask.h"
@ -14,6 +12,7 @@ namespace tt {
typedef TaskHandle_t ThreadId; typedef TaskHandle_t ThreadId;
class Thread { class Thread {
public: public:
enum class State{ enum class State{
@ -34,6 +33,7 @@ public:
Critical = 7U Critical = 7U
}; };
/** ThreadCallback Your callback to run in new thread /** ThreadCallback Your callback to run in new thread
* @warning never use osThreadExit in Thread * @warning never use osThreadExit in Thread
*/ */
@ -51,22 +51,27 @@ public:
*/ */
typedef void (*StateCallback)(State state, void* context); typedef void (*StateCallback)(State state, void* context);
typedef struct { private:
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;
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 /** Allocate Thread, shortcut version
* @param[in] name the name of the thread * @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) * @param[in] affinity Which CPU core to pin this task to, -1 means unpinned (only works on ESP32)
*/ */
Thread( Thread(
const std::string& name, std::string name,
configSTACK_DEPTH_TYPE stackSize, configSTACK_DEPTH_TYPE stackSize,
Callback callback, Callback callback,
_Nullable void* callbackContext, _Nullable void* callbackContext,
@ -88,7 +93,7 @@ public:
/** Set Thread name /** Set Thread name
* @param[in] name string * @param[in] name string
*/ */
void setName(const std::string& name); void setName(std::string name);
/** Set Thread stack size /** Set Thread stack size
* @param[in] stackSize stack size in bytes * @param[in] stackSize stack size in bytes
@ -140,9 +145,39 @@ public:
*/ */
int32_t getReturnCode() const; 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 #define THREAD_PRIORITY_APP Thread::PriorityNormal
@ -150,59 +185,4 @@ private:
#define THREAD_PRIORITY_RENDER Thread::Priority::Higher #define THREAD_PRIORITY_RENDER Thread::Priority::Higher
#define THREAD_PRIORITY_ISR Thread::Priority::Critical #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 } // namespace

View File

@ -6,7 +6,7 @@
namespace tt { namespace tt {
static void timer_callback(TimerHandle_t hTimer) { void Timer::onCallback(TimerHandle_t hTimer) {
auto* timer = static_cast<Timer*>(pvTimerGetTimerID(hTimer)); auto* timer = static_cast<Timer*>(pvTimerGetTimerID(hTimer));
if (timer != nullptr) { if (timer != nullptr) {
@ -14,54 +14,59 @@ static void timer_callback(TimerHandle_t hTimer) {
} }
} }
Timer::Timer(Type type, Callback callback, std::shared_ptr<void> callbackContext) { static inline TimerHandle_t createTimer(Timer::Type type, void* timerId, TimerCallbackFunction_t callback) {
assert((!TT_IS_ISR()) && (callback != nullptr)); assert(timerId != nullptr);
assert(callback != nullptr);
this->callback = callback;
this->callbackContext = std::move(callbackContext);
UBaseType_t reload; UBaseType_t reload;
if (type == Type::Once) { if (type == Timer::Type::Once) {
reload = pdFALSE; reload = pdFALSE;
} else { } else {
reload = pdTRUE; reload = pdTRUE;
} }
this->timerHandle = xTimerCreate(nullptr, portMAX_DELAY, (BaseType_t)reload, this, timer_callback); return xTimerCreate(nullptr, portMAX_DELAY, (BaseType_t)reload, timerId, callback);
assert(this->timerHandle); }
Timer::Timer(Type type, Callback callback, std::shared_ptr<void> callbackContext) :
callback(callback),
callbackContext(std::move(callbackContext)),
handle(createTimer(type, this, onCallback))
{
assert(!TT_IS_ISR());
assert(handle != nullptr);
} }
Timer::~Timer() { Timer::~Timer() {
assert(!TT_IS_ISR()); assert(!TT_IS_ISR());
tt_check(xTimerDelete(timerHandle, portMAX_DELAY) == pdPASS);
} }
bool Timer::start(TickType_t interval) { bool Timer::start(TickType_t interval) {
assert(!TT_IS_ISR()); assert(!TT_IS_ISR());
assert(interval < portMAX_DELAY); 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) { bool Timer::restart(TickType_t interval) {
assert(!TT_IS_ISR()); assert(!TT_IS_ISR());
assert(interval < portMAX_DELAY); assert(interval < portMAX_DELAY);
return xTimerChangePeriod(timerHandle, interval, portMAX_DELAY) == pdPASS && return xTimerChangePeriod(handle.get(), interval, portMAX_DELAY) == pdPASS &&
xTimerReset(timerHandle, portMAX_DELAY) == pdPASS; xTimerReset(handle.get(), portMAX_DELAY) == pdPASS;
} }
bool Timer::stop() { bool Timer::stop() {
assert(!TT_IS_ISR()); assert(!TT_IS_ISR());
return xTimerStop(timerHandle, portMAX_DELAY) == pdPASS; return xTimerStop(handle.get(), portMAX_DELAY) == pdPASS;
} }
bool Timer::isRunning() { bool Timer::isRunning() {
assert(!TT_IS_ISR()); assert(!TT_IS_ISR());
return xTimerIsTimerActive(timerHandle) == pdTRUE; return xTimerIsTimerActive(handle.get()) == pdTRUE;
} }
TickType_t Timer::getExpireTime() { TickType_t Timer::getExpireTime() {
assert(!TT_IS_ISR()); assert(!TT_IS_ISR());
return xTimerGetExpiryTime(timerHandle); return xTimerGetExpiryTime(handle.get());
} }
bool Timer::setPendingCallback(PendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeout) { bool Timer::setPendingCallback(PendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeout) {

View File

@ -1,7 +1,5 @@
#pragma once #pragma once
#include "CoreTypes.h"
#include "RtosCompatTimers.h" #include "RtosCompatTimers.h"
#include "Thread.h" #include "Thread.h"
#include <memory> #include <memory>
@ -9,15 +7,27 @@
namespace tt { namespace tt {
class Timer { class Timer {
private:
TimerHandle_t timerHandle;
public: public:
typedef void (*Callback)(std::shared_ptr<void> context); typedef void (*Callback)(std::shared_ptr<void> context);
typedef void (*PendingCallback)(void* context, uint32_t arg); typedef void (*PendingCallback)(void* context, uint32_t arg);
private:
struct TimerHandleDeleter {
void operator()(TimerHandle_t handleToDelete) {
xTimerDelete(handleToDelete, portMAX_DELAY);
}
};
Callback callback; Callback callback;
std::shared_ptr<void> callbackContext; std::shared_ptr<void> callbackContext;
std::unique_ptr<std::remove_pointer_t<TimerHandle_t>, TimerHandleDeleter> handle;
static void onCallback(TimerHandle_t hTimer);
public:
enum class Type { enum class Type {
Once = 0, ///< One-shot timer. Once = 0, ///< One-shot timer.

View File

@ -1,12 +1,11 @@
#include "kernel/Kernel.h" #include "kernel/Kernel.h"
#include "Check.h"
#include "CoreDefines.h" #include "CoreDefines.h"
#include "CoreTypes.h"
#include "RtosCompatTask.h" #include "RtosCompatTask.h"
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include "rom/ets_sys.h" #include "rom/ets_sys.h"
#else #else
#include <cassert>
#include <unistd.h> #include <unistd.h>
#endif #endif
@ -16,90 +15,72 @@ bool isRunning() {
return xTaskGetSchedulerState() != taskSCHEDULER_RUNNING; return xTaskGetSchedulerState() != taskSCHEDULER_RUNNING;
} }
int32_t lock() { bool lock() {
assert(!TT_IS_ISR()); assert(!TT_IS_ISR());
int32_t lock; int32_t lock;
switch (xTaskGetSchedulerState()) { switch (xTaskGetSchedulerState()) {
// Already suspended
case taskSCHEDULER_SUSPENDED: case taskSCHEDULER_SUSPENDED:
lock = 1; return true;
break;
case taskSCHEDULER_RUNNING: case taskSCHEDULER_RUNNING:
vTaskSuspendAll(); vTaskSuspendAll();
lock = 0; return true;
break;
case taskSCHEDULER_NOT_STARTED: case taskSCHEDULER_NOT_STARTED:
default: default:
lock = (int32_t)TtStatusError; return false;
break;
} }
/* Return previous lock state */ /* Return previous lock state */
return (lock); return (lock);
} }
int32_t unlock() { bool unlock() {
assert(!TT_IS_ISR()); assert(!TT_IS_ISR());
int32_t lock;
switch (xTaskGetSchedulerState()) { switch (xTaskGetSchedulerState()) {
case taskSCHEDULER_SUSPENDED: case taskSCHEDULER_SUSPENDED:
lock = 1;
if (xTaskResumeAll() != pdTRUE) { if (xTaskResumeAll() != pdTRUE) {
if (xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) { if (xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) {
lock = (int32_t)TtStatusError; return false;
} }
} }
break; return true;
case taskSCHEDULER_RUNNING: case taskSCHEDULER_RUNNING:
lock = 0; return true;
break;
case taskSCHEDULER_NOT_STARTED: case taskSCHEDULER_NOT_STARTED:
default: default:
lock = (int32_t)TtStatusError; return false;
break;
} }
/* Return previous lock state */
return (lock);
} }
int32_t restoreLock(int32_t lock) { bool restoreLock(bool lock) {
assert(!TT_IS_ISR()); assert(!TT_IS_ISR());
switch (xTaskGetSchedulerState()) { switch (xTaskGetSchedulerState()) {
case taskSCHEDULER_SUSPENDED: case taskSCHEDULER_SUSPENDED:
case taskSCHEDULER_RUNNING: case taskSCHEDULER_RUNNING:
if (lock == 1) { if (lock) {
vTaskSuspendAll(); vTaskSuspendAll();
return true;
} else { } else {
if (lock != 0) { if (xTaskResumeAll() != pdTRUE) {
lock = (int32_t)TtStatusError; if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
} else { return false;
if (xTaskResumeAll() != pdTRUE) {
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
lock = (int32_t)TtStatusError;
}
} }
} }
return true;
} }
break;
case taskSCHEDULER_NOT_STARTED: case taskSCHEDULER_NOT_STARTED:
default: default:
lock = (int32_t)TtStatusError; return false;
break;
} }
/* Return new lock state */
return (lock);
} }
uint32_t getTickFrequency() { 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()); assert(!TT_IS_ISR());
TickType_t tcnt, delay; TickType_t tcnt, delay;
TtStatus stat;
stat = TtStatusOk;
tcnt = xTaskGetTickCount(); tcnt = xTaskGetTickCount();
/* Determine remaining number of tick to delay */ /* Determine remaining number of tick to delay */
@ -130,29 +109,20 @@ TtStatus delayUntilTick(TickType_t tick) {
/* Check if target tick has not expired */ /* Check if target tick has not expired */
if ((delay != 0U) && (0 == (delay >> (8 * sizeof(TickType_t) - 1)))) { if ((delay != 0U) && (0 == (delay >> (8 * sizeof(TickType_t) - 1)))) {
if (xTaskDelayUntil(&tcnt, delay) == pdFALSE) { if (xTaskDelayUntil(&tcnt, delay) == pdPASS) {
/* Did not delay */ return true;
stat = TtStatusError;
} }
} else {
/* No delay or already expired */
stat = TtStatusErrorParameter;
} }
/* Return execution status */ return false;
return (stat);
} }
TickType_t getTicks() { TickType_t getTicks() {
TickType_t ticks;
if (TT_IS_ISR() != 0U) { if (TT_IS_ISR() != 0U) {
ticks = xTaskGetTickCountFromISR(); return xTaskGetTickCountFromISR();
} else { } else {
ticks = xTaskGetTickCount(); return xTaskGetTickCount();
} }
return ticks;
} }
TickType_t millisToTicks(uint32_t milliseconds) { TickType_t millisToTicks(uint32_t milliseconds) {

View File

@ -1,7 +1,5 @@
#pragma once #pragma once
#include "CoreTypes.h"
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#else #else
@ -23,22 +21,22 @@ bool isRunning();
/** Lock kernel, pause process scheduling /** Lock kernel, pause process scheduling
* @warning don't call from ISR context * @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 /** Unlock kernel, resume process scheduling
* @warning don't call from ISR context * @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 /** Restore kernel lock state
* @warning don't call from ISR context * @warning don't call from ISR context
* @param[in] lock The lock state * @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 /** Get kernel systick frequency
* @return systick counts per second * @return systick counts per second
@ -57,9 +55,9 @@ void delayTicks(TickType_t ticks);
/** Delay until tick /** Delay until tick
* @warning don't call from ISR context * @warning don't call from ISR context
* @param[in] ticks The tick until which kerel should delay task execution * @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 /** Convert milliseconds to ticks
* *

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "I2cCompat.h" #include "I2cCompat.h"
#include "CoreTypes.h"
#include "RtosCompat.h" #include "RtosCompat.h"
#include <climits> #include <climits>
#include <string> #include <string>
@ -29,10 +28,10 @@ struct Configuration {
i2c_config_t config; i2c_config_t config;
}; };
enum Status { enum class Status {
STARTED, Started,
STOPPED, Stopped,
UNKNOWN Unknown
}; };
bool init(const std::vector<i2c::Configuration>& configurations); bool init(const std::vector<i2c::Configuration>& configurations);

View File

@ -1,4 +1,5 @@
#include "I2cDevice.h" #include "I2cDevice.h"
#include <cstdint>
bool I2cDevice::readRegister12(uint8_t reg, float& out) const { bool I2cDevice::readRegister12(uint8_t reg, float& out) const {
std::uint8_t data[2] = {0}; std::uint8_t data[2] = {0};

View File

@ -25,7 +25,7 @@ void addService(std::shared_ptr<const ServiceManifest> manifest, bool autoStart)
TT_LOG_I(TAG, "Adding %s", id.c_str()); TT_LOG_I(TAG, "Adding %s", id.c_str());
manifest_mutex.lock(TtWaitForever); manifest_mutex.lock();
if (service_manifest_map[id] == nullptr) { if (service_manifest_map[id] == nullptr) {
service_manifest_map[id] = std::move(manifest); service_manifest_map[id] = std::move(manifest);
} else { } else {
@ -43,18 +43,18 @@ void addService(const ServiceManifest& manifest, bool autoStart) {
} }
std::shared_ptr<const ServiceManifest> _Nullable findManifestId(const std::string& id) { std::shared_ptr<const ServiceManifest> _Nullable findManifestId(const std::string& id) {
manifest_mutex.acquire(TtWaitForever); manifest_mutex.lock();
auto iterator = service_manifest_map.find(id); auto iterator = service_manifest_map.find(id);
auto manifest = iterator != service_manifest_map.end() ? iterator->second : nullptr; auto manifest = iterator != service_manifest_map.end() ? iterator->second : nullptr;
manifest_mutex.release(); manifest_mutex.unlock();
return manifest; return manifest;
} }
static std::shared_ptr<ServiceInstance> _Nullable findServiceInstanceById(const std::string& id) { static std::shared_ptr<ServiceInstance> _Nullable findServiceInstanceById(const std::string& id) {
manifest_mutex.acquire(TtWaitForever); manifest_mutex.lock();
auto iterator = service_instance_map.find(id); auto iterator = service_instance_map.find(id);
auto service = iterator != service_instance_map.end() ? iterator->second : nullptr; auto service = iterator != service_instance_map.end() ? iterator->second : nullptr;
manifest_mutex.release(); manifest_mutex.unlock();
return service; return service;
} }
@ -70,9 +70,9 @@ bool startService(const std::string& id) {
auto service_instance = std::make_shared<ServiceInstance>(manifest); auto service_instance = std::make_shared<ServiceInstance>(manifest);
// Register first, so that a service can retrieve itself during onStart() // 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; service_instance_map[manifest->id] = service_instance;
instance_mutex.release(); instance_mutex.unlock();
service_instance->getService()->onStart(*service_instance); service_instance->getService()->onStart(*service_instance);
@ -100,9 +100,9 @@ bool stopService(const std::string& id) {
service_instance->getService()->onStop(*service_instance); service_instance->getService()->onStop(*service_instance);
instance_mutex.acquire(TtWaitForever); instance_mutex.lock();
service_instance_map.erase(id); service_instance_map.erase(id);
instance_mutex.release(); instance_mutex.unlock();
if (service_instance.use_count() > 1) { 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); TT_LOG_W(TAG, "Possible memory leak: service %s still has %ld references", service_instance->getManifest().id.c_str(), service_instance.use_count() - 1);

View File

@ -20,11 +20,11 @@ private:
hal::SdCard::State lastState = hal::SdCard::State::Unmounted; hal::SdCard::State lastState = hal::SdCard::State::Unmounted;
bool lock(TickType_t timeout) const { bool lock(TickType_t timeout) const {
return mutex.acquire(timeout) == TtStatusOk; return mutex.lock(timeout);
} }
void unlock() const { void unlock() const {
tt_check(mutex.release() == TtStatusOk); mutex.unlock();
} }
void update() { void update() {

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "Pubsub.h" #include "PubSub.h"
#include "WifiGlobals.h" #include "WifiGlobals.h"
#include "WifiSettings.h" #include "WifiSettings.h"
#include <cstdio> #include <cstdio>

View File

@ -73,53 +73,53 @@ public:
RadioState getRadioState() const { RadioState getRadioState() const {
auto lockable = dataMutex.scoped(); auto lockable = dataMutex.scoped();
lockable->lock(TtWaitForever); lockable->lock();
// TODO: Handle lock failure // TODO: Handle lock failure
return radio_state; return radio_state;
} }
void setRadioState(RadioState newState) { void setRadioState(RadioState newState) {
auto lockable = dataMutex.scoped(); auto lockable = dataMutex.scoped();
lockable->lock(TtWaitForever); lockable->lock();
// TODO: Handle lock failure // TODO: Handle lock failure
radio_state = newState; radio_state = newState;
} }
bool isScanning() const { bool isScanning() const {
auto lockable = dataMutex.scoped(); auto lockable = dataMutex.scoped();
lockable->lock(TtWaitForever); lockable->lock();
// TODO: Handle lock failure // TODO: Handle lock failure
return scan_active; return scan_active;
} }
void setScanning(bool newState) { void setScanning(bool newState) {
auto lockable = dataMutex.scoped(); auto lockable = dataMutex.scoped();
lockable->lock(TtWaitForever); lockable->lock();
// TODO: Handle lock failure // TODO: Handle lock failure
scan_active = newState; scan_active = newState;
} }
bool isScanActive() const { bool isScanActive() const {
auto lcokable = dataMutex.scoped(); auto lcokable = dataMutex.scoped();
lcokable->lock(TtWaitForever); lcokable->lock();
return scan_active; return scan_active;
} }
void setScanActive(bool newState) { void setScanActive(bool newState) {
auto lockable = dataMutex.scoped(); auto lockable = dataMutex.scoped();
lockable->lock(TtWaitForever); lockable->lock();
scan_active = newState; scan_active = newState;
} }
bool isSecureConnection() const { bool isSecureConnection() const {
auto lockable = dataMutex.scoped(); auto lockable = dataMutex.scoped();
lockable->lock(TtWaitForever); lockable->lock();
return secure_connection; return secure_connection;
} }
void setSecureConnection(bool newState) { void setSecureConnection(bool newState) {
auto lockable = dataMutex.scoped(); auto lockable = dataMutex.scoped();
lockable->lock(TtWaitForever); lockable->lock();
secure_connection = newState; secure_connection = newState;
} }
}; };
@ -325,7 +325,7 @@ int getRssi() {
static void scan_list_alloc(std::shared_ptr<Wifi> wifi) { static void scan_list_alloc(std::shared_ptr<Wifi> wifi) {
auto lockable = wifi->dataMutex.scoped(); auto lockable = wifi->dataMutex.scoped();
if (lockable->lock(TtWaitForever)) { if (lockable->lock()) {
assert(wifi->scan_list == nullptr); assert(wifi->scan_list == nullptr);
wifi->scan_list = static_cast<wifi_ap_record_t*>(malloc(sizeof(wifi_ap_record_t) * wifi->scan_list_limit)); wifi->scan_list = static_cast<wifi_ap_record_t*>(malloc(sizeof(wifi_ap_record_t) * wifi->scan_list_limit));
wifi->scan_list_count = 0; wifi->scan_list_count = 0;
@ -334,7 +334,7 @@ static void scan_list_alloc(std::shared_ptr<Wifi> wifi) {
static void scan_list_alloc_safely(std::shared_ptr<Wifi> wifi) { static void scan_list_alloc_safely(std::shared_ptr<Wifi> wifi) {
auto lockable = wifi->dataMutex.scoped(); auto lockable = wifi->dataMutex.scoped();
if (lockable->lock(TtWaitForever)) { if (lockable->lock()) {
if (wifi->scan_list == nullptr) { if (wifi->scan_list == nullptr) {
scan_list_alloc(wifi); scan_list_alloc(wifi);
} }
@ -343,7 +343,7 @@ static void scan_list_alloc_safely(std::shared_ptr<Wifi> wifi) {
static void scan_list_free(std::shared_ptr<Wifi> wifi) { static void scan_list_free(std::shared_ptr<Wifi> wifi) {
auto lockable = wifi->dataMutex.scoped(); auto lockable = wifi->dataMutex.scoped();
if (lockable->lock(TtWaitForever)) { if (lockable->lock()) {
assert(wifi->scan_list != nullptr); assert(wifi->scan_list != nullptr);
free(wifi->scan_list); free(wifi->scan_list);
wifi->scan_list = nullptr; wifi->scan_list = nullptr;
@ -353,7 +353,7 @@ static void scan_list_free(std::shared_ptr<Wifi> wifi) {
static void scan_list_free_safely(std::shared_ptr<Wifi> wifi) { static void scan_list_free_safely(std::shared_ptr<Wifi> wifi) {
auto lockable = wifi->dataMutex.scoped(); auto lockable = wifi->dataMutex.scoped();
if (lockable->lock(TtWaitForever)) { if (lockable->lock()) {
if (wifi->scan_list != nullptr) { if (wifi->scan_list != nullptr) {
scan_list_free(wifi); scan_list_free(wifi);
} }
@ -362,9 +362,9 @@ static void scan_list_free_safely(std::shared_ptr<Wifi> wifi) {
static void publish_event_simple(std::shared_ptr<Wifi> wifi, EventType type) { static void publish_event_simple(std::shared_ptr<Wifi> wifi, EventType type) {
auto lockable = wifi->dataMutex.scoped(); auto lockable = wifi->dataMutex.scoped();
if (lockable->lock(TtWaitForever)) { if (lockable->lock()) {
Event turning_on_event = {.type = type}; 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> wifi) {
} }
auto lockable = wifi->dataMutex.scoped(); auto lockable = wifi->dataMutex.scoped();
if (!lockable->lock(TtWaitForever)) { if (!lockable->lock()) {
return false; 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) { if (event_base == WIFI_EVENT) {
TT_LOG_I(TAG, "eventHandler: WIFI_EVENT (%ld)", event_id); TT_LOG_I(TAG, "eventHandler: WIFI_EVENT (%ld)", event_id);
} else if (event_base == IP_EVENT) { } 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) { 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 wifi->autoConnectTimer = nullptr; // Must release as it holds a reference to this Wifi instance
// Acquire all mutexes // Acquire all mutexes
wifi->dataMutex.acquire(TtWaitForever); wifi->dataMutex.lock();
wifi->radioMutex.acquire(TtWaitForever); wifi->radioMutex.lock();
// Detach // Detach
wifi_singleton = nullptr; wifi_singleton = nullptr;
// Release mutexes // Release mutexes
wifi->dataMutex.release(); wifi->dataMutex.unlock();
wifi->radioMutex.release(); wifi->radioMutex.unlock();
// Release (hopefully) last Wifi instance by scope // Release (hopefully) last Wifi instance by scope
} }

View File

@ -6,7 +6,7 @@
#include "Log.h" #include "Log.h"
#include "MessageQueue.h" #include "MessageQueue.h"
#include "Mutex.h" #include "Mutex.h"
#include "Pubsub.h" #include "PubSub.h"
#include "service/ServiceContext.h" #include "service/ServiceContext.h"
namespace tt::service::wifi { namespace tt::service::wifi {
@ -36,7 +36,7 @@ static Wifi* wifi = nullptr;
static void publish_event_simple(Wifi* wifi, EventType type) { static void publish_event_simple(Wifi* wifi, EventType type) {
Event turning_on_event = { .type = type }; Event turning_on_event = { .type = type };
tt_pubsub_publish(wifi->pubsub, &turning_on_event); wifi->pubsub->publish(&turning_on_event);
} }
// endregion Static // endregion Static