Skip to content

Commit

Permalink
Add TCP port connection timeout.
Browse files Browse the repository at this point in the history
Use poll as a reasonably portable implementation. This takes advantage
of the existing FAKE_POLL implementation as well.

getsockopt(SO_ERROR) on Windows uses "int", removing the requirement for
any custom code for that platform.

We don't add any handling for EINTR, since it would add considerably
more code, and the connection can simply be retried. For that reason,
poll returning -1 is always an error.
  • Loading branch information
ericonr committed Dec 3, 2024
1 parent b59d1c5 commit f44ddc8
Showing 1 changed file with 44 additions and 10 deletions.
54 changes: 44 additions & 10 deletions asyn/drvAsynSerial/drvAsynIPPort.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,13 +505,56 @@ connectIt(void *drvPvt, asynUser *pasynUser)
}
}

}

#ifdef USE_POLL
if (setNonBlock(fd, 1) < 0) {
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"Can't set %s O_NONBLOCK option: %s",
tty->IPDeviceName, strerror(SOCKERRNO));
epicsSocketDestroy(fd);
return asynError;
}
#endif

if (pasynUser->reason <= 0) {

/*
* Connect to the remote host
* If the connect fails, arrange for another DNS lookup in case the
* problem is just that the device has DHCP'd itself an new number.
*/
if (tty->socketType != SOCK_DGRAM) {
if (connect(fd, &tty->farAddr.oa.sa, (int)tty->farAddrSize) < 0) {
int connectResult = connect(fd, &tty->farAddr.oa.sa, (int)tty->farAddrSize);
#ifdef USE_POLL
if (connectResult < 0 && ((SOCKERRNO == EWOULDBLOCK) || (SOCKERRNO == EINPROGRESS))) {
double connectTimeout;
int msConnectTimeout;
struct pollfd pollfd;

pasynManager->getAutoConnectTimeout(&connectTimeout);
msConnectTimeout = 1000 * connectTimeout;
pollfd.fd = fd;
pollfd.events = POLLOUT;

/*
* poll() returning 1 is the only case where connect might have been successful.
* Otherwise connectResult will remain -1.
*/
if (poll(&pollfd, 1, msConnectTimeout) == 1) {
int so_error;
socklen_t len = sizeof so_error;

/*
* We must verify SO_ERROR to make sure the connection was successful.
*/
getsockopt(fd, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error == 0)
connectResult = 0;
}
}
#endif
if (connectResult < 0) {
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"Can't connect to %s: %s",
tty->IPDeviceName, strerror(SOCKERRNO));
Expand All @@ -532,15 +575,6 @@ connectIt(void *drvPvt, asynUser *pasynUser)
epicsSocketDestroy(fd);
return asynError;
}
#ifdef USE_POLL
if (setNonBlock(fd, 1) < 0) {
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"Can't set %s O_NONBLOCK option: %s",
tty->IPDeviceName, strerror(SOCKERRNO));
epicsSocketDestroy(fd);
return asynError;
}
#endif

asynPrint(pasynUser, ASYN_TRACE_FLOW,
"Opened connection OK to %s\n", tty->IPDeviceName);
Expand Down

0 comments on commit f44ddc8

Please sign in to comment.