From 515eb2a16bdc1367969ca6d8c079e3a55bf4ab62 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 13 Jun 2026 06:59:43 +0000 Subject: [PATCH] fix: run message/sendmail shunting-yard synchronously to avoid main-loop deadlock command_message() and command_sendmail() handed their value to WebSchedulerService via raw_value and busy-waited up to 2s for the scheduler loop (running in a separate task) to compute it. After the scheduler was moved to run synchronously in the main loop, any caller running in the main loop (MQTT-triggered commands, scheduler-triggered commands) deadlocks: the loop that would compute raw_value cannot run while the caller is blocking inside it. The 2s wait then times out and system/message fails entirely (sendmail sends the un-computed body). Compute the value directly with compute() instead, which restores correct behaviour for all callers. Co-authored-by: Proddy --- src/core/system.cpp | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/core/system.cpp b/src/core/system.cpp index 208e657c5..0bf2ec654 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -32,6 +32,7 @@ #include #include "firmwareVersion.h" +#include "shuntingYard.h" // for compute() used by the message and sendmail commands #if defined(EMSESP_TEST) #include "../test/test.h" @@ -194,15 +195,11 @@ bool System::command_sendmail(const char * value, const int8_t id) { // msg.headers.addCustom("Importance", PRIORITY); // msg.headers.addCustom("X-MSMail-Priority", PRIORITY); // msg.headers.addCustom("X-Priority", PRIORITY_NUM); - EMSESP::webSchedulerService.computed_value.clear(); - EMSESP::webSchedulerService.raw_value = body.c_str(); - for (uint16_t wait = 0; wait < 2000 && !EMSESP::webSchedulerService.raw_value.empty(); wait++) { - delay(1); - } - if (!EMSESP::webSchedulerService.computed_value.empty()) { - body = EMSESP::webSchedulerService.computed_value.c_str(); - EMSESP::webSchedulerService.computed_value.clear(); - EMSESP::webSchedulerService.computed_value.shrink_to_fit(); // free allocated memory + // run the body through the Shunting Yard calculator (entity substitution, expressions, optional {url} fetch) + // keep the original body if the calculator returns nothing + std::string computed_body = compute(body.c_str()); + if (!computed_body.empty()) { + body = computed_body.c_str(); } msg.text.body(body); @@ -344,22 +341,16 @@ bool System::command_message(const char * value, const int8_t id, JsonObject out return false; // must have a string value } - EMSESP::webSchedulerService.computed_value.clear(); - EMSESP::webSchedulerService.raw_value = value; - for (uint16_t wait = 0; wait < 2000 && !EMSESP::webSchedulerService.raw_value.empty(); wait++) { - delay(1); - } - - if (EMSESP::webSchedulerService.computed_value.empty()) { + // process the message via the Shunting Yard calculator (entity substitution, expressions, optional {url} fetch) + std::string computed_value = compute(value); + if (computed_value.empty()) { LOG_WARNING("Message result is empty"); return false; } - LOG_INFO("Message: %s", EMSESP::webSchedulerService.computed_value.c_str()); // send to log - Mqtt::queue_publish(F_(message), EMSESP::webSchedulerService.computed_value); // send to MQTT if enabled - output["api_data"] = EMSESP::webSchedulerService.computed_value; // send to API - EMSESP::webSchedulerService.computed_value.clear(); - EMSESP::webSchedulerService.computed_value.shrink_to_fit(); + LOG_INFO("Message: %s", computed_value.c_str()); // send to log + Mqtt::queue_publish(F_(message), computed_value); // send to MQTT if enabled + output["api_data"] = computed_value; // send to API return true; }