fix(piethernet): bound the EAGAIN retry loop in writeDevice by writeTimeout

The loop may run in a thread that belongs to another device (the picloud
dispatcher forwards data from one connection's read thread to another
connection's socket), so isThreadedReadStopping() alone cannot stop it:
it sees the wrong device's flag. Stalling for at most writeTimeout()
without progress makes every write finite; the receiver resynchronizes
on the next packet signature.
This commit is contained in:
2026-08-24 15:14:30 +03:00
parent 22a09ddd1c
commit a9e356810a
+16 -8
View File
@@ -388,7 +388,7 @@ void PIEthernet::applyBuffers() {
void PIEthernet::applyTimeout(int fd, int opt, PISystemTime tm) {
if (fd == 0) return;
// piCoutObj << "setReadIsBlocking" << yes;
// piCoutObj << "setReadIsBlocking" << yes;
#ifdef WINDOWS
DWORD _tm = tm.toMilliseconds();
#else
@@ -851,13 +851,17 @@ ssize_t PIEthernet::writeDevice(const void * data, ssize_t max_size) {
} else {
ssize_t remain_size = max_size;
const char * remain_data = (const char *)data;
// the socket is non-blocking, so EAGAIN means the send buffer is full
// and the peer is not draining it. The write may be issued from a thread
// that belongs to another device (forwarding in the picloud dispatcher),
// so this loop must be bounded by itself: stall for at most
// writeTimeout() without progress, then give up. The receiver
// resynchronizes on the next packet signature, and any concurrent
// stopAndWait() on the writing thread's device becomes finite.
PITimeMeasurer stalled_tm;
while (remain_size > 0) {
if (isThreadedReadStopping()) {
// socket is non-blocking and peer may not drain the send buffer,
// without this check the loop below would spin forever and the
// thread could never be stopped by stop()/stopAndWait()
break;
}
if (isThreadedReadStopping()) break;
if (stalled_tm.elapsed_s() > writeTimeout().toSeconds()) break;
int sr = ::send(sock, remain_data, remain_size, 0);
if (sr < 0) {
int err = ethErrorCore();
@@ -877,8 +881,12 @@ ssize_t PIEthernet::writeDevice(const void * data, ssize_t max_size) {
ret += sr;
remain_data += sr;
remain_size -= sr;
stalled_tm.reset();
}
if (remain_size > 0) {
piCoutObj << "Error: write stalled," << remain_size << "bytes not sent";
return -1;
}
if (remain_size > 0) return -1;
}
return ret;
}