rem: jobPtr = NewJob((itPtr->servPtr ? itPtr->servPtr->server : NULL), job_type, objv[3]);
context: NsTclJobObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv){    NsInterp            *itPtr = arg;    Queue               *queuePtr;    Job                 *jobPtr = NULL, **nextPtrPtr;    int                 code, new, create = 0, max;    char                *job_id = NULL, buf[20], *queue_name;    Tcl_HashEntry       *hPtr;    Tcl_HashSearch      search;    static CONST char *opts[] = {        "cancel", "create", "jobs", "job_list", "queue", "queues", "queue_list",        "wait", "wait_any",  NULL    };    enum {        JCancelIdx, JCreateIdx, JJobsIdx, JJobsListIdx,        JQueueIdx, JQueuesIdx, JQueueListIdx, JWaitIdx, JWaitAnyIdx    } 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:        {            /*             * Create a new thread pool queue.             */            if (objc != 3 && objc != 4) {                Tcl_WrongNumArgs(interp, 2, objv, "queue_name ?maxthreads?");                return TCL_ERROR;            }            max = 4;            if (objc == 4 && Tcl_GetIntFromObj(interp, objv[3], &max) != TCL_OK) {                return TCL_ERROR;            }            queue_name = Tcl_GetString(objv[2]);            Ns_MutexLock(&queuelock);            hPtr = Tcl_CreateHashEntry(&queues, queue_name, &new);            if (new) {                queuePtr = NewQueue(Tcl_GetHashKey(&queues, hPtr), max);                Tcl_SetHashValue(hPtr, queuePtr);            }            Ns_MutexUnlock(&queuelock);            if (!new) {                Tcl_AppendResult(interp, "queue already exists: ", queue_name, NULL);                return TCL_ERROR;            }            SetQueueInternalRep(objv[2], queuePtr);            Tcl_SetObjResult(interp, objv[2]);        }        break;        case JQueueIdx:        {            /*             * Add a new job the specified queue.             */            int job_type;            if ((objc != 4) && (objc != 5)) {                Tcl_WrongNumArgs(interp, 2, objv, "queue_name script ?detached?");                return TCL_ERROR;            }            if (GetQueueFromObj(interp, objv[2], &queuePtr) != TCL_OK) {                return TCL_ERROR;            }            job_type = JOB_NON_DETACHED;            if ((objc == 5) && Tcl_GetIntFromObj(interp, objv[4], &job_type) != TCL_OK) {                return TCL_ERROR;            }                    Ns_MutexLock(&queuePtr->lock);            if (queuePtr->req == QUEUE_REQ_STOP) {                            Tcl_AppendResult(interp,                                 "The specified queue is going away. "                                 "Queue Name : ", queuePtr->name, NULL);                Ns_MutexUnlock(&queuePtr->lock);                return TCL_ERROR;            }            jobPtr = NewJob((itPtr->servPtr ? itPtr->servPtr->server : NULL),                            job_type,                            objv[3]);            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;            }            job_id = buf;            do {                sprintf(job_id, "job%d", queuePtr->nextid++);                hPtr = Tcl_CreateHashEntry(&queuePtr->jobs, job_id, &new);            } while (!new);            Tcl_DStringAppend(&jobPtr->id, job_id, -1);            Tcl_SetHashValue(hPtr, jobPtr);            Ns_CondBroadcast(&queuePtr->cond);            Ns_MutexUnlock(&queuePtr->lock);            if (create) {                Ns_ThreadCreate(JobThread, queuePtr, 0, NULL);            }            Tcl_SetResult(interp, job_id, TCL_VOLATILE);        }        break;        case JWaitIdx:        {            /*             * Wait for the specified job.             */            if (objc != 4) {                Tcl_WrongNumArgs(interp, 2, objv, "queue_name id");                return TCL_ERROR;            }            if (GetQueueFromObj(interp, objv[2], &queuePtr) != TCL_OK) {                return TCL_ERROR;            }            job_id = Tcl_GetString(objv[3]);            Ns_MutexLock(&queuePtr->lock);            hPtr = Tcl_FindHashEntry(&queuePtr->jobs, job_id);            if (hPtr == NULL) {                Ns_MutexUnlock(&queuePtr->lock);                Tcl_AppendResult(interp, "no such job: ", job_id, NULL);                return TCL_ERROR;            }            jobPtr = Tcl_GetHashValue(hPtr);            if (jobPtr->type == JOB_DETACHED) {                Tcl_AppendResult(interp,                                 "Cannot wait on a detached job. Job ID :",                                 Tcl_DStringValue(&jobPtr->id), NULL);                Ns_MutexUnlock(&queuePtr->lock);                return TCL_ERROR;            }                    if (jobPtr->req == JOB_CANCEL) {                Tcl_AppendResult(interp,                                 "Cannot wait on a cancelled job.. Job ID :",                                 Tcl_DStringValue(&jobPtr->id), NULL);                Ns_MutexUnlock(&queuePtr->lock);                return TCL_ERROR;            }            if (jobPtr->req == JOB_WAIT) {                Tcl_AppendResult(interp,                                 "Some thread is already waiting for "                                 "the specified job. Job ID :",                                 Tcl_DStringValue(&jobPtr->id), NULL);                Ns_MutexUnlock(&queuePtr->lock);                return TCL_ERROR;            }                    jobPtr->req = JOB_WAIT;                    while (jobPtr->state != JOB_DONE) {                Ns_CondWait(&queuePtr->cond, &queuePtr->lock);            }            /*             * The following is a sanity check to ensure that the no             * other process removed this job's entry.             */            hPtr = Tcl_FindHashEntry(&queuePtr->jobs, job_id);            assert(hPtr != NULL);            assert(jobPtr == Tcl_GetHashValue(hPtr));            /*             * At this point the job we were waiting on has completed, so we return             * the job's results and errorcodes, then clean up the job.             */            Ns_MutexUnlock(&queuePtr->lock);            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;            if (hPtr != NULL) {                Tcl_DeleteHashEntry(hPtr);                            }            FreeJob(jobPtr);        }        break;        case JCancelIdx:        {            /*             * Cancel the specified job.             */            if (objc != 4) {                Tcl_WrongNumArgs(interp, 2, objv, "queue_name id");                return TCL_ERROR;            }            if (GetQueueFromObj(interp, objv[2], &queuePtr) != TCL_OK) {                return TCL_ERROR;            }            job_id = Tcl_GetString(objv[3]);            Ns_MutexLock(&queuePtr->lock);            hPtr = Tcl_FindHashEntry(&queuePtr->jobs, job_id);            if (hPtr == NULL) {                Ns_MutexUnlock(&queuePtr->lock);                Tcl_AppendResult(interp, "no such job: ", job_id, NULL);                return TCL_ERROR;            }            jobPtr = Tcl_GetHashValue(hPtr);            if (jobPtr->req == JOB_WAIT) {                Tcl_AppendResult(interp,                                 "Can not cancel this job because someone"                                 " is waiting on it. Job ID: ",                                 Tcl_DStringValue(&jobPtr->id), NULL);                            Ns_MutexUnlock(&queuePtr->lock);                return TCL_ERROR;            }                            if (jobPtr->state != JOB_RUNNING) {                Tcl_DeleteHashEntry(hPtr);            }            jobPtr->req = JOB_CANCEL;            Ns_MutexUnlock(&queuePtr->lock);            Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (jobPtr->state == JOB_RUNNING));        }        break;        case JWaitAnyIdx:        {            /*             * Wait for any job on the queue complete.             */            if (objc != 3) {                Tcl_WrongNumArgs(interp, 2, objv, "queue_name");                return TCL_ERROR;            }                   if (GetQueueFromObj(interp, objv[2], &queuePtr) != TCL_OK) {                return TCL_ERROR;            }            Ns_MutexLock(&queuePtr->lock);            /*              * While there are jobs in queue or no jobs are "done", wait             * on the queue condition variable.              */            while ((Tcl_FirstHashEntry(&queuePtr->jobs, &search) != NULL) &&                   (!AnyDone(queuePtr))) {                Ns_CondWait(&queuePtr->cond, &queuePtr->lock);            }            Ns_MutexUnlock(&queuePtr->lock);        }        break;        case JJobsIdx:        {            /*             * Returns a list of job "results" in arbitrary order. (I don't             * see how this function is useful.             */            if (objc != 3) {                Tcl_WrongNumArgs(interp, 2, objv, "queue_name");                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);                Tcl_AppendElement(interp, jobPtr->ds.string);                hPtr = Tcl_NextHashEntry(&search);            }            Ns_MutexUnlock(&queuePtr->lock);        }        break;        case JQueuesIdx:        {            /*             * Returns a list of the current queues.             */            hPtr = Tcl_FirstHashEntry(&queues, &search);            while (hPtr != NULL) {                queuePtr = Tcl_GetHashValue(hPtr);                Tcl_AppendElement(interp, queuePtr->name);                hPtr = Tcl_NextHashEntry(&search);            }        }        break;         case JJobsListIdx:        {            /*             * Returns a list of all the jobs in the queue. The "job" consists of:             *             * Job ID             * Job State (Scheduled, Running, or Done)             * Job Results (or job script, if job has not yet completed).             * Job Code (TCL_OK, TCL_ERROR, TCL_RETURN, TCL_BREAK, TCL_CONTINE)             * Job Running Time (TBD)             */            Tcl_Obj     *jobList, *jobFieldList;            char        *jobId, *jobState, *jobCode, *jobType, *jobResults, *jobReq;            if (objc != 3) {                Tcl_WrongNumArgs(interp, 2, objv, "queue_name");                return TCL_ERROR;            }            if (GetQueueFromObj(interp, objv[2], &queuePtr) != TCL_OK) {                return TCL_ERROR;            }            Ns_MutexLock(&queuePtr->lock);            /* Create a Tcl List to hold the list of jobs. */            jobList = Tcl_NewListObj(0, NULL);            hPtr = Tcl_FirstHashEntry(&queuePtr->jobs, &search);            while (hPtr != NULL) {                jobPtr = Tcl_GetHashValue(hPtr);                jobId = Tcl_GetHashKey(&queuePtr->jobs, hPtr);                jobCode = GetJobCodeStr(jobPtr->code);                jobState = GetJobStateStr(jobPtr->state);                jobType = GetJobTypeStr(jobPtr->type);                jobReq = GetJobReqStr(jobPtr->req);                jobResults = Tcl_DStringValue(&jobPtr->ds);                /* Create a Tcl List to hold the list of job fields. */                jobFieldList = Tcl_NewListObj(0, NULL);                /* Add Job ID */                if (AppendFieldEntry(interp, jobFieldList, "ID", jobId) != TCL_OK)                {                    /* AppendFieldEntry sets results if an error occurs. */                    Tcl_DecrRefCount(jobList);                    Tcl_DecrRefCount(jobFieldList);                    Ns_MutexUnlock(&queuePtr->lock);                    return TCL_ERROR;                }                /* Add Job State */                if (AppendFieldEntry(interp, jobFieldList, "STATE", jobState) != TCL_OK) {                    Tcl_DecrRefCount(jobList);                    Tcl_DecrRefCount(jobFieldList);                    Ns_MutexUnlock(&queuePtr->lock);                    return TCL_ERROR;                }                /* Add Job Results */                if (AppendFieldEntry(interp, jobFieldList, "RESULTS", jobResults) != TCL_OK)                {                    Tcl_DecrRefCount(jobList);                    Tcl_DecrRefCount(jobFieldList);                    Ns_MutexUnlock(&queuePtr->lock);                    return TCL_ERROR;                }                /* Add Job Code */                if (AppendFieldEntry(interp, jobFieldList, "CODE", jobCode) != TCL_OK)                {                    Tcl_DecrRefCount(jobList);                    Tcl_DecrRefCount(jobFieldList);                    Ns_MutexUnlock(&queuePtr->lock);                    return TCL_ERROR;                }                /* Add Job Type */                if (AppendFieldEntry(interp, jobFieldList, "TYPE", jobType) != TCL_OK)                {                    Tcl_DecrRefCount(jobList);                    Tcl_DecrRefCount(jobFieldList);                    Ns_MutexUnlock(&queuePtr->lock);                    return TCL_ERROR;                }                /* Add Job Request */                if (AppendFieldEntry(interp, jobFieldList, "REQ", jobReq) != TCL_OK)                {                    Tcl_DecrRefCount(jobList);                    Tcl_DecrRefCount(jobFieldList);                    Ns_MutexUnlock(&queuePtr->lock);                    return TCL_ERROR;                }                /* Add the job to the job list */                if (Tcl_ListObjAppendElement(interp, jobList, jobFieldList) != TCL_OK) {                    Tcl_DecrRefCount(jobList);                    Tcl_DecrRefCount(jobFieldList);                    Ns_MutexUnlock(&queuePtr->lock);                    return TCL_ERROR;                }                hPtr = Tcl_NextHashEntry(&search);            }            Tcl_SetObjResult(interp, jobList);            Ns_MutexUnlock(&queuePtr->lock);        }        break;        case JQueueListIdx:        {            /*             * Returns a list of all the queues and the queue information.             */            Tcl_Obj     *queueList, *queueFieldList;            char        *queueReq;            /* Create a Tcl List to hold the list of jobs. */            queueList = Tcl_NewListObj(0, NULL);            Ns_MutexLock(&queuelock);            hPtr = Tcl_FirstHashEntry(&queues, &search);            while (hPtr != NULL) {                queuePtr = Tcl_GetHashValue(hPtr);                /* Create a Tcl List to hold the list of queue fields. */                queueFieldList = Tcl_NewListObj(0, NULL);                queueReq = GetQueueReqStr(queuePtr->req);                /* Add queue name */                if (AppendFieldEntry(interp, queueFieldList,                                     "NAME", queuePtr->name) != TCL_OK)                {                    /* AppendFieldEntry sets results if an error occurs. */                    Tcl_DecrRefCount(queueList);                    Tcl_DecrRefCount(queueFieldList);                    Ns_MutexUnlock(&queuelock);                    return TCL_ERROR;                }                /* Add max threads */                if (AppendFieldEntryInt(interp, queueFieldList,                                        "MAX_THREADS", queuePtr->maxthreads) != TCL_OK)                {                    Tcl_DecrRefCount(queueList);                    Tcl_DecrRefCount(queueFieldList);                    Ns_MutexUnlock(&queuelock);                    return TCL_ERROR;                }                /* Add number of current threads */                if (AppendFieldEntryInt(interp, queueFieldList,                                        "NUM_THREADS", queuePtr->nthreads) != TCL_OK)                {                    Tcl_DecrRefCount(queueList);                    Tcl_DecrRefCount(queueFieldList);                    Ns_MutexUnlock(&queuelock);                    return TCL_ERROR;                }                /* Add number idle */                if (AppendFieldEntryInt(interp, queueFieldList,                                        "NUM_IDLE", queuePtr->nidle) != TCL_OK)                {                    Tcl_DecrRefCount(queueList);                    Tcl_DecrRefCount(queueFieldList);                    Ns_MutexUnlock(&queuelock);                    return TCL_ERROR;                }                /* Add queue req */                if (AppendFieldEntry(interp, queueFieldList,                                     "REQ", queueReq) != TCL_OK)                {                    Tcl_DecrRefCount(queueList);                    Tcl_DecrRefCount(queueFieldList);                    Ns_MutexUnlock(&queuelock);                    return TCL_ERROR;                }                /* Add the job to the job list */                if (Tcl_ListObjAppendElement(interp, queueList,                                             queueFieldList) != TCL_OK) {                    Tcl_DecrRefCount(queueList);                    Tcl_DecrRefCount(queueFieldList);                    Ns_MutexUnlock(&queuelock);                    return TCL_ERROR;                }                hPtr = Tcl_NextHashEntry(&search);            }            Tcl_SetObjResult(interp, queueList);            Ns_MutexUnlock(&queuelock);        }        break;     }    return code;}