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:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user