Implemented timeout on send (TCP only, bug #33820)

This commit is contained in:
Simon Goldschmidt 2011-09-21 19:36:09 +02:00
parent 8d5514603e
commit a2aa43a426
7 changed files with 135 additions and 62 deletions

View File

@ -6,6 +6,10 @@ HISTORY
++ New features: ++ New features:
2011-09-21: Simon Goldschmidt
* opt.h, api.h, api_lib.c, api_msg.h/.c, sockets.c: Implemented timeout on
send (TCP only, bug #33820)
2011-09-21: Simon Goldschmidt 2011-09-21: Simon Goldschmidt
* init.c: Converted runtime-sanity-checks into compile-time checks that can * init.c: Converted runtime-sanity-checks into compile-time checks that can
be disabled (since runtime checks can often not be seen on embedded targets) be disabled (since runtime checks can often not be seen on embedded targets)

View File

@ -611,12 +611,26 @@ netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
msg.msg.msg.w.dataptr = dataptr; msg.msg.msg.w.dataptr = dataptr;
msg.msg.msg.w.apiflags = apiflags; msg.msg.msg.w.apiflags = apiflags;
msg.msg.msg.w.len = size; msg.msg.msg.w.len = size;
#if LWIP_SO_SNDTIMEO
if (conn->send_timeout != 0) {
/* get the time we started, which is later compared to
sys_now() + conn->send_timeout */
msg.msg.msg.w.time_started = sys_now();
} else {
msg.msg.msg.w.time_started = 0;
}
#endif /* LWIP_SO_SNDTIMEO */
/* For locking the core: this _can_ be delayed on low memory/low send buffer, /* For locking the core: this _can_ be delayed on low memory/low send buffer,
but if it is, this is done inside api_msg.c:do_write(), so we can use the but if it is, this is done inside api_msg.c:do_write(), so we can use the
non-blocking version here. */ non-blocking version here. */
err = TCPIP_APIMSG(&msg); err = TCPIP_APIMSG(&msg);
if ((err == ERR_OK) && (bytes_written != NULL)) { if ((err == ERR_OK) && (bytes_written != NULL)) {
if (dontblock) { if (dontblock
#if LWIP_SO_SNDTIMEO
|| (conn->send_timeout != 0)
#endif /* LWIP_SO_SNDTIMEO */
) {
/* nonblocking write: maybe the data has been sent partly */ /* nonblocking write: maybe the data has been sent partly */
*bytes_written = msg.msg.msg.w.len; *bytes_written = msg.msg.msg.w.len;
} else { } else {

View File

@ -626,6 +626,9 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
conn->current_msg = NULL; conn->current_msg = NULL;
conn->write_offset = 0; conn->write_offset = 0;
#endif /* LWIP_TCP */ #endif /* LWIP_TCP */
#if LWIP_SO_SNDTIMEO
conn->send_timeout = 0;
#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO #if LWIP_SO_RCVTIMEO
conn->recv_timeout = 0; conn->recv_timeout = 0;
#endif /* LWIP_SO_RCVTIMEO */ #endif /* LWIP_SO_RCVTIMEO */
@ -1217,79 +1220,95 @@ do_writemore(struct netconn *conn)
LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len", LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len",
conn->write_offset < conn->current_msg->msg.w.len); conn->write_offset < conn->current_msg->msg.w.len);
dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset; #if LWIP_SO_SNDTIMEO
diff = conn->current_msg->msg.w.len - conn->write_offset; if ((conn->send_timeout != 0) &&
if (diff > 0xffffUL) { /* max_u16_t */ ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
len = 0xffff; write_finished = 1;
#if LWIP_TCPIP_CORE_LOCKING if (conn->write_offset == 0) {
conn->flags |= NETCONN_FLAG_WRITE_DELAYED; /* nothing has been written */
#endif err = ERR_WOULDBLOCK;
apiflags |= TCP_WRITE_FLAG_MORE; conn->current_msg->msg.w.len = 0;
} else {
len = (u16_t)diff;
}
available = tcp_sndbuf(conn->pcb.tcp);
if (available < len) {
/* don't try to write more than sendbuf */
len = available;
if (dontblock){
if (!len) {
err = ERR_WOULDBLOCK;
goto err_mem;
}
} else { } else {
/* partial write */
err = ERR_OK;
conn->current_msg->msg.w.len = conn->write_offset;
}
} else
#endif /* LWIP_SO_SNDTIMEO */
{
dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset;
diff = conn->current_msg->msg.w.len - conn->write_offset;
if (diff > 0xffffUL) { /* max_u16_t */
len = 0xffff;
#if LWIP_TCPIP_CORE_LOCKING #if LWIP_TCPIP_CORE_LOCKING
conn->flags |= NETCONN_FLAG_WRITE_DELAYED; conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
#endif #endif
apiflags |= TCP_WRITE_FLAG_MORE; apiflags |= TCP_WRITE_FLAG_MORE;
} else {
len = (u16_t)diff;
} }
} available = tcp_sndbuf(conn->pcb.tcp);
LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len)); if (available < len) {
err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags); /* don't try to write more than sendbuf */
/* if OK or memory error, check available space */ len = available;
if ((err == ERR_OK) || (err == ERR_MEM)) { if (dontblock){
if (!len) {
err = ERR_WOULDBLOCK;
goto err_mem;
}
} else {
#if LWIP_TCPIP_CORE_LOCKING
conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
#endif
apiflags |= TCP_WRITE_FLAG_MORE;
}
}
LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len));
err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
/* if OK or memory error, check available space */
if ((err == ERR_OK) || (err == ERR_MEM)) {
err_mem: err_mem:
if (dontblock && (len < conn->current_msg->msg.w.len)) { if (dontblock && (len < conn->current_msg->msg.w.len)) {
/* non-blocking write did not write everything: mark the pcb non-writable /* non-blocking write did not write everything: mark the pcb non-writable
and let poll_tcp check writable space to mark the pcb writable again */ and let poll_tcp check writable space to mark the pcb writable again */
API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE; conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
} else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) || } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
(tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) { (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
/* The queued byte- or pbuf-count exceeds the configured low-water limit, /* The queued byte- or pbuf-count exceeds the configured low-water limit,
let select mark this pcb as non-writable. */ let select mark this pcb as non-writable. */
API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
}
} }
}
if (err == ERR_OK) { if (err == ERR_OK) {
conn->write_offset += len; conn->write_offset += len;
if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) { if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) {
/* return sent length */ /* return sent length */
conn->current_msg->msg.w.len = conn->write_offset; conn->current_msg->msg.w.len = conn->write_offset;
/* everything was written */ /* everything was written */
write_finished = 1; write_finished = 1;
conn->write_offset = 0; conn->write_offset = 0;
} }
tcp_output(conn->pcb.tcp); tcp_output(conn->pcb.tcp);
} else if ((err == ERR_MEM) && !dontblock) { } else if ((err == ERR_MEM) && !dontblock) {
/* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
we do NOT return to the application thread, since ERR_MEM is we do NOT return to the application thread, since ERR_MEM is
only a temporary error! */ only a temporary error! */
/* tcp_write returned ERR_MEM, try tcp_output anyway */ /* tcp_write returned ERR_MEM, try tcp_output anyway */
tcp_output(conn->pcb.tcp); tcp_output(conn->pcb.tcp);
#if LWIP_TCPIP_CORE_LOCKING #if LWIP_TCPIP_CORE_LOCKING
conn->flags |= NETCONN_FLAG_WRITE_DELAYED; conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
#endif #endif
} else { } else {
/* On errors != ERR_MEM, we don't try writing any more but return /* On errors != ERR_MEM, we don't try writing any more but return
the error to the application thread. */ the error to the application thread. */
write_finished = 1; write_finished = 1;
conn->current_msg->msg.w.len = 0; conn->current_msg->msg.w.len = 0;
}
} }
if (write_finished) { if (write_finished) {
/* everything was written: set back connection state /* everything was written: set back connection state
and back to application task */ and back to application task */

View File

@ -1535,7 +1535,9 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
case SO_ERROR: case SO_ERROR:
case SO_KEEPALIVE: case SO_KEEPALIVE:
/* UNIMPL case SO_CONTIMEO: */ /* UNIMPL case SO_CONTIMEO: */
/* UNIMPL case SO_SNDTIMEO: */ #if LWIP_SO_SNDTIMEO
case SO_SNDTIMEO:
#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO #if LWIP_SO_RCVTIMEO
case SO_RCVTIMEO: case SO_RCVTIMEO:
#endif /* LWIP_SO_RCVTIMEO */ #endif /* LWIP_SO_RCVTIMEO */
@ -1780,6 +1782,11 @@ lwip_getsockopt_internal(void *arg)
s, *(int *)optval)); s, *(int *)optval));
break; break;
#if LWIP_SO_SNDTIMEO
case SO_SNDTIMEO:
*(int *)optval = netconn_get_sendtimeout(sock->conn);
break;
#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO #if LWIP_SO_RCVTIMEO
case SO_RCVTIMEO: case SO_RCVTIMEO:
*(int *)optval = netconn_get_recvtimeout(sock->conn); *(int *)optval = netconn_get_recvtimeout(sock->conn);
@ -1934,7 +1941,9 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt
/* UNIMPL case SO_DONTROUTE: */ /* UNIMPL case SO_DONTROUTE: */
case SO_KEEPALIVE: case SO_KEEPALIVE:
/* UNIMPL case case SO_CONTIMEO: */ /* UNIMPL case case SO_CONTIMEO: */
/* UNIMPL case case SO_SNDTIMEO: */ #if LWIP_SO_SNDTIMEO
case SO_SNDTIMEO:
#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO #if LWIP_SO_RCVTIMEO
case SO_RCVTIMEO: case SO_RCVTIMEO:
#endif /* LWIP_SO_RCVTIMEO */ #endif /* LWIP_SO_RCVTIMEO */
@ -2160,6 +2169,11 @@ lwip_setsockopt_internal(void *arg)
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
s, optname, (*(int*)optval?"on":"off"))); s, optname, (*(int*)optval?"on":"off")));
break; break;
#if LWIP_SO_SNDTIMEO
case SO_SNDTIMEO:
netconn_set_sendtimeout(sock->conn, (s32_t)*(int*)optval);
break;
#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO #if LWIP_SO_RCVTIMEO
case SO_RCVTIMEO: case SO_RCVTIMEO:
netconn_set_recvtimeout(sock->conn, *(int*)optval); netconn_set_recvtimeout(sock->conn, *(int*)optval);

View File

@ -180,6 +180,11 @@ struct netconn {
#if LWIP_SOCKET #if LWIP_SOCKET
int socket; int socket;
#endif /* LWIP_SOCKET */ #endif /* LWIP_SOCKET */
#if LWIP_SO_SNDTIMEO
/** timeout to wait for sending data (which means enqueueing data for sending
in internal buffers) */
s32_t send_timeout;
#endif /* LWIP_SO_RCVTIMEO */
#if LWIP_SO_RCVTIMEO #if LWIP_SO_RCVTIMEO
/** timeout to wait for new data to be received /** timeout to wait for new data to be received
(or connections to arrive for listening netconns) */ (or connections to arrive for listening netconns) */
@ -299,6 +304,12 @@ err_t netconn_gethostbyname(const char *name, ip_addr_t *addr);
/** TCP: Get the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */ /** TCP: Get the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */
#define netconn_get_noautorecved(conn) (((conn)->flags & NETCONN_FLAG_NO_AUTO_RECVED) != 0) #define netconn_get_noautorecved(conn) (((conn)->flags & NETCONN_FLAG_NO_AUTO_RECVED) != 0)
#if LWIP_SO_SNDTIMEO
/** Set the send timeout in milliseconds */
#define netconn_set_sendtimeout(conn, timeout) ((conn)->send_timeout = (timeout))
/** Get the send timeout in milliseconds */
#define netconn_get_sendtimeout(conn) ((conn)->send_timeout)
#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO #if LWIP_SO_RCVTIMEO
/** Set the receive timeout in milliseconds */ /** Set the receive timeout in milliseconds */
#define netconn_set_recvtimeout(conn, timeout) ((conn)->recv_timeout = (timeout)) #define netconn_set_recvtimeout(conn, timeout) ((conn)->recv_timeout = (timeout))

View File

@ -89,6 +89,9 @@ struct api_msg_msg {
const void *dataptr; const void *dataptr;
size_t len; size_t len;
u8_t apiflags; u8_t apiflags;
#if LWIP_SO_SNDTIMEO
u32_t time_started;
#endif /* LWIP_SO_SNDTIMEO */
} w; } w;
/** used for do_recv */ /** used for do_recv */
struct { struct {

View File

@ -1434,6 +1434,14 @@
#define LWIP_TCP_KEEPALIVE 0 #define LWIP_TCP_KEEPALIVE 0
#endif #endif
/**
* LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and
* SO_SNDTIMEO processing.
*/
#ifndef LWIP_SO_SNDTIMEO
#define LWIP_SO_SNDTIMEO 0
#endif
/** /**
* LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and
* SO_RCVTIMEO processing. * SO_RCVTIMEO processing.