mirror of
https://github.com/esphome/esphome.git
synced 2026-02-25 04:45:29 -07:00
Merge branch 'extend_ultra_low_latency_select_libretiny' into integration
This commit is contained in:
@@ -149,9 +149,10 @@ def require_wake_loop_threadsafe() -> None:
|
||||
):
|
||||
CORE.data[KEY_WAKE_LOOP_THREADSAFE_REQUIRED] = True
|
||||
cg.add_define("USE_WAKE_LOOP_THREADSAFE")
|
||||
if not CORE.is_esp32:
|
||||
# Only non-ESP32 platforms need a UDP socket for wake notifications.
|
||||
# ESP32 uses FreeRTOS task notifications instead (no socket needed).
|
||||
if not CORE.is_esp32 and not CORE.is_libretiny:
|
||||
# Only platforms without fast select need a UDP socket for wake
|
||||
# notifications. ESP32 and LibreTiny use FreeRTOS task notifications
|
||||
# instead (no socket needed).
|
||||
consume_sockets(1, "socket.wake_loop_threadsafe", SocketType.UDP)({})
|
||||
|
||||
|
||||
@@ -187,6 +188,10 @@ async def to_code(config):
|
||||
elif impl == IMPLEMENTATION_BSD_SOCKETS:
|
||||
cg.add_define("USE_SOCKET_IMPL_BSD_SOCKETS")
|
||||
cg.add_define("USE_SOCKET_SELECT_SUPPORT")
|
||||
# ESP32 and LibreTiny both have LwIP >= 2.1.3 with lwip_socket_dbg_get_socket()
|
||||
# and FreeRTOS task notifications — enable fast select to bypass lwip_select()
|
||||
if CORE.is_esp32 or CORE.is_libretiny:
|
||||
cg.add_define("USE_LWIP_FAST_SELECT")
|
||||
|
||||
|
||||
def FILTER_SOURCE_FILES() -> list[str]:
|
||||
|
||||
@@ -9,10 +9,17 @@
|
||||
#endif
|
||||
#ifdef USE_ESP32
|
||||
#include <esp_chip_info.h>
|
||||
#endif
|
||||
#ifdef USE_LWIP_FAST_SELECT
|
||||
#include "esphome/core/lwip_fast_select.h"
|
||||
#ifdef USE_ESP32
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#else
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
#endif
|
||||
#endif // USE_LWIP_FAST_SELECT
|
||||
#include "esphome/core/version.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include <algorithm>
|
||||
@@ -164,14 +171,14 @@ void Application::setup() {
|
||||
clear_setup_priority_overrides();
|
||||
#endif
|
||||
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_ESP32)
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_LWIP_FAST_SELECT)
|
||||
// Initialize fast select: saves main loop task handle for xTaskNotifyGive wake.
|
||||
// Always init on ESP32 — the fast path (rcvevent reads + ulTaskNotifyTake) is used
|
||||
// unconditionally when USE_SOCKET_SELECT_SUPPORT is enabled.
|
||||
// The fast path (rcvevent reads + ulTaskNotifyTake) is used unconditionally
|
||||
// when USE_LWIP_FAST_SELECT is enabled (ESP32 and LibreTiny).
|
||||
esphome_lwip_fast_select_init();
|
||||
#endif
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) && !defined(USE_ESP32)
|
||||
// Set up wake socket for waking main loop from tasks (non-ESP32 only)
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) && !defined(USE_LWIP_FAST_SELECT)
|
||||
// Set up wake socket for waking main loop from tasks (platforms without fast select only)
|
||||
this->setup_wake_loop_threadsafe_();
|
||||
#endif
|
||||
|
||||
@@ -602,7 +609,7 @@ bool Application::register_socket_fd(int fd) {
|
||||
#endif
|
||||
|
||||
this->socket_fds_.push_back(fd);
|
||||
#ifdef USE_ESP32
|
||||
#ifdef USE_LWIP_FAST_SELECT
|
||||
// Hook the socket's netconn callback for instant wake on receive events
|
||||
esphome_lwip_hook_socket(fd);
|
||||
#else
|
||||
@@ -631,7 +638,7 @@ void Application::unregister_socket_fd(int fd) {
|
||||
if (i < this->socket_fds_.size() - 1)
|
||||
this->socket_fds_[i] = this->socket_fds_.back();
|
||||
this->socket_fds_.pop_back();
|
||||
#ifndef USE_ESP32
|
||||
#ifndef USE_LWIP_FAST_SELECT
|
||||
this->socket_fds_changed_ = true;
|
||||
// Only recalculate max_fd if we removed the current max
|
||||
if (fd == this->max_fd_) {
|
||||
@@ -650,8 +657,8 @@ void Application::unregister_socket_fd(int fd) {
|
||||
|
||||
void Application::yield_with_select_(uint32_t delay_ms) {
|
||||
// Delay while monitoring sockets. When delay_ms is 0, always yield() to ensure other tasks run.
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_ESP32)
|
||||
// ESP32 fast path: reads rcvevent directly via lwip_socket_dbg_get_socket() (~215 ns per socket).
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_LWIP_FAST_SELECT)
|
||||
// Fast path (ESP32/LibreTiny): reads rcvevent directly via lwip_socket_dbg_get_socket().
|
||||
// Safe because this runs on the main loop which owns socket lifetime (create, read, close).
|
||||
if (delay_ms == 0) [[unlikely]] {
|
||||
yield();
|
||||
@@ -676,9 +683,8 @@ void Application::yield_with_select_(uint32_t delay_ms) {
|
||||
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(delay_ms));
|
||||
|
||||
#elif defined(USE_SOCKET_SELECT_SUPPORT)
|
||||
// Non-ESP32 select() path (LibreTiny bk72xx/rtl87xx, host platform).
|
||||
// ESP32 is excluded by the #if above — both BSD_SOCKETS and LWIP_SOCKETS on ESP32
|
||||
// use LwIP under the hood, so the fast path handles all ESP32 socket implementations.
|
||||
// Fallback select() path (host platform and any future platforms without fast select).
|
||||
// ESP32 and LibreTiny are excluded by the #if above — they use the fast path.
|
||||
if (!this->socket_fds_.empty()) [[likely]] {
|
||||
// Update fd_set if socket list has changed
|
||||
if (this->socket_fds_changed_) [[unlikely]] {
|
||||
@@ -742,12 +748,12 @@ alignas(Application) char app_storage[sizeof(Application)] asm("_ZN7esphome3AppE
|
||||
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#ifdef USE_LWIP_FAST_SELECT
|
||||
void Application::wake_loop_threadsafe() {
|
||||
// Direct FreeRTOS task notification — <1 us, task context only (NOT ISR-safe)
|
||||
esphome_lwip_wake_main_loop();
|
||||
}
|
||||
#else // !USE_ESP32
|
||||
#else // !USE_LWIP_FAST_SELECT
|
||||
|
||||
void Application::setup_wake_loop_threadsafe_() {
|
||||
// Create UDP socket for wake notifications
|
||||
@@ -815,7 +821,7 @@ void Application::wake_loop_threadsafe() {
|
||||
lwip_send(this->wake_socket_fd_, &dummy, 1, 0);
|
||||
}
|
||||
}
|
||||
#endif // USE_ESP32
|
||||
#endif // USE_LWIP_FAST_SELECT
|
||||
|
||||
#endif // defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#endif
|
||||
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
#ifdef USE_ESP32
|
||||
#ifdef USE_LWIP_FAST_SELECT
|
||||
#include "esphome/core/lwip_fast_select.h"
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
@@ -631,7 +631,7 @@ class Application {
|
||||
/// Main loop only — on ESP32, reads rcvevent via lwip_socket_dbg_get_socket()
|
||||
/// which has no refcount; safe only because the main loop owns socket lifetime
|
||||
/// (creates, reads, and closes sockets on the same thread).
|
||||
#ifdef USE_ESP32
|
||||
#ifdef USE_LWIP_FAST_SELECT
|
||||
bool is_socket_ready_(int fd) const { return esphome_lwip_socket_has_data(fd); }
|
||||
#else
|
||||
bool is_socket_ready_(int fd) const { return FD_ISSET(fd, &this->read_fds_); }
|
||||
@@ -663,7 +663,7 @@ class Application {
|
||||
/// Perform a delay while also monitoring socket file descriptors for readiness
|
||||
void yield_with_select_(uint32_t delay_ms);
|
||||
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) && !defined(USE_ESP32)
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) && !defined(USE_LWIP_FAST_SELECT)
|
||||
void setup_wake_loop_threadsafe_(); // Create wake notification socket
|
||||
inline void drain_wake_notifications_(); // Read pending wake notifications in main loop (hot path - inlined)
|
||||
#endif
|
||||
@@ -702,7 +702,7 @@ class Application {
|
||||
FixedVector<Component *> looping_components_{};
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
std::vector<int> socket_fds_; // Vector of all monitored socket file descriptors
|
||||
#if defined(USE_WAKE_LOOP_THREADSAFE) && !defined(USE_ESP32)
|
||||
#if defined(USE_WAKE_LOOP_THREADSAFE) && !defined(USE_LWIP_FAST_SELECT)
|
||||
int wake_socket_fd_{-1}; // Shared wake notification socket for waking main loop from tasks
|
||||
#endif
|
||||
#endif
|
||||
@@ -718,7 +718,7 @@ class Application {
|
||||
uint32_t setup_heap_stats_baseline_{0};
|
||||
#endif
|
||||
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && !defined(USE_ESP32)
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && !defined(USE_LWIP_FAST_SELECT)
|
||||
int max_fd_{-1}; // Highest file descriptor number for select()
|
||||
#endif
|
||||
|
||||
@@ -734,12 +734,12 @@ class Application {
|
||||
bool in_loop_{false};
|
||||
volatile bool has_pending_enable_loop_requests_{false};
|
||||
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && !defined(USE_ESP32)
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && !defined(USE_LWIP_FAST_SELECT)
|
||||
bool socket_fds_changed_{false}; // Flag to rebuild base_read_fds_ when socket_fds_ changes
|
||||
#endif
|
||||
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && !defined(USE_ESP32)
|
||||
// Variable-sized members (not needed on ESP32 — is_socket_ready_ reads rcvevent directly)
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && !defined(USE_LWIP_FAST_SELECT)
|
||||
// Variable-sized members (not needed with fast select — is_socket_ready_ reads rcvevent directly)
|
||||
fd_set read_fds_{}; // Working fd_set: populated by select()
|
||||
fd_set base_read_fds_{}; // Cached fd_set rebuilt only when socket_fds_ changes
|
||||
#endif
|
||||
|
||||
@@ -225,6 +225,7 @@
|
||||
#define USE_SENDSPIN_PORT 8928 // NOLINT
|
||||
#define USE_SOCKET_IMPL_BSD_SOCKETS
|
||||
#define USE_SOCKET_SELECT_SUPPORT
|
||||
#define USE_LWIP_FAST_SELECT
|
||||
#define USE_WAKE_LOOP_THREADSAFE
|
||||
#define USE_SPEAKER
|
||||
#define USE_SPI
|
||||
@@ -333,6 +334,7 @@
|
||||
#define USE_CAPTIVE_PORTAL
|
||||
#define USE_SOCKET_IMPL_LWIP_SOCKETS
|
||||
#define USE_SOCKET_SELECT_SUPPORT
|
||||
#define USE_LWIP_FAST_SELECT
|
||||
#define USE_WEBSERVER
|
||||
#define USE_WEBSERVER_AUTH
|
||||
#define USE_WEBSERVER_PORT 80 // NOLINT
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Fast socket monitoring for ESP32 (ESP-IDF LwIP)
|
||||
// Fast socket monitoring for ESP32 and LibreTiny (LwIP >= 2.1.3)
|
||||
// Replaces lwip_select() with direct rcvevent reads and FreeRTOS task notifications.
|
||||
//
|
||||
// This must be a .c file (not .cpp) because:
|
||||
// 1. lwip/priv/sockets_priv.h conflicts with C++ compilation units that include bootloader headers
|
||||
// 1. lwip/priv/sockets_priv.h conflicts with C++ compilation units
|
||||
// 2. The netconn callback is a C function pointer
|
||||
//
|
||||
// defines.h is force-included by the build system (-include flag), providing USE_ESP32 etc.
|
||||
// defines.h is force-included by the build system (-include flag), providing USE_LWIP_FAST_SELECT etc.
|
||||
//
|
||||
// Thread safety analysis
|
||||
// ======================
|
||||
@@ -81,20 +81,21 @@
|
||||
// Written by main loop in hook_socket(). Never restored — all LwIP sockets share
|
||||
// the same static event_callback (DEFAULT_SOCKET_EVENTCB), so the wrapper stays permanently.
|
||||
// Read by TCP/IP thread when invoking the callback.
|
||||
// Safe: 32-bit aligned pointer writes are atomic on Xtensa and RISC-V (ESP32).
|
||||
// The TCP/IP thread will see either the old or new pointer atomically — never a
|
||||
// torn value. Both the wrapper and original callbacks are valid at all times
|
||||
// (the wrapper itself calls the original), so either value is correct.
|
||||
// Safe: 32-bit aligned pointer writes are atomic on Xtensa, RISC-V (ESP32),
|
||||
// and ARM Cortex-M (LibreTiny). The TCP/IP thread will see either the old or
|
||||
// new pointer atomically — never a torn value. Both the wrapper and original
|
||||
// callbacks are valid at all times (the wrapper itself calls the original),
|
||||
// so either value is correct.
|
||||
//
|
||||
// sock->rcvevent (s16_t, 2 bytes):
|
||||
// Written by TCP/IP thread in event_callback under SYS_ARCH_PROTECT.
|
||||
// Read by main loop in has_data() via volatile cast.
|
||||
// Safe: SYS_ARCH_UNPROTECT releases a FreeRTOS mutex, which internally
|
||||
// uses a critical section with memory barrier (rsync on dual-core Xtensa; on
|
||||
// single-core builds the spinlock is compiled out, but cross-core visibility is
|
||||
// not an issue). The volatile cast prevents the compiler
|
||||
// from caching the read. Aligned 16-bit reads are single-instruction loads on
|
||||
// Xtensa (L16SI) and RISC-V (LH), which cannot produce torn values.
|
||||
// Safe: SYS_ARCH_UNPROTECT releases a FreeRTOS mutex (ESP32) or resumes the
|
||||
// scheduler (LibreTiny), both providing a memory barrier. The volatile cast
|
||||
// prevents the compiler from caching the read. Aligned 16-bit reads are
|
||||
// single-instruction loads on Xtensa (L16SI), RISC-V (LH), and ARM Cortex-M
|
||||
// (LDRH), which cannot produce torn values. On single-core chips (LibreTiny,
|
||||
// ESP32-C3/C6/H2) cross-core visibility is not an issue.
|
||||
//
|
||||
// FreeRTOS task notification value:
|
||||
// Written by TCP/IP thread (xTaskNotifyGive in callback) and background tasks
|
||||
@@ -103,20 +104,30 @@
|
||||
// critical sections). Multiple concurrent xTaskNotifyGive calls are safe —
|
||||
// the notification count simply increments.
|
||||
|
||||
#ifdef USE_ESP32
|
||||
// USE_ESP32 and USE_LIBRETINY are build flags (-D), always available to .c files.
|
||||
// USE_LWIP_FAST_SELECT is in the generated defines.h (force-included for .cpp but
|
||||
// may not reach .c files on all build systems), so we use platform flags here.
|
||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||
|
||||
// LwIP headers must come first — they define netconn_callback, struct lwip_sock, etc.
|
||||
#include <lwip/api.h>
|
||||
#include <lwip/priv/sockets_priv.h>
|
||||
// FreeRTOS include paths differ: ESP-IDF uses freertos/ prefix, LibreTiny does not
|
||||
#ifdef USE_ESP32
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#else
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
#endif
|
||||
|
||||
#include "esphome/core/lwip_fast_select.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
// Compile-time verification of thread safety assumptions.
|
||||
// On ESP32 (Xtensa/RISC-V), naturally-aligned reads/writes up to 32 bits are atomic.
|
||||
// On ESP32 (Xtensa/RISC-V) and LibreTiny (ARM Cortex-M), naturally-aligned
|
||||
// reads/writes up to 32 bits are atomic.
|
||||
// These asserts ensure our cross-thread shared state meets those requirements.
|
||||
|
||||
// Pointer types must fit in a single 32-bit store (atomic write)
|
||||
@@ -126,7 +137,7 @@ _Static_assert(sizeof(netconn_callback) <= 4, "netconn_callback must be <= 4 byt
|
||||
// rcvevent must fit in a single atomic read
|
||||
_Static_assert(sizeof(((struct lwip_sock *) 0)->rcvevent) <= 4, "rcvevent must be <= 4 bytes for atomic access");
|
||||
|
||||
// Struct member alignment — natural alignment guarantees atomicity on Xtensa/RISC-V.
|
||||
// Struct member alignment — natural alignment guarantees atomicity on Xtensa/RISC-V/ARM.
|
||||
// Misaligned access would not be atomic even if the size is <= 4 bytes.
|
||||
_Static_assert(offsetof(struct netconn, callback) % sizeof(netconn_callback) == 0,
|
||||
"netconn.callback must be naturally aligned for atomic access");
|
||||
@@ -149,6 +160,9 @@ static void esphome_socket_event_callback(struct netconn *conn, enum netconn_evt
|
||||
s_original_callback(conn, evt, len);
|
||||
// Wake the main loop task if sleeping in ulTaskNotifyTake().
|
||||
// Only notify on receive events to avoid spurious wakeups from send-ready events.
|
||||
// NETCONN_EVT_ERROR is deliberately omitted: LwIP signals errors via RCVPLUS
|
||||
// (rcvevent++ with a NULL pbuf or error in recvmbox), so error conditions
|
||||
// already wake the main loop through the RCVPLUS path.
|
||||
if (evt == NETCONN_EVT_RCVPLUS) {
|
||||
TaskHandle_t task = s_main_loop_task;
|
||||
if (task != NULL) {
|
||||
@@ -180,9 +194,9 @@ bool esphome_lwip_socket_has_data(int fd) {
|
||||
return false;
|
||||
// volatile prevents the compiler from caching/reordering this cross-thread read.
|
||||
// The write side (TCP/IP thread) commits via SYS_ARCH_UNPROTECT which releases a
|
||||
// FreeRTOS mutex with a memory barrier (rsync on Xtensa), ensuring the value is
|
||||
// visible. Aligned 16-bit reads are single-instruction loads (L16SI/LH) on
|
||||
// Xtensa/RISC-V and cannot produce torn values.
|
||||
// FreeRTOS mutex (ESP32) or resumes the scheduler (LibreTiny), ensuring the value
|
||||
// is visible. Aligned 16-bit reads are single-instruction loads (L16SI/LH/LDRH) on
|
||||
// Xtensa/RISC-V/ARM and cannot produce torn values.
|
||||
return *(volatile s16_t *) &sock->rcvevent > 0;
|
||||
}
|
||||
|
||||
@@ -197,7 +211,7 @@ void esphome_lwip_hook_socket(int fd) {
|
||||
s_original_callback = sock->conn->callback;
|
||||
}
|
||||
|
||||
// Replace with our wrapper. Atomic on ESP32 (32-bit aligned pointer write).
|
||||
// Replace with our wrapper. Atomic on all supported platforms (32-bit aligned pointer write).
|
||||
// TCP/IP thread sees either old or new pointer — both are valid.
|
||||
sock->conn->callback = esphome_socket_event_callback;
|
||||
}
|
||||
@@ -210,4 +224,4 @@ void esphome_lwip_wake_main_loop(void) {
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_ESP32
|
||||
#endif // defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
// Fast socket monitoring for ESP32 (ESP-IDF LwIP)
|
||||
// Fast socket monitoring for ESP32 and LibreTiny (LwIP >= 2.1.3)
|
||||
// Replaces lwip_select() with direct rcvevent reads and FreeRTOS task notifications.
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
1
tests/components/socket/test.bk72xx-ard.yaml
Normal file
1
tests/components/socket/test.bk72xx-ard.yaml
Normal file
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
1
tests/components/socket/test.ln882x-ard.yaml
Normal file
1
tests/components/socket/test.ln882x-ard.yaml
Normal file
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
1
tests/components/socket/test.rtl87xx-ard.yaml
Normal file
1
tests/components/socket/test.rtl87xx-ard.yaml
Normal file
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
@@ -2,8 +2,11 @@ from esphome.components import socket
|
||||
from esphome.const import (
|
||||
KEY_CORE,
|
||||
KEY_TARGET_PLATFORM,
|
||||
PLATFORM_BK72XX,
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_ESP8266,
|
||||
PLATFORM_LN882X,
|
||||
PLATFORM_RTL87XX,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
@@ -114,3 +117,48 @@ def test_require_wake_loop_threadsafe__non_esp32_consumes_udp_socket() -> None:
|
||||
# Verify UDP socket was consumed
|
||||
udp_consumers = CORE.data.get(socket.KEY_SOCKET_CONSUMERS_UDP, {})
|
||||
assert udp_consumers.get("socket.wake_loop_threadsafe") == 1
|
||||
|
||||
|
||||
def test_require_wake_loop_threadsafe__bk72xx_no_udp_socket() -> None:
|
||||
"""Test that BK72xx (LibreTiny) uses task notifications instead of UDP socket."""
|
||||
_setup_platform(PLATFORM_BK72XX)
|
||||
CORE.config = {"wifi": True}
|
||||
socket.require_wake_loop_threadsafe()
|
||||
|
||||
# Verify the define was added
|
||||
assert CORE.data[socket.KEY_WAKE_LOOP_THREADSAFE_REQUIRED] is True
|
||||
assert any(d.name == "USE_WAKE_LOOP_THREADSAFE" for d in CORE.defines)
|
||||
|
||||
# Verify no UDP socket was consumed (LibreTiny uses FreeRTOS task notifications)
|
||||
udp_consumers = CORE.data.get(socket.KEY_SOCKET_CONSUMERS_UDP, {})
|
||||
assert "socket.wake_loop_threadsafe" not in udp_consumers
|
||||
|
||||
|
||||
def test_require_wake_loop_threadsafe__rtl87xx_no_udp_socket() -> None:
|
||||
"""Test that RTL87xx (LibreTiny) uses task notifications instead of UDP socket."""
|
||||
_setup_platform(PLATFORM_RTL87XX)
|
||||
CORE.config = {"wifi": True}
|
||||
socket.require_wake_loop_threadsafe()
|
||||
|
||||
# Verify the define was added
|
||||
assert CORE.data[socket.KEY_WAKE_LOOP_THREADSAFE_REQUIRED] is True
|
||||
assert any(d.name == "USE_WAKE_LOOP_THREADSAFE" for d in CORE.defines)
|
||||
|
||||
# Verify no UDP socket was consumed (LibreTiny uses FreeRTOS task notifications)
|
||||
udp_consumers = CORE.data.get(socket.KEY_SOCKET_CONSUMERS_UDP, {})
|
||||
assert "socket.wake_loop_threadsafe" not in udp_consumers
|
||||
|
||||
|
||||
def test_require_wake_loop_threadsafe__ln882x_no_udp_socket() -> None:
|
||||
"""Test that LN882H (LibreTiny) uses task notifications instead of UDP socket."""
|
||||
_setup_platform(PLATFORM_LN882X)
|
||||
CORE.config = {"wifi": True}
|
||||
socket.require_wake_loop_threadsafe()
|
||||
|
||||
# Verify the define was added
|
||||
assert CORE.data[socket.KEY_WAKE_LOOP_THREADSAFE_REQUIRED] is True
|
||||
assert any(d.name == "USE_WAKE_LOOP_THREADSAFE" for d in CORE.defines)
|
||||
|
||||
# Verify no UDP socket was consumed (LibreTiny uses FreeRTOS task notifications)
|
||||
udp_consumers = CORE.data.get(socket.KEY_SOCKET_CONSUMERS_UDP, {})
|
||||
assert "socket.wake_loop_threadsafe" not in udp_consumers
|
||||
|
||||
Reference in New Issue
Block a user