Open mathieucarbou opened 1 week ago
I was actually wondering about it... because if you remember Markus, he is also experiencing same issue. Similar crash log, crashed at loop waiting for print buffer mutex to be released.
I guess switching to delay(1);
will solve it? Let me know if you can test.
I guess switching to
delay(1);
will solve it? Let me know if you can test.
it wont on all ESP.
delay is implemented as follow:
void delay(uint32_t ms) {
vTaskDelay(ms / portTICK_PERIOD_MS);
}
On my ESP32, portTICK_PERIOD_MS
is 1 because configTICK_RATE_HZ is 1000. But this depends on boards.
To make sure you trigger a pause which is not 0, you must use something like:
delay(portTICK_PERIOD_MS); delay(N * portTICK_PERIOD_MS); ...
But the issue is not only here: triggering a short pause is OK, but doing it in a loop is not OK: for example, on a a single core, this small delay might not be enough to let the ESP give some time for bluetooth, wifi, core tasks, etc.
There is no simple solution here, and I think the root of the issue is the mutex implementation on a dual-core system which is not OK.
1) shared resources across core should be volatile
for concurrent read and writes only from 1 core (_buffer_mutex is not)
2) only 1 writer core you ever mutate a volatile variable to avoid any unexpected state (but several cores can read) - which is not possible in your impl since both core 1 and core 0 can mutate the flag
3) Otherwise use proper mutex c++ classes
Here, _buffer_mutex
is not volatile so a mutation from 1 core will not always be visible to the other core (memory is not always shared across core)
I don't know if Arduino internals are made in a way to simplify that, but these notions apply to the Java language and C++ at least.
https://www.arduino.cc/reference/en/language/variables/variable-scope-qualifiers/volatile/
Right... I see into removing these mutex locks completely.
@mathieucarbou , What about yield()
?
yield
yield() is called by delay() and only is relevant for multi-core since it allows another core to run, but yielding in a loop might not solve the problem.
To really solve the issue, the while loop should be removed. It's usually not a good practice to have a busy loop.
A real mutex could be used instead (std:mutex) to protect the shared resource. And depending on the access types (more read than writes, or the opposite), a std::shared_mutex can be used instead to optimise and still allow concurrency access but only lock when needed.
That's one of the reason I don't like the buffered approach within the library, which, IMO, has nothing to do here because it adds complexity and does not really solve the buffering issue which is better solved on the caller side. The websocket layer already has a buffer of messages to dequeue and send, so it can be leveraged instead of having a common shared resource that requires access synchronisation.
I've been mistakenly running with the non high-perf version of webserial and found an issue in the busy loop which is triggerring the watchdog if activated:
Besides blocking, the while loop is not allowing the ESP to execute another task because
delayMicroseconds(1);
is too small.delay or vTaskDelay could be used instead with a value no less than
portTICK_PERIOD_MS
. The default tick rate is 100Hz, which means portTICK_PERIOD_MS is 10 (10ms per tick). Therefore a delay lower than 10 ms causes no delay.delayMicroseconds
is instead a busy wait as it is implemented so it prevents any task scheduling to happen.