We are using LWIP 1.3.2(as ported by TexasInstruments), IAR compiler targeting a stellaris MCU. The TI implementation uses a timer tick interrupt 100 times/sec to service the LWIP stack. No RTOS involved. Our application code also runs on this same timer tick interrupt so no concurrency issues in play.
We implemented a webservice http client over tcp. Periodically(about 1-2 times per day), we miss ACKs in the application layer. Plain and simple, our tcp_sent callback does not get called. We have timers that expire if the ack is not seen. I have captured the WireShark trace and the ack is being transmitted. For some reason the lwip stack does not inform the application layer. I don’t know if the LWIP stack sees the ack or not.
The odd thing is that under normal load conditions the code works and transmits 2-3 large HTTP posts/second. This means that within a 12 hour period it works 2*60*60 *12= 86,400 times without error.
I have adjusted the lwipopts.h settings over and over and nothing seems to affect the problem.
I have scanned the 1.4.1 changelog and do not see any defects that specifically address this problem, Further complicating the issue is that TI has not ported 1.4.1. If they had I would have tried it to see if it solves the problem. I am not sure how much effort it will be to do the port myself. I am willing to do it if there is a specific defect that address my issue.
Here is how the callbacks are being set up:
if(s_Pcb == NULL) { s_Pcb = tcp_new(); err_t bindResult = tcp_bind(s_Pcb, IP_ADDR_ANY, PortNumber);
UpdateCounters(&g_Diagnostics.BindCounters, bindResult);
if(bindResult != ERR_OK) { ClosePcb(); // try again next time return; }
// Setup the TCP callback functions tcp_err(s_Pcb, OnError); tcp_sent(s_Pcb, OnSent); tcp_recv(s_Pcb, OnReceive);
return; }
How we send a chunk of the overall HTTP message:
//***************************************************************************** // Send next chunk of the message. //***************************************************************************** void ContinueTransmittingHttpPostMessage() {
err_t result;
g_BytesAvailable = tcp_sndbuf(s_Pcb); g_BytesToSend = Message.StopIndex - Message.TransmitIndex; if(g_BytesToSend > 0) { if(g_BytesToSend > TCP_WND) { g_BytesToSend = TCP_WND; } if(g_BytesAvailable >= g_BytesToSend) { result = tcp_write(s_Pcb, &Message.Body[Message.TransmitIndex], g_BytesToSend, 0); UpdateCounters(&g_Diagnostics.WriteCounters, result); if(result == ERR_OK) { g_Diagnostics.ulTotalBytesWritten += g_BytesToSend; Message.TransmitIndex += g_BytesToSend; Message.State = Sending; SetTimeout(&Message.StateTimer, 2 Sec); } else { StopTransmittingHttpPostMessage(); } } else { g_Diagnostics.ulSendBufferFullErrors++; } }
}
The tcp_sent callback that doesn’t get call (sometimes).
//***************************************************************************** // Our TCP sent callback. // Look at tcp_sent for for information. //***************************************************************************** static err_t OnSent(void *arg, struct tcp_pcb *pcb, u16_t numBytesAcknowledged) {
// Service the watchdog timer ServiceWatchdogTimer();
// We only get called if the web service received > 0 bytes g_Diagnostics.ulOnSentCallbacks++; g_Diagnostics.ulTotalBytesAcknowledged +=numBytesAcknowledged;
if(Message.State == Sending) { Message.BytesAcked += numBytesAcknowledged; if(Message.TransmitIndex >= Message.StopIndex) { if(Message.BytesAcked >= Message.StopIndex) { Message.State = WaitingForOk; SetTimeout(&Message.StateTimer, 2 Sec); } } else { ContinueTransmittingHttpPostMessage(); } } return ERR_OK;
}