We can easily (although annoyingly) do IP fragmentation with
the assembler now, so I don't see *any* features that are apriori
impossible to implement anymore.
The previous implementation made no sense. It is obvious that
poll_at() should use the same mechanisms to decide whether dispatch()
should be called as dispatch() itself uses to decide whether to send
anything.
This fixes numerous busy looping issues that arise if the return
value of poll() is used for waiting.
1. Apart from non-empty transmit buffer, a state which transmits
a FIN flag should also be considerd. Otherwise, closing a socket
with an empty transmit buffer may retransmit the FIN flag forever.
2. Timeout poll requests should only be overridden by timer poll
requests when the latter is earlier.
Mention SACKs, don't spend many words on junk no one wants,
reword everything, and reorder the features so that the ones
with a higher cost/benefit ratio are higher up.
This reverts commit 51b2f18d1165bf7257de8894df101299cc93b094.
There's no throughput difference so far as I could measure, but
this greatly increases worst-case latency. At some later point
we could perhaps pass a deadline to the poll function, but for now
reverting this is simple enough.
Typically, the poll function is used as a part of a larger RTOS.
If we only dispatch one packet per socket per poll() call,
then we have to wait for a complete scheduler roundtrip,
which is potentially a lot of time, and as a result we are
not filling the peer's window efficiently.
Some of them already use Result, but the ones that can return
an empty slice (or a slice shorter than requested) also must have
their return value (at least) checked.
After this change, if an 1s timeout is set on a socket that received
no packet for 2s, it will be instantly aborted, which seems
more reasonable. Also, this makes the `self.remote_last_ts.is_none()`
branch in TcpSocket::dispatch() actually behave as described.