From 6836b6967aa9a3ef7f7ee8a2b338d64109743adf Mon Sep 17 00:00:00 2001 From: DeFiDude <59237470+DeFiDude@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:15:22 -0600 Subject: [PATCH] =?UTF-8?q?Fix=20bidirectional=20comms=20regression:=20rem?= =?UTF-8?q?ove=20unsafe=20core=200=20persist=20task,=20restore=20better-pa?= =?UTF-8?q?th=20announce=20bypass=20Increase=20TCP=20drain=20budget=20(5?= =?UTF-8?q?=E2=86=9210=20frames),=20raise=20RNS-overload=20skip=20threshol?= =?UTF-8?q?d=20(200=E2=86=92500ms)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.cpp | 4 +- src/reticulum/ReticulumManager.cpp | 99 +++++++++++----------------- src/reticulum/ReticulumManager.h | 10 --- src/transport/TCPClientInterface.cpp | 4 +- 4 files changed, 41 insertions(+), 76 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 59078bf..b393e4e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1059,9 +1059,9 @@ void loop() { } } - // 8. WiFi + TCP loops (with global budget) — skip if RNS was overloaded + // 8. WiFi + TCP loops (with global budget) — skip only if RNS severely overloaded { - bool skipTcp = (rnsDuration > 200); + bool skipTcp = (rnsDuration > 500); if (!skipTcp && wifiImpl) wifiImpl->loop(); if (!skipTcp) { unsigned long tcpBudgetStart = millis(); diff --git a/src/reticulum/ReticulumManager.cpp b/src/reticulum/ReticulumManager.cpp index 88a73b3..3b6d1eb 100644 --- a/src/reticulum/ReticulumManager.cpp +++ b/src/reticulum/ReticulumManager.cpp @@ -3,7 +3,6 @@ #include "config/Config.h" #include #include -#include #include #include @@ -120,9 +119,13 @@ bool ReticulumManager::begin(SX1262* radio, FlashStore* flash) { if (++count > maxRate) return false; // Skip re-validation of known paths (saves ~100ms Ed25519 per announce) - // Allow through once per 5 min for name/ratchet updates. - // Uses raw hash bytes as key (avoids expensive toHex + hops_to per packet). + // Allow through if better path discovered, or once per 5 min for name/ratchet updates. + // Uses raw hash bytes as key (avoids expensive toHex per packet). if (RNS::Transport::has_path(packet.destination_hash())) { + // Always allow announces with a better (shorter) path through + uint8_t existingHops = RNS::Transport::hops_to(packet.destination_hash()); + if (packet.hops() < existingHops) return true; + static std::unordered_map lastRevalidate; std::string key((const char*)packet.destination_hash().data(), packet.destination_hash().size()); @@ -157,7 +160,6 @@ bool ReticulumManager::begin(SX1262* radio, FlashStore* flash) { _destination.accepts_links(true); _transportActive = true; - startPersistTask(); Serial.println("[RNS] Endpoint active"); return true; } @@ -252,68 +254,41 @@ void ReticulumManager::loop() { } } -// --- Background persist task (runs on core 0) --- -// Flash writes take 0.5-4+ seconds on LittleFS. Running them on core 0 -// keeps the main loop (core 1) responsive for UI and radio. - -void ReticulumManager::startPersistTask() { - _persistQueue = xQueueCreate(1, sizeof(uint8_t)); - xTaskCreatePinnedToCore(persistTaskFunc, "persist", 8192, this, 1, &_persistTask, 0); - Serial.println("[RNS] Persist task started on core 0"); -} - -void ReticulumManager::persistTaskFunc(void* param) { - ReticulumManager* self = (ReticulumManager*)param; - uint8_t cycle; - for (;;) { - if (xQueueReceive(self->_persistQueue, &cycle, portMAX_DELAY) == pdTRUE) { - unsigned long start = millis(); - switch (cycle) { - case 0: - RNS::Transport::persist_data(); - break; - case 1: - RNS::Identity::persist_data(); - break; - case 2: - if (self->_sd && self->_sd->isReady()) { - static const char* files[] = {"/destination_table", "/packet_hashlist", "/known_destinations"}; - for (const char* name : files) { - File f = LittleFS.open(name, "r"); - if (f && f.size() > 0) { - size_t sz = f.size(); - uint8_t* buf = (uint8_t*)malloc(sz); - if (buf) { - f.readBytes((char*)buf, sz); - char sdPath[64]; - snprintf(sdPath, sizeof(sdPath), "/ratputer/transport%s", name); - self->_sd->ensureDir("/ratputer/transport"); - self->_sd->writeSimple(sdPath, buf, sz); - free(buf); - } - } - if (f) f.close(); +// Synchronous persist — one cycle per call to spread I/O across intervals. +// Runs on core 1 (main loop) to avoid data races with microReticulum's +// single-threaded transport state. +void ReticulumManager::persistData() { + unsigned long start = millis(); + switch (_persistCycle) { + case 0: + RNS::Transport::persist_data(); + break; + case 1: + RNS::Identity::persist_data(); + break; + case 2: + if (_sd && _sd->isReady()) { + static const char* files[] = {"/destination_table", "/packet_hashlist", "/known_destinations"}; + for (const char* name : files) { + File f = LittleFS.open(name, "r"); + if (f && f.size() > 0) { + size_t sz = f.size(); + uint8_t* buf = (uint8_t*)malloc(sz); + if (buf) { + f.readBytes((char*)buf, sz); + char sdPath[64]; + snprintf(sdPath, sizeof(sdPath), "/ratputer/transport%s", name); + _sd->ensureDir("/ratputer/transport"); + _sd->writeSimple(sdPath, buf, sz); + free(buf); } } - break; + if (f) f.close(); + } } - Serial.printf("[PERSIST] Cycle %d done (%lums, core %d)\n", - cycle, millis() - start, xPortGetCoreID()); - } - } -} - -void ReticulumManager::persistData() { - if (_persistQueue) { - // Queue the cycle for background execution — non-blocking - xQueueOverwrite(_persistQueue, &_persistCycle); - } else { - // Fallback: synchronous (before task is started) - switch (_persistCycle) { - case 0: RNS::Transport::persist_data(); break; - case 1: RNS::Identity::persist_data(); break; - } + break; } + Serial.printf("[PERSIST] Cycle %d done (%lums)\n", _persistCycle, millis() - start); _persistCycle = (_persistCycle + 1) % 3; } diff --git a/src/reticulum/ReticulumManager.h b/src/reticulum/ReticulumManager.h index c4a450a..02557dd 100644 --- a/src/reticulum/ReticulumManager.h +++ b/src/reticulum/ReticulumManager.h @@ -7,10 +7,6 @@ #include #include #include -#include -#include -#include - #include "transport/LoRaInterface.h" #include "storage/FlashStore.h" #include "storage/SDStore.h" @@ -63,8 +59,6 @@ public: private: bool loadOrCreateIdentity(); void saveIdentityToAll(const RNS::Bytes& keyData); - void startPersistTask(); - static void persistTaskFunc(void* param); RNS::Reticulum _reticulum; RNS::Identity _identity; @@ -77,8 +71,4 @@ private: unsigned long _lastPersist = 0; unsigned long _lastAnnounceTime = 0; uint8_t _persistCycle = 0; // Rotating: 0=Transport, 1=Identity, 2=SD backup - - // Background persist task (core 0) — flash writes don't block main loop - QueueHandle_t _persistQueue = nullptr; - TaskHandle_t _persistTask = nullptr; }; diff --git a/src/transport/TCPClientInterface.cpp b/src/transport/TCPClientInterface.cpp index 6b1e9f9..364d153 100644 --- a/src/transport/TCPClientInterface.cpp +++ b/src/transport/TCPClientInterface.cpp @@ -83,9 +83,9 @@ void TCPClientInterface::loop() { return; // Will reconnect on next loop iteration } - // Drain incoming frames per loop (up to 5, time-boxed to prevent announce flood blocking) + // Drain incoming frames per loop (up to 10, time-boxed) unsigned long tcpStart = millis(); - for (int i = 0; i < 5 && _client.available() && (millis() - tcpStart < TCP_LOOP_BUDGET_MS); i++) { + for (int i = 0; i < 10 && _client.available() && (millis() - tcpStart < TCP_LOOP_BUDGET_MS); i++) { unsigned long rxStart = millis(); int len = readFrame(); if (len > 0) {