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.