/* 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; }
/* 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; }
/* 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 }
/* 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); }
/* 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; }
/* 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); }
/* 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; }
/* 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); }
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; }