From 1d6cb6357a2cde40822712211fb51c4a4bbe41cf Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 3 Jul 2019 15:50:13 +1000 Subject: [PATCH] extmod/modlwip: For TCP send keep trying tcp_write if it returns ERR_MEM If tcp_write returns ERR_MEM then it's not a fatal error but instead means the caller should retry the write later on (and this is what lwIP's netconn API does). This fixes problems where a TCP send would raise OSError(ENOMEM) in situations where the TCP/IP stack is under heavy load. See eg issues #1897 and #1971. --- extmod/modlwip.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 4127d21add..aed69f9200 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -668,7 +668,25 @@ STATIC mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui u16_t write_len = MIN(available, len); - err_t err = tcp_write(socket->pcb.tcp, buf, write_len, TCP_WRITE_FLAG_COPY); + // If tcp_write returns ERR_MEM then there's currently not enough memory to + // queue the write, so wait and keep trying until it succeeds (with 10s limit). + // Note: if the socket is non-blocking then this code will actually block until + // there's enough memory to do the write, but by this stage we have already + // committed to being able to write the data. + err_t err; + for (int i = 0; i < 200; ++i) { + err = tcp_write(socket->pcb.tcp, buf, write_len, TCP_WRITE_FLAG_COPY); + if (err != ERR_MEM) { + break; + } + err = tcp_output(socket->pcb.tcp); + if (err != ERR_OK) { + break; + } + MICROPY_PY_LWIP_EXIT + mp_hal_delay_ms(50); + MICROPY_PY_LWIP_REENTER + } // If the output buffer is getting full then send the data to the lower layers if (err == ERR_OK && tcp_sndbuf(socket->pcb.tcp) < TCP_SND_BUF / 4) {