Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nonblocking connect using poll #211

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions asyn/asynDriver/asynDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ typedef struct asynManager {
asynStatus (*isEnabled)(asynUser *pasynUser,int *yesNo);
asynStatus (*isAutoConnect)(asynUser *pasynUser,int *yesNo);
asynStatus (*setAutoConnectTimeout)(double timeout);
asynStatus (*getAutoConnectTimeout)(double *timeout);
asynStatus (*waitConnect)(asynUser *pasynUser, double timeout);
/*The following are methods for interrupts*/
asynStatus (*registerInterruptSource)(const char *portName,
Expand Down
11 changes: 11 additions & 0 deletions asyn/asynDriver/asynManager.c
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ static asynStatus isConnected(asynUser *pasynUser,int *yesNo);
static asynStatus isEnabled(asynUser *pasynUser,int *yesNo);
static asynStatus isAutoConnect(asynUser *pasynUser,int *yesNo);
static asynStatus setAutoConnectTimeout(double timeout);
static asynStatus getAutoConnectTimeout(double *timeout);
static asynStatus waitConnect(asynUser *pasynUser, double timeout);
static asynStatus registerInterruptSource(const char *portName,
asynInterface *pasynInterface, void **pasynPvt);
Expand Down Expand Up @@ -374,6 +375,7 @@ static asynManager manager = {
isEnabled,
isAutoConnect,
setAutoConnectTimeout,
getAutoConnectTimeout,
waitConnect,
registerInterruptSource,
getInterruptPvt,
Expand Down Expand Up @@ -2376,6 +2378,15 @@ static asynStatus setAutoConnectTimeout(double timeout)
return asynSuccess;
}

static asynStatus getAutoConnectTimeout(double *timeout)
{
if(!pasynBase) asynInit();
epicsMutexMustLock(pasynBase->lock);
*timeout = pasynBase->autoConnectTimeout;
epicsMutexUnlock(pasynBase->lock);
return asynSuccess;
}

static asynStatus setQueueLockPortTimeout(asynUser *pasynUser, double timeout)
{
userPvt *puserPvt = asynUserToUserPvt(pasynUser);
Expand Down
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;
Comment on lines +535 to +536
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing to note, which I don't know if it's relevant, is that any timeouts below 1ms will be rounded down to 0, causing poll to return immediately.

I don't think anyone actually would have such strict timeout requirements, though.

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
Loading