Skip to content

Commit f44ddc8

Browse files
committed
Add TCP port connection timeout.
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.
1 parent b59d1c5 commit f44ddc8

File tree

1 file changed

+44
-10
lines changed

1 file changed

+44
-10
lines changed

asyn/drvAsynSerial/drvAsynIPPort.c

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -505,13 +505,56 @@ connectIt(void *drvPvt, asynUser *pasynUser)
505505
}
506506
}
507507

508+
}
509+
510+
#ifdef USE_POLL
511+
if (setNonBlock(fd, 1) < 0) {
512+
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
513+
"Can't set %s O_NONBLOCK option: %s",
514+
tty->IPDeviceName, strerror(SOCKERRNO));
515+
epicsSocketDestroy(fd);
516+
return asynError;
517+
}
518+
#endif
519+
520+
if (pasynUser->reason <= 0) {
521+
508522
/*
509523
* Connect to the remote host
510524
* If the connect fails, arrange for another DNS lookup in case the
511525
* problem is just that the device has DHCP'd itself an new number.
512526
*/
513527
if (tty->socketType != SOCK_DGRAM) {
514-
if (connect(fd, &tty->farAddr.oa.sa, (int)tty->farAddrSize) < 0) {
528+
int connectResult = connect(fd, &tty->farAddr.oa.sa, (int)tty->farAddrSize);
529+
#ifdef USE_POLL
530+
if (connectResult < 0 && ((SOCKERRNO == EWOULDBLOCK) || (SOCKERRNO == EINPROGRESS))) {
531+
double connectTimeout;
532+
int msConnectTimeout;
533+
struct pollfd pollfd;
534+
535+
pasynManager->getAutoConnectTimeout(&connectTimeout);
536+
msConnectTimeout = 1000 * connectTimeout;
537+
pollfd.fd = fd;
538+
pollfd.events = POLLOUT;
539+
540+
/*
541+
* poll() returning 1 is the only case where connect might have been successful.
542+
* Otherwise connectResult will remain -1.
543+
*/
544+
if (poll(&pollfd, 1, msConnectTimeout) == 1) {
545+
int so_error;
546+
socklen_t len = sizeof so_error;
547+
548+
/*
549+
* We must verify SO_ERROR to make sure the connection was successful.
550+
*/
551+
getsockopt(fd, SOL_SOCKET, SO_ERROR, &so_error, &len);
552+
if (so_error == 0)
553+
connectResult = 0;
554+
}
555+
}
556+
#endif
557+
if (connectResult < 0) {
515558
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
516559
"Can't connect to %s: %s",
517560
tty->IPDeviceName, strerror(SOCKERRNO));
@@ -532,15 +575,6 @@ connectIt(void *drvPvt, asynUser *pasynUser)
532575
epicsSocketDestroy(fd);
533576
return asynError;
534577
}
535-
#ifdef USE_POLL
536-
if (setNonBlock(fd, 1) < 0) {
537-
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
538-
"Can't set %s O_NONBLOCK option: %s",
539-
tty->IPDeviceName, strerror(SOCKERRNO));
540-
epicsSocketDestroy(fd);
541-
return asynError;
542-
}
543-
#endif
544578

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

0 commit comments

Comments
 (0)