Beispiel #1
0
/* terminate a thread via the non-cancel interface
 * This is a separate function as it involves a bit more of code.
 * rgerhads, 2009-10-15
 */
static inline rsRetVal
thrdTerminateNonCancel(thrdInfo_t *pThis)
{
	struct timespec tTimeout;
	int ret;
	DEFiRet;
	assert(pThis != NULL);

	DBGPRINTF("request term via SIGTTIN for input thread 0x%x\n", (unsigned) pThis->thrdID);
	pThis->bShallStop = TRUE;
	do {
		d_pthread_mutex_lock(&pThis->mutThrd);
		pthread_kill(pThis->thrdID, SIGTTIN);
		timeoutComp(&tTimeout, 10); /* a fixed 10ms timeout, do after lock (may take long!) */
		ret = d_pthread_cond_timedwait(&pThis->condThrdTerm, &pThis->mutThrd, &tTimeout);
		d_pthread_mutex_unlock(&pThis->mutThrd);
		if(Debug) {
			if(ret == ETIMEDOUT) {
				dbgprintf("input thread term: had a timeout waiting on thread termination\n");
			} else if(ret == 0) {
				dbgprintf("input thread term: thread returned normally and is terminated\n");
			} else {
				char errStr[1024];
				int err = errno;
				rs_strerror_r(err, errStr, sizeof(errStr));
				dbgprintf("input thread term: cond_wait returned with error %d: %s\n",
					  err, errStr);
			}
		}
	} while(pThis->bIsActive);
	DBGPRINTF("non-cancel input thread termination succeeded for thread 0x%x\n", (unsigned) pThis->thrdID);

	RETiRet;
}
Beispiel #2
0
/* The getenv function. Note that we guard the OS call by a mutex, as that
 * function is not guaranteed to be thread-safe. This implementation here is far from
 * being optimal, at least we should cache the result. This is left TODO for
 * a later revision.
 * rgerhards, 2009-11-03
 */
static rsRetVal
rsf_getenv(vmstk_t *pStk, int numOperands)
{
	DEFiRet;
	var_t *operand1;
	char *envResult;
	cstr_t *pCstr;

	if(numOperands != 1)
		ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS);

	/* pop args and do operaton (trivial case here...) */
	vmstk.PopString(pStk, &operand1);
	d_pthread_mutex_lock(&mutGetenv);
	envResult = getenv((char*) rsCStrGetSzStr(operand1->val.pStr));
	DBGPRINTF("rsf_getenv(): envvar '%s', return '%s'\n", rsCStrGetSzStr(operand1->val.pStr),
		   envResult == NULL ? "(NULL)" : envResult);
	iRet = rsCStrConstructFromszStr(&pCstr, (envResult == NULL) ? UCHAR_CONSTANT("") : (uchar*)envResult);
	d_pthread_mutex_unlock(&mutGetenv);
	if(iRet != RS_RET_OK)
		FINALIZE; /* need to do this after mutex is unlocked! */

	/* Store result and cleanup */
	var.SetString(operand1, pCstr);
	vmstk.Push(pStk, operand1);
finalize_it:
	RETiRet;
}
Beispiel #3
0
/* cancellation cleanup handler - frees provided mutex
 * rgerhards, 2008-01-14
 */
void
mutexCancelCleanup(void *arg)
{
	BEGINfunc
	assert(arg != NULL);
	d_pthread_mutex_unlock((pthread_mutex_t*) arg);
	ENDfunc
}
Beispiel #4
0
/* This is an internal wrapper around the user thread function. Its
 * purpose is to handle all the necessary housekeeping stuff so that the
 * user function needs not to be aware of the threading calls. The user
 * function call has just "normal", non-threading semantics.
 * rgerhards, 2007-12-17
 */
static void* thrdStarter(void *arg)
{
    DEFiRet;
    thrdInfo_t *pThis = (thrdInfo_t*) arg;
#	if HAVE_PRCTL && defined PR_SET_NAME
    uchar thrdName[32] = "in:";
#	endif

    assert(pThis != NULL);
    assert(pThis->pUsrThrdMain != NULL);

    ustrncpy(thrdName+3, pThis->name, 20);
    dbgOutputTID((char*)thrdName);

#	if HAVE_PRCTL && defined PR_SET_NAME
    /* set thread name - we ignore if the call fails, has no harsh consequences... */
    if(prctl(PR_SET_NAME, thrdName, 0, 0, 0) != 0) {
        DBGPRINTF("prctl failed, not setting thread name for '%s'\n", pThis->name);
    } else {
        DBGPRINTF("set thread name to '%s'\n", thrdName);
    }
#	endif

    /* block all signals */
    sigset_t sigSet;
    sigfillset(&sigSet);
    pthread_sigmask(SIG_BLOCK, &sigSet, NULL);

    /* but ignore SIGTTN, which we (ab)use to signal the thread to shutdown -- rgerhards, 2009-07-20 */
    sigemptyset(&sigSet);
    sigaddset(&sigSet, SIGTTIN);
    pthread_sigmask(SIG_UNBLOCK, &sigSet, NULL);

    /* setup complete, we are now ready to execute the user code. We will not
     * regain control until the user code is finished, in which case we terminate
     * the thread.
     */
    iRet = pThis->pUsrThrdMain(pThis);

    dbgprintf("thrdStarter: usrThrdMain %s - 0x%lx returned with iRet %d, exiting now.\n",
              pThis->name, (unsigned long) pThis->thrdID, iRet);

    /* signal master control that we exit (we do the mutex lock mostly to
     * keep the thread debugger happer, it would not really be necessary with
     * the logic we employ...)
     */
    d_pthread_mutex_lock(&pThis->mutThrd);
    pThis->bIsActive = 0;
    pthread_cond_signal(&pThis->condThrdTerm);
    d_pthread_mutex_unlock(&pThis->mutThrd);

    ENDfunc
    pthread_exit(0);
}
Beispiel #5
0
/* terminate a thread via the non-cancel interface
 * This is a separate function as it involves a bit more of code.
 * rgerhads, 2009-10-15
 */
static inline rsRetVal
thrdTerminateNonCancel(thrdInfo_t *pThis)
{
	struct timespec tTimeout;
	int ret;
	int was_active;
	DEFiRet;
	assert(pThis != NULL);

	DBGPRINTF("request term via SIGTTIN for input thread '%s' 0x%x\n",
		  pThis->name, (unsigned) pThis->thrdID);

	pThis->bShallStop = RSTRUE;
	timeoutComp(&tTimeout, 1000); /* a fixed 1sec timeout */
	d_pthread_mutex_lock(&pThis->mutThrd);
	was_active = pThis->bIsActive;
	while(was_active) {
		pthread_kill(pThis->thrdID, SIGTTIN);
		ret = d_pthread_cond_timedwait(&pThis->condThrdTerm, &pThis->mutThrd, &tTimeout);
		if(ret == ETIMEDOUT) {
			DBGPRINTF("input thread term: timeout expired waiting on thread %s termination - canceling\n",
				pThis->name);
			pthread_cancel(pThis->thrdID);
			break;
		} else if(ret != 0) {
			char errStr[1024];
			int err = errno;
			rs_strerror_r(err, errStr, sizeof(errStr));
			DBGPRINTF("input thread term: cond_wait returned with error %d: %s\n",
				  err, errStr);
		}
		was_active = pThis->bIsActive;
	}
	d_pthread_mutex_unlock(&pThis->mutThrd);

	if(was_active) {
		DBGPRINTF("non-cancel input thread termination FAILED for thread %s 0x%x\n",
			  pThis->name, (unsigned) pThis->thrdID);
	} else {
		DBGPRINTF("non-cancel input thread termination succeeded for thread %s 0x%x\n",
			  pThis->name, (unsigned) pThis->thrdID);
	}

	RETiRet;
}
Beispiel #6
0
/* This is an internal wrapper around the user thread function. Its
 * purpose is to handle all the necessary housekeeping stuff so that the
 * user function needs not to be aware of the threading calls. The user
 * function call has just "normal", non-threading semantics.
 * rgerhards, 2007-12-17
 */
static void* thrdStarter(void *arg)
{
	DEFiRet;
	thrdInfo_t *pThis = (thrdInfo_t*) arg;

	assert(pThis != NULL);
	assert(pThis->pUsrThrdMain != NULL);

	/* block all signals */
	sigset_t sigSet;
	sigfillset(&sigSet);
	pthread_sigmask(SIG_BLOCK, &sigSet, NULL);

	/* but ignore SIGTTN, which we (ab)use to signal the thread to shutdown -- rgerhards, 2009-07-20 */
	sigemptyset(&sigSet);
	sigaddset(&sigSet, SIGTTIN);
	pthread_sigmask(SIG_UNBLOCK, &sigSet, NULL);

	/* setup complete, we are now ready to execute the user code. We will not
	 * regain control until the user code is finished, in which case we terminate
	 * the thread.
	 */
	iRet = pThis->pUsrThrdMain(pThis);

	dbgprintf("thrdStarter: usrThrdMain 0x%lx returned with iRet %d, exiting now.\n", (unsigned long) pThis->thrdID, iRet);

	/* signal master control that we exit (we do the mutex lock mostly to 
	 * keep the thread debugger happer, it would not really be necessary with
	 * the logic we employ...)
	 */
	pThis->bIsActive = 0;
	d_pthread_mutex_lock(&pThis->mutThrd);
	pthread_cond_signal(&pThis->condThrdTerm);
	d_pthread_mutex_unlock(&pThis->mutThrd);

	ENDfunc
	pthread_exit(0);
}
Beispiel #7
0
/* This function is called to gather input. It tries doing that via the epoll()
 * interface. If the driver does not support that, it falls back to calling its
 * select() equivalent.
 * rgerhards, 2009-11-18
 */
static rsRetVal
Run(tcpsrv_t *pThis)
{
	DEFiRet;
	int i;
	nsd_epworkset_t workset[128]; /* 128 is currently fixed num of concurrent requests */
	int numEntries;
	nspoll_t *pPoll = NULL;
	rsRetVal localRet;

	ISOBJ_TYPE_assert(pThis, tcpsrv);

	/* check if we need to start the worker pool. Once it is running, all is
	 * well. Shutdown is done on modExit.
	 */
	d_pthread_mutex_lock(&wrkrMut);
	if(!bWrkrRunning) {
		bWrkrRunning = 1;
		startWorkerPool();
	}
	d_pthread_mutex_unlock(&wrkrMut);

	/* this is an endless loop - it is terminated by the framework canelling
	 * this thread. Thus, we also need to instantiate a cancel cleanup handler
	 * to prevent us from leaking anything. -- rgerhards, 20080-04-24
	 */
	if((localRet = nspoll.Construct(&pPoll)) == RS_RET_OK) {
		if(pThis->pszDrvrName != NULL)
			CHKiRet(nspoll.SetDrvrName(pPoll, pThis->pszDrvrName));
		localRet = nspoll.ConstructFinalize(pPoll);
	}
	if(localRet != RS_RET_OK) {
		/* fall back to select */
		DBGPRINTF("tcpsrv could not use epoll() interface, iRet=%d, using select()\n", localRet);
		iRet = RunSelect(pThis, workset, sizeof(workset)/sizeof(nsd_epworkset_t));
		FINALIZE;
	}

	DBGPRINTF("tcpsrv uses epoll() interface, nsdpoll driver found\n");

	/* flag that we are in epoll mode */
	pThis->bUsingEPoll = RSTRUE;

	/* Add the TCP listen sockets to the list of sockets to monitor */
	for(i = 0 ; i < pThis->iLstnCurr ; ++i) {
		DBGPRINTF("Trying to add listener %d, pUsr=%p\n", i, pThis->ppLstn);
		CHKiRet(nspoll.Ctl(pPoll, pThis->ppLstn[i], i, pThis->ppLstn, NSDPOLL_IN, NSDPOLL_ADD));
		DBGPRINTF("Added listener %d\n", i);
	}

	while(1) {
		numEntries = sizeof(workset)/sizeof(nsd_epworkset_t);
		localRet = nspoll.Wait(pPoll, -1, &numEntries, workset);
		if(glbl.GetGlobalInputTermState() == 1)
			break; /* terminate input! */

		/* check if we need to ignore the i/o ready state. We do this if we got an invalid
		 * return state. Validly, this can happen for RS_RET_EINTR, for other cases it may
		 * not be the right thing, but what is the right thing is really hard at this point...
		 */
		if(localRet != RS_RET_OK)
			continue;

		processWorkset(pThis, pPoll, numEntries, workset);
	}

	/* remove the tcp listen sockets from the epoll set */
	for(i = 0 ; i < pThis->iLstnCurr ; ++i) {
		CHKiRet(nspoll.Ctl(pPoll, pThis->ppLstn[i], i, pThis->ppLstn, NSDPOLL_IN, NSDPOLL_DEL));
	}

finalize_it:
	if(pPoll != NULL)
		nspoll.Destruct(&pPoll);
	RETiRet;
}
Beispiel #8
0
/* cancellation cleanup handler - frees provided mutex
 * rgerhards, 2008-01-14
 */
void
mutexCancelCleanup(void *arg)
{
	assert(arg != NULL);
	d_pthread_mutex_unlock((pthread_mutex_t*) arg);
}
Beispiel #9
0
rsRetVal
wtiWorker(wti_t *pThis)
{
	wtp_t *pWtp;		/* our worker thread pool */
	int bInactivityTOOccured = 0;
	rsRetVal localRet;
	rsRetVal terminateRet;
	int iCancelStateSave;
	DEFiRet;

	ISOBJ_TYPE_assert(pThis, wti);
	pWtp = pThis->pWtp; /* shortcut */
	ISOBJ_TYPE_assert(pWtp, wtp);

	dbgSetThrdName(pThis->pszDbgHdr);
	pthread_cleanup_push(wtiWorkerCancelCleanup, pThis);
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave);

	/* now we have our identity, on to real processing */
	while(1) { /* loop will be broken below - need to do mutex locks */
		if(pWtp->pfRateLimiter != NULL) { /* call rate-limiter, if defined */
			pWtp->pfRateLimiter(pWtp->pUsr);
		}
		
		d_pthread_mutex_lock(pWtp->pmutUsr);

		/* first check if we are in shutdown process (but evaluate a bit later) */
		terminateRet = wtpChkStopWrkr(pWtp, MUTEX_ALREADY_LOCKED);
		if(terminateRet == RS_RET_TERMINATE_NOW) {
			/* we now need to free the old batch */
			localRet = pWtp->pfObjProcessed(pWtp->pUsr, pThis);
			dbgoprint((obj_t*) pThis, "terminating worker because of TERMINATE_NOW mode, del iRet %d\n",
				 localRet);
			d_pthread_mutex_unlock(pWtp->pmutUsr);
			break;
		}

		/* try to execute and process whatever we have */
		/* Note that this function releases and re-aquires the mutex. The returned
		 * information on idle state must be processed before releasing the mutex again.
		 */
		localRet = pWtp->pfDoWork(pWtp->pUsr, pThis);

		if(localRet == RS_RET_IDLE) {
			if(terminateRet == RS_RET_TERMINATE_WHEN_IDLE || bInactivityTOOccured) {
				d_pthread_mutex_unlock(pWtp->pmutUsr);
				dbgoprint((obj_t*) pThis, "terminating worker terminateRet=%d, bInactivityTOOccured=%d\n",
					  terminateRet, bInactivityTOOccured);
				break;	/* end of loop */
			}
			doIdleProcessing(pThis, pWtp, &bInactivityTOOccured);
			d_pthread_mutex_unlock(pWtp->pmutUsr);
			continue; /* request next iteration */
		}

		d_pthread_mutex_unlock(pWtp->pmutUsr);

		bInactivityTOOccured = 0; /* reset for next run */
	}

	/* indicate termination */
	pthread_cleanup_pop(0); /* remove cleanup handler */
	pthread_setcancelstate(iCancelStateSave, NULL);

	RETiRet;
}