rem: Job *jobPtr, **nextPtrPtr; int code, new, create, running, max; char *id, buf[20], *queue;
add: Job *jobPtr = NULL, **nextPtrPtr; int code, new, create = 0, running = 0, max; char *id = NULL, buf[20], *queue;
context: static Ns_ThreadProc JobThread;/* *---------------------------------------------------------------------- * * NsInitTclQueueType -- * *	Initialize the Tcl job queue. * * Results: *	None. * * Side effects: *	None. * *---------------------------------------------------------------------- */voidNsTclInitQueueType(void){    Tcl_RegisterObjType(&queueType);    Tcl_InitHashTable(&queues, TCL_STRING_KEYS);}/* *---------------------------------------------------------------------- * * NsStartJobsShutdown -- * *	Signal stop of the Tcl job threads. * * Results: *	None. * * Side effects: *	All pending jobs are cancelled and waiting threads interrupted. * *---------------------------------------------------------------------- */voidNsStartJobsShutdown(void){    Tcl_HashSearch search;    Tcl_HashEntry *hPtr;    Queue *queuePtr;    hPtr = Tcl_FirstHashEntry(&queues, &search);    while (hPtr != NULL) {    	queuePtr = Tcl_GetHashValue(hPtr);	Ns_MutexLock(&queuePtr->lock);    	queuePtr->stopping = 1;    	Ns_CondBroadcast(&queuePtr->cond);    	Ns_MutexUnlock(&queuePtr->lock);	hPtr = Tcl_NextHashEntry(&search);    }}/* *---------------------------------------------------------------------- * * NsWaitJobsShutdown -- * *	Wait for Tcl job threads to exit. * * Results: *	None. * * Side effects: *	None. * *---------------------------------------------------------------------- */voidNsWaitJobsShutdown(Ns_Time *toPtr){    Tcl_HashSearch search;    Tcl_HashEntry *hPtr;    Queue *queuePtr;    int status = NS_OK;    hPtr = Tcl_FirstHashEntry(&queues, &search);    while (status == NS_OK && hPtr != NULL) {    	queuePtr = Tcl_GetHashValue(hPtr);	Ns_MutexLock(&queuePtr->lock);    	while (status == NS_OK && queuePtr->nthreads > 0) {	    status = Ns_CondTimedWait(&queuePtr->cond, &queuePtr->lock, toPtr);    	}    	Ns_MutexUnlock(&queuePtr->lock);	hPtr = Tcl_NextHashEntry(&search);    }    if (status != NS_OK) {	Ns_Log(Warning, "tcljobs: timeout waiting for exit");    }}/* *---------------------------------------------------------------------- * * NsTclJobCmd -- * *	Implement the ns_job command to manage background tasks. * * Results: *	Standard Tcl result. * * Side effects: *	Jobs may be queued to run in another thread. * *---------------------------------------------------------------------- */static voidAppendJob(Tcl_Interp *interp, Job *jobPtr){    Tcl_AppendElement(interp, jobPtr->ds.string);}intNsTclJobObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv){    NsInterp *itPtr = arg;    Queue *queuePtr;    Job *jobPtr, **nextPtrPtr;    int code, new, create, running, max;    char *id, buf[20], *queue;    Tcl_HashEntry *hPtr;    Tcl_HashSearch search;    static CONST char *opts[] = {        "cancel", "create", "jobs", "queue", "queues", "wait", NULL    };    enum {        JCancelIdx, JCreateIdx, JJobsIdx, JQueueIdx, JQueuesIdx, JWaitIdx     } opt;    if (objc < 2) {	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?");	return TCL_ERROR;    }    if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0,                            (int *) &opt) != TCL_OK) {        return TCL_ERROR;    }    code = TCL_OK;    switch (opt) {    case JCreateIdx:	if (objc != 3 && objc != 4) {	    Tcl_WrongNumArgs(interp, 2, objv, "queue ?maxthreads?");	    return TCL_ERROR;	}	max = 4;	if (objc == 4 && Tcl_GetIntFromObj(interp, objv[3], &max) != TCL_OK) {	    return TCL_ERROR;	}	queue = Tcl_GetString(objv[2]);	Ns_MutexLock(&queuelock);	hPtr = Tcl_CreateHashEntry(&queues, queue, &new);	if (new) {	    queuePtr = ns_calloc(1, sizeof(Queue));	    queuePtr->name = Tcl_GetHashKey(&queues, hPtr);	    queuePtr->maxthreads = max;	    Ns_MutexSetName2(&queuePtr->lock, "tcljob", queue);	    Tcl_InitHashTable(&queuePtr->jobs, TCL_STRING_KEYS);	    Tcl_SetHashValue(hPtr, queuePtr);	}	Ns_MutexUnlock(&queuelock);	if (!new) {	    Tcl_AppendResult(interp, "queue already exists: ", queue, NULL);	    return TCL_ERROR;	}	SetQueueInternalRep(objv[2], queuePtr);	Tcl_SetObjResult(interp, objv[2]);	break;    case JQueueIdx:	if (objc != 4) {	    Tcl_WrongNumArgs(interp, 2, objv, "queue script");	    return TCL_ERROR;	}	if (GetQueueFromObj(interp, objv[2], &queuePtr) != TCL_OK) {	    return TCL_ERROR;	}	jobPtr = ns_malloc(sizeof(Job));	jobPtr->flags = jobPtr->code = 0;	jobPtr->server = (itPtr->servPtr ? itPtr->servPtr->server : NULL);	jobPtr->nextPtr = NULL;	jobPtr->errorCode = jobPtr->errorInfo = NULL;	Tcl_DStringInit(&jobPtr->ds);	Tcl_DStringAppend(&jobPtr->ds, Tcl_GetString(objv[3]), -1);	Ns_MutexLock(&queuePtr->lock);	if (!queuePtr->stopping) {	    nextPtrPtr = &queuePtr->firstPtr;	    while (*nextPtrPtr != NULL) {		nextPtrPtr = &((*nextPtrPtr)->nextPtr);	    }	    *nextPtrPtr = jobPtr;	    if (queuePtr->nidle == 0 && queuePtr->nthreads < queuePtr->maxthreads) {		create = 1;		++queuePtr->nthreads;	    } else {		create = 0;	    }	    id = buf;	    do {		sprintf(id, "job%d", queuePtr->nextid++);		hPtr = Tcl_CreateHashEntry(&queuePtr->jobs, id, &new);	    } while (!new);	    Tcl_SetHashValue(hPtr, jobPtr);	    Ns_CondBroadcast(&queuePtr->cond);	    jobPtr = NULL;	}	Ns_MutexUnlock(&queuePtr->lock);	if (jobPtr != NULL) {	    FreeJob(jobPtr);	    Tcl_SetResult(interp, "server stopping", TCL_STATIC);	    return TCL_ERROR;	}	if (create) {	    Ns_ThreadCreate(JobThread, queuePtr, 0, NULL);	}	Tcl_SetResult(interp, id, TCL_VOLATILE);	break;    case JWaitIdx:    case JCancelIdx:	if (objc != 4) {	    Tcl_WrongNumArgs(interp, 2, objv, "queue id");	    return TCL_ERROR;	}	if (GetQueueFromObj(interp, objv[2], &queuePtr) != TCL_OK) {	    return TCL_ERROR;	}	id = Tcl_GetString(objv[3]);	Ns_MutexLock(&queuePtr->lock);	hPtr = Tcl_FindHashEntry(&queuePtr->jobs, id);	if (hPtr != NULL) {	    jobPtr = Tcl_GetHashValue(hPtr);	    Tcl_DeleteHashEntry(hPtr);	    if (opt == JCancelIdx) {		jobPtr->flags |= JOB_CANCEL;		running = jobPtr->flags & JOB_RUNNING;	    } else {		while (!(jobPtr->flags & JOB_DONE)) {		    Ns_CondWait(&queuePtr->cond, &queuePtr->lock);		}	    }	}	Ns_MutexUnlock(&queuePtr->lock);	if (hPtr == NULL) {	    Tcl_AppendResult(interp, "no such job: ", id, NULL);	    return TCL_ERROR;	}	if (opt == JCancelIdx) {	    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), running);	} else {	    Tcl_DStringResult(interp, &jobPtr->ds);	    if (jobPtr->errorCode != NULL) {		Tcl_SetVar(interp, "errorCode", jobPtr->errorCode, TCL_GLOBAL_ONLY);	    }	    if (jobPtr->errorInfo != NULL) {	 	Tcl_SetVar(interp, "errorInfo", jobPtr->errorInfo, TCL_GLOBAL_ONLY);	    }	    code = jobPtr->code;	    FreeJob(jobPtr);	}	break;    case JJobsIdx:	if (objc != 3) {	    Tcl_WrongNumArgs(interp, 2, objv, "queue id");	    return TCL_ERROR;	}	if (GetQueueFromObj(interp, objv[2], &queuePtr) != TCL_OK) {	    return TCL_ERROR;	}	Ns_MutexLock(&queuePtr->lock);	hPtr = Tcl_FirstHashEntry(&queuePtr->jobs, &search);	while (hPtr != NULL) {	    jobPtr = Tcl_GetHashValue(hPtr);	    AppendJob(interp, jobPtr);	    hPtr = Tcl_NextHashEntry(&search);	}	Ns_MutexUnlock(&queuePtr->lock);	break;    case JQueuesIdx:        hPtr = Tcl_FirstHashEntry(&queues, &search);        while (hPtr != NULL) {            queuePtr = Tcl_GetHashValue(hPtr);            Tcl_AppendElement(interp, queuePtr->name);            hPtr = Tcl_NextHashEntry(&search);        }        break;     }        return code;}/* *---------------------------------------------------------------------- * * JobThread -- * *	Background thread for the ns_job command. * * Results: *	None. * * Side effects: *	Jobs will be run from the queue. * *---------------------------------------------------------------------- */static voidJobThread(void *arg){    Tcl_Interp *interp;    Job *jobPtr;    Queue *queuePtr = arg;    char buf[100];    CONST char *err;    Ns_WaitForStartup();    Ns_MutexLock(&queuePtr->lock);    sprintf(buf, "-tcljob%d-", queuePtr->nthreads);    Ns_ThreadSetName(buf);    Ns_Log(Notice, "starting");    while (1) {	++queuePtr->nidle;	while (queuePtr->firstPtr == NULL && !queuePtr->stopping) {	    Ns_CondWait(&queuePtr->cond, &queuePtr->lock);	}	--queuePtr->nidle;	if (queuePtr->firstPtr == NULL) {	    break;	}	jobPtr = queuePtr->firstPtr;	queuePtr->firstPtr = jobPtr->nextPtr;	jobPtr->flags |= JOB_RUNNING;	if (jobPtr->flags & JOB_CANCEL) {	    FreeJob(jobPtr);	    continue;	}	Ns_MutexUnlock(&queuePtr->lock);	interp = Ns_TclAllocateInterp(jobPtr->server);	jobPtr->code = Tcl_EvalEx(interp, jobPtr->ds.string, -1, 0);	Tcl_DStringTrunc(&jobPtr->ds, 0);	Tcl_DStringAppend(&jobPtr->ds, Tcl_GetStringResult(interp), -1);	err = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY);	if (err != NULL) {	    jobPtr->errorCode = ns_strdup(err);	}	err = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);	if (err != NULL) {	    jobPtr->errorInfo = ns_strdup(err);	}	Ns_TclDeAllocateInterp(interp);	Ns_MutexLock(&queuePtr->lock);	jobPtr->flags |= JOB_DONE;	if (jobPtr->flags & JOB_CANCEL) {	    FreeJob(jobPtr);	} else {	    Ns_CondBroadcast(&queuePtr->cond);	}    }    --queuePtr->nthreads;    Ns_CondBroadcast(&queuePtr->cond);    Ns_MutexUnlock(&queuePtr->lock);    Ns_Log(Notice, "exiting");}