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.
This commit is contained in:
Damien George 2019-07-03 15:50:13 +10:00
parent ef00048fed
commit 1d6cb6357a
1 changed files with 19 additions and 1 deletions

View File

@ -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) {