ESPHome 2026.4.0-dev
Loading...
Searching...
No Matches
task_log_buffer_host.cpp
Go to the documentation of this file.
1#ifdef USE_HOST
2
4
5#ifdef USE_ESPHOME_TASK_LOG_BUFFER
6
7#include "esphome/core/log.h"
8#include <algorithm>
9#include <cstdio>
10
11namespace esphome::logger {
12
13int TaskLogBuffer::acquire_write_slot_() {
14 // Try to reserve a slot using compare-and-swap
15 size_t current_reserve = this->reserve_index_.load(std::memory_order_relaxed);
16
17 while (true) {
18 // Calculate next index (with wrap-around)
19 size_t next_reserve = (current_reserve + 1) % ESPHOME_TASK_LOG_BUFFER_SIZE;
20
21 // Check if buffer would be full
22 // Buffer is full when next write position equals read position
23 size_t current_read = this->read_index_.load(std::memory_order_acquire);
24 if (next_reserve == current_read) {
25 return -1; // Buffer full
26 }
27
28 // Try to claim this slot
29 if (this->reserve_index_.compare_exchange_weak(current_reserve, next_reserve, std::memory_order_acq_rel,
30 std::memory_order_relaxed)) {
31 return static_cast<int>(current_reserve);
32 }
33 // If CAS failed, current_reserve was updated, retry with new value
34 }
35}
36
37void TaskLogBuffer::commit_write_slot_(int slot_index) {
38 // Mark the slot as ready for reading
39 this->slots_[slot_index].ready.store(true, std::memory_order_release);
40
41 // Try to advance the write_index if we're the next expected commit
42 // This ensures messages are read in order
43 size_t expected = slot_index;
44 size_t next = (slot_index + 1) % ESPHOME_TASK_LOG_BUFFER_SIZE;
45
46 // We only advance write_index if this slot is the next one expected
47 // This handles out-of-order commits correctly
48 while (true) {
49 if (!this->write_index_.compare_exchange_weak(expected, next, std::memory_order_release,
50 std::memory_order_relaxed)) {
51 // Someone else advanced it or we're not next in line, that's fine
52 break;
53 }
54
55 // Successfully advanced, check if next slot is also ready
56 expected = next;
57 next = (next + 1) % ESPHOME_TASK_LOG_BUFFER_SIZE;
58 if (!this->slots_[expected].ready.load(std::memory_order_acquire)) {
59 break;
60 }
61 }
62}
63
64bool TaskLogBuffer::send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *thread_name,
65 const char *format, va_list args) {
66 // Acquire a slot
67 int slot_index = this->acquire_write_slot_();
68 if (slot_index < 0) {
69 return false; // Buffer full
70 }
71
72 LogMessage &msg = this->slots_[slot_index];
73
74 // Fill in the message header
75 msg.level = level;
76 msg.tag = tag;
77 msg.line = line;
78
79 // Store the thread name now to avoid crashes if thread exits before processing
80 if (thread_name != nullptr) {
81 strncpy(msg.thread_name, thread_name, sizeof(msg.thread_name) - 1);
82 msg.thread_name[sizeof(msg.thread_name) - 1] = '\0';
83 } else {
84 msg.thread_name[0] = '\0';
85 }
86
87 // Format the message text
88 int ret = vsnprintf(msg.text, sizeof(msg.text), format, args);
89 if (ret < 0) {
90 // Formatting error - still commit the slot but with empty text
91 msg.text[0] = '\0';
92 msg.text_length = 0;
93 } else {
94 msg.text_length = static_cast<uint16_t>(std::min(static_cast<size_t>(ret), sizeof(msg.text) - 1));
95 }
96
97 // Remove trailing newlines
98 while (msg.text_length > 0 && msg.text[msg.text_length - 1] == '\n') {
99 msg.text_length--;
100 }
101 msg.text[msg.text_length] = '\0';
102
103 // Commit the slot
104 this->commit_write_slot_(slot_index);
105
106 return true;
107}
108
109bool TaskLogBuffer::borrow_message_main_loop(LogMessage *&message, uint16_t &text_length) {
110 size_t current_read = this->read_index_.load(std::memory_order_relaxed);
111 size_t current_write = this->write_index_.load(std::memory_order_acquire);
112
113 // Check if buffer is empty
114 if (current_read == current_write) {
115 return false;
116 }
117
118 // Check if the slot is ready (should always be true if write_index advanced)
119 LogMessage &msg = this->slots_[current_read];
120 if (!msg.ready.load(std::memory_order_acquire)) {
121 return false;
122 }
123
124 message = &msg;
125 text_length = msg.text_length;
126 return true;
127}
128
130 size_t current_read = this->read_index_.load(std::memory_order_relaxed);
131
132 // Clear the ready flag
133 this->slots_[current_read].ready.store(false, std::memory_order_release);
134
135 // Advance read index
136 size_t next_read = (current_read + 1) % ESPHOME_TASK_LOG_BUFFER_SIZE;
137 this->read_index_.store(next_read, std::memory_order_release);
138}
139
140} // namespace esphome::logger
141
142#endif // USE_ESPHOME_TASK_LOG_BUFFER
143#endif // USE_HOST
bool borrow_message_main_loop(LogMessage *&message, uint16_t &text_length)
bool send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *thread_name, const char *format, va_list args)
const char * message
Definition component.cpp:35
const char int line
Definition log.h:74
const char * tag
Definition log.h:74
const char int const __FlashStringHelper * format
Definition log.h:74
const char int const __FlashStringHelper va_list args
Definition log.h:74