Another fix for bug #20021: by not returning an error if tcp_output fails in tcp_close, the code in do_close_internal gets simpler (tcp_output is called again later from tcp timers).

This commit is contained in:
goldsimon 2007-07-25 19:24:27 +00:00
parent f9c30017b0
commit f4036e8352
3 changed files with 36 additions and 47 deletions

View File

@ -256,6 +256,11 @@ HISTORY
++ Bug fixes: ++ Bug fixes:
2007-07-25 Simon Goldschmidt
* api_msg.c, tcp.c: Another fix for bug #20021: by not returning an error if
tcp_output fails in tcp_close, the code in do_close_internal gets simpler
(tcp_output is called again later from tcp timers).
2007-07-25 Simon Goldschmidt 2007-07-25 Simon Goldschmidt
* ip_frag.c: Fixed bug #20429: use the new pbuf_copy_partial instead of the old * ip_frag.c: Fixed bug #20429: use the new pbuf_copy_partial instead of the old
copy_from_pbuf, which illegally modified the given pbuf. copy_from_pbuf, which illegally modified the given pbuf.

View File

@ -207,14 +207,17 @@ sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
LWIP_ASSERT("conn != NULL", (conn != NULL)); LWIP_ASSERT("conn != NULL", (conn != NULL));
if (conn->state == NETCONN_WRITE) { if (conn->state == NETCONN_WRITE) {
LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
do_writemore(conn); do_writemore(conn);
} else if (conn->state == NETCONN_CLOSE) { } else if (conn->state == NETCONN_CLOSE) {
do_close_internal(conn); do_close_internal(conn);
} }
if (conn && conn->callback) if (conn && conn->callback) {
if (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT)) {
(*conn->callback)(conn, NETCONN_EVT_SENDPLUS, len); (*conn->callback)(conn, NETCONN_EVT_SENDPLUS, len);
}
}
return ERR_OK; return ERR_OK;
} }
@ -421,62 +424,39 @@ do_newconn(struct api_msg_msg *msg)
static void static void
do_close_internal(struct netconn *conn) do_close_internal(struct netconn *conn)
{ {
err_t err;
LWIP_ASSERT("invalid conn", (conn != NULL)); LWIP_ASSERT("invalid conn", (conn != NULL));
LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP)); LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP));
LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE)); LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL)); LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
/* Set back some callback pointers */
if (conn->pcb.tcp->state == LISTEN) { if (conn->pcb.tcp->state == LISTEN) {
tcp_arg(conn->pcb.tcp, NULL); tcp_arg(conn->pcb.tcp, NULL);
tcp_accept(conn->pcb.tcp, NULL); tcp_accept(conn->pcb.tcp, NULL);
/* for state==LISTEN, tcp_close can't fail */ } else {
tcp_close(conn->pcb.tcp); tcp_recv(conn->pcb.tcp, NULL);
}
/* Try to close the connection */
err = tcp_close(conn->pcb.tcp);
if (err == ERR_OK) {
/* Closing succeeded */
conn->state = NETCONN_NONE; conn->state = NETCONN_NONE;
conn->pcb.tcp = NULL; conn->pcb.tcp = NULL;
conn->err = ERR_OK; conn->err = err;
sys_mbox_post(conn->mbox, NULL); /* Trigger select() in socket layer */
} else { if (conn->callback) {
err_t err; /* this should send something else so the errorfd is set,
enum tcp_state old_state = conn->pcb.tcp->state; not the read and write fd! */
if ((old_state == SYN_RCVD) || (old_state == ESTABLISHED) || (*conn->callback)(conn, NETCONN_EVT_RCVPLUS, 0);
(old_state == CLOSE_WAIT)) { (*conn->callback)(conn, NETCONN_EVT_SENDPLUS, 0);
tcp_arg(conn->pcb.tcp, NULL);
tcp_sent(conn->pcb.tcp, NULL);
tcp_recv(conn->pcb.tcp, NULL);
tcp_poll(conn->pcb.tcp, NULL, 0);
tcp_err(conn->pcb.tcp, NULL);
err = tcp_close(conn->pcb.tcp);
switch (err) {
default:
/* any other error: abort! */
tcp_abort(conn->pcb.tcp);
/* fall through */
case (ERR_OK):
/* ERR_OK: fall through */
case (ERR_BUF):
/* ERR_BUF: tcp_output failed,
will be called again by internal tcp timers */
conn->state = NETCONN_NONE;
conn->pcb.tcp = NULL;
conn->err = err;
/* Trigger select() in socket layer */
if (conn->callback) {
/* this should send something else so the errorfd is set,
not the read and write fd! */
(*conn->callback)(conn, NETCONN_EVT_RCVPLUS, 0);
(*conn->callback)(conn, NETCONN_EVT_SENDPLUS, 0);
}
/* wake up the application task */
sys_mbox_post(conn->mbox, NULL);
break;
case (ERR_MEM):
/* ERR_MEM: error sending FIN?
try again in sent_tcp or poll_tcp */
/* stay in state NETCONN_CLOSE */
break;
}
} }
/* wake up the application task */
sys_mbox_post(conn->mbox, NULL);
} }
/* If closing didn't succeed, we get called again either
from poll_tcp or from sent_tcp */
} }
/** /**

View File

@ -190,7 +190,11 @@ tcp_close(struct tcp_pcb *pcb)
} }
if (pcb != NULL && err == ERR_OK) { if (pcb != NULL && err == ERR_OK) {
err = tcp_output(pcb); /* @todo: to ensure all has been sent when tcp_close returns, we have to
make sure tcp_output doesn't fail.
For now (and as long as we don't need this (no LINGER)), it's enough
to let the TCP timers deal with this. */
tcp_output(pcb);
} }
return err; return err;
} }