Example #1
0
/* 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;
}
Example #2
0
/* 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;
}