Skip to content

Commit 600e1d1

Browse files
committed
Merge branch 'ul-gh-fix_884' into yuboxfixes
2 parents d9d8b4d + dc6e2a3 commit 600e1d1

File tree

3 files changed

+110
-34
lines changed

3 files changed

+110
-34
lines changed

src/AsyncEventSource.cpp

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,9 @@ AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, A
173173
}
174174

175175
AsyncEventSourceClient::~AsyncEventSourceClient(){
176-
_messageQueue.free();
176+
_lockmq.lock();
177+
_messageQueue.free();
178+
_lockmq.unlock();
177179
close();
178180
}
179181

@@ -184,33 +186,41 @@ void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage)
184186
delete dataMessage;
185187
return;
186188
}
189+
//length() is not thread-safe, thus acquiring the lock before this call..
190+
_lockmq.lock();
187191
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
188192
ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
189193
delete dataMessage;
190194
} else {
191-
_messageQueue.add(dataMessage);
195+
_messageQueue.add(dataMessage);
196+
// runqueue trigger when new messages added
197+
if(_client->canSend()) {
198+
_runQueue();
199+
}
192200
}
193-
if(_client->canSend())
194-
_runQueue();
201+
_lockmq.unlock();
195202
}
196203

197204
void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){
205+
// Same here, acquiring the lock early
206+
_lockmq.lock();
198207
while(len && !_messageQueue.isEmpty()){
199208
len = _messageQueue.front()->ack(len, time);
200209
if(_messageQueue.front()->finished())
201210
_messageQueue.remove(_messageQueue.front());
202211
}
203-
204212
_runQueue();
213+
_lockmq.unlock();
205214
}
206215

207216
void AsyncEventSourceClient::_onPoll(){
217+
_lockmq.lock();
208218
if(!_messageQueue.isEmpty()){
209219
_runQueue();
210220
}
221+
_lockmq.unlock();
211222
}
212223

213-
214224
void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){
215225
_client->close(true);
216226
}
@@ -225,7 +235,7 @@ void AsyncEventSourceClient::close(){
225235
_client->close();
226236
}
227237

228-
void AsyncEventSourceClient::write(const char * message, size_t len){
238+
void AsyncEventSourceClient::_write(const char * message, size_t len){
229239
_queueMessage(new AsyncEventSourceMessage(message, len));
230240
}
231241

@@ -234,15 +244,23 @@ void AsyncEventSourceClient::send(const char *message, const char *event, uint32
234244
_queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length()));
235245
}
236246

237-
void AsyncEventSourceClient::_runQueue(){
238-
while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){
239-
_messageQueue.remove(_messageQueue.front());
240-
}
247+
size_t AsyncEventSourceClient::packetsWaiting() const {
248+
size_t len;
249+
_lockmq.lock();
250+
len = _messageQueue.length();
251+
_lockmq.unlock();
252+
return len;
253+
}
241254

242-
for(auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i)
243-
{
244-
if(!(*i)->sent())
255+
void AsyncEventSourceClient::_runQueue() {
256+
// Calls to this private method now already protected by _lockmq acquisition
257+
// so no extra call of _lockmq.lock() here..
258+
for (auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) {
259+
// If it crashes here, iterator (i) has been invalidated as _messageQueue
260+
// has been changed... (UL 2020-11-15: Not supposed to happen any more ;-) )
261+
if (!(*i)->sent()) {
245262
(*i)->send(_client);
263+
}
246264
}
247265
}
248266

@@ -280,56 +298,70 @@ void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
280298
client->write((const char *)temp, 2053);
281299
free(temp);
282300
}*/
283-
301+
_client_queue_lock.lock();
284302
_clients.add(client);
285303
if(_connectcb)
286304
_connectcb(client);
305+
_client_queue_lock.unlock();
287306
}
288307

289308
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){
309+
_client_queue_lock.lock();
290310
_clients.remove(client);
311+
_client_queue_lock.unlock();
291312
}
292313

293314
void AsyncEventSource::close(){
315+
// While the whole loop is not done, the linked list is locked and so the
316+
// iterator should remain valid even when AsyncEventSource::_handleDisconnect()
317+
// is called very early
318+
_client_queue_lock.lock();
294319
for(const auto &c: _clients){
295320
if(c->connected())
296321
c->close();
297322
}
323+
_client_queue_lock.unlock();
298324
}
299325

300326
// pmb fix
301327
size_t AsyncEventSource::avgPacketsWaiting() const {
302-
if(_clients.isEmpty())
328+
size_t aql = 0;
329+
uint32_t nConnectedClients = 0;
330+
_client_queue_lock.lock();
331+
if (_clients.isEmpty()) {
332+
_client_queue_lock.unlock();
303333
return 0;
304-
305-
size_t aql=0;
306-
uint32_t nConnectedClients=0;
307-
334+
}
308335
for(const auto &c: _clients){
309336
if(c->connected()) {
310-
aql+=c->packetsWaiting();
337+
aql += c->packetsWaiting();
311338
++nConnectedClients;
312339
}
313340
}
314-
// return aql / nConnectedClients;
315-
return ((aql) + (nConnectedClients/2))/(nConnectedClients); // round up
341+
_client_queue_lock.unlock();
342+
return ((aql) + (nConnectedClients/2)) / (nConnectedClients); // round up
316343
}
317344

318-
void AsyncEventSource::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){
319-
320-
345+
void AsyncEventSource::send(
346+
const char *message, const char *event, uint32_t id, uint32_t reconnect){
321347
String ev = generateEventMessage(message, event, id, reconnect);
348+
_client_queue_lock.lock();
322349
for(const auto &c: _clients){
323350
if(c->connected()) {
324-
c->write(ev.c_str(), ev.length());
351+
c->_write(ev.c_str(), ev.length());
325352
}
326353
}
354+
_client_queue_lock.unlock();
327355
}
328356

329357
size_t AsyncEventSource::count() const {
330-
return _clients.count_if([](AsyncEventSourceClient *c){
331-
return c->connected();
332-
});
358+
size_t n_clients;
359+
_client_queue_lock.lock();
360+
n_clients = _clients.count_if([](AsyncEventSourceClient *c){
361+
return c->connected();
362+
});
363+
_client_queue_lock.unlock();
364+
return n_clients;
333365
}
334366

335367
bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){

src/AsyncEventSource.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ class AsyncEventSourceClient {
7373
AsyncEventSource *_server;
7474
uint32_t _lastId;
7575
LinkedList<AsyncEventSourceMessage *> _messageQueue;
76+
// ArFi 2020-08-27 for protecting/serializing _messageQueue
77+
AsyncPlainLock _lockmq;
7678
void _queueMessage(AsyncEventSourceMessage *dataMessage);
7779
void _runQueue();
7880

@@ -83,12 +85,12 @@ class AsyncEventSourceClient {
8385

8486
AsyncClient* client(){ return _client; }
8587
void close();
86-
void write(const char * message, size_t len);
8788
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
8889
bool connected() const { return (_client != NULL) && _client->connected(); }
8990
uint32_t lastId() const { return _lastId; }
90-
size_t packetsWaiting() const { return _messageQueue.length(); }
91+
size_t packetsWaiting() const;
9192

93+
void _write(const char * message, size_t len);
9294
//system callbacks (do not call)
9395
void _onAck(size_t len, uint32_t time);
9496
void _onPoll();
@@ -100,6 +102,9 @@ class AsyncEventSource: public AsyncWebHandler {
100102
private:
101103
String _url;
102104
LinkedList<AsyncEventSourceClient *> _clients;
105+
// Same as for individual messages, protect mutations of _clients list
106+
// since simultaneous access from different tasks is possible
107+
AsyncPlainLock _client_queue_lock;
103108
ArEventHandlerFunction _connectcb;
104109
ArAuthorizeConnectHandler _authorizeConnectHandler;
105110
public:
@@ -111,7 +116,7 @@ class AsyncEventSource: public AsyncWebHandler {
111116
void onConnect(ArEventHandlerFunction cb);
112117
void authorizeConnect(ArAuthorizeConnectHandler cb);
113118
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
114-
size_t count() const; //number clinets connected
119+
size_t count() const; //number clients connected
115120
size_t avgPacketsWaiting() const;
116121

117122
//system callbacks (do not call)

src/AsyncWebSynchronization.h

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,38 @@
77

88
#ifdef ESP32
99

10+
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
11+
// Modified 'AsyncWebLock' to just only use mutex since pxCurrentTCB is not
12+
// always available. According to example by Arjan Filius, changed name,
13+
// added unimplemented version for ESP8266
14+
class AsyncPlainLock
15+
{
16+
private:
17+
SemaphoreHandle_t _lock;
18+
19+
public:
20+
AsyncPlainLock() {
21+
_lock = xSemaphoreCreateBinary();
22+
// In this fails, the system is likely that much out of memory that
23+
// we should abort anyways. If assertions are disabled, nothing is lost..
24+
assert(_lock);
25+
xSemaphoreGive(_lock);
26+
}
27+
28+
~AsyncPlainLock() {
29+
vSemaphoreDelete(_lock);
30+
}
31+
32+
bool lock() const {
33+
xSemaphoreTake(_lock, portMAX_DELAY);
34+
return true;
35+
}
36+
37+
void unlock() const {
38+
xSemaphoreGive(_lock);
39+
}
40+
};
41+
1042
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
1143
class AsyncWebLock
1244
{
@@ -17,6 +49,9 @@ class AsyncWebLock
1749
public:
1850
AsyncWebLock() {
1951
_lock = xSemaphoreCreateBinary();
52+
// In this fails, the system is likely that much out of memory that
53+
// we should abort anyways. If assertions are disabled, nothing is lost..
54+
assert(_lock);
2055
_lockedBy = NULL;
2156
xSemaphoreGive(_lock);
2257
}
@@ -61,6 +96,10 @@ class AsyncWebLock
6196
void unlock() const {
6297
}
6398
};
99+
100+
// Same for AsyncPlainLock, for ESP8266 this is just the unimplemented version above.
101+
using AsyncPlainLock = AsyncWebLock;
102+
64103
#endif
65104

66105
class AsyncWebLockGuard
@@ -84,4 +123,4 @@ class AsyncWebLockGuard
84123
}
85124
};
86125

87-
#endif // ASYNCWEBSYNCHRONIZATION_H_
126+
#endif // ASYNCWEBSYNCHRONIZATION_H_

0 commit comments

Comments
 (0)