-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Fix #884, protect list concurrent access with mutex #887
base: master
Are you sure you want to change the base?
Changes from 2 commits
344062a
dc6e2a3
2b09f82
594a2a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -7,6 +7,38 @@ | |||||||||
|
||||||||||
#ifdef ESP32 | ||||||||||
|
||||||||||
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore | ||||||||||
// Modified 'AsyncWebLock' to just only use mutex since pxCurrentTCB is not | ||||||||||
// always available. According to example by Arjan Filius, changed name, | ||||||||||
// added unimplemented version for ESP8266 | ||||||||||
class AsyncPlainLock | ||||||||||
{ | ||||||||||
private: | ||||||||||
SemaphoreHandle_t _lock; | ||||||||||
|
||||||||||
public: | ||||||||||
AsyncPlainLock() { | ||||||||||
_lock = xSemaphoreCreateBinary(); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. xSemaphoreCreateBinary() can fail in case of less memory. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The original implementation of the AsyncWebLock (below the new class) does the same without an assert etc. What would you suggest, put an abort() or assert() there? If yes, this should go in both places. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. assert() is fine and give the developer enough information I think. |
||||||||||
// In this fails, the system is likely that much out of memory that | ||||||||||
// we should abort anyways. If assertions are disabled, nothing is lost.. | ||||||||||
assert(_lock); | ||||||||||
xSemaphoreGive(_lock); | ||||||||||
} | ||||||||||
|
||||||||||
~AsyncPlainLock() { | ||||||||||
vSemaphoreDelete(_lock); | ||||||||||
} | ||||||||||
|
||||||||||
bool lock() const { | ||||||||||
xSemaphoreTake(_lock, portMAX_DELAY); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. xSemaphoreTake() can fail There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (edited) No, actually in this case, xSemaphoreTake, which is an alias to xQueueGenericReceive, would only ever have a pdFALSE return value if an effective timeout was supplied as an argument. But the portMAX_DELAY in this case means the timeout is disabled. xSemaphoreTake does either lock indefinitely or return pdTRUE. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. xQueueGenericReceive, I meant it /can/ only return "pdFALSE" when invoked with a non-infinite timeout. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, you are right. I expected a |
||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
void unlock() const { | ||||||||||
xSemaphoreGive(_lock); | ||||||||||
} | ||||||||||
}; | ||||||||||
|
||||||||||
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its the ESP8266 version, not ESP32 ;-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, the define is OK and for ESP8266, but still I noticed a bug there, as I accidentally left the "mutable void *_lockedBy;" member in for the supposedly "plain" lock class. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh yes, your right here. Looks like I read the comment and scrolled to fast down. |
||||||||||
class AsyncWebLock | ||||||||||
{ | ||||||||||
|
@@ -17,6 +49,9 @@ class AsyncWebLock | |||||||||
public: | ||||||||||
AsyncWebLock() { | ||||||||||
_lock = xSemaphoreCreateBinary(); | ||||||||||
// In this fails, the system is likely that much out of memory that | ||||||||||
// we should abort anyways. If assertions are disabled, nothing is lost.. | ||||||||||
assert(_lock); | ||||||||||
_lockedBy = NULL; | ||||||||||
xSemaphoreGive(_lock); | ||||||||||
} | ||||||||||
|
@@ -61,6 +96,10 @@ class AsyncWebLock | |||||||||
void unlock() const { | ||||||||||
} | ||||||||||
}; | ||||||||||
|
||||||||||
// Same for AsyncPlainLock, for ESP8266 this is just the unimplemented version above. | ||||||||||
using AsyncPlainLock = AsyncWebLock; | ||||||||||
|
||||||||||
#endif | ||||||||||
|
||||||||||
class AsyncWebLockGuard | ||||||||||
|
@@ -84,4 +123,4 @@ class AsyncWebLockGuard | |||||||||
} | ||||||||||
}; | ||||||||||
|
||||||||||
#endif // ASYNCWEBSYNCHRONIZATION_H_ | ||||||||||
#endif // ASYNCWEBSYNCHRONIZATION_H_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By calling the connect callback with the client queue lock held, this code introduces a self-deadlock if the connect callback, in turn, tries to broadcast a message to the event source using AsyncEventSource::send (or tries to use any other public API that tries to take that very same lock).
I had this exact scenario in my project, and I had to fix it as shown in commit yubox-node-org@646eab7 of yuboxfixes branch of the ESPAsyncWebServer fork at yubox-node-org.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just now realized that the extern void *pxCurrentTCB member of class AsyncWebLock does /not/ point to the TCP TCB struct (TCB as in "transfer control block") but to the FreeRTOS stack pointer, and that this is how the AsyncWebLockGuard works..
I only did minimal testing, but I assume this is safe to pull into this PR for completeness.
I hope that the original author does find some time in the near future to go through all these forks and PRs...
Thank you very much for the notice!