Tue 27 Jul 2010 10:27:06 AM UTC, original submission:
This assertion failed while I was closing a connection:
if (pcb->snd_queuelen != 0) {
LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||pcb->unsent != NULL);
}
This is what I do:
1. I send the HEADER of a short POST request to a HTTP server.
2. I got TCP_EVENT_SENT for this request.
3. I put the application in Idle state for 5 sec.
4. While the application is idle (these 5 seconds) the server sends back a long answer (the HTTP HEADER with Error 404), which I was constantly refusing just for the test (by returning ERR_MEM in the receive callback).
5. Because the application is simple at this stage, it does not bother to check if the server rejects the POST request. It assumes that it can send the BODY because step 2 was successful.
6. I send the BODY (which is 20 bytes long) and wait for the server's TCP/IP stack to ACK it.
7. I would expect to get this ACK (TCP_EVENT_SENT to fire) in a short time, but I don't. Instead lwip is still calling the recv callback, desperately trying to deliver the HEADER. The application keeps refusing it.
8. After e certain timeout the application gives up waiting for the BODY to be ACKed. (During all this time the input data is refused)
9. Now I clear the flag that forces the recv callback to refuse the data. Then I put the application in idle state for 5 seconds. Now the application "consumes" the HEADER which lwip was trying to deliver so many times.
10. Now the application is idle again for a couple of seconds. Nothing happens. I would expect to get the ACK for the BODY of the request.
11. I try to close the connection (from within the poll handler if it matters) using the following code:
tcp_pcb_purge();
err = tcp_close();
//tcp_close returned ERR_OK
12. Now the application waits for the recv callback to be called with (p==NULL), which would indicate that the peer has closed the connection from its side.
13. At this moment the assertion fired. Later I split it in two ASSERTs to check which condition was not satisfied. It turned out that they both fail. Both unacked and unsent were NULL.
14. Now I finally got the ACK for the BODY I sent at step 6.
15. I keep waiting for the recv callback to fire with p==NULL, but this did not happen. The temporary flag for refusing is already cleared, so I am not skipping this code, as someone may suspect.
16. After a certain timeout I give up waiting for p==NULL and the connection is aborted.
I know that such stupid application must not be released in the final product (and it will not be), but this showed that lwip is not behaving properly:
- At step 7 TCP_EVENT_SENT (probably) should be called. The presence of refused data in the input direction should not keep the application away from the fact that the peer has ACKed the output data.
- Even if the upper statement is not correct, at step 10, when there is no more refused data, TCP_EVENT_SENT should fire. There is something that does not allow lwip to do this. After calling tcp_pcb_purge() and tcp_close() lwip is able to do what it had to do.
- As a consequence (?) of what happened till now, the recv callback with p==NULL is not called.
I will be able to investigate this further after a couple of days. I really hope that the fault is mine. Otherwise this problem will delay a bit the release of lwip 1.4.
If the cost for fixing this (as code size) is too high, I'm OK to live with such limitation, as long as we are aware of it.
|