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

This commit is contained in:
Simon Goldschmidt 2011-09-21 19:36:09 +02:00 committed by goldsimon
parent 223307fa38
commit 3d48abb98d
7 changed files with 135 additions and 62 deletions

View File

@ -6,6 +6,10 @@ HISTORY
++ 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
* 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)

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.apiflags = apiflags;
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,
but if it is, this is done inside api_msg.c:do_write(), so we can use the
non-blocking version here. */
err = TCPIP_APIMSG(&msg);
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 */
*bytes_written = msg.msg.msg.w.len;
} else {

View File

@ -622,6 +622,9 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
conn->current_msg = NULL;
conn->write_offset = 0;
#endif /* LWIP_TCP */
#if LWIP_SO_SNDTIMEO
conn->send_timeout = 0;
#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO
conn->recv_timeout = 0;
#endif /* LWIP_SO_RCVTIMEO */
@ -1212,79 +1215,95 @@ do_writemore(struct netconn *conn)
LWIP_ASSERT("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;
diff = conn->current_msg->msg.w.len - conn->write_offset;
if (diff > 0xffffUL) { /* max_u16_t */
len = 0xffff;
#if LWIP_TCPIP_CORE_LOCKING
conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
#endif
apiflags |= TCP_WRITE_FLAG_MORE;
} 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;
}
#if LWIP_SO_SNDTIMEO
if ((conn->send_timeout != 0) &&
((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
write_finished = 1;
if (conn->write_offset == 0) {
/* nothing has been written */
err = ERR_WOULDBLOCK;
conn->current_msg->msg.w.len = 0;
} 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
conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
#endif
apiflags |= TCP_WRITE_FLAG_MORE;
} else {
len = (u16_t)diff;
}
}
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)) {
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 {
#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:
if (dontblock && (len < conn->current_msg->msg.w.len)) {
/* 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 */
API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
} else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
(tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
/* The queued byte- or pbuf-count exceeds the configured low-water limit,
let select mark this pcb as non-writable. */
API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
if (dontblock && (len < conn->current_msg->msg.w.len)) {
/* 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 */
API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
} else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
(tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
/* The queued byte- or pbuf-count exceeds the configured low-water limit,
let select mark this pcb as non-writable. */
API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
}
}
}
if (err == ERR_OK) {
conn->write_offset += len;
if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) {
/* return sent length */
conn->current_msg->msg.w.len = conn->write_offset;
/* everything was written */
write_finished = 1;
conn->write_offset = 0;
}
tcp_output(conn->pcb.tcp);
} else if ((err == ERR_MEM) && !dontblock) {
/* 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
only a temporary error! */
if (err == ERR_OK) {
conn->write_offset += len;
if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) {
/* return sent length */
conn->current_msg->msg.w.len = conn->write_offset;
/* everything was written */
write_finished = 1;
conn->write_offset = 0;
}
tcp_output(conn->pcb.tcp);
} else if ((err == ERR_MEM) && !dontblock) {
/* 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
only a temporary error! */
/* tcp_write returned ERR_MEM, try tcp_output anyway */
tcp_output(conn->pcb.tcp);
/* tcp_write returned ERR_MEM, try tcp_output anyway */
tcp_output(conn->pcb.tcp);
#if LWIP_TCPIP_CORE_LOCKING
conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
#endif
} else {
/* On errors != ERR_MEM, we don't try writing any more but return
the error to the application thread. */
write_finished = 1;
conn->current_msg->msg.w.len = 0;
} else {
/* On errors != ERR_MEM, we don't try writing any more but return
the error to the application thread. */
write_finished = 1;
conn->current_msg->msg.w.len = 0;
}
}
if (write_finished) {
/* everything was written: set back connection state
and back to application task */

View File

@ -1470,7 +1470,9 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
case SO_ERROR:
case SO_KEEPALIVE:
/* UNIMPL case SO_CONTIMEO: */
/* UNIMPL case SO_SNDTIMEO: */
#if LWIP_SO_SNDTIMEO
case SO_SNDTIMEO:
#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO
case SO_RCVTIMEO:
#endif /* LWIP_SO_RCVTIMEO */
@ -1715,6 +1717,11 @@ lwip_getsockopt_internal(void *arg)
s, *(int *)optval));
break;
#if LWIP_SO_SNDTIMEO
case SO_SNDTIMEO:
*(int *)optval = netconn_get_sendtimeout(sock->conn);
break;
#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO
case SO_RCVTIMEO:
*(int *)optval = netconn_get_recvtimeout(sock->conn);
@ -1869,7 +1876,9 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt
/* UNIMPL case SO_DONTROUTE: */
case SO_KEEPALIVE:
/* 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
case SO_RCVTIMEO:
#endif /* LWIP_SO_RCVTIMEO */
@ -2095,6 +2104,11 @@ lwip_setsockopt_internal(void *arg)
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
s, optname, (*(int*)optval?"on":"off")));
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
case SO_RCVTIMEO:
netconn_set_recvtimeout(sock->conn, *(int*)optval);

View File

@ -159,6 +159,11 @@ struct netconn {
#if LWIP_SOCKET
int 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
/** timeout to wait for new data to be received
(or connections to arrive for listening netconns) */
@ -264,6 +269,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) */
#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
/** Set the receive timeout in milliseconds */
#define netconn_set_recvtimeout(conn, timeout) ((conn)->recv_timeout = (timeout))

View File

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

View File

@ -1423,6 +1423,14 @@
#define LWIP_TCP_KEEPALIVE 0
#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
* SO_RCVTIMEO processing.