ESPHome 2025.6.3
Loading...
Searching...
No Matches
queue.h
Go to the documentation of this file.
1#pragma once
2
3#ifdef USE_ESP32
4
5#include <atomic>
6#include <cstddef>
7
8/*
9 * BLE events come in from a separate Task (thread) in the ESP32 stack. Rather
10 * than using mutex-based locking, this lock-free queue allows the BLE
11 * task to enqueue events without blocking. The main loop() then processes
12 * these events at a safer time.
13 *
14 * This is a Single-Producer Single-Consumer (SPSC) lock-free ring buffer.
15 * The BLE task is the only producer, and the main loop() is the only consumer.
16 */
17
18namespace esphome {
19namespace esp32_ble {
20
21template<class T, uint8_t SIZE> class LockFreeQueue {
22 public:
24
25 bool push(T *element) {
26 if (element == nullptr)
27 return false;
28
29 uint8_t current_tail = tail_.load(std::memory_order_relaxed);
30 uint8_t next_tail = (current_tail + 1) % SIZE;
31
32 if (next_tail == head_.load(std::memory_order_acquire)) {
33 // Buffer full
34 dropped_count_.fetch_add(1, std::memory_order_relaxed);
35 return false;
36 }
37
38 buffer_[current_tail] = element;
39 tail_.store(next_tail, std::memory_order_release);
40 return true;
41 }
42
43 T *pop() {
44 uint8_t current_head = head_.load(std::memory_order_relaxed);
45
46 if (current_head == tail_.load(std::memory_order_acquire)) {
47 return nullptr; // Empty
48 }
49
50 T *element = buffer_[current_head];
51 head_.store((current_head + 1) % SIZE, std::memory_order_release);
52 return element;
53 }
54
55 size_t size() const {
56 uint8_t tail = tail_.load(std::memory_order_acquire);
57 uint8_t head = head_.load(std::memory_order_acquire);
58 return (tail - head + SIZE) % SIZE;
59 }
60
61 uint16_t get_and_reset_dropped_count() { return dropped_count_.exchange(0, std::memory_order_relaxed); }
62
63 void increment_dropped_count() { dropped_count_.fetch_add(1, std::memory_order_relaxed); }
64
65 bool empty() const { return head_.load(std::memory_order_acquire) == tail_.load(std::memory_order_acquire); }
66
67 bool full() const {
68 uint8_t next_tail = (tail_.load(std::memory_order_relaxed) + 1) % SIZE;
69 return next_tail == head_.load(std::memory_order_acquire);
70 }
71
72 protected:
73 T *buffer_[SIZE];
74 // Atomic: written by producer (push/increment), read+reset by consumer (get_and_reset)
75 std::atomic<uint16_t> dropped_count_; // 65535 max - more than enough for drop tracking
76 // Atomic: written by consumer (pop), read by producer (push) to check if full
77 std::atomic<uint8_t> head_;
78 // Atomic: written by producer (push), read by consumer (pop) to check if empty
79 std::atomic<uint8_t> tail_;
80};
81
82} // namespace esp32_ble
83} // namespace esphome
84
85#endif
std::atomic< uint8_t > head_
Definition queue.h:77
std::atomic< uint16_t > dropped_count_
Definition queue.h:75
std::atomic< uint8_t > tail_
Definition queue.h:79
uint16_t get_and_reset_dropped_count()
Definition queue.h:61
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7