mirror of
https://github.com/micropython/micropython.git
synced 2026-03-10 19:00:30 +01:00
extmod,rp2: Keep LWIP timer running if lwip poll_sockets() is called.
Fixes rp2 issue where socket.getaddrinfo() could block indefinitely if an interface goes down and still has a DNS server configured, as the LWIP timer stops running and can't time out the DNS query. Adds a regression test under multi_wlan that times out on rp2 without this fix. Fixes issue #18797. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
This commit is contained in:
committed by
Damien George
parent
95b3e72799
commit
bfc69dbe83
@@ -95,6 +95,11 @@
|
||||
#define MICROPY_PY_LWIP_EXIT
|
||||
#endif
|
||||
|
||||
#ifndef MICROPY_PY_LWIP_POLL_HOOK
|
||||
// Optional port-level hook called if/when LWIP is being polled
|
||||
#define MICROPY_PY_LWIP_POLL_HOOK
|
||||
#endif
|
||||
|
||||
#ifdef MICROPY_PY_LWIP_SLIP
|
||||
#include "netif/slipif.h"
|
||||
#include "lwip/sio.h"
|
||||
@@ -360,6 +365,7 @@ static inline bool socket_is_timedout(lwip_socket_obj_t *socket, mp_uint_t ticks
|
||||
}
|
||||
|
||||
static inline void poll_sockets(void) {
|
||||
MICROPY_PY_LWIP_POLL_HOOK
|
||||
mp_event_wait_ms(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -294,6 +294,7 @@ typedef intptr_t mp_off_t;
|
||||
#include "pico/rand.h"
|
||||
extern void lwip_lock_acquire(void);
|
||||
extern void lwip_lock_release(void);
|
||||
extern void lwip_poll_hook(void);
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH || MICROPY_PY_BLUETOOTH_CYW43
|
||||
// Bluetooth code only runs in the scheduler, no locking/mutex required.
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#define MICROPY_PY_LWIP_ENTER lwip_lock_acquire();
|
||||
#define MICROPY_PY_LWIP_REENTER lwip_lock_acquire();
|
||||
#define MICROPY_PY_LWIP_EXIT lwip_lock_release();
|
||||
#define MICROPY_PY_LWIP_POLL_HOOK lwip_poll_hook();
|
||||
|
||||
// Port level Wait-for-Event macro
|
||||
//
|
||||
|
||||
@@ -134,6 +134,15 @@ void lwip_lock_release(void) {
|
||||
pendsv_resume();
|
||||
}
|
||||
|
||||
void lwip_poll_hook(void) {
|
||||
// Start the network soft timer if necessary (it may still only run once,
|
||||
// but ensure timeouts will complete inside any loop that's polling lwip)
|
||||
if (mp_network_soft_timer.mode == SOFT_TIMER_MODE_ONE_SHOT) {
|
||||
mp_network_soft_timer.mode = SOFT_TIMER_MODE_PERIODIC;
|
||||
soft_timer_reinsert(&mp_network_soft_timer, LWIP_TICK_RATE_MS);
|
||||
}
|
||||
}
|
||||
|
||||
// This is called by soft_timer and executes at PendSV level.
|
||||
static void mp_network_soft_timer_callback(soft_timer_entry_t *self) {
|
||||
// Run the lwIP internal updates.
|
||||
@@ -179,10 +188,8 @@ void mod_network_lwip_init(void) {
|
||||
static void mp_network_netif_status_cb(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args) {
|
||||
// Start the network soft timer any time an interface comes up, unless
|
||||
// it's already running
|
||||
if (reason == LWIP_NSC_LINK_CHANGED && args->link_changed.state
|
||||
&& mp_network_soft_timer.mode == SOFT_TIMER_MODE_ONE_SHOT) {
|
||||
mp_network_soft_timer.mode = SOFT_TIMER_MODE_PERIODIC;
|
||||
soft_timer_reinsert(&mp_network_soft_timer, LWIP_TICK_RATE_MS);
|
||||
if (reason == LWIP_NSC_LINK_CHANGED && args->link_changed.state) {
|
||||
lwip_poll_hook();
|
||||
}
|
||||
|
||||
if (reason == LWIP_NSC_NETIF_REMOVED) {
|
||||
|
||||
39
tests/multi_wlan/getaddrinfo.py
Normal file
39
tests/multi_wlan/getaddrinfo.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# This is a regression test to ensure getaddrinfo() fails (after a timeout)
|
||||
# when Wi-Fi is disconnected.
|
||||
#
|
||||
# It doesn't require multiple instances, but it does require Wi-Fi to be present
|
||||
# but not active, and for no other network interface to be configured. The only
|
||||
# tests which already meet these conditions is here in multi_wlan tests.
|
||||
try:
|
||||
from network import WLAN
|
||||
except (ImportError, NameError):
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
|
||||
import socket
|
||||
|
||||
|
||||
def instance0():
|
||||
WLAN(WLAN.IF_AP).active(0)
|
||||
wlan = WLAN(WLAN.IF_STA)
|
||||
wlan.active(0)
|
||||
|
||||
multitest.next()
|
||||
|
||||
try:
|
||||
socket.getaddrinfo("micropython.org", 80)
|
||||
except OSError as er:
|
||||
print(
|
||||
"active(0) failed"
|
||||
) # This may fail with code -6 or -2 depending on whether WLAN has been active since reset
|
||||
|
||||
wlan.active(1)
|
||||
|
||||
try:
|
||||
socket.getaddrinfo("micropython.org", 80)
|
||||
except OSError as er:
|
||||
print(
|
||||
"active(1) failed", er.errno in (-2, -202)
|
||||
) # This one should always be -2 or -202 depending on port
|
||||
|
||||
wlan.active(0)
|
||||
3
tests/multi_wlan/getaddrinfo.py.exp
Normal file
3
tests/multi_wlan/getaddrinfo.py.exp
Normal file
@@ -0,0 +1,3 @@
|
||||
--- instance0 ---
|
||||
active(0) failed
|
||||
active(1) failed True
|
||||
Reference in New Issue
Block a user