Queue up to eight directives for loop(); drop the repeat of a directive while it arrives
8ea7778kept one directive for loop() and recognised the repeat that the broker mirror delivers in two ways: bytes compared with the directive that still waited, and the messageId once loop() had parsed it. A repeat that arrived after its directive had been read therefore took the one place until the next loop(), and a different directive behind it was dropped. Alexa sends a group command as one directive per endpoint, so a board with several endpoints lost directives whenever they arrived faster than loop() ran; 1.1.0 queued five tokens. AlexaDirectiveBuffer is now a queue. The arriving message is collected in a heap block of its own and hashed (FNV-1a, 64 bit) as its fragments come in. At the last fragment it is one of three things: a repeat, when the hash is among the last 16 that were queued, and then it is freed and never takes a place; a directive, which is queued; or lost, when eight directives (ALEX2ESP_MAX_QUEUED_DIRECTIVES) or 8188 bytes (ALEX2ESP_MAX_QUEUED_BYTES, four times the largest directive) already wait. Whether there is a place is decided at the last fragment, because loop() may have read a directive by then. A message for which there is no memory is still hashed, so its loss is reported only when it is not a repeat. loop() parses one directive per call, in the order of arrival, and frees its block before the handler runs. The messageId check stays for a directive that comes again with other bytes; it remembers 16 ids instead of 4 and shares the ring with the buffer (AlexaRecentHashes). The two limits are in src/AlexaLimits.h, with #error for values that cannot work. The constructor of the buffer takes the allocator, malloc by default, so the tests can let it fail. For a sketch: nothing to change. The error line of a directive that finds no place reads "directive of N bytes on <topic> dropped: 8 directives, M bytes, already wait for loop()". The heap holds up to 8188 bytes of waiting directives and one arriving directive of up to 2048, where it held one directive. Measured with the bridge built for the host against a fake MQTT client (not in the repository), directives of 793 bytes, every message delivered twice,8ea7778-> this commit: repeat of D1 and a new D2 after D1 was handled D2 dropped -> D2 handled group of 5 in one burst 1 of 5 handled, 8 error lines -> 5 of 5, none group of 8 in one burst 8 of 8 handled, no error line group of 10 in one burst, not mirrored 8 of 10 handled, 2 error lines group of 10, a loop() after every fourth message 10 of 10 handled, no error line Tests: 41 host tests (33 before). New: the order of the queue and the reuse of its places, the ninth directive, a place that becomes free while a directive arrives, the limit in bytes, the largest directive in an empty queue, the repeat of a waiting directive and of one that was read, a group of five with repeats, a repeat when no place is free, a directive that differs in one byte, how long a repeat is remembered, a directive and a repeat without memory, the FNV-1a test vectors. They also pass under -fsanitize=address,undefined. Seven faults planted in a copy of AlexaBridgeLogic.cpp (no repeat check, no limit in bytes, no limit in places, no bounds check, a lost directive remembered, release() that keeps the bytes, last in first out) were each noticed: six by failing tests, the missing bounds check by AddressSanitizer as a heap-buffer-overflow. Built for d1_mini with empty credentials (PlatformIO 6.2.0, espressif8266 4.2.1), static RAM / flash in bytes,e48f858-> this commit, no warnings: basicLight 34,116 / 336,757 -> 34,444 / 337,145 lightWithBrightness 34,232 / 340,517 -> 34,560 / 340,921 lightWithColorTemp 34,380 / 341,177 -> 34,708 / 341,581 tempSensor 34,024 / 335,457 -> 34,352 / 335,845 blindControl 34,256 / 338,925 -> 34,584 / 339,313 The 328 bytes of RAM are the two rings of 16 hashes (256) and the eight places of the queue. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
e48f8580f9
commit
6fe8311a6f
8 changed files with 574 additions and 275 deletions
|
|
@ -49,8 +49,8 @@ public:
|
|||
void begin(const char *username, const char *password, const char *rootTopic);
|
||||
Alex2ESPState getState() const;
|
||||
|
||||
// Call from the sketch's loop(): connects, answers discovery requests and hands directives to the devices.
|
||||
// It does not block; the handlers of the sketch run inside it.
|
||||
// Call from the sketch's loop(): connects, answers discovery requests and hands one directive per call to its
|
||||
// device. It does not block; the handlers of the sketch run inside it.
|
||||
void loop();
|
||||
|
||||
AsyncMqttClientDisconnectReason getDisconnectReason() const;
|
||||
|
|
@ -102,8 +102,8 @@ private:
|
|||
unsigned long discoveryStarted; // millis() when the Discover arrived
|
||||
unsigned long discoveryLastAttempt; // millis() of the publish that was refused
|
||||
|
||||
AlexaDirectiveBuffer directive; // The directive that waits for loop(), or is still arriving
|
||||
AlexaRecentIds recentIds; // messageIds of the last directives handled
|
||||
AlexaDirectiveBuffer directives; // The directives that wait for loop(), and the one that is arriving
|
||||
AlexaRecentIds recentIds; // messageIds of the last directives handled
|
||||
|
||||
// Internal event handlers (called by the MQTT client from the network context: they only take notes)
|
||||
void onMqttConnect(bool sessionPresent);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue