#ifdef USE_HOST #include "task_log_buffer_host.h" #ifdef USE_ESPHOME_TASK_LOG_BUFFER #include "esphome/core/log.h" #include #include namespace esphome::logger { int TaskLogBuffer::acquire_write_slot_() { // Try to reserve a slot using compare-and-swap size_t current_reserve = this->reserve_index_.load(std::memory_order_relaxed); while (true) { // Calculate next index (with wrap-around) size_t next_reserve = (current_reserve + 1) % ESPHOME_TASK_LOG_BUFFER_SIZE; // Check if buffer would be full // Buffer is full when next write position equals read position size_t current_read = this->read_index_.load(std::memory_order_acquire); if (next_reserve == current_read) { return -1; // Buffer full } // Try to claim this slot if (this->reserve_index_.compare_exchange_weak(current_reserve, next_reserve, std::memory_order_acq_rel, std::memory_order_relaxed)) { return static_cast(current_reserve); } // If CAS failed, current_reserve was updated, retry with new value } } void TaskLogBuffer::commit_write_slot_(int slot_index) { // Mark the slot as ready for reading this->slots_[slot_index].ready.store(true, std::memory_order_release); // Try to advance the write_index if we're the next expected commit // This ensures messages are read in order size_t expected = slot_index; size_t next = (slot_index + 1) % ESPHOME_TASK_LOG_BUFFER_SIZE; // We only advance write_index if this slot is the next one expected // This handles out-of-order commits correctly while (true) { if (!this->write_index_.compare_exchange_weak(expected, next, std::memory_order_release, std::memory_order_relaxed)) { // Someone else advanced it or we're not next in line, that's fine break; } // Successfully advanced, check if next slot is also ready expected = next; next = (next + 1) % ESPHOME_TASK_LOG_BUFFER_SIZE; if (!this->slots_[expected].ready.load(std::memory_order_acquire)) { break; } } } bool TaskLogBuffer::send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *thread_name, const char *format, va_list args) { // Acquire a slot int slot_index = this->acquire_write_slot_(); if (slot_index < 0) { return false; // Buffer full } LogMessage &msg = this->slots_[slot_index]; // Fill in the message header msg.level = level; msg.tag = tag; msg.line = line; // Store the thread name now to avoid crashes if thread exits before processing if (thread_name != nullptr) { strncpy(msg.thread_name, thread_name, sizeof(msg.thread_name) - 1); msg.thread_name[sizeof(msg.thread_name) - 1] = '\0'; } else { msg.thread_name[0] = '\0'; } // Format the message text int ret = vsnprintf(msg.text, sizeof(msg.text), format, args); if (ret < 0) { // Formatting error - still commit the slot but with empty text msg.text[0] = '\0'; msg.text_length = 0; } else { msg.text_length = static_cast(std::min(static_cast(ret), sizeof(msg.text) - 1)); } // Remove trailing newlines while (msg.text_length > 0 && msg.text[msg.text_length - 1] == '\n') { msg.text_length--; } msg.text[msg.text_length] = '\0'; // Commit the slot this->commit_write_slot_(slot_index); return true; } bool TaskLogBuffer::borrow_message_main_loop(LogMessage *&message, uint16_t &text_length) { size_t current_read = this->read_index_.load(std::memory_order_relaxed); size_t current_write = this->write_index_.load(std::memory_order_acquire); // Check if buffer is empty if (current_read == current_write) { return false; } // Check if the slot is ready (should always be true if write_index advanced) LogMessage &msg = this->slots_[current_read]; if (!msg.ready.load(std::memory_order_acquire)) { return false; } message = &msg; text_length = msg.text_length; return true; } void TaskLogBuffer::release_message_main_loop() { size_t current_read = this->read_index_.load(std::memory_order_relaxed); // Clear the ready flag this->slots_[current_read].ready.store(false, std::memory_order_release); // Advance read index size_t next_read = (current_read + 1) % ESPHOME_TASK_LOG_BUFFER_SIZE; this->read_index_.store(next_read, std::memory_order_release); } } // namespace esphome::logger #endif // USE_ESPHOME_TASK_LOG_BUFFER #endif // USE_HOST