chore: Initial multi-core compatibility (#2294)
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
#include <Serialization.h>
|
#include <Serialization.h>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "I18nKeys.h"
|
#include "I18nKeys.h"
|
||||||
@@ -96,6 +97,7 @@ uint8_t CrossPointSettings::sleepTimeoutEnumToMinutes(const uint8_t legacyValue)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CrossPointSettings::saveToFile() const {
|
bool CrossPointSettings::saveToFile() const {
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
Storage.mkdir("/.crosspoint");
|
Storage.mkdir("/.crosspoint");
|
||||||
return JsonSettingsIO::saveSettings(*this, SETTINGS_FILE_JSON);
|
return JsonSettingsIO::saveSettings(*this, SETTINGS_FILE_JSON);
|
||||||
}
|
}
|
||||||
@@ -106,7 +108,11 @@ bool CrossPointSettings::loadFromFile() {
|
|||||||
String json = Storage.readFile(SETTINGS_FILE_JSON);
|
String json = Storage.readFile(SETTINGS_FILE_JSON);
|
||||||
if (!json.isEmpty()) {
|
if (!json.isEmpty()) {
|
||||||
bool resave = false;
|
bool resave = false;
|
||||||
bool result = JsonSettingsIO::loadSettings(*this, json.c_str(), &resave);
|
bool result;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
result = JsonSettingsIO::loadSettings(*this, json.c_str(), &resave);
|
||||||
|
}
|
||||||
if (result && resave) {
|
if (result && resave) {
|
||||||
if (saveToFile()) {
|
if (saveToFile()) {
|
||||||
LOG_DBG("CPS", "Resaved settings to update format");
|
LOG_DBG("CPS", "Resaved settings to update format");
|
||||||
@@ -166,6 +172,7 @@ bool CrossPointSettings::loadFromBinaryFile() {
|
|||||||
if (!Storage.openFileForRead("CPS", SETTINGS_FILE_BIN, inputFile)) {
|
if (!Storage.openFileForRead("CPS", SETTINGS_FILE_BIN, inputFile)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
serialization::readPod(inputFile, version);
|
serialization::readPod(inputFile, version);
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
class CrossPointSettings {
|
class CrossPointSettings {
|
||||||
private:
|
private:
|
||||||
|
mutable std::mutex _mutex;
|
||||||
|
|
||||||
// Private constructor for singleton
|
// Private constructor for singleton
|
||||||
CrossPointSettings() = default;
|
CrossPointSettings() = default;
|
||||||
|
|
||||||
@@ -17,6 +20,10 @@ class CrossPointSettings {
|
|||||||
CrossPointSettings(const CrossPointSettings&) = delete;
|
CrossPointSettings(const CrossPointSettings&) = delete;
|
||||||
CrossPointSettings& operator=(const CrossPointSettings&) = delete;
|
CrossPointSettings& operator=(const CrossPointSettings&) = delete;
|
||||||
|
|
||||||
|
// Access the settings mutex for protecting multi-field reads/writes from other cores.
|
||||||
|
// Callers must not re-enter SETTINGS methods that lock _mutex while holding it.
|
||||||
|
std::mutex& getMutex() const { return _mutex; }
|
||||||
|
|
||||||
enum SLEEP_SCREEN_MODE {
|
enum SLEEP_SCREEN_MODE {
|
||||||
DARK = 0,
|
DARK = 0,
|
||||||
LIGHT = 1,
|
LIGHT = 1,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <Serialization.h>
|
#include <Serialization.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr uint8_t STATE_FILE_VERSION = 4;
|
constexpr uint8_t STATE_FILE_VERSION = 4;
|
||||||
@@ -32,6 +33,7 @@ void CrossPointState::pushRecentSleep(uint16_t idx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CrossPointState::saveToFile() const {
|
bool CrossPointState::saveToFile() const {
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
Storage.mkdir("/.crosspoint");
|
Storage.mkdir("/.crosspoint");
|
||||||
return JsonSettingsIO::saveState(*this, STATE_FILE_JSON);
|
return JsonSettingsIO::saveState(*this, STATE_FILE_JSON);
|
||||||
}
|
}
|
||||||
@@ -41,6 +43,7 @@ bool CrossPointState::loadFromFile() {
|
|||||||
if (Storage.exists(STATE_FILE_JSON)) {
|
if (Storage.exists(STATE_FILE_JSON)) {
|
||||||
String json = Storage.readFile(STATE_FILE_JSON);
|
String json = Storage.readFile(STATE_FILE_JSON);
|
||||||
if (!json.isEmpty()) {
|
if (!json.isEmpty()) {
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
return JsonSettingsIO::loadState(*this, json.c_str());
|
return JsonSettingsIO::loadState(*this, json.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,6 +70,7 @@ bool CrossPointState::loadFromBinaryFile() {
|
|||||||
if (!Storage.openFileForRead("CPS", STATE_FILE_BIN, inputFile)) {
|
if (!Storage.openFileForRead("CPS", STATE_FILE_BIN, inputFile)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
serialization::readPod(inputFile, version);
|
serialization::readPod(inputFile, version);
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class CrossPointState {
|
class CrossPointState {
|
||||||
|
mutable std::mutex _mutex;
|
||||||
|
|
||||||
// Static instance
|
// Static instance
|
||||||
static CrossPointState instance;
|
static CrossPointState instance;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// Access the state mutex for protecting multi-field reads/writes from other cores.
|
||||||
|
std::mutex& getMutex() const { return _mutex; }
|
||||||
|
|
||||||
static constexpr uint8_t SLEEP_RECENT_COUNT = 16;
|
static constexpr uint8_t SLEEP_RECENT_COUNT = 16;
|
||||||
|
|
||||||
std::string openEpubPath;
|
std::string openEpubPath;
|
||||||
|
|||||||
@@ -19,12 +19,15 @@
|
|||||||
#include "settings/SettingsActivity.h"
|
#include "settings/SettingsActivity.h"
|
||||||
#include "util/FullScreenMessageActivity.h"
|
#include "util/FullScreenMessageActivity.h"
|
||||||
|
|
||||||
|
static portMUX_TYPE activityManagerSpinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||||
|
|
||||||
void ActivityManager::begin() {
|
void ActivityManager::begin() {
|
||||||
xTaskCreate(&renderTaskTrampoline, "ActivityManagerRender",
|
xTaskCreatePinnedToCore(&renderTaskTrampoline, "ActivityManagerRender",
|
||||||
8192, // Stack size
|
8192, // Stack size
|
||||||
this, // Parameters
|
this, // Parameters
|
||||||
1, // Priority
|
1, // Priority
|
||||||
&renderTaskHandle // Task handle
|
&renderTaskHandle, // Task handle
|
||||||
|
0 // Pin to core 0 (PRO_CPU)
|
||||||
);
|
);
|
||||||
assert(renderTaskHandle != nullptr && "Failed to create render task");
|
assert(renderTaskHandle != nullptr && "Failed to create render task");
|
||||||
}
|
}
|
||||||
@@ -46,10 +49,10 @@ void ActivityManager::renderTaskLoop() {
|
|||||||
}
|
}
|
||||||
// Notify any task blocked in requestUpdateAndWait() that the render is done.
|
// Notify any task blocked in requestUpdateAndWait() that the render is done.
|
||||||
TaskHandle_t waiter = nullptr;
|
TaskHandle_t waiter = nullptr;
|
||||||
taskENTER_CRITICAL(nullptr);
|
taskENTER_CRITICAL(&activityManagerSpinlock);
|
||||||
waiter = waitingTaskHandle;
|
waiter = waitingTaskHandle;
|
||||||
waitingTaskHandle = nullptr;
|
waitingTaskHandle = nullptr;
|
||||||
taskEXIT_CRITICAL(nullptr);
|
taskEXIT_CRITICAL(&activityManagerSpinlock);
|
||||||
if (waiter) {
|
if (waiter) {
|
||||||
xTaskNotify(waiter, 1, eIncrement);
|
xTaskNotify(waiter, 1, eIncrement);
|
||||||
}
|
}
|
||||||
@@ -137,8 +140,7 @@ void ActivityManager::loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requestedUpdate) {
|
if (requestedUpdate.exchange(false)) {
|
||||||
requestedUpdate = false;
|
|
||||||
// Using direct notification to signal the render task to update
|
// Using direct notification to signal the render task to update
|
||||||
// Increment counter so multiple rapid calls won't be lost
|
// Increment counter so multiple rapid calls won't be lost
|
||||||
if (renderTaskHandle) {
|
if (renderTaskHandle) {
|
||||||
@@ -280,7 +282,7 @@ void ActivityManager::requestUpdateAndWait() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Atomic section to perform checks
|
// Atomic section to perform checks
|
||||||
taskENTER_CRITICAL(nullptr);
|
taskENTER_CRITICAL(&activityManagerSpinlock);
|
||||||
auto currTaskHandler = xTaskGetCurrentTaskHandle();
|
auto currTaskHandler = xTaskGetCurrentTaskHandle();
|
||||||
auto mutexHolder = xSemaphoreGetMutexHolder(renderingMutex);
|
auto mutexHolder = xSemaphoreGetMutexHolder(renderingMutex);
|
||||||
bool isRenderTask = (currTaskHandler == renderTaskHandle);
|
bool isRenderTask = (currTaskHandler == renderTaskHandle);
|
||||||
@@ -289,7 +291,7 @@ void ActivityManager::requestUpdateAndWait() {
|
|||||||
if (!alreadyWaiting && !isRenderTask && !holdingRenderLock) {
|
if (!alreadyWaiting && !isRenderTask && !holdingRenderLock) {
|
||||||
waitingTaskHandle = currTaskHandler;
|
waitingTaskHandle = currTaskHandler;
|
||||||
}
|
}
|
||||||
taskEXIT_CRITICAL(nullptr);
|
taskEXIT_CRITICAL(&activityManagerSpinlock);
|
||||||
|
|
||||||
// Render task cannot call requestUpdateAndWait() or it will cause a deadlock
|
// Render task cannot call requestUpdateAndWait() or it will cause a deadlock
|
||||||
assert(!isRenderTask && "Render task cannot call requestUpdateAndWait()");
|
assert(!isRenderTask && "Render task cannot call requestUpdateAndWait()");
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <freertos/semphr.h>
|
#include <freertos/semphr.h>
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -63,7 +64,7 @@ class ActivityManager {
|
|||||||
|
|
||||||
// Whether to trigger a render after the current loop()
|
// Whether to trigger a render after the current loop()
|
||||||
// This variable must only be set by the main loop, to avoid race conditions
|
// This variable must only be set by the main loop, to avoid race conditions
|
||||||
bool requestedUpdate = false;
|
std::atomic<bool> requestedUpdate{false};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ActivityManager(GfxRenderer& renderer, MappedInputManager& mappedInput)
|
explicit ActivityManager(GfxRenderer& renderer, MappedInputManager& mappedInput)
|
||||||
|
|||||||
Reference in New Issue
Block a user