Reduce room board recovery stack use under sustained reads

This commit is contained in:
mikecarper committed 2026-10-18 00:00:47 -07:00
1 parent f88b9e65ed
commit 48bad8ddfb
6 files changed
+106 -22

No files matched your search

+29
View File
@@ -1,6 +1,7 @@
#include "filesystem.h"
#include <cstdlib>
#include <new>
#include <memory>
#include <array>
#include <ArduinoJson.h>
static bool deny_json_allocation = false;
@@ -24,6 +25,11 @@ static void* trackedRealloc(void* ptr, size_t size) {
#undef free
#undef realloc
#include <helpers/RoomBoardStore.h>
static bool deny_board_scratch_allocation = false;
void* operator new(size_t size, const std::nothrow_t&) noexcept {
if (deny_board_scratch_allocation) return nullptr;
try { return ::operator new(size); } catch (const std::bad_alloc&) { return nullptr; }
}
#include <helpers/RoomMailWeb.h>
#include <helpers/RoomHistoryStore.h>
#include <helpers/RoomAccessPolicy.h>
@@ -172,6 +178,10 @@ struct MyMesh {
mesh::RoomJsonWriter&) __attribute__((noinline));
void processRoomWebAdmin(const char*, JsonObjectConst, mesh::RoomJsonWriter&) __attribute__((noinline));
void processRoomWebBoard(const char*, JsonObjectConst, mesh::RoomJsonWriter&) __attribute__((noinline));
void processRoomWebBoardRead(JsonObjectConst, const mesh::RoomBoardIndex&,
mesh::RoomJsonWriter&) __attribute__((noinline));
void processRoomWebBoardWrite(const char*, JsonObjectConst, const mesh::RoomBoardIndex&,
mesh::RoomJsonWriter&) __attribute__((noinline));
void processRoomRequest(const uint8_t*, uint32_t, char*, char*, size_t);
void serviceRoomQuotas();
bool storePost(const mesh::Identity&, const char*);
@@ -1032,6 +1042,25 @@ int main() {
assert(result(m, saveRequest(1, 0, "Reused", "new"), 36)["ok"] == true);
error(m, request("board.read", "", ",\"id\":1,\"version\":2,\"offset\":0"), 37, "article changed"); ++scenarios;
}
{
MyMesh m; enable(m);
assert(result(m, saveRequest(1, 0, "Retained", std::string(2048, 's')), 1)["ok"] == true);
std::map<std::string, std::vector<uint8_t>> before;
for (const auto& file : m.storage.files) before[file.first] = *file.second;
m.storage.trace.clear();
deny_board_scratch_allocation = true;
error(m, request("board.index", "", ",\"revision\":0,\"cursor\":0"), 2, "insufficient memory");
error(m, request("board.read", "", ",\"id\":1,\"version\":1,\"offset\":0"), 3, "insufficient memory");
error(m, saveRequest(1, 1, "Changed", "new"), 4, "insufficient memory");
error(m, request("board.delete", "admin-secret", ",\"boot\":71,\"id\":1,\"version\":1"), 5, "insufficient memory");
deny_board_scratch_allocation = false;
assert(m.storage.trace.empty() && m.storage.files.size() == before.size());
for (const auto& file : m.storage.files) assert(*file.second == before.at(file.first));
const auto page = result(m, request("board.index", "", ",\"revision\":0,\"cursor\":0"), 6);
assert(page["revision"] == 1 && page["articles"][0]["length"] == 2048);
assert(result(m, request("board.read", "", ",\"id\":1,\"version\":1,\"offset\":0"), 7)["total"] == 2048);
++scenarios;
}
{
MyMesh m; enable(m); uint32_t sequence = 1;
for (const std::string& input : {std::string("{"), std::string("[]"), std::string("null"), std::string("{\"op\":{\"a\":{\"b\":{\"c\":{\"d\":1}}}}}"),
+8
View File
@@ -95,7 +95,15 @@ static void checksum(std::vector<uint8_t>& image) {
static void preserveUnavailable(MemoryFS& fs) {
const auto before = snapshot(fs); const auto directories = fs.directories;
Index index; index.count = 7; index.revision = 99;
for (auto& article : index.articles) {
article.id = 7; article.bank = 1; article.body_length = 2000; article.version = 98;
memset(article.title, 'x', sizeof(article.title));
}
assert(!load(fs, index) && index.count == 0 && index.revision == 0);
for (const auto& article : index.articles) {
assert(article.id == 0 && article.bank == 0 && article.body_length == 0 && article.version == 0);
for (char value : article.title) assert(value == 0);
}
assert(save(fs, 1, "Changed", NEW_BODY) == Result::Unavailable);
assert(erase(fs, 1) == Result::Unavailable);
uint8_t out[128]; size_t count = 99;
+3 -1
View File
@@ -31,6 +31,7 @@ class RoomWebServiceTest(unittest.TestCase):
"bool MyMesh::handleRoomWebCommand(", "static const char* roomWebString(",
"bool MyMesh::processRoomWebMail(", "void MyMesh::processRoomWebAdmin(",
"void MyMesh::processRoomWebBoard(",
"void MyMesh::processRoomWebBoardRead(", "void MyMesh::processRoomWebBoardWrite(",
"void MyMesh::processRoomRequest(", "void MyMesh::serviceRoomQuotas(",
"bool MyMesh::storePost(", "bool MyMesh::snapshotRoomHistory(",
"bool MyMesh::executeClientPathCommand(", "bool MyMesh::setRoomClientPath(",
@@ -111,7 +112,8 @@ class RoomWebServiceTest(unittest.TestCase):
usage = "\n".join(path.read_text() for path in work.glob("*.su"))
frames = {}
for method, budget in (("processRoomRequest", 1024), ("processRoomWebAdmin", 1536),
("processRoomWebBoard", 2048), ("processRoomWebMail", 3072)):
("processRoomWebBoard", 384), ("processRoomWebBoardRead", 1152),
("processRoomWebBoardWrite", 1536), ("processRoomWebMail", 3072)):
sizes = re.findall(r"(?:void|bool) MyMesh::" + method
+ r"\([^\t\n]*\)\s*\t(\d+)\t[^\n]*", usage)
self.assertEqual(len(sizes), 1, "missing or ambiguous stack frame: " + method)