1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00

ClientContext: don’t read DataSource in TCP callback

Previously, _write_some function would be called each time TCP stack
notifies the application that some data was delivered (via the `sent`
callback). In turn, _write_some would obtain more data to be sent from
the DataSource. In case of a DataSource backed by a Stream, this would
read from a stream. Some libraries (such as SD) may call `yield` and
other blocking operations from Stream read function, which can not be
used in TCP stack callbacks.

This change moves the data sending loop back into the Arduino task, with
a negligible loss of performance. TCP callback now wakes the main task
via `esp_schedule`, which performs stream read and provides more data
to the TCP stack.

Possible future optimization would be to buffer Stream data ahead of
time. That way, buffered data could be sent immediately from the TCP
callback. On the other hand, this optimization would need extra TCP_MSS
of temporary storage, per connection.

Fixes #2399.
This commit is contained in:
Ivan Grokhotkov 2017-05-08 15:27:51 +08:00 committed by Ivan Grokhotkov
parent 03baea27ef
commit 7de81270a3

View File

@ -317,9 +317,7 @@ protected:
void _cancel_write()
{
if (_datasource) {
delete _datasource;
_datasource = nullptr;
if (_send_waiting) {
esp_schedule();
}
}
@ -327,14 +325,22 @@ protected:
size_t _write_from_source(DataSource* ds)
{
assert(_datasource == nullptr);
assert(_send_waiting == 0);
_datasource = ds;
_written = 0;
do {
_write_some();
while (_datasource && !_noblock) {
_send_waiting = true;
esp_yield();
if (!_datasource->available() || _noblock || state() == CLOSED) {
delete _datasource;
_datasource = nullptr;
break;
}
_send_waiting = false;
++_send_waiting;
esp_yield();
} while(true);
_send_waiting = 0;
return _written;
}
@ -350,31 +356,36 @@ protected:
can_send = 0;
}
size_t will_send = (can_send < left) ? can_send : left;
bool did_write = false;
while( will_send ) {
DEBUGV(":wr %d %d %d\r\n", will_send, left, _written);
bool need_output = false;
while( will_send && _datasource) {
size_t next_chunk =
will_send > _write_chunk_size ? _write_chunk_size : will_send;
const uint8_t* buf = _datasource->get_buffer(next_chunk);
if (state() == CLOSED) {
need_output = false;
break;
}
err_t err = tcp_write(_pcb, buf, next_chunk, TCP_WRITE_FLAG_COPY);
DEBUGV(":wrc %d %d %d\r\n", next_chunk, will_send, err);
_datasource->release_buffer(buf, next_chunk);
if (err == ERR_OK) {
_written += next_chunk;
did_write = true;
need_output = true;
} else {
break;
}
will_send -= next_chunk;
}
if( did_write ) tcp_output(_pcb);
if (!_datasource->available() || _noblock) {
delete _datasource;
_datasource = nullptr;
if( need_output ) {
tcp_output(_pcb);
}
}
void _write_some_from_cb()
{
_write_some();
if (!_datasource && _send_waiting) {
if (_send_waiting == 1) {
_send_waiting--;
esp_schedule();
}
}
@ -490,7 +501,7 @@ private:
size_t _written = 0;
size_t _write_chunk_size = 256;
bool _noblock = false;
bool _send_waiting = false;
int _send_waiting = 0;
};
#endif//CLIENTCONTEXT_H