mirror of
https://github.com/me-no-dev/ESPAsyncWebServer.git
synced 2025-08-06 06:04:38 +02:00
Merge pull request #171 from mathieucarbou/buffcredit
in-flight buffer credits
This commit is contained in:
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -55,7 +55,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Install AsyncTCP (ESP32)
|
- name: Install AsyncTCP (ESP32)
|
||||||
if: ${{ matrix.core == 'esp32:esp32' }}
|
if: ${{ matrix.core == 'esp32:esp32' }}
|
||||||
run: ARDUINO_LIBRARY_ENABLE_UNSAFE_INSTALL=true arduino-cli lib install --git-url https://github.com/mathieucarbou/AsyncTCP#v3.2.15
|
run: ARDUINO_LIBRARY_ENABLE_UNSAFE_INSTALL=true arduino-cli lib install --git-url https://github.com/mathieucarbou/AsyncTCP#v3.3.0
|
||||||
|
|
||||||
- name: Install ESPAsyncTCP (ESP8266)
|
- name: Install ESPAsyncTCP (ESP8266)
|
||||||
if: ${{ matrix.core == 'esp8266:esp8266' }}
|
if: ${{ matrix.core == 'esp8266:esp8266' }}
|
||||||
|
@@ -80,8 +80,8 @@ lib_deps = mathieucarbou/ESPAsyncWebServer @ 3.4.0
|
|||||||
|
|
||||||
**Dependencies:**
|
**Dependencies:**
|
||||||
|
|
||||||
- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.2.15`
|
- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.3.0`
|
||||||
Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.2.15](https://github.com/mathieucarbou/AsyncTCP/releases)
|
Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.3.0](https://github.com/mathieucarbou/AsyncTCP/releases)
|
||||||
|
|
||||||
- **ESP32 with AsyncTCPSock**: `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`
|
- **ESP32 with AsyncTCPSock**: `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ AsyncTCPSock can be used instead of AsyncTCP by excluding AsyncTCP from the libr
|
|||||||
lib_compat_mode = strict
|
lib_compat_mode = strict
|
||||||
lib_ldf_mode = chain
|
lib_ldf_mode = chain
|
||||||
lib_deps =
|
lib_deps =
|
||||||
; mathieucarbou/AsyncTCP @ 3.2.15
|
; mathieucarbou/AsyncTCP @ 3.3.0
|
||||||
https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
|
https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
|
||||||
mathieucarbou/ESPAsyncWebServer @ 3.4.0
|
mathieucarbou/ESPAsyncWebServer @ 3.4.0
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
@@ -116,7 +116,7 @@ Performance of `mathieucarbou/ESPAsyncWebServer @ 3.4.0`:
|
|||||||
> autocannon -c 10 -w 10 -d 20 http://192.168.4.1
|
> autocannon -c 10 -w 10 -d 20 http://192.168.4.1
|
||||||
```
|
```
|
||||||
|
|
||||||
With `mathieucarbou/AsyncTCP @ 3.2.15`
|
With `mathieucarbou/AsyncTCP @ 3.3.0`
|
||||||
|
|
||||||
[](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)
|
[](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)
|
||||||
|
|
||||||
|
@@ -80,8 +80,8 @@ lib_deps = mathieucarbou/ESPAsyncWebServer @ 3.4.0
|
|||||||
|
|
||||||
**Dependencies:**
|
**Dependencies:**
|
||||||
|
|
||||||
- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.2.15`
|
- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.3.0`
|
||||||
Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.2.15](https://github.com/mathieucarbou/AsyncTCP/releases)
|
Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.3.0](https://github.com/mathieucarbou/AsyncTCP/releases)
|
||||||
|
|
||||||
- **ESP32 with AsyncTCPSock**: `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`
|
- **ESP32 with AsyncTCPSock**: `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ AsyncTCPSock can be used instead of AsyncTCP by excluding AsyncTCP from the libr
|
|||||||
lib_compat_mode = strict
|
lib_compat_mode = strict
|
||||||
lib_ldf_mode = chain
|
lib_ldf_mode = chain
|
||||||
lib_deps =
|
lib_deps =
|
||||||
; mathieucarbou/AsyncTCP @ 3.2.15
|
; mathieucarbou/AsyncTCP @ 3.3.0
|
||||||
https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
|
https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
|
||||||
mathieucarbou/ESPAsyncWebServer @ 3.4.0
|
mathieucarbou/ESPAsyncWebServer @ 3.4.0
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
@@ -116,7 +116,7 @@ Performance of `mathieucarbou/ESPAsyncWebServer @ 3.4.0`:
|
|||||||
> autocannon -c 10 -w 10 -d 20 http://192.168.4.1
|
> autocannon -c 10 -w 10 -d 20 http://192.168.4.1
|
||||||
```
|
```
|
||||||
|
|
||||||
With `mathieucarbou/AsyncTCP @ 3.2.15`
|
With `mathieucarbou/AsyncTCP @ 3.3.0`
|
||||||
|
|
||||||
[](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)
|
[](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)
|
||||||
|
|
||||||
|
@@ -28,7 +28,7 @@
|
|||||||
{
|
{
|
||||||
"owner": "mathieucarbou",
|
"owner": "mathieucarbou",
|
||||||
"name": "AsyncTCP",
|
"name": "AsyncTCP",
|
||||||
"version": "^3.2.15",
|
"version": "^3.3.0",
|
||||||
"platforms": "espressif32"
|
"platforms": "espressif32"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -31,7 +31,7 @@ lib_deps =
|
|||||||
; bblanchon/ArduinoJson @ 5.13.4
|
; bblanchon/ArduinoJson @ 5.13.4
|
||||||
; bblanchon/ArduinoJson @ 6.21.5
|
; bblanchon/ArduinoJson @ 6.21.5
|
||||||
bblanchon/ArduinoJson @ 7.2.1
|
bblanchon/ArduinoJson @ 7.2.1
|
||||||
mathieucarbou/AsyncTCP @ 3.2.15
|
mathieucarbou/AsyncTCP @ 3.3.0
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
board_build.partitions = partitions-4MB.csv
|
board_build.partitions = partitions-4MB.csv
|
||||||
board_build.filesystem = littlefs
|
board_build.filesystem = littlefs
|
||||||
@@ -49,7 +49,7 @@ platform = https://github.com/pioarduino/platform-espressif32/releases/download/
|
|||||||
; board = esp32-s3-devkitc-1
|
; board = esp32-s3-devkitc-1
|
||||||
; board = esp32-c6-devkitc-1
|
; board = esp32-c6-devkitc-1
|
||||||
lib_deps =
|
lib_deps =
|
||||||
mathieucarbou/AsyncTCP @ 3.2.15
|
mathieucarbou/AsyncTCP @ 3.3.0
|
||||||
|
|
||||||
[env:arduino-310]
|
[env:arduino-310]
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip
|
||||||
@@ -102,7 +102,7 @@ board = ${sysenv.PIO_BOARD}
|
|||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||||
board = ${sysenv.PIO_BOARD}
|
board = ${sysenv.PIO_BOARD}
|
||||||
lib_deps =
|
lib_deps =
|
||||||
mathieucarbou/AsyncTCP @ 3.2.15
|
mathieucarbou/AsyncTCP @ 3.3.0
|
||||||
|
|
||||||
[env:ci-arduino-310]
|
[env:ci-arduino-310]
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip
|
||||||
|
@@ -47,6 +47,10 @@ class AsyncBasicResponse : public AsyncWebServerResponse {
|
|||||||
|
|
||||||
class AsyncAbstractResponse : public AsyncWebServerResponse {
|
class AsyncAbstractResponse : public AsyncWebServerResponse {
|
||||||
private:
|
private:
|
||||||
|
// amount of responce data in-flight, i.e. sent, but not acked yet
|
||||||
|
size_t _in_flight{0};
|
||||||
|
// in-flight queue credits
|
||||||
|
size_t _in_flight_credit{2};
|
||||||
String _head;
|
String _head;
|
||||||
// Data is inserted into cache at begin().
|
// Data is inserted into cache at begin().
|
||||||
// This is inefficient with vector, but if we use some other container,
|
// This is inefficient with vector, but if we use some other container,
|
||||||
|
@@ -352,7 +352,21 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
|
|||||||
request->client()->close();
|
request->client()->close();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
// return a credit for each chunk of acked data (polls does not give any credits)
|
||||||
|
if (len)
|
||||||
|
++_in_flight_credit;
|
||||||
|
|
||||||
|
// for chunked responses ignore acks if there are no _in_flight_credits left
|
||||||
|
if (_chunked && !_in_flight_credit) {
|
||||||
|
#ifdef ESP32
|
||||||
|
log_d("(chunk) out of in-flight credits");
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
_ackedLength += len;
|
_ackedLength += len;
|
||||||
|
_in_flight -= (_in_flight > len) ? len : _in_flight;
|
||||||
|
// get the size of available sock space
|
||||||
size_t space = request->client()->space();
|
size_t space = request->client()->space();
|
||||||
|
|
||||||
size_t headLen = _head.length();
|
size_t headLen = _head.length();
|
||||||
@@ -364,16 +378,31 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
|
|||||||
String out = _head.substring(0, space);
|
String out = _head.substring(0, space);
|
||||||
_head = _head.substring(space);
|
_head = _head.substring(space);
|
||||||
_writtenLength += request->client()->write(out.c_str(), out.length());
|
_writtenLength += request->client()->write(out.c_str(), out.length());
|
||||||
|
_in_flight += out.length();
|
||||||
|
--_in_flight_credit; // take a credit
|
||||||
return out.length();
|
return out.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_state == RESPONSE_CONTENT) {
|
if (_state == RESPONSE_CONTENT) {
|
||||||
|
// for response data we need to control the queue and in-flight fragmentation. Sending small chunks could give low latency,
|
||||||
|
// but flood asynctcp's queue and fragment socket buffer space for large responses.
|
||||||
|
// Let's ignore polled acks and acks in case when we have more in-flight data then the available socket buff space.
|
||||||
|
// That way we could balance on having half the buffer in-flight while another half is filling up, while minimizing events in asynctcp q
|
||||||
|
if (_in_flight > space) {
|
||||||
|
// log_d("defer user call %u/%u", _in_flight, space);
|
||||||
|
// take the credit back since we are ignoring this ack and rely on other inflight data
|
||||||
|
if (len)
|
||||||
|
--_in_flight_credit;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
size_t outLen;
|
size_t outLen;
|
||||||
if (_chunked) {
|
if (_chunked) {
|
||||||
if (space <= 8) {
|
if (space <= 8) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
outLen = space;
|
outLen = space;
|
||||||
} else if (!_sendContentLength) {
|
} else if (!_sendContentLength) {
|
||||||
outLen = space;
|
outLen = space;
|
||||||
@@ -422,6 +451,8 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
|
|||||||
|
|
||||||
if (outLen) {
|
if (outLen) {
|
||||||
_writtenLength += request->client()->write((const char*)buf, outLen);
|
_writtenLength += request->client()->write((const char*)buf, outLen);
|
||||||
|
_in_flight += outLen;
|
||||||
|
--_in_flight_credit; // take a credit
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_chunked) {
|
if (_chunked) {
|
||||||
|
Reference in New Issue
Block a user