Properly use nonzero select() timeout for writable sockets

A socket that is "at rest" (no pending reads or writes) is by definition
"writable", and using select() to check for writability will return TRUE
 for the socket without any delay. Therefore each active connection must
 be checked for pending data to write before being added to the write
 check set. This fixes failure to yield to other tasks due to a busy
 loop.
This commit is contained in:
Alex Villacís Lasso
2021-09-06 16:03:31 -05:00
parent 511459ca3b
commit a5ae72c0f6
2 changed files with 20 additions and 11 deletions

View File

@@ -93,7 +93,9 @@ void _asynctcpsock_task(void *)
for (it = _socketBaseList.begin(); it != _socketBaseList.end(); it++) {
if ((*it)->_socket != -1) {
FD_SET((*it)->_socket, &sockSet_r);
FD_SET((*it)->_socket, &sockSet_w);
if ((*it)->_pendingWrite()) {
FD_SET((*it)->_socket, &sockSet_w);
}
(*it)->_selected = true;
if (max_sock <= (*it)->_socket) max_sock = (*it)->_socket + 1;
}
@@ -102,10 +104,15 @@ void _asynctcpsock_task(void *)
// Wait for activity on all monitored sockets
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
tv.tv_usec = ASYNCTCPSOCK_POLL_INTERVAL * 1000;
uint32_t t1 = millis();
xSemaphoreGiveRecursive(_asyncsock_mutex);
int r = select(max_sock, &sockSet_r, &sockSet_w, NULL, &tv);
xSemaphoreTakeRecursive(_asyncsock_mutex, (TickType_t)portMAX_DELAY);
// Check all sockets to see which ones are active
uint32_t nActive = 0;
if (r > 0) {
@@ -182,15 +189,6 @@ void _asynctcpsock_task(void *)
xSemaphoreGiveRecursive(_asyncsock_mutex);
uint32_t t2 = millis();
// Ugly hack to work around the apparent situation of select() call NOT
// yielding to other tasks if using a nonzero wait period.
uint32_t d = (nActive == 0 && t2 - t1 < ASYNCTCPSOCK_POLL_INTERVAL)
? ASYNCTCPSOCK_POLL_INTERVAL - (t2 - t1)
: 1;
delay(d);
// Collect and run activity poll on all pollable sockets
xSemaphoreTakeRecursive(_asyncsock_mutex, (TickType_t)portMAX_DELAY);
for (it = _socketBaseList.begin(); it != _socketBaseList.end(); it++) {
@@ -865,6 +863,14 @@ bool AsyncClient::send()
return true;
}
bool AsyncClient::_pendingWrite(void)
{
xSemaphoreTake(_write_mutex, (TickType_t)portMAX_DELAY);
bool pending = (_writeQueue.size() > 0);
xSemaphoreGive(_write_mutex);
return pending;
}
// In normal operation this should be a no-op. Will only free something in case
// of errors before all data was written.
void AsyncClient::_clearWriteQueue(void)

View File

@@ -70,6 +70,8 @@ protected:
virtual void _sockPoll(void) {} // Action to take on idle socket activity poll
virtual void _sockDelayedConnect(void) {} // Action to take on DNS-resolve finished
virtual bool _pendingWrite(void) { return false; } // Test if there is data pending to be written
public:
AsyncSocketBase(void);
virtual ~AsyncSocketBase();
@@ -142,6 +144,7 @@ class AsyncClient : public AsyncSocketBase
void _sockIsReadable(void);
void _sockPoll(void);
void _sockDelayedConnect(void);
bool _pendingWrite(void);
private: