mirror of
https://github.com/esphome/esphome.git
synced 2026-03-01 10:24:19 -07:00
104 lines
4.3 KiB
C++
104 lines
4.3 KiB
C++
#pragma once
|
|
|
|
#ifdef USE_LIBRETINY
|
|
|
|
#include "esphome/core/defines.h"
|
|
#include "esphome/core/helpers.h"
|
|
|
|
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
|
|
#include <cstdarg>
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include <FreeRTOS.h>
|
|
#include <semphr.h>
|
|
#include <task.h>
|
|
|
|
namespace esphome::logger {
|
|
|
|
/**
|
|
* @brief Task log buffer for LibreTiny platform using mutex-protected circular buffer.
|
|
*
|
|
* Why This Is Critical:
|
|
* Without thread-safe logging, when a non-main task logs a message, it would directly
|
|
* call the logger which builds a protobuf message in a shared buffer. If this happens
|
|
* while the main loop is also using that buffer (e.g., sending API responses), the
|
|
* buffer gets corrupted, sending garbage to all connected API clients and breaking
|
|
* their connections. This buffer ensures log messages from other tasks are queued
|
|
* safely and processed only from the main loop.
|
|
*
|
|
* Threading Model: Multi-Producer Single-Consumer (MPSC)
|
|
* - Multiple FreeRTOS tasks can safely call send_message_thread_safe() concurrently
|
|
* - Only the main loop task calls borrow_message_main_loop() and release_message_main_loop()
|
|
*
|
|
* This uses a simple circular buffer protected by a FreeRTOS mutex. Unlike ESP32,
|
|
* LibreTiny lacks hardware atomic support (ARM968E-S has no LDREX/STREX), so we use
|
|
* a volatile counter for fast has_messages() checks instead of atomics.
|
|
*
|
|
* Design:
|
|
* - Variable-size messages with header + text stored contiguously (NOSPLIT style)
|
|
* - FreeRTOS mutex protects all buffer operations
|
|
* - Volatile counter enables fast has_messages() without lock overhead
|
|
* - If message doesn't fit at end, padding is added and message wraps to start
|
|
*/
|
|
class TaskLogBuffer {
|
|
public:
|
|
// Structure for a log message header (text data follows immediately after)
|
|
struct LogMessage {
|
|
const char *tag; // We store the pointer, assuming tags are static
|
|
char thread_name[16]; // Store thread name directly (only used for non-main threads)
|
|
uint16_t text_length; // Length of the message text (up to ~64KB)
|
|
uint16_t line; // Source code line number
|
|
uint8_t level; // Log level (0-7)
|
|
|
|
// Methods for accessing message contents
|
|
inline char *text_data() { return reinterpret_cast<char *>(this) + sizeof(LogMessage); }
|
|
inline const char *text_data() const { return reinterpret_cast<const char *>(this) + sizeof(LogMessage); }
|
|
};
|
|
|
|
// Padding marker level to indicate wrap-around point (stored in LogMessage.level field)
|
|
// Valid log levels are 0-7, so 0xFF cannot be a real message
|
|
static constexpr uint8_t PADDING_MARKER_LEVEL = 0xFF;
|
|
|
|
// Constructor that takes a total buffer size
|
|
explicit TaskLogBuffer(size_t total_buffer_size);
|
|
~TaskLogBuffer();
|
|
|
|
// NOT thread-safe - borrow a message from the buffer, only call from main loop
|
|
bool borrow_message_main_loop(LogMessage *&message, uint16_t &text_length);
|
|
|
|
// NOT thread-safe - release a message buffer, only call from main loop
|
|
void release_message_main_loop();
|
|
|
|
// Thread-safe - send a message to the buffer from any thread
|
|
bool send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *thread_name,
|
|
const char *format, va_list args);
|
|
|
|
// Fast check using volatile counter - no lock needed
|
|
// Worst case: miss a message for one loop iteration (~8ms at 7000 loops/min)
|
|
inline bool HOT has_messages() const { return this->message_count_ != 0; }
|
|
|
|
// Get the total buffer size in bytes
|
|
inline size_t size() const { return this->size_; }
|
|
|
|
private:
|
|
// Calculate total size needed for a message (header + text + null terminator)
|
|
static inline size_t message_total_size(size_t text_length) { return sizeof(LogMessage) + text_length + 1; }
|
|
|
|
// Calculate available contiguous space at write position
|
|
size_t available_contiguous_space() const;
|
|
|
|
uint8_t *storage_{nullptr}; // Pointer to allocated memory
|
|
size_t size_{0}; // Size of allocated memory
|
|
size_t head_{0}; // Write position
|
|
size_t tail_{0}; // Read position
|
|
|
|
SemaphoreHandle_t mutex_{nullptr}; // FreeRTOS mutex for thread safety
|
|
volatile uint16_t message_count_{0}; // Fast check counter (dirty read OK)
|
|
size_t current_message_size_{0}; // Size of currently borrowed message
|
|
};
|
|
|
|
} // namespace esphome::logger
|
|
|
|
#endif // USE_ESPHOME_TASK_LOG_BUFFER
|
|
#endif // USE_LIBRETINY
|