@@ -173,7 +173,9 @@ AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, A
173
173
}
174
174
175
175
AsyncEventSourceClient::~AsyncEventSourceClient (){
176
- _messageQueue.free ();
176
+ _lockmq.lock ();
177
+ _messageQueue.free ();
178
+ _lockmq.unlock ();
177
179
close ();
178
180
}
179
181
@@ -184,33 +186,41 @@ void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage)
184
186
delete dataMessage;
185
187
return ;
186
188
}
189
+ // length() is not thread-safe, thus acquiring the lock before this call..
190
+ _lockmq.lock ();
187
191
if (_messageQueue.length () >= SSE_MAX_QUEUED_MESSAGES){
188
- ets_printf (" ERROR: Too many messages queued\n " );
189
- delete dataMessage;
192
+ ets_printf (" ERROR: Too many messages queued\n " );
193
+ delete dataMessage;
190
194
} else {
191
- _messageQueue.add (dataMessage);
195
+ _messageQueue.add (dataMessage);
196
+ // runqueue trigger when new messages added
197
+ if (_client->canSend ()) {
198
+ _runQueue ();
199
+ }
192
200
}
193
- if (_client->canSend ())
194
- _runQueue ();
201
+ _lockmq.unlock ();
195
202
}
196
203
197
204
void AsyncEventSourceClient::_onAck (size_t len, uint32_t time){
205
+ // Same here, acquiring the lock early
206
+ _lockmq.lock ();
198
207
while (len && !_messageQueue.isEmpty ()){
199
208
len = _messageQueue.front ()->ack (len, time);
200
209
if (_messageQueue.front ()->finished ())
201
210
_messageQueue.remove (_messageQueue.front ());
202
211
}
203
-
204
212
_runQueue ();
213
+ _lockmq.unlock ();
205
214
}
206
215
207
216
void AsyncEventSourceClient::_onPoll (){
217
+ _lockmq.lock ();
208
218
if (!_messageQueue.isEmpty ()){
209
219
_runQueue ();
210
220
}
221
+ _lockmq.unlock ();
211
222
}
212
223
213
-
214
224
void AsyncEventSourceClient::_onTimeout (uint32_t time __attribute__ ((unused))){
215
225
_client->close (true );
216
226
}
@@ -225,7 +235,7 @@ void AsyncEventSourceClient::close(){
225
235
_client->close ();
226
236
}
227
237
228
- void AsyncEventSourceClient::write (const char * message, size_t len){
238
+ void AsyncEventSourceClient::_write (const char * message, size_t len){
229
239
_queueMessage (new AsyncEventSourceMessage (message, len));
230
240
}
231
241
@@ -234,15 +244,23 @@ void AsyncEventSourceClient::send(const char *message, const char *event, uint32
234
244
_queueMessage (new AsyncEventSourceMessage (ev.c_str (), ev.length ()));
235
245
}
236
246
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
+ }
241
254
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 ()) {
245
262
(*i)->send (_client);
263
+ }
246
264
}
247
265
}
248
266
@@ -276,56 +294,70 @@ void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
276
294
client->write((const char *)temp, 2053);
277
295
free(temp);
278
296
}*/
279
-
297
+ _client_queue_lock. lock ();
280
298
_clients.add (client);
281
299
if (_connectcb)
282
300
_connectcb (client);
301
+ _client_queue_lock.unlock ();
283
302
}
284
303
285
304
void AsyncEventSource::_handleDisconnect (AsyncEventSourceClient * client){
305
+ _client_queue_lock.lock ();
286
306
_clients.remove (client);
307
+ _client_queue_lock.unlock ();
287
308
}
288
309
289
310
void AsyncEventSource::close (){
311
+ // While the whole loop is not done, the linked list is locked and so the
312
+ // iterator should remain valid even when AsyncEventSource::_handleDisconnect()
313
+ // is called very early
314
+ _client_queue_lock.lock ();
290
315
for (const auto &c: _clients){
291
316
if (c->connected ())
292
317
c->close ();
293
318
}
319
+ _client_queue_lock.unlock ();
294
320
}
295
321
296
322
// pmb fix
297
323
size_t AsyncEventSource::avgPacketsWaiting () const {
298
- if (_clients.isEmpty ())
324
+ size_t aql = 0 ;
325
+ uint32_t nConnectedClients = 0 ;
326
+ _client_queue_lock.lock ();
327
+ if (_clients.isEmpty ()) {
328
+ _client_queue_lock.unlock ();
299
329
return 0 ;
300
-
301
- size_t aql=0 ;
302
- uint32_t nConnectedClients=0 ;
303
-
330
+ }
304
331
for (const auto &c: _clients){
305
332
if (c->connected ()) {
306
- aql+= c->packetsWaiting ();
333
+ aql += c->packetsWaiting ();
307
334
++nConnectedClients;
308
335
}
309
336
}
310
- // return aql / nConnectedClients ;
311
- return ((aql) + (nConnectedClients/2 ))/ (nConnectedClients); // round up
337
+ _client_queue_lock. unlock () ;
338
+ return ((aql) + (nConnectedClients/2 )) / (nConnectedClients); // round up
312
339
}
313
340
314
- void AsyncEventSource::send (const char *message, const char *event, uint32_t id, uint32_t reconnect){
315
-
316
-
341
+ void AsyncEventSource::send (
342
+ const char *message, const char *event, uint32_t id, uint32_t reconnect){
317
343
String ev = generateEventMessage (message, event, id, reconnect);
344
+ _client_queue_lock.lock ();
318
345
for (const auto &c: _clients){
319
346
if (c->connected ()) {
320
- c->write (ev.c_str (), ev.length ());
347
+ c->_write (ev.c_str (), ev.length ());
321
348
}
322
349
}
350
+ _client_queue_lock.unlock ();
323
351
}
324
352
325
353
size_t AsyncEventSource::count () const {
326
- return _clients.count_if ([](AsyncEventSourceClient *c){
327
- return c->connected ();
328
- });
354
+ size_t n_clients;
355
+ _client_queue_lock.lock ();
356
+ n_clients = _clients.count_if ([](AsyncEventSourceClient *c){
357
+ return c->connected ();
358
+ });
359
+ _client_queue_lock.unlock ();
360
+ return n_clients;
329
361
}
330
362
331
363
bool AsyncEventSource::canHandle (AsyncWebServerRequest *request){
0 commit comments