mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-04-18 17:35:05 +00:00
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:
parent
c2edbad0fb
commit
686f7cce83
8
.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
vendored
Normal file
8
.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
vendored
Normal 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)
|
||||
2
.github/workflows/build-firmware.yml
vendored
2
.github/workflows/build-firmware.yml
vendored
@ -1,6 +1,8 @@
|
||||
name: Build Firmware
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
|
||||
2
.github/workflows/build-sdk.yml
vendored
2
.github/workflows/build-sdk.yml
vendored
@ -1,6 +1,8 @@
|
||||
name: Build SDK
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
|
||||
2
.github/workflows/build-simulator.yml
vendored
2
.github/workflows/build-simulator.yml
vendored
@ -2,6 +2,8 @@
|
||||
name: Build Simulator
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
jobs:
|
||||
|
||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@ -1,6 +1,8 @@
|
||||
name: Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
jobs:
|
||||
|
||||
@ -19,12 +19,12 @@ static bool task_running = false;
|
||||
|
||||
static void lvgl_task(void* arg);
|
||||
|
||||
static bool task_lock(int timeout_ticks) {
|
||||
return task_mutex.acquire(timeout_ticks) == tt::TtStatusOk;
|
||||
static bool task_lock(TickType_t timeout) {
|
||||
return task_mutex.lock(timeout);
|
||||
}
|
||||
|
||||
static void task_unlock() {
|
||||
task_mutex.release();
|
||||
task_mutex.unlock();
|
||||
}
|
||||
|
||||
static void task_set_running(bool running) {
|
||||
@ -41,15 +41,15 @@ bool lvgl_task_is_running() {
|
||||
}
|
||||
|
||||
static bool lvgl_lock(uint32_t timeoutMillis) {
|
||||
return lvgl_mutex.acquire(pdMS_TO_TICKS(timeoutMillis)) == tt::TtStatusOk;
|
||||
return lvgl_mutex.lock(pdMS_TO_TICKS(timeoutMillis));
|
||||
}
|
||||
|
||||
static void lvgl_unlock() {
|
||||
lvgl_mutex.release();
|
||||
lvgl_mutex.unlock();
|
||||
}
|
||||
|
||||
void lvgl_task_interrupt() {
|
||||
tt_check(task_lock(tt::TtWaitForever));
|
||||
tt_check(task_lock(portMAX_DELAY));
|
||||
task_set_running(false); // interrupt task with boolean as flag
|
||||
task_unlock();
|
||||
}
|
||||
|
||||
@ -1,22 +1,9 @@
|
||||
# Issues
|
||||
- WiFi bug: when pressing disconnect while between `WIFI_EVENT_STA_START` and `IP_EVENT_STA_GOT_IP`, then auto-connect becomes active again.
|
||||
- ESP32 (CYD) memory issues (or any device without PSRAM):
|
||||
- Boot app doesn't show logo
|
||||
- WiFi is on and navigating back to Desktop makes desktop icons disappear
|
||||
- WiFi might fail quietly when trying to enable it: this shows no feedback (force it by increasing LVGL buffers to 100kB)
|
||||
Possible mitigations:
|
||||
- When no PSRAM is available, use simplified desktop buttons
|
||||
- Add statusbar icon for memory pressure.
|
||||
- Show error in WiFi screen (e.g. AlertDialog when SPI is not enabled and available memory is below a certain amount)
|
||||
- Clean up static_cast when casting to base class.
|
||||
- EventFlag: Fix return value of set/get/wait (the errors are weirdly mixed in)
|
||||
- Fix bug in T-Deck/etc: esp_lvgl_port settings has a large stack size (~9kB) to fix an issue where the T-Deck would get a stackoverflow. This sometimes happens when WiFi is auto-enabled and you open the app while it is still connecting.
|
||||
- M5Stack Core only shows 4MB of SPIRAM in use
|
||||
|
||||
# TODOs
|
||||
- Fix bug in T-Deck/etc: esp_lvgl_port settings has a large stack size (~9kB) to fix an issue where the T-Deck would get a stackoverflow. This sometimes happens when WiFi is auto-enabled and you open the app while it is still connecting.
|
||||
- Clean up static_cast when casting to base class.
|
||||
- Mutex: Implement give/take from ISR support (works only for non-recursive ones)
|
||||
- Extend unPhone power driver: add charging status, usb connection status, etc.
|
||||
- Expose app::Paths to TactilityC
|
||||
- Refactor ServiceManifest into C++ class-based design like the App class
|
||||
- Experiment with what happens when using C++ code in an external app (without using standard library!)
|
||||
- Boards' CMakeLists.txt manually declare each source folder. Update them all to do a recursive search of all folders.
|
||||
- We currently build all boards for a given platform (e.g. ESP32S3), but it's better to filter all irrelevant ones based on the Kconfig board settings:
|
||||
|
||||
@ -42,7 +42,7 @@ public:
|
||||
bool setEntriesForPath(const std::string& path);
|
||||
|
||||
const std::vector<dirent>& lockEntries() const {
|
||||
mutex.lock(TtWaitForever);
|
||||
mutex.lock();
|
||||
return dir_entries;
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ private:
|
||||
.onConnectSsidContext = nullptr
|
||||
};
|
||||
View view = View(&bindings, &state);
|
||||
PubSubSubscription* wifiSubscription;
|
||||
PubSub::SubscriptionHandle wifiSubscription;
|
||||
bool view_enabled = false;
|
||||
|
||||
public:
|
||||
|
||||
@ -12,7 +12,7 @@ class WifiManage : public App {
|
||||
|
||||
private:
|
||||
|
||||
PubSubSubscription* wifiSubscription = nullptr;
|
||||
PubSub::SubscriptionHandle wifiSubscription = nullptr;
|
||||
Mutex mutex;
|
||||
Bindings bindings = { };
|
||||
State state;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
#include "MessageQueue.h"
|
||||
#include "Mutex.h"
|
||||
#include "Pubsub.h"
|
||||
#include "PubSub.h"
|
||||
#include "service/gui/Gui.h"
|
||||
#include <cstdio>
|
||||
#include <lvgl.h>
|
||||
@ -19,7 +19,7 @@ struct Gui {
|
||||
// Thread and lock
|
||||
Thread* thread = nullptr;
|
||||
Mutex mutex = Mutex(Mutex::Type::Recursive);
|
||||
PubSubSubscription* loader_pubsub_subscription = nullptr;
|
||||
PubSub::SubscriptionHandle loader_pubsub_subscription = nullptr;
|
||||
|
||||
// Layers and Canvas
|
||||
lv_obj_t* appRootWidget = nullptr;
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/AppManifest.h"
|
||||
#include "app/AppInstance.h"
|
||||
#include "EventFlag.h"
|
||||
#include "MessageQueue.h"
|
||||
#include "Pubsub.h"
|
||||
#include "Thread.h"
|
||||
#include "service/loader/Loader.h"
|
||||
#include "PubSub.h"
|
||||
#include "RtosCompatSemaphore.h"
|
||||
#include "Thread.h"
|
||||
#include "app/AppInstance.h"
|
||||
#include "app/AppManifest.h"
|
||||
#include "service/loader/Loader.h"
|
||||
#include <DispatcherThread.h>
|
||||
#include <stack>
|
||||
#include <utility>
|
||||
#include <DispatcherThread.h>
|
||||
|
||||
namespace tt::service::loader {
|
||||
|
||||
|
||||
@ -163,7 +163,7 @@ void run(const Configuration& config) {
|
||||
|
||||
TT_LOG_I(TAG, "Processing main dispatcher");
|
||||
while (true) {
|
||||
getMainDispatcher().consume(TtWaitForever);
|
||||
getMainDispatcher().consume();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,15 +6,15 @@ namespace tt::app {
|
||||
#define TAG "app"
|
||||
|
||||
void AppInstance::setState(State newState) {
|
||||
mutex.acquire(TtWaitForever);
|
||||
mutex.lock();
|
||||
state = newState;
|
||||
mutex.release();
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
State AppInstance::getState() const {
|
||||
mutex.acquire(TtWaitForever);
|
||||
mutex.lock();
|
||||
auto result = state;
|
||||
mutex.release();
|
||||
mutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -30,22 +30,22 @@ const AppManifest& AppInstance::getManifest() const {
|
||||
}
|
||||
|
||||
Flags AppInstance::getFlags() const {
|
||||
mutex.acquire(TtWaitForever);
|
||||
mutex.lock();
|
||||
auto result = flags;
|
||||
mutex.release();
|
||||
mutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
void AppInstance::setFlags(Flags newFlags) {
|
||||
mutex.acquire(TtWaitForever);
|
||||
mutex.lock();
|
||||
flags = newFlags;
|
||||
mutex.release();
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
std::shared_ptr<const Bundle> AppInstance::getParameters() const {
|
||||
mutex.acquire(TtWaitForever);
|
||||
mutex.lock();
|
||||
std::shared_ptr<const Bundle> result = parameters;
|
||||
mutex.release();
|
||||
mutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ static Mutex hash_mutex(Mutex::Type::Normal);
|
||||
void addApp(const AppManifest& manifest) {
|
||||
TT_LOG_I(TAG, "Registering manifest %s", manifest.id.c_str());
|
||||
|
||||
hash_mutex.acquire(TtWaitForever);
|
||||
hash_mutex.lock();
|
||||
|
||||
if (!app_manifest_map.contains(manifest.id)) {
|
||||
app_manifest_map[manifest.id] = std::make_shared<AppManifest>(manifest);
|
||||
@ -23,13 +23,13 @@ void addApp(const AppManifest& manifest) {
|
||||
TT_LOG_E(TAG, "App id in use: %s", manifest.id.c_str());
|
||||
}
|
||||
|
||||
hash_mutex.release();
|
||||
hash_mutex.unlock();
|
||||
}
|
||||
|
||||
_Nullable std::shared_ptr<AppManifest> findAppById(const std::string& id) {
|
||||
hash_mutex.acquire(TtWaitForever);
|
||||
hash_mutex.lock();
|
||||
auto result = app_manifest_map.find(id);
|
||||
hash_mutex.release();
|
||||
hash_mutex.unlock();
|
||||
if (result != app_manifest_map.end()) {
|
||||
return result->second;
|
||||
} else {
|
||||
@ -39,11 +39,11 @@ _Nullable std::shared_ptr<AppManifest> findAppById(const std::string& id) {
|
||||
|
||||
std::vector<std::shared_ptr<AppManifest>> getApps() {
|
||||
std::vector<std::shared_ptr<AppManifest>> manifests;
|
||||
hash_mutex.acquire(TtWaitForever);
|
||||
hash_mutex.lock();
|
||||
for (const auto& item: app_manifest_map) {
|
||||
manifests.push_back(item.second);
|
||||
}
|
||||
hash_mutex.release();
|
||||
hash_mutex.unlock();
|
||||
return manifests;
|
||||
}
|
||||
|
||||
|
||||
@ -26,11 +26,11 @@ private:
|
||||
public:
|
||||
|
||||
void lock() const {
|
||||
tt_check(mutex.acquire(1000) == TtStatusOk);
|
||||
tt_check(mutex.lock(1000));
|
||||
}
|
||||
|
||||
void unlock() const {
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
tt_check(mutex.unlock());
|
||||
}
|
||||
|
||||
void onShow(AppContext& app, lv_obj_t* parent) override;
|
||||
|
||||
@ -115,12 +115,12 @@ void I2cScannerApp::onShow(AppContext& app, lv_obj_t* parent) {
|
||||
|
||||
void I2cScannerApp::onHide(AppContext& app) {
|
||||
bool isRunning = false;
|
||||
if (mutex.acquire(250 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
if (mutex.lock(250 / portTICK_PERIOD_MS)) {
|
||||
auto* timer = scanTimer.get();
|
||||
if (timer != nullptr) {
|
||||
isRunning = timer->isRunning();
|
||||
}
|
||||
mutex.release();
|
||||
mutex.unlock();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@ -158,9 +158,9 @@ void I2cScannerApp::onScanTimerCallback(TT_UNUSED std::shared_ptr<void> context)
|
||||
// endregion Callbacks
|
||||
|
||||
bool I2cScannerApp::getPort(i2c_port_t* outPort) {
|
||||
if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
if (mutex.lock(100 / portTICK_PERIOD_MS)) {
|
||||
*outPort = this->port;
|
||||
assert(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
return true;
|
||||
} else {
|
||||
TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "getPort");
|
||||
@ -169,9 +169,9 @@ bool I2cScannerApp::getPort(i2c_port_t* outPort) {
|
||||
}
|
||||
|
||||
bool I2cScannerApp::addAddressToList(uint8_t address) {
|
||||
if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
if (mutex.lock(100 / portTICK_PERIOD_MS)) {
|
||||
scannedAddresses.push_back(address);
|
||||
assert(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
return true;
|
||||
} else {
|
||||
TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "addAddressToList");
|
||||
@ -180,9 +180,9 @@ bool I2cScannerApp::addAddressToList(uint8_t address) {
|
||||
}
|
||||
|
||||
bool I2cScannerApp::shouldStopScanTimer() {
|
||||
if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
if (mutex.lock(100 / portTICK_PERIOD_MS)) {
|
||||
bool is_scanning = scanState == ScanStateScanning;
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
return !is_scanning;
|
||||
} else {
|
||||
return true;
|
||||
@ -222,9 +222,9 @@ void I2cScannerApp::onScanTimer() {
|
||||
|
||||
bool I2cScannerApp::hasScanThread() {
|
||||
bool has_thread;
|
||||
if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
if (mutex.lock(100 / portTICK_PERIOD_MS)) {
|
||||
has_thread = scanTimer != nullptr;
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
return has_thread;
|
||||
} else {
|
||||
// Unsafe way
|
||||
@ -238,7 +238,7 @@ void I2cScannerApp::startScanning() {
|
||||
stopScanning();
|
||||
}
|
||||
|
||||
if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
if (mutex.lock(100 / portTICK_PERIOD_MS)) {
|
||||
scannedAddresses.clear();
|
||||
|
||||
lv_obj_add_flag(scanListWidget, LV_OBJ_FLAG_HIDDEN);
|
||||
@ -250,16 +250,16 @@ void I2cScannerApp::startScanning() {
|
||||
onScanTimerCallback
|
||||
);
|
||||
scanTimer->start(10);
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
} else {
|
||||
TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "startScanning");
|
||||
}
|
||||
}
|
||||
void I2cScannerApp::stopScanning() {
|
||||
if (mutex.acquire(250 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
if (mutex.lock(250 / portTICK_PERIOD_MS)) {
|
||||
assert(scanTimer != nullptr);
|
||||
scanState = ScanStateStopped;
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
} else {
|
||||
TT_LOG_E(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED);
|
||||
}
|
||||
@ -271,11 +271,11 @@ void I2cScannerApp::onSelectBus(lv_event_t* event) {
|
||||
auto i2c_devices = tt::getConfiguration()->hardware->i2c;
|
||||
assert(selected < i2c_devices.size());
|
||||
|
||||
if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
if (mutex.lock(100 / portTICK_PERIOD_MS)) {
|
||||
scannedAddresses.clear();
|
||||
port = i2c_devices[selected].port;
|
||||
scanState = ScanStateInitial;
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
|
||||
updateViews();
|
||||
}
|
||||
@ -293,7 +293,7 @@ void I2cScannerApp::onPressScan(TT_UNUSED lv_event_t* event) {
|
||||
}
|
||||
|
||||
void I2cScannerApp::updateViews() {
|
||||
if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
if (mutex.lock(100 / portTICK_PERIOD_MS)) {
|
||||
if (scanState == ScanStateScanning) {
|
||||
lv_label_set_text(scanButtonLabelWidget, STOP_SCAN_TEXT);
|
||||
lv_obj_remove_flag(portDropdownWidget, LV_OBJ_FLAG_CLICKABLE);
|
||||
@ -317,7 +317,7 @@ void I2cScannerApp::updateViews() {
|
||||
lv_obj_add_flag(scanListWidget, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
} else {
|
||||
TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "updateViews");
|
||||
}
|
||||
@ -333,12 +333,12 @@ void I2cScannerApp::updateViewsSafely() {
|
||||
}
|
||||
|
||||
void I2cScannerApp::onScanTimerFinished() {
|
||||
if (mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) {
|
||||
if (mutex.lock(100 / portTICK_PERIOD_MS)) {
|
||||
if (scanState == ScanStateScanning) {
|
||||
scanState = ScanStateStopped;
|
||||
updateViewsSafely();
|
||||
}
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
} else {
|
||||
TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "onScanTimerFinished");
|
||||
}
|
||||
|
||||
@ -1,47 +1,46 @@
|
||||
#include "app/wificonnect/State.h"
|
||||
#include "Check.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace tt::app::wificonnect {
|
||||
|
||||
void State::setConnectionError(bool error) {
|
||||
tt_check(lock.acquire(TtWaitForever) == TtStatusOk);
|
||||
lock.lock();
|
||||
connectionError = error;
|
||||
tt_check(lock.release() == TtStatusOk);
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
bool State::hasConnectionError() const {
|
||||
tt_check(lock.acquire(TtWaitForever) == TtStatusOk);
|
||||
lock.lock();
|
||||
auto result = connectionError;
|
||||
tt_check(lock.release() == TtStatusOk);
|
||||
lock.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
void State::setApSettings(const service::wifi::settings::WifiApSettings* newSettings) {
|
||||
tt_check(lock.acquire(TtWaitForever) == TtStatusOk);
|
||||
lock.lock();
|
||||
memcpy(&this->apSettings, newSettings, sizeof(service::wifi::settings::WifiApSettings));
|
||||
tt_check(lock.release() == TtStatusOk);
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
const service::wifi::settings::WifiApSettings& State::lockApSettings() {
|
||||
tt_check(lock.acquire(TtWaitForever) == TtStatusOk);
|
||||
lock.lock();
|
||||
return apSettings;
|
||||
}
|
||||
|
||||
void State::unlockApSettings() {
|
||||
tt_check(lock.release() == TtStatusOk);
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
void State::setConnecting(bool isConnecting) {
|
||||
tt_check(lock.acquire(TtWaitForever) == TtStatusOk);
|
||||
lock.lock();
|
||||
connecting = isConnecting;
|
||||
tt_check(lock.release() == TtStatusOk);
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
bool State::isConnecting() const {
|
||||
tt_check(lock.acquire(TtWaitForever) == TtStatusOk);
|
||||
lock.lock();
|
||||
auto result = connecting;
|
||||
tt_check(lock.release() == TtStatusOk);
|
||||
lock.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -56,8 +56,7 @@ static void onConnect(const service::wifi::settings::WifiApSettings* ap_settings
|
||||
}
|
||||
|
||||
WifiConnect::WifiConnect() {
|
||||
auto wifi_pubsub = service::wifi::getPubsub();
|
||||
wifiSubscription = tt_pubsub_subscribe(wifi_pubsub, &eventCallback, this);
|
||||
wifiSubscription = service::wifi::getPubsub()->subscribe(&eventCallback, this);
|
||||
bindings = (Bindings) {
|
||||
.onConnectSsid = onConnect,
|
||||
.onConnectSsidContext = this,
|
||||
@ -65,16 +64,15 @@ WifiConnect::WifiConnect() {
|
||||
}
|
||||
|
||||
WifiConnect::~WifiConnect() {
|
||||
auto pubsub = service::wifi::getPubsub();
|
||||
tt_pubsub_unsubscribe(pubsub, wifiSubscription);
|
||||
service::wifi::getPubsub()->unsubscribe(wifiSubscription);
|
||||
}
|
||||
|
||||
void WifiConnect::lock() {
|
||||
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk);
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
void WifiConnect::unlock() {
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void WifiConnect::requestViewUpdate() {
|
||||
|
||||
@ -1,63 +1,61 @@
|
||||
#include <Check.h>
|
||||
#include "app/wifimanage/WifiManagePrivate.h"
|
||||
|
||||
namespace tt::app::wifimanage {
|
||||
|
||||
void State::setScanning(bool isScanning) {
|
||||
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk);
|
||||
mutex.lock();
|
||||
scanning = isScanning;
|
||||
scannedAfterRadioOn |= isScanning;
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void State::setRadioState(service::wifi::RadioState state) {
|
||||
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk);
|
||||
mutex.lock();
|
||||
radioState = state;
|
||||
if (radioState == service::wifi::RadioState::Off) {
|
||||
scannedAfterRadioOn = false;
|
||||
}
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
service::wifi::RadioState State::getRadioState() const {
|
||||
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk);
|
||||
mutex.lock();
|
||||
auto result = radioState;
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool State::isScanning() const {
|
||||
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk);
|
||||
mutex.lock();
|
||||
bool result = scanning;
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::vector<service::wifi::ApRecord>& State::lockApRecords() const {
|
||||
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk);
|
||||
mutex.lock();
|
||||
return apRecords;
|
||||
}
|
||||
|
||||
void State::unlockApRecords() const {
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void State::updateApRecords() {
|
||||
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk);
|
||||
mutex.lock();
|
||||
apRecords = service::wifi::getScanResults();
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void State::setConnectSsid(const std::string& ssid) {
|
||||
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk);
|
||||
mutex.lock();
|
||||
connectSsid = ssid;
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
std::string State::getConnectSsid() const {
|
||||
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk);
|
||||
mutex.lock();
|
||||
auto result = connectSsid;
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -64,11 +64,11 @@ WifiManage::WifiManage() {
|
||||
}
|
||||
|
||||
void WifiManage::lock() {
|
||||
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk);
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
void WifiManage::unlock() {
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void WifiManage::requestViewUpdate() {
|
||||
@ -111,8 +111,7 @@ static void wifiManageEventCallback(const void* message, void* context) {
|
||||
}
|
||||
|
||||
void WifiManage::onShow(AppContext& app, lv_obj_t* parent) {
|
||||
auto wifi_pubsub = service::wifi::getPubsub();
|
||||
wifiSubscription = tt_pubsub_subscribe(wifi_pubsub, &wifiManageEventCallback, this);
|
||||
wifiSubscription = service::wifi::getPubsub()->subscribe(&wifiManageEventCallback, this);
|
||||
|
||||
// State update (it has its own locking)
|
||||
state.setRadioState(service::wifi::getRadioState());
|
||||
@ -139,8 +138,7 @@ void WifiManage::onShow(AppContext& app, lv_obj_t* parent) {
|
||||
|
||||
void WifiManage::onHide(TT_UNUSED AppContext& app) {
|
||||
lock();
|
||||
auto wifi_pubsub = service::wifi::getPubsub();
|
||||
tt_pubsub_unsubscribe(wifi_pubsub, wifiSubscription);
|
||||
service::wifi::getPubsub()->unsubscribe(wifiSubscription);
|
||||
wifiSubscription = nullptr;
|
||||
isViewEnabled = false;
|
||||
unlock();
|
||||
|
||||
@ -6,11 +6,11 @@ namespace tt::lvgl {
|
||||
static Mutex lockMutex;
|
||||
|
||||
static bool defaultLock(uint32_t timeoutMillis) {
|
||||
return lockMutex.acquire(timeoutMillis) == TtStatusOk;
|
||||
return lockMutex.lock(timeoutMillis);
|
||||
}
|
||||
|
||||
static void defaultUnlock() {
|
||||
lockMutex.release();
|
||||
lockMutex.unlock();
|
||||
}
|
||||
|
||||
static LvglLock lock_singleton = defaultLock;
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
#include "Statusbar.h"
|
||||
|
||||
#include "Mutex.h"
|
||||
#include "Pubsub.h"
|
||||
#include "PubSub.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "lvgl/Style.h"
|
||||
|
||||
@ -43,10 +43,10 @@ typedef struct {
|
||||
lv_obj_t* time;
|
||||
lv_obj_t* icons[STATUSBAR_ICON_LIMIT];
|
||||
lv_obj_t* battery_icon;
|
||||
PubSubSubscription* pubsub_subscription;
|
||||
PubSub::SubscriptionHandle pubsub_subscription;
|
||||
} Statusbar;
|
||||
|
||||
static bool statusbar_lock(uint32_t timeoutTicks) {
|
||||
static bool statusbar_lock(uint32_t timeoutTicks = portMAX_DELAY) {
|
||||
return statusbar_data.mutex.lock(timeoutTicks);
|
||||
}
|
||||
|
||||
@ -83,7 +83,7 @@ static void onUpdateTime(TT_UNUSED std::shared_ptr<void> context) {
|
||||
statusbar_data.time_update_timer->start(getNextUpdateTime());
|
||||
|
||||
// Notify widget
|
||||
tt_pubsub_publish(statusbar_data.pubsub, nullptr);
|
||||
statusbar_data.pubsub->publish(nullptr);
|
||||
} else {
|
||||
statusbar_data.time_update_timer->start(pdMS_TO_TICKS(60000U));
|
||||
}
|
||||
@ -132,7 +132,7 @@ static void statusbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj)
|
||||
lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
|
||||
LV_TRACE_OBJ_CREATE("finished");
|
||||
auto* statusbar = (Statusbar*)obj;
|
||||
statusbar->pubsub_subscription = tt_pubsub_subscribe(statusbar_data.pubsub, &statusbar_pubsub_event, statusbar);
|
||||
statusbar->pubsub_subscription = statusbar_data.pubsub->subscribe(&statusbar_pubsub_event, statusbar);
|
||||
|
||||
if (!statusbar_data.time_update_timer->isRunning()) {
|
||||
statusbar_data.time_update_timer->start(50 / portTICK_PERIOD_MS);
|
||||
@ -145,7 +145,7 @@ static void statusbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj)
|
||||
|
||||
static void statusbar_destructor(TT_UNUSED const lv_obj_class_t* class_p, lv_obj_t* obj) {
|
||||
auto* statusbar = (Statusbar*)obj;
|
||||
tt_pubsub_unsubscribe(statusbar_data.pubsub, statusbar->pubsub_subscription);
|
||||
statusbar_data.pubsub->unsubscribe(statusbar->pubsub_subscription);
|
||||
}
|
||||
|
||||
static void update_icon(lv_obj_t* image, const StatusbarIcon* icon) {
|
||||
@ -181,7 +181,7 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) {
|
||||
obj_set_style_bg_invisible(left_spacer);
|
||||
lv_obj_set_flex_grow(left_spacer, 1);
|
||||
|
||||
statusbar_lock(TtWaitForever);
|
||||
statusbar_lock(portMAX_DELAY);
|
||||
for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) {
|
||||
auto* image = lv_image_create(obj);
|
||||
lv_obj_set_size(image, STATUSBAR_ICON_SIZE, STATUSBAR_ICON_SIZE);
|
||||
@ -233,7 +233,7 @@ static void statusbar_event(TT_UNUSED const lv_obj_class_t* class_p, lv_event_t*
|
||||
}
|
||||
|
||||
int8_t statusbar_icon_add(const std::string& image) {
|
||||
statusbar_lock(TtWaitForever);
|
||||
statusbar_lock();
|
||||
int8_t result = -1;
|
||||
for (int8_t i = 0; i < STATUSBAR_ICON_LIMIT; ++i) {
|
||||
if (!statusbar_data.icons[i].claimed) {
|
||||
@ -245,7 +245,7 @@ int8_t statusbar_icon_add(const std::string& image) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
tt_pubsub_publish(statusbar_data.pubsub, nullptr);
|
||||
statusbar_data.pubsub->publish(nullptr);
|
||||
statusbar_unlock();
|
||||
return result;
|
||||
}
|
||||
@ -257,12 +257,12 @@ int8_t statusbar_icon_add() {
|
||||
void statusbar_icon_remove(int8_t id) {
|
||||
TT_LOG_I(TAG, "id %d: remove", id);
|
||||
tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT);
|
||||
statusbar_lock(TtWaitForever);
|
||||
statusbar_lock();
|
||||
StatusbarIcon* icon = &statusbar_data.icons[id];
|
||||
icon->claimed = false;
|
||||
icon->visible = false;
|
||||
icon->image = "";
|
||||
tt_pubsub_publish(statusbar_data.pubsub, nullptr);
|
||||
statusbar_data.pubsub->publish(nullptr);
|
||||
statusbar_unlock();
|
||||
}
|
||||
|
||||
@ -273,7 +273,7 @@ void statusbar_icon_set_image(int8_t id, const std::string& image) {
|
||||
StatusbarIcon* icon = &statusbar_data.icons[id];
|
||||
tt_check(icon->claimed);
|
||||
icon->image = image;
|
||||
tt_pubsub_publish(statusbar_data.pubsub, nullptr);
|
||||
statusbar_data.pubsub->publish(nullptr);
|
||||
statusbar_unlock();
|
||||
}
|
||||
}
|
||||
@ -285,7 +285,7 @@ void statusbar_icon_set_visibility(int8_t id, bool visible) {
|
||||
StatusbarIcon* icon = &statusbar_data.icons[id];
|
||||
tt_check(icon->claimed);
|
||||
icon->visible = visible;
|
||||
tt_pubsub_publish(statusbar_data.pubsub, nullptr);
|
||||
statusbar_data.pubsub->publish(nullptr);
|
||||
statusbar_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ Gui* gui_alloc() {
|
||||
&guiMain,
|
||||
nullptr
|
||||
);
|
||||
instance->loader_pubsub_subscription = tt_pubsub_subscribe(loader::getPubsub(), &onLoaderMessage, instance);
|
||||
instance->loader_pubsub_subscription = loader::getPubsub()->subscribe(&onLoaderMessage, instance);
|
||||
tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS));
|
||||
instance->keyboardGroup = lv_group_create();
|
||||
auto* screen_root = lv_scr_act();
|
||||
@ -90,7 +90,7 @@ void unlock() {
|
||||
void requestDraw() {
|
||||
assert(gui);
|
||||
ThreadId thread_id = gui->thread->getId();
|
||||
thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW);
|
||||
Thread::setFlags(thread_id, GUI_THREAD_FLAG_DRAW);
|
||||
}
|
||||
|
||||
void showApp(std::shared_ptr<app::AppContext> app) {
|
||||
@ -120,19 +120,19 @@ static int32_t guiMain(TT_UNUSED void* p) {
|
||||
Gui* local_gui = gui;
|
||||
|
||||
while (true) {
|
||||
uint32_t flags = thread_flags_wait(
|
||||
uint32_t flags = Thread::awaitFlags(
|
||||
GUI_THREAD_FLAG_ALL,
|
||||
TtFlagWaitAny,
|
||||
TtWaitForever
|
||||
EventFlag::WaitAny,
|
||||
portMAX_DELAY
|
||||
);
|
||||
// Process and dispatch draw call
|
||||
if (flags & GUI_THREAD_FLAG_DRAW) {
|
||||
thread_flags_clear(GUI_THREAD_FLAG_DRAW);
|
||||
Thread::clearFlags(GUI_THREAD_FLAG_DRAW);
|
||||
redraw(local_gui);
|
||||
}
|
||||
|
||||
if (flags & GUI_THREAD_FLAG_EXIT) {
|
||||
thread_flags_clear(GUI_THREAD_FLAG_EXIT);
|
||||
Thread::clearFlags(GUI_THREAD_FLAG_EXIT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -159,7 +159,7 @@ public:
|
||||
lock();
|
||||
|
||||
ThreadId thread_id = gui->thread->getId();
|
||||
thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT);
|
||||
Thread::setFlags(thread_id, GUI_THREAD_FLAG_EXIT);
|
||||
gui->thread->join();
|
||||
delete gui->thread;
|
||||
|
||||
|
||||
@ -125,13 +125,13 @@ static void transitionAppToState(std::shared_ptr<app::AppInstance> app, app::Sta
|
||||
break;
|
||||
case app::StateShowing: {
|
||||
LoaderEvent event_showing = { .type = LoaderEventTypeApplicationShowing };
|
||||
tt_pubsub_publish(loader_singleton->pubsubExternal, &event_showing);
|
||||
loader_singleton->pubsubExternal->publish(&event_showing);
|
||||
app->setState(app::StateShowing);
|
||||
break;
|
||||
}
|
||||
case app::StateHiding: {
|
||||
LoaderEvent event_hiding = { .type = LoaderEventTypeApplicationHiding };
|
||||
tt_pubsub_publish(loader_singleton->pubsubExternal, &event_hiding);
|
||||
loader_singleton->pubsubExternal->publish(&event_hiding);
|
||||
app->setState(app::StateHiding);
|
||||
break;
|
||||
}
|
||||
@ -174,7 +174,7 @@ static LoaderStatus startAppWithManifestInternal(
|
||||
transitionAppToState(new_app, app::StateShowing);
|
||||
|
||||
LoaderEvent event_external = { .type = LoaderEventTypeApplicationStarted };
|
||||
tt_pubsub_publish(loader_singleton->pubsubExternal, &event_external);
|
||||
loader_singleton->pubsubExternal->publish(&event_external);
|
||||
|
||||
return LoaderStatus::Ok;
|
||||
}
|
||||
@ -265,7 +265,7 @@ static void stopAppInternal() {
|
||||
// WARNING: After this point we cannot change the app states from this method directly anymore as we don't have a lock!
|
||||
|
||||
LoaderEvent event_external = { .type = LoaderEventTypeApplicationStopped };
|
||||
tt_pubsub_publish(loader_singleton->pubsubExternal, &event_external);
|
||||
loader_singleton->pubsubExternal->publish(&event_external);
|
||||
|
||||
if (instance_to_resume != nullptr) {
|
||||
if (result_set) {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/AppManifest.h"
|
||||
#include "Bundle.h"
|
||||
#include "Pubsub.h"
|
||||
#include "PubSub.h"
|
||||
#include "app/AppManifest.h"
|
||||
#include "service/ServiceManifest.h"
|
||||
#include <memory>
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ bool ScreenshotTask::isFinished() {
|
||||
|
||||
void ScreenshotTask::setFinished() {
|
||||
auto scoped_lockable = mutex.scoped();
|
||||
scoped_lockable->lock(TtWaitForever);
|
||||
scoped_lockable->lock();
|
||||
finished = true;
|
||||
}
|
||||
|
||||
|
||||
@ -138,11 +138,11 @@ private:
|
||||
std::unique_ptr<service::Paths> paths;
|
||||
|
||||
void lock() const {
|
||||
tt_check(mutex.acquire(TtWaitForever) == TtStatusOk);
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
void unlock() const {
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void updateWifiIcon() {
|
||||
|
||||
@ -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
|
||||
@ -5,17 +5,13 @@
|
||||
namespace tt {
|
||||
|
||||
#define TAG "dispatcher"
|
||||
#define BACKPRESSURE_WARNING_COUNT 100
|
||||
#define WAIT_FLAG 1
|
||||
|
||||
Dispatcher::Dispatcher() :
|
||||
mutex(Mutex::Type::Normal)
|
||||
{}
|
||||
#define BACKPRESSURE_WARNING_COUNT ((EventBits_t)100)
|
||||
#define WAIT_FLAG ((EventBits_t)1)
|
||||
|
||||
Dispatcher::~Dispatcher() {
|
||||
// Wait for Mutex usage
|
||||
mutex.acquire(TtWaitForever);
|
||||
mutex.release();
|
||||
mutex.lock();
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void Dispatcher::dispatch(Function function, std::shared_ptr<void> context) {
|
||||
@ -37,7 +33,7 @@ void Dispatcher::dispatch(Function function, std::shared_ptr<void> context) {
|
||||
uint32_t Dispatcher::consume(TickType_t timeout) {
|
||||
// Wait for signal and clear
|
||||
TickType_t start_ticks = kernel::getTicks();
|
||||
if (eventFlag.wait(WAIT_FLAG, TtFlagWaitAny, timeout) == WAIT_FLAG) {
|
||||
if (eventFlag.wait(WAIT_FLAG, EventFlag::WaitAny, timeout)) {
|
||||
eventFlag.clear(WAIT_FLAG);
|
||||
} else {
|
||||
return 0;
|
||||
|
||||
@ -43,7 +43,7 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
explicit Dispatcher();
|
||||
explicit Dispatcher() = default;
|
||||
~Dispatcher();
|
||||
|
||||
/**
|
||||
@ -58,7 +58,7 @@ public:
|
||||
* @param[in] timeout the ticks to wait for a message
|
||||
* @return the amount of messages that were consumed
|
||||
*/
|
||||
uint32_t consume(TickType_t timeout);
|
||||
uint32_t consume(TickType_t timeout = portMAX_DELAY);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -8,15 +8,15 @@
|
||||
|
||||
namespace tt {
|
||||
|
||||
EventFlag::EventFlag() {
|
||||
EventFlag::EventFlag() :
|
||||
handle(xEventGroupCreate())
|
||||
{
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
handle = xEventGroupCreate();
|
||||
tt_check(handle);
|
||||
}
|
||||
|
||||
EventFlag::~EventFlag() {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
vEventGroupDelete(handle);
|
||||
}
|
||||
|
||||
uint32_t EventFlag::set(uint32_t flags) const {
|
||||
@ -28,14 +28,14 @@ uint32_t EventFlag::set(uint32_t flags) const {
|
||||
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
yield = pdFALSE;
|
||||
if (xEventGroupSetBitsFromISR(handle, (EventBits_t)flags, &yield) == pdFAIL) {
|
||||
rflags = (uint32_t)TtFlagErrorResource;
|
||||
if (xEventGroupSetBitsFromISR(handle.get(), (EventBits_t)flags, &yield) == pdFAIL) {
|
||||
rflags = (uint32_t)ErrorResource;
|
||||
} else {
|
||||
rflags = flags;
|
||||
portYIELD_FROM_ISR(yield);
|
||||
}
|
||||
} else {
|
||||
rflags = xEventGroupSetBits(handle, (EventBits_t)flags);
|
||||
rflags = xEventGroupSetBits(handle.get(), (EventBits_t)flags);
|
||||
}
|
||||
|
||||
/* Return event flags after setting */
|
||||
@ -48,10 +48,10 @@ uint32_t EventFlag::clear(uint32_t flags) const {
|
||||
uint32_t rflags;
|
||||
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
rflags = xEventGroupGetBitsFromISR(handle);
|
||||
rflags = xEventGroupGetBitsFromISR(handle.get());
|
||||
|
||||
if (xEventGroupClearBitsFromISR(handle, (EventBits_t)flags) == pdFAIL) {
|
||||
rflags = (uint32_t)TtStatusErrorResource;
|
||||
if (xEventGroupClearBitsFromISR(handle.get(), (EventBits_t)flags) == pdFAIL) {
|
||||
rflags = (uint32_t)ErrorResource;
|
||||
} else {
|
||||
/* xEventGroupClearBitsFromISR only registers clear operation in the timer command queue. */
|
||||
/* Yield is required here otherwise clear operation might not execute in the right order. */
|
||||
@ -59,7 +59,7 @@ uint32_t EventFlag::clear(uint32_t flags) const {
|
||||
portYIELD_FROM_ISR(pdTRUE);
|
||||
}
|
||||
} else {
|
||||
rflags = xEventGroupClearBits(handle, (EventBits_t)flags);
|
||||
rflags = xEventGroupClearBits(handle.get(), (EventBits_t)flags);
|
||||
}
|
||||
|
||||
/* Return event flags before clearing */
|
||||
@ -70,9 +70,9 @@ uint32_t EventFlag::get() const {
|
||||
uint32_t rflags;
|
||||
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
rflags = xEventGroupGetBitsFromISR(handle);
|
||||
rflags = xEventGroupGetBitsFromISR(handle.get());
|
||||
} else {
|
||||
rflags = xEventGroupGetBits(handle);
|
||||
rflags = xEventGroupGetBits(handle.get());
|
||||
}
|
||||
|
||||
/* Return current event flags */
|
||||
@ -91,40 +91,40 @@ uint32_t EventFlag::wait(
|
||||
BaseType_t exit_clear;
|
||||
uint32_t rflags;
|
||||
|
||||
if (options & TtFlagWaitAll) {
|
||||
if (options & WaitAll) {
|
||||
wait_all = pdTRUE;
|
||||
} else {
|
||||
wait_all = pdFAIL;
|
||||
}
|
||||
|
||||
if (options & TtFlagNoClear) {
|
||||
if (options & NoClear) {
|
||||
exit_clear = pdFAIL;
|
||||
} else {
|
||||
exit_clear = pdTRUE;
|
||||
}
|
||||
|
||||
rflags = xEventGroupWaitBits(
|
||||
handle,
|
||||
handle.get(),
|
||||
(EventBits_t)flags,
|
||||
exit_clear,
|
||||
wait_all,
|
||||
(TickType_t)timeout
|
||||
);
|
||||
|
||||
if (options & TtFlagWaitAll) {
|
||||
if (options & WaitAll) {
|
||||
if ((flags & rflags) != flags) {
|
||||
if (timeout > 0U) {
|
||||
rflags = (uint32_t)TtStatusErrorTimeout;
|
||||
rflags = (uint32_t)ErrorTimeout;
|
||||
} else {
|
||||
rflags = (uint32_t)TtStatusErrorResource;
|
||||
rflags = (uint32_t)ErrorResource;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((flags & rflags) == 0U) {
|
||||
if (timeout > 0U) {
|
||||
rflags = (uint32_t)TtStatusErrorTimeout;
|
||||
rflags = (uint32_t)ErrorTimeout;
|
||||
} else {
|
||||
rflags = (uint32_t)TtStatusErrorResource;
|
||||
rflags = (uint32_t)ErrorResource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "RtosCompatEventGroups.h"
|
||||
#include <memory>
|
||||
|
||||
namespace tt {
|
||||
|
||||
@ -10,17 +10,40 @@ namespace tt {
|
||||
*/
|
||||
class EventFlag {
|
||||
private:
|
||||
EventGroupHandle_t handle;
|
||||
|
||||
struct EventGroupHandleDeleter {
|
||||
void operator()(EventGroupHandle_t handleToDelete) {
|
||||
vEventGroupDelete(handleToDelete);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<std::remove_pointer_t<EventGroupHandle_t>, EventGroupHandleDeleter> handle;
|
||||
|
||||
public:
|
||||
|
||||
EventFlag();
|
||||
~EventFlag();
|
||||
|
||||
enum Flag {
|
||||
WaitAny = 0x00000000U, ///< Wait for any flag (default).
|
||||
WaitAll = 0x00000001U, ///< Wait for all flags.
|
||||
NoClear = 0x00000002U, ///< Do not clear flags which have been specified to wait for.
|
||||
|
||||
Error = 0x80000000U, ///< Error indicator.
|
||||
ErrorUnknown = 0xFFFFFFFFU, ///< TtStatusError (-1).
|
||||
ErrorTimeout = 0xFFFFFFFEU, ///< TtStatusErrorTimeout (-2).
|
||||
ErrorResource = 0xFFFFFFFDU, ///< TtStatusErrorResource (-3).
|
||||
ErrorParameter = 0xFFFFFFFCU, ///< TtStatusErrorParameter (-4).
|
||||
ErrorISR = 0xFFFFFFFAU, ///< TtStatusErrorISR (-6).
|
||||
};
|
||||
|
||||
uint32_t set(uint32_t flags) const;
|
||||
uint32_t clear(uint32_t flags) const;
|
||||
uint32_t get() const;
|
||||
uint32_t wait(
|
||||
uint32_t flags,
|
||||
uint32_t options = TtFlagWaitAny,
|
||||
uint32_t timeout = TtWaitForever
|
||||
uint32_t options = WaitAny,
|
||||
uint32_t timeout = portMAX_DELAY
|
||||
) const;
|
||||
};
|
||||
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
#include "Check.h"
|
||||
#include "RtosCompat.h"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
|
||||
namespace tt {
|
||||
|
||||
@ -15,6 +17,8 @@ public:
|
||||
|
||||
virtual bool lock(TickType_t timeoutTicks) const = 0;
|
||||
|
||||
virtual bool lock() const { return lock(portMAX_DELAY); }
|
||||
|
||||
virtual bool unlock() const = 0;
|
||||
|
||||
std::unique_ptr<ScopedLockableUsage> scoped() const;
|
||||
@ -43,6 +47,8 @@ public:
|
||||
return lockable.lock(timeout);
|
||||
}
|
||||
|
||||
bool lock() const override { return lock(portMAX_DELAY); }
|
||||
|
||||
bool unlock() const override {
|
||||
return lockable.unlock();
|
||||
}
|
||||
|
||||
@ -4,63 +4,64 @@
|
||||
|
||||
namespace tt {
|
||||
|
||||
MessageQueue::MessageQueue(uint32_t capacity, uint32_t msg_size) {
|
||||
assert(!TT_IS_ISR() && (capacity > 0U) && (msg_size > 0U));
|
||||
queue_handle = xQueueCreate(capacity, msg_size);
|
||||
tt_check(queue_handle);
|
||||
static inline QueueHandle_t createQueue(uint32_t capacity, uint32_t messageSize) {
|
||||
assert(!TT_IS_ISR() && (capacity > 0U) && (messageSize > 0U));
|
||||
return xQueueCreate(capacity, messageSize);
|
||||
}
|
||||
|
||||
MessageQueue::MessageQueue(uint32_t capacity, uint32_t messageSize) : handle(createQueue(capacity, messageSize)) {
|
||||
tt_check(handle != nullptr);
|
||||
}
|
||||
|
||||
MessageQueue::~MessageQueue() {
|
||||
assert(!TT_IS_ISR());
|
||||
vQueueDelete(queue_handle);
|
||||
}
|
||||
|
||||
bool MessageQueue::put(const void* message, uint32_t timeout) {
|
||||
bool MessageQueue::put(const void* message, TickType_t timeout) {
|
||||
bool result = true;
|
||||
BaseType_t yield;
|
||||
|
||||
if (TT_IS_ISR()) {
|
||||
if ((queue_handle == nullptr) || (message == nullptr) || (timeout != 0U)) {
|
||||
if ((handle == nullptr) || (message == nullptr) || (timeout != 0U)) {
|
||||
result = false;
|
||||
} else {
|
||||
yield = pdFALSE;
|
||||
|
||||
if (xQueueSendToBackFromISR(queue_handle, message, &yield) != pdTRUE) {
|
||||
if (xQueueSendToBackFromISR(handle.get(), message, &yield) != pdTRUE) {
|
||||
result = false;
|
||||
} else {
|
||||
portYIELD_FROM_ISR(yield);
|
||||
}
|
||||
}
|
||||
} else if ((queue_handle == nullptr) || (message == nullptr)) {
|
||||
} else if ((handle == nullptr) || (message == nullptr)) {
|
||||
result = false;
|
||||
} else if (xQueueSendToBack(queue_handle, message, (TickType_t)timeout) != pdPASS) {
|
||||
} else if (xQueueSendToBack(handle.get(), message, (TickType_t)timeout) != pdPASS) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MessageQueue::get(void* msg_ptr, uint32_t timeout_ticks) {
|
||||
bool MessageQueue::get(void* msg_ptr, TickType_t timeout) {
|
||||
bool result = true;
|
||||
BaseType_t yield;
|
||||
|
||||
|
||||
if (TT_IS_ISR()) {
|
||||
if ((queue_handle == nullptr) || (msg_ptr == nullptr) || (timeout_ticks != 0U)) {
|
||||
if ((handle == nullptr) || (msg_ptr == nullptr) || (timeout != 0U)) {
|
||||
result = false;
|
||||
} else {
|
||||
yield = pdFALSE;
|
||||
|
||||
if (xQueueReceiveFromISR(queue_handle, msg_ptr, &yield) != pdPASS) {
|
||||
if (xQueueReceiveFromISR(handle.get(), msg_ptr, &yield) != pdPASS) {
|
||||
result = false;
|
||||
} else {
|
||||
portYIELD_FROM_ISR(yield);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((queue_handle == nullptr) || (msg_ptr == nullptr)) {
|
||||
if ((handle == nullptr) || (msg_ptr == nullptr)) {
|
||||
result = false;
|
||||
} else if (xQueueReceive(queue_handle, msg_ptr, (TickType_t)timeout_ticks) != pdPASS) {
|
||||
} else if (xQueueReceive(handle.get(), msg_ptr, (TickType_t)timeout) != pdPASS) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
@ -69,7 +70,7 @@ bool MessageQueue::get(void* msg_ptr, uint32_t timeout_ticks) {
|
||||
}
|
||||
|
||||
uint32_t MessageQueue::getCapacity() const {
|
||||
auto* mq = (StaticQueue_t*)(queue_handle);
|
||||
auto* mq = (StaticQueue_t*)(handle.get());
|
||||
if (mq == nullptr) {
|
||||
return 0U;
|
||||
} else {
|
||||
@ -78,7 +79,7 @@ uint32_t MessageQueue::getCapacity() const {
|
||||
}
|
||||
|
||||
uint32_t MessageQueue::getMessageSize() const {
|
||||
auto* mq = (StaticQueue_t*)(queue_handle);
|
||||
auto* mq = (StaticQueue_t*)(handle.get());
|
||||
if (mq == nullptr) {
|
||||
return 0U;
|
||||
} else {
|
||||
@ -89,12 +90,12 @@ uint32_t MessageQueue::getMessageSize() const {
|
||||
uint32_t MessageQueue::getCount() const {
|
||||
UBaseType_t count;
|
||||
|
||||
if (queue_handle == nullptr) {
|
||||
if (handle == nullptr) {
|
||||
count = 0U;
|
||||
} else if (TT_IS_ISR()) {
|
||||
count = uxQueueMessagesWaitingFromISR(queue_handle);
|
||||
count = uxQueueMessagesWaitingFromISR(handle.get());
|
||||
} else {
|
||||
count = uxQueueMessagesWaiting(queue_handle);
|
||||
count = uxQueueMessagesWaiting(handle.get());
|
||||
}
|
||||
|
||||
/* Return number of queued messages */
|
||||
@ -102,7 +103,7 @@ uint32_t MessageQueue::getCount() const {
|
||||
}
|
||||
|
||||
uint32_t MessageQueue::getSpace() const {
|
||||
auto* mq = (StaticQueue_t*)(queue_handle);
|
||||
auto* mq = (StaticQueue_t*)(handle.get());
|
||||
uint32_t space;
|
||||
uint32_t isrm;
|
||||
|
||||
@ -124,10 +125,10 @@ uint32_t MessageQueue::getSpace() const {
|
||||
|
||||
bool MessageQueue::reset() {
|
||||
tt_check(!TT_IS_ISR());
|
||||
if (queue_handle == nullptr) {
|
||||
if (handle == nullptr) {
|
||||
return false;
|
||||
} else {
|
||||
xQueueReset(queue_handle);
|
||||
xQueueReset(handle.get());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include <memory>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@ -25,7 +25,14 @@ namespace tt {
|
||||
*/
|
||||
class MessageQueue {
|
||||
private:
|
||||
QueueHandle_t queue_handle;
|
||||
|
||||
struct QueueHandleDeleter {
|
||||
void operator()(QueueHandle_t handleToDelete) {
|
||||
vQueueDelete(handleToDelete);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<std::remove_pointer_t<QueueHandle_t>, QueueHandleDeleter> handle;
|
||||
|
||||
public:
|
||||
/** Allocate message queue
|
||||
@ -39,17 +46,17 @@ public:
|
||||
/** Post a message to the queue.
|
||||
* The message is queued by copy, not by reference.
|
||||
* @param[in] message A pointer to a message. The message will be copied into a buffer.
|
||||
* @param[in] timeoutTicks
|
||||
* @param[in] timeout
|
||||
* @return success result
|
||||
*/
|
||||
bool put(const void* message, uint32_t timeoutTicks);
|
||||
bool put(const void* message, TickType_t timeout);
|
||||
|
||||
/** Get message from queue
|
||||
* @param[out] message A pointer to an already allocated message object
|
||||
* @param[in] timeoutTicks
|
||||
* @param[in] timeout
|
||||
* @return success result
|
||||
*/
|
||||
bool get(void* message, uint32_t timeoutTicks);
|
||||
bool get(void* message, TickType_t timeout);
|
||||
|
||||
/**
|
||||
* @return The maximum amount of messages that can be in the queue at any given time.
|
||||
|
||||
@ -22,88 +22,56 @@ namespace tt {
|
||||
#define tt_mutex_info(mutex, text)
|
||||
#endif
|
||||
|
||||
Mutex::Mutex(Type type) : type(type) {
|
||||
tt_mutex_info(data, "alloc");
|
||||
static inline SemaphoreHandle_t createSemaphoreHandle(Mutex::Type type) {
|
||||
switch (type) {
|
||||
case Type::Normal:
|
||||
semaphore = xSemaphoreCreateMutex();
|
||||
break;
|
||||
case Type::Recursive:
|
||||
semaphore = xSemaphoreCreateRecursiveMutex();
|
||||
break;
|
||||
case Mutex::Type::Normal:
|
||||
return xSemaphoreCreateMutex();
|
||||
case Mutex::Type::Recursive:
|
||||
return xSemaphoreCreateRecursiveMutex();
|
||||
default:
|
||||
tt_crash("Mutex type unknown/corrupted");
|
||||
}
|
||||
|
||||
assert(semaphore != nullptr);
|
||||
}
|
||||
|
||||
Mutex::~Mutex() {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
vSemaphoreDelete(semaphore);
|
||||
semaphore = nullptr; // If the mutex is used after release, this might help debugging
|
||||
Mutex::Mutex(Type type) : handle(createSemaphoreHandle(type)), type(type) {
|
||||
tt_mutex_info(data, "alloc");
|
||||
assert(handle != nullptr);
|
||||
}
|
||||
|
||||
TtStatus Mutex::acquire(TickType_t timeout) const {
|
||||
bool Mutex::lock(TickType_t timeout) const {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
assert(semaphore != nullptr);
|
||||
|
||||
assert(handle != nullptr);
|
||||
tt_mutex_info(mutex, "acquire");
|
||||
|
||||
switch (type) {
|
||||
case Type::Normal:
|
||||
if (xSemaphoreTake(semaphore, timeout) != pdPASS) {
|
||||
if (timeout != 0U) {
|
||||
return TtStatusErrorTimeout;
|
||||
} else {
|
||||
return TtStatusErrorResource;
|
||||
}
|
||||
} else {
|
||||
return TtStatusOk;
|
||||
}
|
||||
return xSemaphoreTake(handle.get(), timeout) == pdPASS;
|
||||
case Type::Recursive:
|
||||
if (xSemaphoreTakeRecursive(semaphore, timeout) != pdPASS) {
|
||||
if (timeout != 0U) {
|
||||
return TtStatusErrorTimeout;
|
||||
} else {
|
||||
return TtStatusErrorResource;
|
||||
}
|
||||
} else {
|
||||
return TtStatusOk;
|
||||
}
|
||||
return xSemaphoreTakeRecursive(handle.get(), timeout) == pdPASS;
|
||||
default:
|
||||
tt_crash("mutex type unknown/corrupted");
|
||||
tt_crash();
|
||||
}
|
||||
}
|
||||
|
||||
TtStatus Mutex::release() const {
|
||||
bool Mutex::unlock() const {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
assert(semaphore);
|
||||
assert(handle != nullptr);
|
||||
tt_mutex_info(mutex, "release");
|
||||
|
||||
switch (type) {
|
||||
case Type::Normal: {
|
||||
if (xSemaphoreGive(semaphore) != pdPASS) {
|
||||
return TtStatusErrorResource;
|
||||
} else {
|
||||
return TtStatusOk;
|
||||
}
|
||||
}
|
||||
case Type::Normal:
|
||||
return xSemaphoreGive(handle.get()) == pdPASS;
|
||||
case Type::Recursive:
|
||||
if (xSemaphoreGiveRecursive(semaphore) != pdPASS) {
|
||||
return TtStatusErrorResource;
|
||||
} else {
|
||||
return TtStatusOk;
|
||||
}
|
||||
return xSemaphoreGiveRecursive(handle.get()) == pdPASS;
|
||||
default:
|
||||
tt_crash("mutex type unknown/corrupted");
|
||||
tt_crash();
|
||||
}
|
||||
}
|
||||
|
||||
ThreadId Mutex::getOwner() const {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
assert(semaphore);
|
||||
return (ThreadId)xSemaphoreGetMutexHolder(semaphore);
|
||||
assert(handle != nullptr);
|
||||
return (ThreadId)xSemaphoreGetMutexHolder(handle.get());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Thread.h"
|
||||
#include "RtosCompatSemaphore.h"
|
||||
#include "Check.h"
|
||||
@ -15,9 +14,9 @@ namespace tt {
|
||||
|
||||
/**
|
||||
* Wrapper for FreeRTOS xSemaphoreCreateMutex and xSemaphoreCreateRecursiveMutex
|
||||
* Can be used in IRQ mode (within ISR context)
|
||||
* Cannot be used in IRQ mode (within ISR context)
|
||||
*/
|
||||
class Mutex : public Lockable {
|
||||
class Mutex final : public Lockable {
|
||||
|
||||
public:
|
||||
|
||||
@ -28,35 +27,36 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
SemaphoreHandle_t semaphore;
|
||||
struct SemaphoreHandleDeleter {
|
||||
void operator()(QueueHandle_t handleToDelete) {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
vSemaphoreDelete(handleToDelete);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<std::remove_pointer_t<QueueHandle_t>, SemaphoreHandleDeleter> handle;
|
||||
Type type;
|
||||
|
||||
public:
|
||||
|
||||
explicit Mutex(Type type = Type::Normal);
|
||||
~Mutex() override;
|
||||
|
||||
/** Attempt to lock the mutex. Blocks until timeout passes or lock is acquired.
|
||||
* @param[in] timeout
|
||||
* @return status result
|
||||
*/
|
||||
TtStatus acquire(TickType_t timeout) const;
|
||||
|
||||
/** Attempt to unlock the mutex.
|
||||
* @return status result
|
||||
*/
|
||||
TtStatus release() const;
|
||||
~Mutex() override = default;
|
||||
|
||||
/** Attempt to lock the mutex. Blocks until timeout passes or lock is acquired.
|
||||
* @param[in] timeout
|
||||
* @return success result
|
||||
*/
|
||||
bool lock(TickType_t timeout) const override { return acquire(timeout) == TtStatusOk; }
|
||||
bool lock(TickType_t timeout) const override;
|
||||
|
||||
/** Attempt to lock the mutex. Blocks until lock is acquired, without timeout.
|
||||
* @return success result
|
||||
*/
|
||||
bool lock() const override { return lock(portMAX_DELAY); }
|
||||
|
||||
/** Attempt to unlock the mutex.
|
||||
* @return success result
|
||||
*/
|
||||
bool unlock() const override { return release() == TtStatusOk; }
|
||||
bool unlock() const override;
|
||||
|
||||
/** @return the owner of the thread */
|
||||
ThreadId getOwner() const;
|
||||
|
||||
47
TactilityCore/Source/PubSub.cpp
Normal file
47
TactilityCore/Source/PubSub.cpp
Normal 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
|
||||
61
TactilityCore/Source/PubSub.h
Normal file
61
TactilityCore/Source/PubSub.h
Normal 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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -4,28 +4,30 @@
|
||||
|
||||
namespace tt {
|
||||
|
||||
Semaphore::Semaphore(uint32_t maxCount, uint32_t initialCount) {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
static inline struct QueueDefinition* createHandle(uint32_t maxCount, uint32_t initialCount) {
|
||||
assert((maxCount > 0U) && (initialCount <= maxCount));
|
||||
|
||||
if (maxCount == 1U) {
|
||||
handle = xSemaphoreCreateBinary();
|
||||
auto handle = xSemaphoreCreateBinary();
|
||||
if ((handle != nullptr) && (initialCount != 0U)) {
|
||||
if (xSemaphoreGive(handle) != pdPASS) {
|
||||
vSemaphoreDelete(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
}
|
||||
return handle;
|
||||
} else {
|
||||
handle = xSemaphoreCreateCounting(maxCount, initialCount);
|
||||
return xSemaphoreCreateCounting(maxCount, initialCount);
|
||||
}
|
||||
}
|
||||
|
||||
tt_check(handle);
|
||||
Semaphore::Semaphore(uint32_t maxCount, uint32_t initialCount) : handle(createHandle(maxCount, initialCount)){
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
tt_check(handle != nullptr);
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore() {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
vSemaphoreDelete(handle);
|
||||
}
|
||||
|
||||
bool Semaphore::acquire(uint32_t timeout) const {
|
||||
@ -35,7 +37,7 @@ bool Semaphore::acquire(uint32_t timeout) const {
|
||||
} else {
|
||||
BaseType_t yield = pdFALSE;
|
||||
|
||||
if (xSemaphoreTakeFromISR(handle, &yield) != pdPASS) {
|
||||
if (xSemaphoreTakeFromISR(handle.get(), &yield) != pdPASS) {
|
||||
return false;
|
||||
} else {
|
||||
portYIELD_FROM_ISR(yield);
|
||||
@ -43,21 +45,21 @@ bool Semaphore::acquire(uint32_t timeout) const {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return xSemaphoreTake(handle, (TickType_t)timeout) == pdPASS;
|
||||
return xSemaphoreTake(handle.get(), (TickType_t)timeout) == pdPASS;
|
||||
}
|
||||
}
|
||||
|
||||
bool Semaphore::release() const {
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
BaseType_t yield = pdFALSE;
|
||||
if (xSemaphoreGiveFromISR(handle, &yield) != pdTRUE) {
|
||||
if (xSemaphoreGiveFromISR(handle.get(), &yield) != pdTRUE) {
|
||||
return false;
|
||||
} else {
|
||||
portYIELD_FROM_ISR(yield);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return xSemaphoreGive(handle) == pdPASS;
|
||||
return xSemaphoreGive(handle.get()) == pdPASS;
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,12 +67,12 @@ uint32_t Semaphore::getCount() const {
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
// TODO: uxSemaphoreGetCountFromISR is not supported on esp-idf 5.1.2 - perhaps later on?
|
||||
#ifdef uxSemaphoreGetCountFromISR
|
||||
return uxSemaphoreGetCountFromISR(handle);
|
||||
return uxSemaphoreGetCountFromISR(handle.get());
|
||||
#else
|
||||
return uxQueueMessagesWaitingFromISR((QueueHandle_t)hSemaphore);
|
||||
#endif
|
||||
} else {
|
||||
return uxSemaphoreGetCount(handle);
|
||||
return uxSemaphoreGetCount(handle.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Thread.h"
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@ -18,8 +19,18 @@ namespace tt {
|
||||
* Can be used from IRQ/ISR mode, but cannot be created/destroyed from such a context.
|
||||
*/
|
||||
class Semaphore {
|
||||
|
||||
private:
|
||||
SemaphoreHandle_t handle;
|
||||
|
||||
struct SemaphoreHandleDeleter {
|
||||
void operator()(QueueHandle_t handleToDelete) {
|
||||
assert(!TT_IS_IRQ_MODE());
|
||||
vSemaphoreDelete(handleToDelete);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<std::remove_pointer_t<QueueHandle_t>, SemaphoreHandleDeleter> handle;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Cannot be called from IRQ/ISR mode.
|
||||
|
||||
@ -2,22 +2,20 @@
|
||||
|
||||
#include "Check.h"
|
||||
#include "CoreDefines.h"
|
||||
#include "CoreTypes.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
StreamBuffer::StreamBuffer(size_t size, size_t triggerLevel) {
|
||||
inline static StreamBufferHandle_t createStreamBuffer(size_t size, size_t triggerLevel) {
|
||||
assert(size != 0);
|
||||
handle = xStreamBufferCreate(size, triggerLevel);
|
||||
return xStreamBufferCreate(size, triggerLevel);
|
||||
}
|
||||
|
||||
StreamBuffer::StreamBuffer(size_t size, size_t triggerLevel) : handle(createStreamBuffer(size, triggerLevel)) {
|
||||
tt_check(handle);
|
||||
};
|
||||
|
||||
StreamBuffer::~StreamBuffer() {
|
||||
vStreamBufferDelete(handle);
|
||||
};
|
||||
|
||||
bool StreamBuffer::setTriggerLevel(size_t triggerLevel) const {
|
||||
return xStreamBufferSetTriggerLevel(handle, triggerLevel) == pdTRUE;
|
||||
return xStreamBufferSetTriggerLevel(handle.get(), triggerLevel) == pdTRUE;
|
||||
};
|
||||
|
||||
size_t StreamBuffer::send(
|
||||
@ -27,11 +25,11 @@ size_t StreamBuffer::send(
|
||||
) const {
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
BaseType_t yield;
|
||||
size_t result = xStreamBufferSendFromISR(handle, data, length, &yield);
|
||||
size_t result = xStreamBufferSendFromISR(handle.get(), data, length, &yield);
|
||||
portYIELD_FROM_ISR(yield);
|
||||
return result;
|
||||
} else {
|
||||
return xStreamBufferSend(handle, data, length, timeout);
|
||||
return xStreamBufferSend(handle.get(), data, length, timeout);
|
||||
}
|
||||
};
|
||||
|
||||
@ -42,32 +40,32 @@ size_t StreamBuffer::receive(
|
||||
) const {
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
BaseType_t yield;
|
||||
size_t result = xStreamBufferReceiveFromISR(handle, data, length, &yield);
|
||||
size_t result = xStreamBufferReceiveFromISR(handle.get(), data, length, &yield);
|
||||
portYIELD_FROM_ISR(yield);
|
||||
return result;
|
||||
} else {
|
||||
return xStreamBufferReceive(handle, data, length, timeout);
|
||||
return xStreamBufferReceive(handle.get(), data, length, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
size_t StreamBuffer::getAvailableReadBytes() const {
|
||||
return xStreamBufferBytesAvailable(handle);
|
||||
return xStreamBufferBytesAvailable(handle.get());
|
||||
};
|
||||
|
||||
size_t StreamBuffer::getAvailableWriteBytes() const {
|
||||
return xStreamBufferSpacesAvailable(handle);
|
||||
return xStreamBufferSpacesAvailable(handle.get());
|
||||
};
|
||||
|
||||
bool StreamBuffer::isFull() const {
|
||||
return xStreamBufferIsFull(handle) == pdTRUE;
|
||||
return xStreamBufferIsFull(handle.get()) == pdTRUE;
|
||||
};
|
||||
|
||||
bool StreamBuffer::isEmpty() const {
|
||||
return xStreamBufferIsEmpty(handle) == pdTRUE;
|
||||
return xStreamBufferIsEmpty(handle.get()) == pdTRUE;
|
||||
};
|
||||
|
||||
bool StreamBuffer::reset() const {
|
||||
return xStreamBufferReset(handle) == pdPASS;
|
||||
return xStreamBufferReset(handle.get()) == pdPASS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@ -25,8 +23,16 @@ namespace tt {
|
||||
* interrupt that will read from the buffer (the reader).
|
||||
*/
|
||||
class StreamBuffer {
|
||||
|
||||
private:
|
||||
StreamBufferHandle_t handle;
|
||||
|
||||
struct StreamBufferHandleDeleter {
|
||||
void operator()(StreamBufferHandle_t handleToDelete) {
|
||||
vStreamBufferDelete(handleToDelete);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<std::remove_pointer_t<StreamBufferHandle_t>, StreamBufferHandleDeleter> handle;
|
||||
|
||||
public:
|
||||
|
||||
@ -42,7 +48,7 @@ public:
|
||||
*/
|
||||
StreamBuffer(size_t size, size_t triggerLevel);
|
||||
|
||||
~StreamBuffer();
|
||||
~StreamBuffer() = default;
|
||||
|
||||
/**
|
||||
* @brief Set trigger level for stream buffer.
|
||||
@ -65,7 +71,7 @@ public:
|
||||
* @param[in] timeout The maximum amount of time the task should remain in the
|
||||
* Blocked state to wait for space to become available if the stream buffer is full.
|
||||
* Will return immediately if timeout is zero.
|
||||
* Setting timeout to TtWaitForever will cause the task to wait indefinitely.
|
||||
* Setting timeout to portMAX_DELAY will cause the task to wait indefinitely.
|
||||
* Ignored if called from ISR.
|
||||
* @return The number of bytes actually written to the stream buffer.
|
||||
*/
|
||||
@ -85,7 +91,7 @@ public:
|
||||
* @param[in] timeout The maximum amount of time the task should remain in the
|
||||
* Blocked state to wait for data to become available if the stream buffer is empty.
|
||||
* Will return immediately if timeout is zero.
|
||||
* Setting timeout to TtWaitForever will cause the task to wait indefinitely.
|
||||
* Setting timeout to portMAX_DELAY will cause the task to wait indefinitely.
|
||||
* Ignored if called from ISR.
|
||||
* @return The number of bytes read from the stream buffer, if any.
|
||||
*/
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#include "Check.h"
|
||||
#include "CoreDefines.h"
|
||||
#include "CoreExtraDefines.h"
|
||||
#include "CoreTypes.h"
|
||||
#include "EventFlag.h"
|
||||
#include "kernel/Kernel.h"
|
||||
#include "kernel/critical/Critical.h"
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
#include "Thread.h"
|
||||
#include <string>
|
||||
|
||||
#include "Check.h"
|
||||
#include "CoreDefines.h"
|
||||
#include "EventFlag.h"
|
||||
#include "kernel/Kernel.h"
|
||||
#include "Log.h"
|
||||
#include "TactilityCoreConfig.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
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(TT_CONFIG_THREAD_MAX_PRIORITIES <= configMAX_PRIORITIES, "highest tactility priority is higher than max FreeRTOS priority");
|
||||
|
||||
void setState(Thread::Data* data, Thread::State state) {
|
||||
data->state = state;
|
||||
if (data->stateCallback) {
|
||||
data->stateCallback(state, data->stateCallbackContext);
|
||||
void Thread::setState(Thread::State newState) {
|
||||
state = newState;
|
||||
if (stateCallback) {
|
||||
stateCallback(state, stateCallbackContext);
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(configSUPPORT_DYNAMIC_ALLOCATION == 1);
|
||||
|
||||
/** Catch threads that are trying to exit wrong way */
|
||||
__attribute__((__noreturn__)) void thread_catch() { //-V1082
|
||||
// If you're here it means you're probably doing something wrong
|
||||
// with critical sections or with scheduler state
|
||||
asm volatile("nop"); // extra magic
|
||||
tt_crash("You are doing it wrong"); //-V779
|
||||
__attribute__((__noreturn__)) void threadCatch() { //-V1082
|
||||
// If you're here it means you're probably doing something wrong with critical sections or with scheduler state
|
||||
asm volatile("nop");
|
||||
tt_crash();
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
|
||||
static void thread_body(void* context) {
|
||||
assert(context);
|
||||
auto* data = static_cast<Thread::Data*>(context);
|
||||
void Thread::mainBody(void* context) {
|
||||
assert(context != nullptr);
|
||||
auto* thread = static_cast<Thread*>(context);
|
||||
|
||||
// Store thread data instance to thread local storage
|
||||
assert(pvTaskGetThreadLocalStoragePointer(nullptr, 0) == nullptr);
|
||||
vTaskSetThreadLocalStoragePointer(nullptr, 0, data->thread);
|
||||
vTaskSetThreadLocalStoragePointer(nullptr, 0, thread);
|
||||
|
||||
assert(data->state == Thread::State::Starting);
|
||||
setState(data, Thread::State::Running);
|
||||
data->callbackResult = data->callback(data->callbackContext);
|
||||
assert(data->state == Thread::State::Running);
|
||||
assert(thread->state == Thread::State::Starting);
|
||||
thread->setState(Thread::State::Running);
|
||||
thread->callbackResult = thread->callback(thread->callbackContext);
|
||||
assert(thread->state == Thread::State::Running);
|
||||
|
||||
setState(data, Thread::State::Stopped);
|
||||
thread->setState(Thread::State::Stopped);
|
||||
|
||||
vTaskSetThreadLocalStoragePointer(nullptr, 0, nullptr);
|
||||
data->taskHandle = nullptr;
|
||||
thread->taskHandle = nullptr;
|
||||
|
||||
vTaskDelete(nullptr);
|
||||
thread_catch();
|
||||
threadCatch();
|
||||
}
|
||||
|
||||
Thread::Thread() :
|
||||
data({
|
||||
.thread = nullptr,
|
||||
.taskHandle = nullptr,
|
||||
.state = State::Stopped,
|
||||
.callback = nullptr,
|
||||
.callbackContext = nullptr,
|
||||
.callbackResult = 0,
|
||||
.stateCallback = nullptr,
|
||||
.stateCallbackContext = nullptr,
|
||||
.name = std::string(),
|
||||
.priority = Priority::Normal,
|
||||
.stackSize = 0,
|
||||
}) { }
|
||||
|
||||
Thread::Thread(
|
||||
const std::string& name,
|
||||
std::string name,
|
||||
configSTACK_DEPTH_TYPE stackSize,
|
||||
Callback callback,
|
||||
_Nullable void* callbackContext,
|
||||
portBASE_TYPE affinity
|
||||
) :
|
||||
data({
|
||||
.thread = nullptr,
|
||||
.taskHandle = nullptr,
|
||||
.state = State::Stopped,
|
||||
.callback = callback,
|
||||
.callbackContext = callbackContext,
|
||||
.callbackResult = 0,
|
||||
.stateCallback = nullptr,
|
||||
.stateCallbackContext = nullptr,
|
||||
.name = name,
|
||||
.priority = Priority::Normal,
|
||||
.stackSize = stackSize,
|
||||
.affinity = affinity
|
||||
}) { }
|
||||
callback(callback),
|
||||
callbackContext(callbackContext),
|
||||
name(std::move(name)),
|
||||
stackSize(stackSize),
|
||||
affinity(affinity)
|
||||
{}
|
||||
|
||||
Thread::~Thread() {
|
||||
// Ensure that use join before free
|
||||
assert(data.state == State::Stopped);
|
||||
assert(data.taskHandle == nullptr);
|
||||
assert(state == State::Stopped);
|
||||
assert(taskHandle == nullptr);
|
||||
}
|
||||
|
||||
void Thread::setName(const std::string& newName) {
|
||||
assert(data.state == State::Stopped);
|
||||
data.name = newName;
|
||||
void Thread::setName(std::string newName) {
|
||||
assert(state == State::Stopped);
|
||||
name = std::move(newName);
|
||||
}
|
||||
|
||||
void Thread::setStackSize(size_t stackSize) {
|
||||
assert(data.state == State::Stopped);
|
||||
void Thread::setStackSize(size_t newStackSize) {
|
||||
assert(state == State::Stopped);
|
||||
assert(stackSize % 4 == 0);
|
||||
data.stackSize = stackSize;
|
||||
stackSize = newStackSize;
|
||||
}
|
||||
|
||||
void Thread::setCallback(Callback callback, _Nullable void* callbackContext) {
|
||||
assert(data.state == State::Stopped);
|
||||
data.callback = callback;
|
||||
data.callbackContext = callbackContext;
|
||||
void Thread::setCallback(Callback newCallback, _Nullable void* newCallbackContext) {
|
||||
assert(state == State::Stopped);
|
||||
callback = newCallback;
|
||||
callbackContext = newCallbackContext;
|
||||
}
|
||||
|
||||
|
||||
void Thread::setPriority(Priority priority) {
|
||||
assert(data.state == State::Stopped);
|
||||
data.priority = priority;
|
||||
void Thread::setPriority(Priority newPriority) {
|
||||
assert(state == State::Stopped);
|
||||
priority = newPriority;
|
||||
}
|
||||
|
||||
|
||||
void Thread::setStateCallback(StateCallback callback, _Nullable void* callbackContext) {
|
||||
assert(data.state == State::Stopped);
|
||||
data.stateCallback = callback;
|
||||
data.stateCallbackContext = callbackContext;
|
||||
assert(state == State::Stopped);
|
||||
stateCallback = callback;
|
||||
stateCallbackContext = callbackContext;
|
||||
}
|
||||
|
||||
Thread::State Thread::getState() const {
|
||||
return data.state;
|
||||
return state;
|
||||
}
|
||||
|
||||
void Thread::start() {
|
||||
assert(data.callback);
|
||||
assert(data.state == State::Stopped);
|
||||
assert(data.stackSize > 0U && data.stackSize < (UINT16_MAX * sizeof(StackType_t)));
|
||||
assert(callback);
|
||||
assert(state == State::Stopped);
|
||||
assert(stackSize > 0U && stackSize < (UINT16_MAX * sizeof(StackType_t)));
|
||||
|
||||
setState(&data, State::Starting);
|
||||
setState(State::Starting);
|
||||
|
||||
uint32_t stack_depth = data.stackSize / sizeof(StackType_t);
|
||||
uint32_t stack_depth = stackSize / sizeof(StackType_t);
|
||||
|
||||
BaseType_t result;
|
||||
if (data.affinity != -1) {
|
||||
if (affinity != -1) {
|
||||
#ifdef ESP_PLATFORM
|
||||
result = xTaskCreatePinnedToCore(
|
||||
thread_body,
|
||||
data.name.c_str(),
|
||||
mainBody,
|
||||
name.c_str(),
|
||||
stack_depth,
|
||||
this,
|
||||
static_cast<UBaseType_t>(data.priority),
|
||||
&(data.taskHandle),
|
||||
data.affinity
|
||||
static_cast<UBaseType_t>(priority),
|
||||
&taskHandle,
|
||||
affinity
|
||||
);
|
||||
#else
|
||||
TT_LOG_W(TAG, "Pinned tasks are not supported by current FreeRTOS platform - creating regular one");
|
||||
result = xTaskCreate(
|
||||
thread_body,
|
||||
data.name.c_str(),
|
||||
mainBody,
|
||||
name.c_str(),
|
||||
stack_depth,
|
||||
this,
|
||||
static_cast<UBaseType_t>(data.priority),
|
||||
&(data.taskHandle)
|
||||
static_cast<UBaseType_t>(priority),
|
||||
&taskHandle
|
||||
);
|
||||
#endif
|
||||
} else {
|
||||
result = xTaskCreate(
|
||||
thread_body,
|
||||
data.name.c_str(),
|
||||
mainBody,
|
||||
name.c_str(),
|
||||
stack_depth,
|
||||
this,
|
||||
static_cast<UBaseType_t>(data.priority),
|
||||
&(data.taskHandle)
|
||||
static_cast<UBaseType_t>(priority),
|
||||
&taskHandle
|
||||
);
|
||||
}
|
||||
|
||||
tt_check(result == pdPASS);
|
||||
tt_check(data.state == State::Stopped || data.taskHandle);
|
||||
tt_check(state == State::Stopped || taskHandle);
|
||||
}
|
||||
|
||||
bool Thread::join(TickType_t timeout, TickType_t pollInterval) {
|
||||
tt_check(thread_get_current() != this);
|
||||
tt_check(getCurrent() != this);
|
||||
|
||||
// !!! IMPORTANT NOTICE !!!
|
||||
//
|
||||
// If your thread exited, but your app stuck here: some other thread uses
|
||||
// all cpu time, which delays kernel from releasing task handle
|
||||
TickType_t start_ticks = kernel::getTicks();
|
||||
while (data.taskHandle) {
|
||||
while (taskHandle) {
|
||||
kernel::delayTicks(pollInterval);
|
||||
if ((kernel::getTicks() - start_ticks) > timeout) {
|
||||
return false;
|
||||
@ -206,45 +184,27 @@ bool Thread::join(TickType_t timeout, TickType_t pollInterval) {
|
||||
}
|
||||
|
||||
ThreadId Thread::getId() const {
|
||||
return data.taskHandle;
|
||||
return taskHandle;
|
||||
}
|
||||
|
||||
int32_t Thread::getReturnCode() const {
|
||||
assert(data.state == State::Stopped);
|
||||
return data.callbackResult;
|
||||
assert(state == State::Stopped);
|
||||
return callbackResult;
|
||||
}
|
||||
|
||||
ThreadId thread_get_current_id() {
|
||||
return xTaskGetCurrentTaskHandle();
|
||||
Thread* Thread::getCurrent() {
|
||||
return static_cast<Thread*>(pvTaskGetThreadLocalStoragePointer(nullptr, 0));
|
||||
}
|
||||
|
||||
Thread* thread_get_current() {
|
||||
auto* thread = static_cast<Thread*>(pvTaskGetThreadLocalStoragePointer(nullptr, 0));
|
||||
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 Thread::setFlags(ThreadId threadId, uint32_t flags) {
|
||||
auto hTask = (TaskHandle_t)threadId;
|
||||
uint32_t rflags;
|
||||
BaseType_t yield;
|
||||
|
||||
if ((hTask == nullptr) || ((flags & THREAD_FLAGS_INVALID_BITS) != 0U)) {
|
||||
rflags = (uint32_t)TtStatusErrorParameter;
|
||||
rflags = (uint32_t)EventFlag::ErrorParameter;
|
||||
} else {
|
||||
rflags = (uint32_t)TtStatusError;
|
||||
rflags = (uint32_t)EventFlag::Error;
|
||||
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
yield = pdFALSE;
|
||||
@ -264,14 +224,14 @@ uint32_t thread_flags_set(ThreadId thread_id, uint32_t flags) {
|
||||
return (rflags);
|
||||
}
|
||||
|
||||
uint32_t thread_flags_clear(uint32_t flags) {
|
||||
uint32_t Thread::clearFlags(uint32_t flags) {
|
||||
TaskHandle_t hTask;
|
||||
uint32_t rflags, cflags;
|
||||
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
rflags = (uint32_t)TtStatusErrorISR;
|
||||
rflags = (uint32_t)EventFlag::ErrorISR;
|
||||
} else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {
|
||||
rflags = (uint32_t)TtStatusErrorParameter;
|
||||
rflags = (uint32_t)EventFlag::ErrorParameter;
|
||||
} else {
|
||||
hTask = xTaskGetCurrentTaskHandle();
|
||||
|
||||
@ -282,10 +242,10 @@ uint32_t thread_flags_clear(uint32_t flags) {
|
||||
|
||||
if (xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, cflags, eSetValueWithOverwrite) !=
|
||||
pdPASS) {
|
||||
rflags = (uint32_t)TtStatusError;
|
||||
rflags = (uint32_t)EventFlag::Error;
|
||||
}
|
||||
} else {
|
||||
rflags = (uint32_t)TtStatusError;
|
||||
rflags = (uint32_t)EventFlag::Error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -293,36 +253,36 @@ uint32_t thread_flags_clear(uint32_t flags) {
|
||||
return (rflags);
|
||||
}
|
||||
|
||||
uint32_t thread_flags_get() {
|
||||
uint32_t Thread::getFlags() {
|
||||
TaskHandle_t hTask;
|
||||
uint32_t rflags;
|
||||
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
rflags = (uint32_t)TtStatusErrorISR;
|
||||
rflags = (uint32_t)EventFlag::ErrorISR;
|
||||
} else {
|
||||
hTask = xTaskGetCurrentTaskHandle();
|
||||
|
||||
if (xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags) !=
|
||||
pdPASS) {
|
||||
rflags = (uint32_t)TtStatusError;
|
||||
rflags = (uint32_t)EventFlag::Error;
|
||||
}
|
||||
}
|
||||
|
||||
return (rflags);
|
||||
}
|
||||
|
||||
uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
|
||||
uint32_t Thread::awaitFlags(uint32_t flags, uint32_t options, uint32_t timeout) {
|
||||
uint32_t rflags, nval;
|
||||
uint32_t clear;
|
||||
TickType_t t0, td, tout;
|
||||
BaseType_t rval;
|
||||
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
rflags = (uint32_t)TtStatusErrorISR;
|
||||
rflags = (uint32_t)EventFlag::ErrorISR;
|
||||
} else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {
|
||||
rflags = (uint32_t)TtStatusErrorParameter;
|
||||
rflags = (uint32_t)EventFlag::ErrorParameter;
|
||||
} else {
|
||||
if ((options & TtFlagNoClear) == TtFlagNoClear) {
|
||||
if ((options & EventFlag::NoClear) == EventFlag::NoClear) {
|
||||
clear = 0U;
|
||||
} else {
|
||||
clear = flags;
|
||||
@ -339,12 +299,12 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
|
||||
rflags &= flags;
|
||||
rflags |= nval;
|
||||
|
||||
if ((options & TtFlagWaitAll) == TtFlagWaitAll) {
|
||||
if ((options & EventFlag::WaitAll) == EventFlag::WaitAll) {
|
||||
if ((flags & rflags) == flags) {
|
||||
break;
|
||||
} else {
|
||||
if (timeout == 0U) {
|
||||
rflags = (uint32_t)TtStatusErrorResource;
|
||||
rflags = (uint32_t)EventFlag::ErrorResource;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -353,7 +313,7 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
|
||||
break;
|
||||
} else {
|
||||
if (timeout == 0U) {
|
||||
rflags = (uint32_t)TtStatusErrorResource;
|
||||
rflags = (uint32_t)EventFlag::ErrorResource;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -369,9 +329,9 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
|
||||
}
|
||||
} else {
|
||||
if (timeout == 0) {
|
||||
rflags = (uint32_t)TtStatusErrorResource;
|
||||
rflags = (uint32_t)EventFlag::ErrorResource;
|
||||
} else {
|
||||
rflags = (uint32_t)TtStatusErrorTimeout;
|
||||
rflags = (uint32_t)EventFlag::ErrorTimeout;
|
||||
}
|
||||
}
|
||||
} while (rval != pdFAIL);
|
||||
@ -381,21 +341,8 @@ uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
|
||||
return (rflags);
|
||||
}
|
||||
|
||||
const char* thread_get_name(ThreadId thread_id) {
|
||||
auto hTask = (TaskHandle_t)thread_id;
|
||||
const char* name;
|
||||
|
||||
if (TT_IS_IRQ_MODE() || (hTask == nullptr)) {
|
||||
name = nullptr;
|
||||
} else {
|
||||
name = pcTaskGetName(hTask);
|
||||
}
|
||||
|
||||
return (name);
|
||||
}
|
||||
|
||||
uint32_t thread_get_stack_space(ThreadId thread_id) {
|
||||
auto hTask = (TaskHandle_t)thread_id;
|
||||
uint32_t Thread::getStackSpace(ThreadId threadId) {
|
||||
auto hTask = (TaskHandle_t)threadId;
|
||||
uint32_t sz;
|
||||
|
||||
if (TT_IS_IRQ_MODE() || (hTask == nullptr)) {
|
||||
@ -407,13 +354,13 @@ uint32_t thread_get_stack_space(ThreadId thread_id) {
|
||||
return (sz);
|
||||
}
|
||||
|
||||
void thread_suspend(ThreadId thread_id) {
|
||||
auto hTask = (TaskHandle_t)thread_id;
|
||||
void Thread::suspend(ThreadId threadId) {
|
||||
auto hTask = (TaskHandle_t)threadId;
|
||||
vTaskSuspend(hTask);
|
||||
}
|
||||
|
||||
void thread_resume(ThreadId thread_id) {
|
||||
auto hTask = (TaskHandle_t)thread_id;
|
||||
void Thread::resume(ThreadId threadId) {
|
||||
auto hTask = (TaskHandle_t)threadId;
|
||||
if (TT_IS_IRQ_MODE()) {
|
||||
xTaskResumeFromISR(hTask);
|
||||
} else {
|
||||
@ -421,8 +368,8 @@ void thread_resume(ThreadId thread_id) {
|
||||
}
|
||||
}
|
||||
|
||||
bool thread_is_suspended(ThreadId thread_id) {
|
||||
auto hTask = (TaskHandle_t)thread_id;
|
||||
bool Thread::isSuspended(ThreadId threadId) {
|
||||
auto hTask = (TaskHandle_t)threadId;
|
||||
return eTaskGetState(hTask) == eSuspended;
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreDefines.h"
|
||||
#include "CoreTypes.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "RtosCompatTask.h"
|
||||
|
||||
@ -14,6 +12,7 @@ namespace tt {
|
||||
typedef TaskHandle_t ThreadId;
|
||||
|
||||
class Thread {
|
||||
|
||||
public:
|
||||
|
||||
enum class State{
|
||||
@ -34,6 +33,7 @@ public:
|
||||
Critical = 7U
|
||||
};
|
||||
|
||||
|
||||
/** ThreadCallback Your callback to run in new thread
|
||||
* @warning never use osThreadExit in Thread
|
||||
*/
|
||||
@ -51,22 +51,27 @@ public:
|
||||
*/
|
||||
typedef void (*StateCallback)(State state, void* context);
|
||||
|
||||
typedef struct {
|
||||
Thread* thread;
|
||||
TaskHandle_t taskHandle;
|
||||
State state;
|
||||
Callback callback;
|
||||
void* callbackContext;
|
||||
int32_t callbackResult;
|
||||
StateCallback stateCallback;
|
||||
void* stateCallbackContext;
|
||||
std::string name;
|
||||
Priority priority;
|
||||
configSTACK_DEPTH_TYPE stackSize;
|
||||
portBASE_TYPE affinity;
|
||||
} Data;
|
||||
private:
|
||||
|
||||
Thread();
|
||||
static void mainBody(void* context);
|
||||
|
||||
TaskHandle_t taskHandle = nullptr;
|
||||
State state = State::Stopped;
|
||||
Callback callback = nullptr;
|
||||
void* callbackContext = nullptr;
|
||||
int32_t callbackResult = 0;
|
||||
StateCallback stateCallback = nullptr;
|
||||
void* stateCallbackContext = nullptr;
|
||||
std::string name = {};
|
||||
Priority priority = Priority::Normal;
|
||||
configSTACK_DEPTH_TYPE stackSize = 0;
|
||||
portBASE_TYPE affinity = -1;
|
||||
|
||||
void setState(Thread::State state);
|
||||
|
||||
public:
|
||||
|
||||
Thread() = default;
|
||||
|
||||
/** Allocate Thread, shortcut version
|
||||
* @param[in] name the name of the thread
|
||||
@ -76,7 +81,7 @@ public:
|
||||
* @param[in] affinity Which CPU core to pin this task to, -1 means unpinned (only works on ESP32)
|
||||
*/
|
||||
Thread(
|
||||
const std::string& name,
|
||||
std::string name,
|
||||
configSTACK_DEPTH_TYPE stackSize,
|
||||
Callback callback,
|
||||
_Nullable void* callbackContext,
|
||||
@ -88,7 +93,7 @@ public:
|
||||
/** Set Thread name
|
||||
* @param[in] name string
|
||||
*/
|
||||
void setName(const std::string& name);
|
||||
void setName(std::string name);
|
||||
|
||||
/** Set Thread stack size
|
||||
* @param[in] stackSize stack size in bytes
|
||||
@ -140,9 +145,39 @@ public:
|
||||
*/
|
||||
int32_t getReturnCode() const;
|
||||
|
||||
private:
|
||||
/** Suspend thread
|
||||
* @param[in] threadId thread id
|
||||
*/
|
||||
static void suspend(ThreadId threadId);
|
||||
|
||||
Data data;
|
||||
/** Resume thread
|
||||
* @param[in] threadId thread id
|
||||
*/
|
||||
static void resume(ThreadId threadId);
|
||||
|
||||
/** Get thread suspended state
|
||||
* @param[in] threadId thread id
|
||||
* @return true if thread is suspended
|
||||
*/
|
||||
static bool isSuspended(ThreadId threadId);
|
||||
|
||||
/**
|
||||
* @brief Get thread stack watermark
|
||||
* @param[in] threadId
|
||||
* @return uint32_t
|
||||
*/
|
||||
static uint32_t getStackSpace(ThreadId threadId);
|
||||
|
||||
/** @return pointer to Thread instance or nullptr if this thread doesn't belong to Tactility */
|
||||
static Thread* getCurrent();
|
||||
|
||||
static uint32_t setFlags(ThreadId threadId, uint32_t flags);
|
||||
|
||||
static uint32_t clearFlags(uint32_t flags);
|
||||
|
||||
static uint32_t getFlags();
|
||||
|
||||
static uint32_t awaitFlags(uint32_t flags, uint32_t options, uint32_t timeout);
|
||||
};
|
||||
|
||||
#define THREAD_PRIORITY_APP Thread::PriorityNormal
|
||||
@ -150,59 +185,4 @@ private:
|
||||
#define THREAD_PRIORITY_RENDER Thread::Priority::Higher
|
||||
#define THREAD_PRIORITY_ISR Thread::Priority::Critical
|
||||
|
||||
/** Set current thread priority
|
||||
* @param[in] priority ThreadPriority value
|
||||
*/
|
||||
void thread_set_current_priority(Thread::Priority priority);
|
||||
|
||||
/** @return ThreadPriority value */
|
||||
Thread::Priority thread_get_current_priority();
|
||||
|
||||
/** @return FreeRTOS ThreadId or NULL */
|
||||
ThreadId thread_get_current_id();
|
||||
|
||||
/** @return pointer to Thread instance or NULL if this thread doesn't belongs to Tactility */
|
||||
Thread* thread_get_current();
|
||||
|
||||
/** Return control to scheduler */
|
||||
void thread_yield();
|
||||
|
||||
uint32_t thread_flags_set(ThreadId thread_id, uint32_t flags);
|
||||
|
||||
uint32_t thread_flags_clear(uint32_t flags);
|
||||
|
||||
uint32_t thread_flags_get();
|
||||
|
||||
uint32_t thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Get thread name
|
||||
* @param[in] threadId
|
||||
* @return const char* name or NULL
|
||||
*/
|
||||
const char* thread_get_name(ThreadId threadId);
|
||||
|
||||
/**
|
||||
* @brief Get thread stack watermark
|
||||
* @param[in] threadId
|
||||
* @return uint32_t
|
||||
*/
|
||||
uint32_t thread_get_stack_space(ThreadId threadId);
|
||||
|
||||
/** Suspend thread
|
||||
* @param[in] threadId thread id
|
||||
*/
|
||||
void thread_suspend(ThreadId threadId);
|
||||
|
||||
/** Resume thread
|
||||
* @param[in] threadId thread id
|
||||
*/
|
||||
void thread_resume(ThreadId threadId);
|
||||
|
||||
/** Get thread suspended state
|
||||
* @param[in] threadId thread id
|
||||
* @return true if thread is suspended
|
||||
*/
|
||||
bool thread_is_suspended(ThreadId threadId);
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
namespace tt {
|
||||
|
||||
static void timer_callback(TimerHandle_t hTimer) {
|
||||
void Timer::onCallback(TimerHandle_t hTimer) {
|
||||
auto* timer = static_cast<Timer*>(pvTimerGetTimerID(hTimer));
|
||||
|
||||
if (timer != nullptr) {
|
||||
@ -14,54 +14,59 @@ static void timer_callback(TimerHandle_t hTimer) {
|
||||
}
|
||||
}
|
||||
|
||||
Timer::Timer(Type type, Callback callback, std::shared_ptr<void> callbackContext) {
|
||||
assert((!TT_IS_ISR()) && (callback != nullptr));
|
||||
|
||||
this->callback = callback;
|
||||
this->callbackContext = std::move(callbackContext);
|
||||
static inline TimerHandle_t createTimer(Timer::Type type, void* timerId, TimerCallbackFunction_t callback) {
|
||||
assert(timerId != nullptr);
|
||||
assert(callback != nullptr);
|
||||
|
||||
UBaseType_t reload;
|
||||
if (type == Type::Once) {
|
||||
if (type == Timer::Type::Once) {
|
||||
reload = pdFALSE;
|
||||
} else {
|
||||
reload = pdTRUE;
|
||||
}
|
||||
|
||||
this->timerHandle = xTimerCreate(nullptr, portMAX_DELAY, (BaseType_t)reload, this, timer_callback);
|
||||
assert(this->timerHandle);
|
||||
return xTimerCreate(nullptr, portMAX_DELAY, (BaseType_t)reload, timerId, callback);
|
||||
}
|
||||
|
||||
Timer::Timer(Type type, Callback callback, std::shared_ptr<void> callbackContext) :
|
||||
callback(callback),
|
||||
callbackContext(std::move(callbackContext)),
|
||||
handle(createTimer(type, this, onCallback))
|
||||
{
|
||||
assert(!TT_IS_ISR());
|
||||
assert(handle != nullptr);
|
||||
}
|
||||
|
||||
Timer::~Timer() {
|
||||
assert(!TT_IS_ISR());
|
||||
tt_check(xTimerDelete(timerHandle, portMAX_DELAY) == pdPASS);
|
||||
}
|
||||
|
||||
bool Timer::start(TickType_t interval) {
|
||||
assert(!TT_IS_ISR());
|
||||
assert(interval < portMAX_DELAY);
|
||||
return xTimerChangePeriod(timerHandle, interval, portMAX_DELAY) == pdPASS;
|
||||
return xTimerChangePeriod(handle.get(), interval, portMAX_DELAY) == pdPASS;
|
||||
}
|
||||
|
||||
bool Timer::restart(TickType_t interval) {
|
||||
assert(!TT_IS_ISR());
|
||||
assert(interval < portMAX_DELAY);
|
||||
return xTimerChangePeriod(timerHandle, interval, portMAX_DELAY) == pdPASS &&
|
||||
xTimerReset(timerHandle, portMAX_DELAY) == pdPASS;
|
||||
return xTimerChangePeriod(handle.get(), interval, portMAX_DELAY) == pdPASS &&
|
||||
xTimerReset(handle.get(), portMAX_DELAY) == pdPASS;
|
||||
}
|
||||
|
||||
bool Timer::stop() {
|
||||
assert(!TT_IS_ISR());
|
||||
return xTimerStop(timerHandle, portMAX_DELAY) == pdPASS;
|
||||
return xTimerStop(handle.get(), portMAX_DELAY) == pdPASS;
|
||||
}
|
||||
|
||||
bool Timer::isRunning() {
|
||||
assert(!TT_IS_ISR());
|
||||
return xTimerIsTimerActive(timerHandle) == pdTRUE;
|
||||
return xTimerIsTimerActive(handle.get()) == pdTRUE;
|
||||
}
|
||||
|
||||
TickType_t Timer::getExpireTime() {
|
||||
assert(!TT_IS_ISR());
|
||||
return xTimerGetExpiryTime(timerHandle);
|
||||
return xTimerGetExpiryTime(handle.get());
|
||||
}
|
||||
|
||||
bool Timer::setPendingCallback(PendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeout) {
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
#include "RtosCompatTimers.h"
|
||||
#include "Thread.h"
|
||||
#include <memory>
|
||||
@ -9,15 +7,27 @@
|
||||
namespace tt {
|
||||
|
||||
class Timer {
|
||||
private:
|
||||
TimerHandle_t timerHandle;
|
||||
|
||||
public:
|
||||
|
||||
typedef void (*Callback)(std::shared_ptr<void> context);
|
||||
typedef void (*PendingCallback)(void* context, uint32_t arg);
|
||||
|
||||
private:
|
||||
|
||||
struct TimerHandleDeleter {
|
||||
void operator()(TimerHandle_t handleToDelete) {
|
||||
xTimerDelete(handleToDelete, portMAX_DELAY);
|
||||
}
|
||||
};
|
||||
|
||||
Callback callback;
|
||||
std::shared_ptr<void> callbackContext;
|
||||
std::unique_ptr<std::remove_pointer_t<TimerHandle_t>, TimerHandleDeleter> handle;
|
||||
|
||||
static void onCallback(TimerHandle_t hTimer);
|
||||
|
||||
public:
|
||||
|
||||
enum class Type {
|
||||
Once = 0, ///< One-shot timer.
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
#include "kernel/Kernel.h"
|
||||
#include "Check.h"
|
||||
#include "CoreDefines.h"
|
||||
#include "CoreTypes.h"
|
||||
#include "RtosCompatTask.h"
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "rom/ets_sys.h"
|
||||
#else
|
||||
#include <cassert>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
@ -16,90 +15,72 @@ bool isRunning() {
|
||||
return xTaskGetSchedulerState() != taskSCHEDULER_RUNNING;
|
||||
}
|
||||
|
||||
int32_t lock() {
|
||||
bool lock() {
|
||||
assert(!TT_IS_ISR());
|
||||
|
||||
int32_t lock;
|
||||
|
||||
switch (xTaskGetSchedulerState()) {
|
||||
// Already suspended
|
||||
case taskSCHEDULER_SUSPENDED:
|
||||
lock = 1;
|
||||
break;
|
||||
return true;
|
||||
|
||||
case taskSCHEDULER_RUNNING:
|
||||
vTaskSuspendAll();
|
||||
lock = 0;
|
||||
break;
|
||||
return true;
|
||||
|
||||
case taskSCHEDULER_NOT_STARTED:
|
||||
default:
|
||||
lock = (int32_t)TtStatusError;
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return previous lock state */
|
||||
return (lock);
|
||||
}
|
||||
|
||||
int32_t unlock() {
|
||||
bool unlock() {
|
||||
assert(!TT_IS_ISR());
|
||||
|
||||
int32_t lock;
|
||||
|
||||
switch (xTaskGetSchedulerState()) {
|
||||
case taskSCHEDULER_SUSPENDED:
|
||||
lock = 1;
|
||||
|
||||
if (xTaskResumeAll() != pdTRUE) {
|
||||
if (xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) {
|
||||
lock = (int32_t)TtStatusError;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
|
||||
case taskSCHEDULER_RUNNING:
|
||||
lock = 0;
|
||||
break;
|
||||
return true;
|
||||
|
||||
case taskSCHEDULER_NOT_STARTED:
|
||||
default:
|
||||
lock = (int32_t)TtStatusError;
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return previous lock state */
|
||||
return (lock);
|
||||
}
|
||||
|
||||
int32_t restoreLock(int32_t lock) {
|
||||
bool restoreLock(bool lock) {
|
||||
assert(!TT_IS_ISR());
|
||||
|
||||
switch (xTaskGetSchedulerState()) {
|
||||
case taskSCHEDULER_SUSPENDED:
|
||||
case taskSCHEDULER_RUNNING:
|
||||
if (lock == 1) {
|
||||
if (lock) {
|
||||
vTaskSuspendAll();
|
||||
} else {
|
||||
if (lock != 0) {
|
||||
lock = (int32_t)TtStatusError;
|
||||
return true;
|
||||
} else {
|
||||
if (xTaskResumeAll() != pdTRUE) {
|
||||
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
|
||||
lock = (int32_t)TtStatusError;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case taskSCHEDULER_NOT_STARTED:
|
||||
default:
|
||||
lock = (int32_t)TtStatusError;
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return new lock state */
|
||||
return (lock);
|
||||
}
|
||||
|
||||
uint32_t getTickFrequency() {
|
||||
@ -116,13 +97,11 @@ void delayTicks(TickType_t ticks) {
|
||||
}
|
||||
}
|
||||
|
||||
TtStatus delayUntilTick(TickType_t tick) {
|
||||
bool delayUntilTick(TickType_t tick) {
|
||||
assert(!TT_IS_ISR());
|
||||
|
||||
TickType_t tcnt, delay;
|
||||
TtStatus stat;
|
||||
|
||||
stat = TtStatusOk;
|
||||
tcnt = xTaskGetTickCount();
|
||||
|
||||
/* Determine remaining number of tick to delay */
|
||||
@ -130,29 +109,20 @@ TtStatus delayUntilTick(TickType_t tick) {
|
||||
|
||||
/* Check if target tick has not expired */
|
||||
if ((delay != 0U) && (0 == (delay >> (8 * sizeof(TickType_t) - 1)))) {
|
||||
if (xTaskDelayUntil(&tcnt, delay) == pdFALSE) {
|
||||
/* Did not delay */
|
||||
stat = TtStatusError;
|
||||
if (xTaskDelayUntil(&tcnt, delay) == pdPASS) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
/* No delay or already expired */
|
||||
stat = TtStatusErrorParameter;
|
||||
}
|
||||
|
||||
/* Return execution status */
|
||||
return (stat);
|
||||
return false;
|
||||
}
|
||||
|
||||
TickType_t getTicks() {
|
||||
TickType_t ticks;
|
||||
|
||||
if (TT_IS_ISR() != 0U) {
|
||||
ticks = xTaskGetTickCountFromISR();
|
||||
return xTaskGetTickCountFromISR();
|
||||
} else {
|
||||
ticks = xTaskGetTickCount();
|
||||
return xTaskGetTickCount();
|
||||
}
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
TickType_t millisToTicks(uint32_t milliseconds) {
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#else
|
||||
@ -23,22 +21,22 @@ bool isRunning();
|
||||
|
||||
/** Lock kernel, pause process scheduling
|
||||
* @warning don't call from ISR context
|
||||
* @return previous lock state(0 - unlocked, 1 - locked)
|
||||
* @return true on success
|
||||
*/
|
||||
int32_t lock();
|
||||
bool lock();
|
||||
|
||||
/** Unlock kernel, resume process scheduling
|
||||
* @warning don't call from ISR context
|
||||
* @return previous lock state(0 - unlocked, 1 - locked)
|
||||
* @return true on success
|
||||
*/
|
||||
int32_t unlock();
|
||||
bool unlock();
|
||||
|
||||
/** Restore kernel lock state
|
||||
* @warning don't call from ISR context
|
||||
* @param[in] lock The lock state
|
||||
* @return new lock state or error
|
||||
* @return true on success
|
||||
*/
|
||||
int32_t restoreLock(int32_t lock);
|
||||
bool restoreLock(bool lock);
|
||||
|
||||
/** Get kernel systick frequency
|
||||
* @return systick counts per second
|
||||
@ -57,9 +55,9 @@ void delayTicks(TickType_t ticks);
|
||||
/** Delay until tick
|
||||
* @warning don't call from ISR context
|
||||
* @param[in] ticks The tick until which kerel should delay task execution
|
||||
* @return the status
|
||||
* @return true on success
|
||||
*/
|
||||
TtStatus delayUntilTick(TickType_t tick);
|
||||
bool delayUntilTick(TickType_t tick);
|
||||
|
||||
/** Convert milliseconds to ticks
|
||||
*
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "I2cCompat.h"
|
||||
#include "CoreTypes.h"
|
||||
#include "RtosCompat.h"
|
||||
#include <climits>
|
||||
#include <string>
|
||||
@ -29,10 +28,10 @@ struct Configuration {
|
||||
i2c_config_t config;
|
||||
};
|
||||
|
||||
enum Status {
|
||||
STARTED,
|
||||
STOPPED,
|
||||
UNKNOWN
|
||||
enum class Status {
|
||||
Started,
|
||||
Stopped,
|
||||
Unknown
|
||||
};
|
||||
|
||||
bool init(const std::vector<i2c::Configuration>& configurations);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "I2cDevice.h"
|
||||
#include <cstdint>
|
||||
|
||||
bool I2cDevice::readRegister12(uint8_t reg, float& out) const {
|
||||
std::uint8_t data[2] = {0};
|
||||
|
||||
@ -25,7 +25,7 @@ void addService(std::shared_ptr<const ServiceManifest> manifest, bool autoStart)
|
||||
|
||||
TT_LOG_I(TAG, "Adding %s", id.c_str());
|
||||
|
||||
manifest_mutex.lock(TtWaitForever);
|
||||
manifest_mutex.lock();
|
||||
if (service_manifest_map[id] == nullptr) {
|
||||
service_manifest_map[id] = std::move(manifest);
|
||||
} else {
|
||||
@ -43,18 +43,18 @@ void addService(const ServiceManifest& manifest, bool autoStart) {
|
||||
}
|
||||
|
||||
std::shared_ptr<const ServiceManifest> _Nullable findManifestId(const std::string& id) {
|
||||
manifest_mutex.acquire(TtWaitForever);
|
||||
manifest_mutex.lock();
|
||||
auto iterator = service_manifest_map.find(id);
|
||||
auto manifest = iterator != service_manifest_map.end() ? iterator->second : nullptr;
|
||||
manifest_mutex.release();
|
||||
manifest_mutex.unlock();
|
||||
return manifest;
|
||||
}
|
||||
|
||||
static std::shared_ptr<ServiceInstance> _Nullable findServiceInstanceById(const std::string& id) {
|
||||
manifest_mutex.acquire(TtWaitForever);
|
||||
manifest_mutex.lock();
|
||||
auto iterator = service_instance_map.find(id);
|
||||
auto service = iterator != service_instance_map.end() ? iterator->second : nullptr;
|
||||
manifest_mutex.release();
|
||||
manifest_mutex.unlock();
|
||||
return service;
|
||||
}
|
||||
|
||||
@ -70,9 +70,9 @@ bool startService(const std::string& id) {
|
||||
auto service_instance = std::make_shared<ServiceInstance>(manifest);
|
||||
|
||||
// Register first, so that a service can retrieve itself during onStart()
|
||||
instance_mutex.acquire(TtWaitForever);
|
||||
instance_mutex.lock();
|
||||
service_instance_map[manifest->id] = service_instance;
|
||||
instance_mutex.release();
|
||||
instance_mutex.unlock();
|
||||
|
||||
service_instance->getService()->onStart(*service_instance);
|
||||
|
||||
@ -100,9 +100,9 @@ bool stopService(const std::string& id) {
|
||||
|
||||
service_instance->getService()->onStop(*service_instance);
|
||||
|
||||
instance_mutex.acquire(TtWaitForever);
|
||||
instance_mutex.lock();
|
||||
service_instance_map.erase(id);
|
||||
instance_mutex.release();
|
||||
instance_mutex.unlock();
|
||||
|
||||
if (service_instance.use_count() > 1) {
|
||||
TT_LOG_W(TAG, "Possible memory leak: service %s still has %ld references", service_instance->getManifest().id.c_str(), service_instance.use_count() - 1);
|
||||
|
||||
@ -20,11 +20,11 @@ private:
|
||||
hal::SdCard::State lastState = hal::SdCard::State::Unmounted;
|
||||
|
||||
bool lock(TickType_t timeout) const {
|
||||
return mutex.acquire(timeout) == TtStatusOk;
|
||||
return mutex.lock(timeout);
|
||||
}
|
||||
|
||||
void unlock() const {
|
||||
tt_check(mutex.release() == TtStatusOk);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void update() {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Pubsub.h"
|
||||
#include "PubSub.h"
|
||||
#include "WifiGlobals.h"
|
||||
#include "WifiSettings.h"
|
||||
#include <cstdio>
|
||||
|
||||
@ -73,53 +73,53 @@ public:
|
||||
|
||||
RadioState getRadioState() const {
|
||||
auto lockable = dataMutex.scoped();
|
||||
lockable->lock(TtWaitForever);
|
||||
lockable->lock();
|
||||
// TODO: Handle lock failure
|
||||
return radio_state;
|
||||
}
|
||||
|
||||
void setRadioState(RadioState newState) {
|
||||
auto lockable = dataMutex.scoped();
|
||||
lockable->lock(TtWaitForever);
|
||||
lockable->lock();
|
||||
// TODO: Handle lock failure
|
||||
radio_state = newState;
|
||||
}
|
||||
|
||||
bool isScanning() const {
|
||||
auto lockable = dataMutex.scoped();
|
||||
lockable->lock(TtWaitForever);
|
||||
lockable->lock();
|
||||
// TODO: Handle lock failure
|
||||
return scan_active;
|
||||
}
|
||||
|
||||
void setScanning(bool newState) {
|
||||
auto lockable = dataMutex.scoped();
|
||||
lockable->lock(TtWaitForever);
|
||||
lockable->lock();
|
||||
// TODO: Handle lock failure
|
||||
scan_active = newState;
|
||||
}
|
||||
|
||||
bool isScanActive() const {
|
||||
auto lcokable = dataMutex.scoped();
|
||||
lcokable->lock(TtWaitForever);
|
||||
lcokable->lock();
|
||||
return scan_active;
|
||||
}
|
||||
|
||||
void setScanActive(bool newState) {
|
||||
auto lockable = dataMutex.scoped();
|
||||
lockable->lock(TtWaitForever);
|
||||
lockable->lock();
|
||||
scan_active = newState;
|
||||
}
|
||||
|
||||
bool isSecureConnection() const {
|
||||
auto lockable = dataMutex.scoped();
|
||||
lockable->lock(TtWaitForever);
|
||||
lockable->lock();
|
||||
return secure_connection;
|
||||
}
|
||||
|
||||
void setSecureConnection(bool newState) {
|
||||
auto lockable = dataMutex.scoped();
|
||||
lockable->lock(TtWaitForever);
|
||||
lockable->lock();
|
||||
secure_connection = newState;
|
||||
}
|
||||
};
|
||||
@ -325,7 +325,7 @@ int getRssi() {
|
||||
|
||||
static void scan_list_alloc(std::shared_ptr<Wifi> wifi) {
|
||||
auto lockable = wifi->dataMutex.scoped();
|
||||
if (lockable->lock(TtWaitForever)) {
|
||||
if (lockable->lock()) {
|
||||
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_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) {
|
||||
auto lockable = wifi->dataMutex.scoped();
|
||||
if (lockable->lock(TtWaitForever)) {
|
||||
if (lockable->lock()) {
|
||||
if (wifi->scan_list == nullptr) {
|
||||
scan_list_alloc(wifi);
|
||||
}
|
||||
@ -343,7 +343,7 @@ static void scan_list_alloc_safely(std::shared_ptr<Wifi> wifi) {
|
||||
|
||||
static void scan_list_free(std::shared_ptr<Wifi> wifi) {
|
||||
auto lockable = wifi->dataMutex.scoped();
|
||||
if (lockable->lock(TtWaitForever)) {
|
||||
if (lockable->lock()) {
|
||||
assert(wifi->scan_list != nullptr);
|
||||
free(wifi->scan_list);
|
||||
wifi->scan_list = nullptr;
|
||||
@ -353,7 +353,7 @@ static void scan_list_free(std::shared_ptr<Wifi> wifi) {
|
||||
|
||||
static void scan_list_free_safely(std::shared_ptr<Wifi> wifi) {
|
||||
auto lockable = wifi->dataMutex.scoped();
|
||||
if (lockable->lock(TtWaitForever)) {
|
||||
if (lockable->lock()) {
|
||||
if (wifi->scan_list != nullptr) {
|
||||
scan_list_free(wifi);
|
||||
}
|
||||
@ -362,9 +362,9 @@ static void scan_list_free_safely(std::shared_ptr<Wifi> wifi) {
|
||||
|
||||
static void publish_event_simple(std::shared_ptr<Wifi> wifi, EventType type) {
|
||||
auto lockable = wifi->dataMutex.scoped();
|
||||
if (lockable->lock(TtWaitForever)) {
|
||||
if (lockable->lock()) {
|
||||
Event turning_on_event = {.type = type};
|
||||
tt_pubsub_publish(wifi->pubsub, &turning_on_event);
|
||||
wifi->pubsub->publish(&turning_on_event);
|
||||
}
|
||||
}
|
||||
|
||||
@ -379,7 +379,7 @@ static bool copy_scan_list(std::shared_ptr<Wifi> wifi) {
|
||||
}
|
||||
|
||||
auto lockable = wifi->dataMutex.scoped();
|
||||
if (!lockable->lock(TtWaitForever)) {
|
||||
if (!lockable->lock()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -449,7 +449,7 @@ static void eventHandler(TT_UNUSED void* arg, esp_event_base_t event_base, int32
|
||||
if (event_base == WIFI_EVENT) {
|
||||
TT_LOG_I(TAG, "eventHandler: WIFI_EVENT (%ld)", event_id);
|
||||
} else if (event_base == IP_EVENT) {
|
||||
TT_LOG_W(TAG, "eventHandler: IP_EVENT (%ld)", event_id);
|
||||
TT_LOG_I(TAG, "eventHandler: IP_EVENT (%ld)", event_id);
|
||||
}
|
||||
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
@ -967,15 +967,15 @@ public:
|
||||
wifi->autoConnectTimer = nullptr; // Must release as it holds a reference to this Wifi instance
|
||||
|
||||
// Acquire all mutexes
|
||||
wifi->dataMutex.acquire(TtWaitForever);
|
||||
wifi->radioMutex.acquire(TtWaitForever);
|
||||
wifi->dataMutex.lock();
|
||||
wifi->radioMutex.lock();
|
||||
|
||||
// Detach
|
||||
wifi_singleton = nullptr;
|
||||
|
||||
// Release mutexes
|
||||
wifi->dataMutex.release();
|
||||
wifi->radioMutex.release();
|
||||
wifi->dataMutex.unlock();
|
||||
wifi->radioMutex.unlock();
|
||||
|
||||
// Release (hopefully) last Wifi instance by scope
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
#include "Log.h"
|
||||
#include "MessageQueue.h"
|
||||
#include "Mutex.h"
|
||||
#include "Pubsub.h"
|
||||
#include "PubSub.h"
|
||||
#include "service/ServiceContext.h"
|
||||
|
||||
namespace tt::service::wifi {
|
||||
@ -36,7 +36,7 @@ static Wifi* wifi = nullptr;
|
||||
|
||||
static void publish_event_simple(Wifi* wifi, EventType type) {
|
||||
Event turning_on_event = { .type = type };
|
||||
tt_pubsub_publish(wifi->pubsub, &turning_on_event);
|
||||
wifi->pubsub->publish(&turning_on_event);
|
||||
}
|
||||
|
||||
// endregion Static
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user