Otherwise a buffer queued past _ack that has been fully written yet not
removed from the queue risks triggering an incorrect timeout event. This
has caused incomplete file downloads with ESPAsyncWebServer.
This is a bare-bones program that opens a basic TCP/IP socket to a
particular non-encrypted web server, on port 80, sends a hardcoded
HTTP/1.1 request, dumps the response, and should disconnect when the
remote side closes the connection. All necessary callbacks for this are
installed. The main loop() starts a connection attempt every 20 seconds,
and otherwise just outputs stars to show it is still running while the
AsyncClient does its thing.
This commit implements a policy of writing as many queued buffers as the
writable socket allows, until non-writable, or the write queue is empty.
This has two objectives: 1) any allocated buffers for data copies will
be freed sooner, 2) the write output will be more responsive, fixing at
least one known delay in WiFi scanlist output in YUBOX Now.
Otherwise the socket poll callback races with the RX timeout (if
nonzero) and will immediately disconnect the socket without waiting for
it to actually finish connecting.
The general rule is that at no time must callbacks be removed from the
AsyncTCP object while the object is still live. Clients expect this from
the AsyncTCP library.
Users of AsyncTCP expect callbacks installed on the object to remain
active on close event and do not reinstall them if the just-closed
object is reopened. Fixes failure to react to events after reopening.
Prior to this commit, the mutex that protected the global client socket
list was non-recursive. This forced the release of the mutex before
iterating through sockets on which to call callbacks. However, releasing
the mutex allowed destructors to proceed, therefore possibly
invalidating the object pointers copied to the local list. This in turn
risked calling callbacks on already destroyed objects. Fixed by the use
of a recursive mutex that allows holding the mutex during the callback
invocation.
The discard callback (installed via onDisconnect()) can legitimately
decide to destroy the AsyncTCP client, calling the destructors which in
turn invalidate the write mutex object. Prior to this commit, calling
_clearWriteQueue() (which takes said write mutex) risked accessing
uninitialized memory and causing memory corruption. Fixed.
This prevents a race condicion of the ::add() method modifying the queue
and being preempted by the high-priority asyncTcpSock task that in turn
flushes the same queue to the socket, therefore modifying the queue.