/* Wait for a specific state of the relp session. This function blocks * until the session is in that state, but runs a receive loop to get hold * of server responses (because otherwise the state would probably never change ;)). * If a timeout occurs, the session is considered broken and control returned to * the caller. Caller must check if the session state has changed to "BROKEN" and * in this case drop the session. The timeout value is in seconds. This function * may be called if the session already has the desired state. In that case, it * returns, but still tries to see if we received anything from the server. If so, * this data is received. The suggested use is to call the function before any * send operation, so that will keep our receive capability alive without * resorting to multi-threading. * rgerhards, 2008-03-20 */ static relpRetVal relpSessWaitState(relpSess_t *const pThis, const relpSessState_t stateExpected, const int timeout) { struct pollfd pfd; int sock; int nfds; struct timespec tCurr; /* current time */ struct timespec tTimeout; /* absolute timeout value */ relpRetVal localRet; ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); /* are we already ready? */ if(pThis->sessState == stateExpected || pThis->sessState == eRelpSessState_BROKEN) { FINALIZE; } /* first read any outstanding data and process the packets. Note that this * call DOES NOT block. */ localRet = relpSessRcvData(pThis); if(localRet != RELP_RET_OK && localRet != RELP_RET_SESSION_BROKEN) ABORT_FINALIZE(localRet); /* re-check if we are already in the desired state. If so, we can immediately * return. That saves us doing a costly clock call to set the timeout. As a * side-effect, the timeout is actually applied without the time needed for * above reception. I think is is OK, even a bit logical ;) */ if(pThis->sessState == stateExpected || pThis->sessState == eRelpSessState_BROKEN) { FINALIZE; } /* ok, looks like we actually need to do a wait... */ clock_gettime(CLOCK_REALTIME, &tCurr); memcpy(&tTimeout, &tCurr, sizeof(struct timespec)); tTimeout.tv_sec += timeout; while(!relpEngineShouldStop(pThis->pEngine)) { sock = relpSessGetSock(pThis); if(tCurr.tv_sec >= tTimeout.tv_sec) { ABORT_FINALIZE(RELP_RET_TIMED_OUT); } pfd.fd = sock; pfd.events = POLLIN; pThis->pEngine->dbgprint("relpSessWaitRsp waiting for data on " "fd %d, timeout %d\n", sock, timeout); nfds = poll(&pfd, 1, timeout*1000); if(nfds == -1) { if(errno == EINTR) { pThis->pEngine->dbgprint("relpSessWaitRsp select interrupted, continue\n"); } else { pThis->pEngine->dbgprint("relpSessWaitRsp select returned error %d\n", errno); ABORT_FINALIZE(RELP_RET_SESSION_BROKEN); } } else pThis->pEngine->dbgprint("relpSessWaitRsp poll returns, " "nfds %d, errno %d\n", nfds, errno); if(relpEngineShouldStop(pThis->pEngine)) break; /* we don't check if we had a timeout-we give it one last chance*/ CHKRet(relpSessRcvData(pThis)); pThis->pEngine->dbgprint("iRet after relpSessRcvData %d\n", iRet); if( pThis->sessState == stateExpected || pThis->sessState == eRelpSessState_BROKEN) { FINALIZE; } clock_gettime(CLOCK_REALTIME, &tCurr); } finalize_it: pThis->pEngine->dbgprint("relpSessWaitState returns %d\n", iRet); if( iRet == RELP_RET_TIMED_OUT || iRet == RELP_RET_SESSION_BROKEN || relpEngineShouldStop(pThis->pEngine)) { /* the session is broken! */ pThis->sessState = eRelpSessState_BROKEN; } LEAVE_RELPFUNC; }
/* Wait for a specific state of the relp session. This function blocks * until the session is in that state, but runs a receive loop to get hold * of server responses (because otherwise the state would probably never change ;)). * If a timeout occurs, the session is considered broken and control returned to * the caller. Caller must check if the session state has changed to "BROKEN" and * in this case drop the session. The timeout value is in seconds. This function * may be called if the session already has the desired state. In that case, it * returns, but still tries to see if we received anything from the server. If so, * this data is received. The suggested use is to call the function before any * send operation, so that will keep our receive capability alive without * resorting to multi-threading. * rgerhards, 2008-03-20 */ static relpRetVal relpSessWaitState(relpSess_t *pThis, relpSessState_t stateExpected, int timeout) { fd_set readfds; int sock; int nfds; #ifdef CLOCK_MONOTONIC struct timespec tCurr; /* current time */ struct timespec tTimeout; /* absolute timeout value */ #else struct timeval tCurr; /* current time */ struct timeval tTimeout; /* absolute timeout value */ #endif struct timeval tvSelect; relpRetVal localRet; ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); /* first read any outstanding data and process the packets. Note that this * call DOES NOT block. */ localRet = relpSessRcvData(pThis); if(localRet != RELP_RET_OK && localRet != RELP_RET_SESSION_BROKEN) ABORT_FINALIZE(localRet); /* check if we are already in the desired state. If so, we can immediately * return. That saves us doing a costly clock call to set the timeout. As a * side-effect, the timeout is actually applied without the time needed for * above reception. I think is is OK, even a bit logical ;) */ if(pThis->sessState == stateExpected || pThis->sessState == eRelpSessState_BROKEN) { FINALIZE; } /* ok, looks like we actually need to do a wait... */ #ifdef CLOCK_MONOTONIC clock_gettime(CLOCK_REALTIME, &tCurr); memcpy(&tTimeout, &tCurr, sizeof(struct timespec)); #else gettimeofday(&tCurr, NULL); memcpy(&tTimeout, &tCurr, sizeof(struct timeval)); #endif tTimeout.tv_sec += timeout; while(1) { sock = relpSessGetSock(pThis); tvSelect.tv_sec = tTimeout.tv_sec - tCurr.tv_sec; #ifdef CLOCK_MONOTONIC tvSelect.tv_usec = (tTimeout.tv_nsec - tCurr.tv_nsec) / 1000000; #else tvSelect.tv_usec = (tTimeout.tv_usec - tCurr.tv_usec) / 1000; #endif if(tvSelect.tv_usec < 0) { tvSelect.tv_usec += 1000000; tvSelect.tv_sec--; } if(tvSelect.tv_sec < 0) { ABORT_FINALIZE(RELP_RET_TIMED_OUT); } FD_ZERO(&readfds); FD_SET(sock, &readfds); pThis->pEngine->dbgprint("relpSessWaitRsp waiting for data on fd %d, timeout %d.%d\n", sock, (int) tvSelect.tv_sec, (int) tvSelect.tv_usec); nfds = select(sock+1, (fd_set *) &readfds, NULL, NULL, &tvSelect); pThis->pEngine->dbgprint("relpSessWaitRsp select returns, nfds %d, errno %d\n", nfds, errno); /* we don't check if we had a timeout - we give it one last chance */ CHKRet(relpSessRcvData(pThis)); pThis->pEngine->dbgprint("iRet after relpSessRcvData %d\n", iRet); if(pThis->sessState == stateExpected || pThis->sessState == eRelpSessState_BROKEN) { FINALIZE; } #ifdef CLOCK_MONOTONIC clock_gettime(CLOCK_REALTIME, &tCurr); #else gettimeofday(&tCurr, NULL); #endif } finalize_it: pThis->pEngine->dbgprint("relpSessWaitState returns %d\n", iRet); if(iRet == RELP_RET_TIMED_OUT) { /* the session is broken! */ pThis->sessState = eRelpSessState_BROKEN; } LEAVE_RELPFUNC; }