There are no available options for this view.

Parent Directory Parent Directory | Revision <a href="/cvs/aolserver/aolserver/nsd/log.c#A_Log">Log</a> Revision <a href="/cvs/aolserver/aolserver/nsd/log.c#A_Log">Log</a>

Revision 1.31 - (show annotations) (download) (as text)
Tue Aug 23 21:41:31 2005 UTC (12 years, 4 months ago) by jgdavidson
Branch: MAIN
CVS Tags: aolserver_v45_r0, aolserver_v45_r2_rc0, HEAD
Branch point for: aolserver_v45_r1, aolserver_v45_r2, aolserver_v45_bp
Changes since 1.30: +2 -2 lines
File MIME type: text/x-chdr
Applied patches from Stephen Deasey for better compile time error checking.
1 /*
2 * The contents of this file are subject to the AOLserver Public License
3 * Version 1.1 (the "License"); you may not use this file except in
4 * compliance with the License. You may obtain a copy of the License at
5 * http://aolserver.com/.
6 *
7 * Software distributed under the License is distributed on an "AS IS"
8 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9 * the License for the specific language governing rights and limitations
10 * under the License.
11 *
12 * The Original Code is AOLserver Code and related documentation
13 * distributed by AOL.
14 *
15 * The Initial Developer of the Original Code is America Online,
16 * Inc. Portions created by AOL are Copyright (C) 1999 America Online,
17 * Inc. All Rights Reserved.
18 *
19 * Alternatively, the contents of this file may be used under the terms
20 * of the GNU General Public License (the "GPL"), in which case the
21 * provisions of GPL are applicable instead of those above. If you wish
22 * to allow use of your version of this file only under the terms of the
23 * GPL and not to allow others to use your version of this file under the
24 * License, indicate your decision by deleting the provisions above and
25 * replace them with the notice and other provisions required by the GPL.
26 * If you do not delete the provisions above, a recipient may use your
27 * version of this file under either the License or the GPL.
28 */
29
30
31 /*
32 * tcljob.c --
33 *
34 * Tcl job queueing routines.
35 *
36 * Lock rules:
37 *
38 * Lock the queuelock when modifing tp structure elements.
39 *
40 * Lock the queue's lock when modifing queue structure elements.
41 *
42 * Jobs are shared between tp and the queue but are owned by the queue,
43 * so use queue's lock is used to control access to the jobs.
44 *
45 * To avoid deadlock, when locking both the queuelock and queue's
46 * lock lock the queuelock first.
47 *
48 *
49 * To avoid deadlock, the tp queuelock should be locked before the
50 * queue's lock.
51 *
52 *
53 * Notes:
54 *
55 * The threadpool's max number of thread is the sum of all the current
56 * queue's max threads.
57 *
58 * The number of threads in the thread pool can be greater than
59 * the current max number of threads. This situtation can occur when
60 * a queue is deleted. Later on if a new queue is created it will simply
61 * use one of the previously created threads. Basically the number of
62 * threads is a "high water mark".
63 *
64 * The queues are reference counted. Only when a queue is empty and
65 * its reference count is zero can it be deleted.
66 *
67 * We can no longer use a Tcl_Obj to represent the queue because queues can
68 * now be deleted. Tcl_Objs are deleted when the object goes out of
69 * scope, whereas queues are deleted when delete is called. By doing
70 * this the queue can be used across tcl interpreters.
71 *
72 * ToDo:
73 *
74 * Users can leak queues. A queue will stay around until a user
75 * cleans it up. It order to help the user out we would like to
76 * add an "-autoclean" option to queue create function. However,
77 * AOLServer does not currently supply a "good" connection cleanup
78 * callback. We tryed to use "<a href="/cvs/aolserver/aolserver/nsd/filter.c#A_Ns_RegisterConnCleanup">Ns_RegisterConnCleanup</a>" however it does
79 * not have a facility to remove registered callbacks.
80 *
81 */
82
83 static const char *RCSID = "@(#) $Header: /cvsroot-fuse/aolserver/aolserver/nsd/tcljob.c,v 1.31 2005/08/23 21:41:31 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__;
84
85 #include "nsd.h"
86
87 /*
88 * Default Max Threads
89 * - If a user does not specify the a max number of threads for a queue,
90 * then the following default is used.
91 */
92 #define NS_JOB_DEFAULT_MAXTHREADS 4
93
94 typedef enum JobStates {
95 JOB_SCHEDULED = 0,
96 JOB_RUNNING,
97 JOB_DONE
98 } JobStates;
99
100 typedef enum JobTypes {
101 JOB_NON_DETACHED = 0,
102 JOB_DETACHED
103 } JobTypes;
104
105 typedef enum JobRequests {
106 JOB_NONE = 0,
107 JOB_WAIT,
108 JOB_CANCEL
109 } JobRequests;
110
111 typedef enum QueueRequests {
112 QUEUE_REQ_NONE = 0,
113 QUEUE_REQ_DELETE
114 } QueueRequests;
115
116 typedef enum ThreadPoolRequests {
117 THREADPOOL_REQ_NONE = 0,
118 THREADPOOL_REQ_STOP
119 } ThreadPoolRequests;
120
121
122 /*
123 * Job structure. Jobs are enqueued on queues.
124 */
125
126 typedef struct Job {
127 struct Job *nextPtr;
128 char *server;
129 JobStates state;
130 int code;
131 JobTypes type;
132 JobRequests req;
133 char *errorCode;
134 char *errorInfo;
135 char *queueId;
136 Tcl_DString id;
137 Tcl_DString script;
138 Tcl_DString results;
139 Ns_Time startTime;
140 Ns_Time endTime;
141 } Job;
142
143 /*
144 * <a href="/cvs/aolserver/aolserver/nsd/sockcallback.c#A_Queue">Queue</a> structure. A queue manages a set of jobs.
145 */
146
147 typedef struct JobQueue {
148 char *name;
149 char *desc;
150 Ns_Mutex lock;
151 Ns_Cond cond;
152 unsigned int nextid;
153 QueueRequests req;
154 int maxThreads;
155 int nRunning;
156 Tcl_HashTable jobs;
157 int refCount;
158 } JobQueue;
159
160 /*
161 * Thread pool structure. ns_job mananges a global set of threads.
162 */
163
164 typedef struct ThreadPool {
165 Ns_Cond cond;
166 Ns_Mutex queuelock;
167 Tcl_HashTable queues;
168 ThreadPoolRequests req;
169 int nextThreadId;
170 unsigned long nextQueueId;
171 int maxThreads;
172 int nthreads;
173 int nidle;
174 Job *firstPtr;
175 } ThreadPool;
176
177 /*
178 * Function prototypes/forward declarations.
179 */
180
181 static void <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_JobThread">JobThread</a>(void *arg);
182 static Job *<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NextJob">NextJob</a>(void);
183 static JobQueue *<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NewQueue">NewQueue</a>(CONST char* queueName, CONST char* queueDesc, int maxThreads);
184 static void <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_FreeQueue">FreeQueue</a>(JobQueue *queuePtr);
185 static Job* <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NewJob">NewJob</a>(CONST char* server, CONST char* queueName, int type, Tcl_Obj *script);
186 static void <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_FreeJob">FreeJob</a>(Job *jobPtr);
187 static int LookupQueue(Tcl_Interp *interp,
188 CONST char *queue_name,
189 JobQueue **queuePtr,
190 int locked);
191 static int <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(JobQueue *queuePtr, int locked);
192 static int <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AnyDone">AnyDone</a>(JobQueue *queue);
193 static CONST char* <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetJobCodeStr">GetJobCodeStr</a>(int code);
194 static CONST char* <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetJobStateStr">GetJobStateStr</a>(JobStates state);
195 static CONST char* <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetJobTypeStr">GetJobTypeStr</a>(JobTypes type);
196 static CONST char* <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetJobReqStr">GetJobReqStr</a>(JobRequests req);
197 static CONST char* <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetQueueReqStr">GetQueueReqStr</a>(QueueRequests req);
198 static CONST char* <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetTpReqStr">GetTpReqStr</a>(ThreadPoolRequests req);
199
200 static int <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a>(Tcl_Interp *interp,
201 Tcl_Obj *list,
202 CONST char *name,
203 CONST char *value);
204
205 static int <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendFieldInt">AppendFieldInt</a>(Tcl_Interp *interp,
206 Tcl_Obj *list,
207 CONST char *name,
208 int value);
209
210 static int <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendFieldLong">AppendFieldLong</a>(Tcl_Interp *interp,
211 Tcl_Obj *list,
212 CONST char *name,
213 long value);
214
215
216 static int <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendFieldDouble">AppendFieldDouble</a>(Tcl_Interp *interp,
217 Tcl_Obj *list,
218 CONST char *name,
219 double value);
220
221 static double ComputeDelta(Ns_Time *start, Ns_Time *end);
222
223 /*
224 * Globals
225 */
226
227 static ThreadPool tp;
228
229
230 /*
231 *----------------------------------------------------------------------
232 *
233 * NsInitTclQueueType --
234 *
235 * Initialize the Tcl job queue.
236 *
237 * Results:
238 * None.
239 *
240 * Side effects:
241 * None.
242 *
243 *----------------------------------------------------------------------
244 */
245
246 void
247 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NsTclInitQueueType">NsTclInitQueueType</a>(void)
248 {
249 Tcl_InitHashTable(&tp.queues, TCL_STRING_KEYS);
250 Ns_MutexSetName(&tp.queuelock, "nsd:tcljobs");
251 tp.nextThreadId = 0;
252 tp.nextQueueId = 0;
253 tp.maxThreads = 0;
254 tp.nthreads = 0;
255 tp.nidle = 0;
256 tp.firstPtr = NULL;
257 tp.req = THREADPOOL_REQ_NONE;
258 }
259
260
261 /*
262 *----------------------------------------------------------------------
263 *
264 * <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NsStartJobsShutdown">NsStartJobsShutdown</a> --
265 *
266 * Signal stop of the Tcl job threads.
267 *
268 * Results:
269 * None.
270 *
271 * Side effects:
272 * All pending jobs are cancelled and waiting threads interrupted.
273 *
274 *----------------------------------------------------------------------
275 */
276
277 void
278 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NsStartJobsShutdown">NsStartJobsShutdown</a>(void)
279 {
280 Tcl_HashSearch search;
281 Tcl_HashEntry *hPtr;
282
283 hPtr = Tcl_FirstHashEntry(&tp.queues, &search);
284 while (hPtr != NULL) {
285 Ns_MutexLock(&tp.queuelock);
286 tp.req = THREADPOOL_REQ_STOP;
287 Ns_CondBroadcast(&tp.cond);
288 Ns_MutexUnlock(&tp.queuelock);
289 hPtr = Tcl_NextHashEntry(&search);
290 }
291 }
292
293
294 /*
295 *----------------------------------------------------------------------
296 *
297 * <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NsWaitJobsShutdown">NsWaitJobsShutdown</a> --
298 *
299 * Wait for Tcl job threads to exit.
300 *
301 * Results:
302 * None.
303 *
304 * Side effects:
305 * None.
306 *
307 *----------------------------------------------------------------------
308 */
309
310 void
311 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NsWaitJobsShutdown">NsWaitJobsShutdown</a>(Ns_Time *toPtr)
312 {
313 Tcl_HashSearch search;
314 Tcl_HashEntry *hPtr;
315 int status = NS_OK;
316
317 hPtr = Tcl_FirstHashEntry(&tp.queues, &search);
318 while (status == NS_OK && hPtr != NULL) {
319 Ns_MutexLock(&tp.queuelock);
320 while (status == NS_OK && tp.nthreads > 0) {
321 status = Ns_CondTimedWait(&tp.cond, &tp.queuelock, toPtr);
322 }
323 Ns_MutexUnlock(&tp.queuelock);
324 hPtr = Tcl_NextHashEntry(&search);
325 }
326 if (status != NS_OK) {
327 <a href="/cvs/aolserver/aolserver/nsd/log.c#A_Ns_Log">Ns_Log</a>(Warning, "tcljobs: timeout waiting for exit");
328 }
329 }
330
331
332 /*
333 *----------------------------------------------------------------------
334 *
335 * NsTclJobCmd --
336 *
337 * Implement the ns_job command to manage background tasks.
338 *
339 * Results:
340 * Standard Tcl result.
341 *
342 * Side effects:
343 * Jobs may be queued to run in another thread.
344 *
345 *----------------------------------------------------------------------
346 */
347
348 int
349 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NsTclJobObjCmd">NsTclJobObjCmd</a>(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv)
350 {
351 NsInterp *itPtr = arg;
352 JobQueue *queuePtr = NULL;
353 Job *jobPtr = NULL, **nextPtrPtr;
354 int code, new, create = 0, max;
355 char *jobId = NULL, buf[100], *queueId;
356 Tcl_HashEntry *hPtr, *jPtr;
357 Tcl_HashSearch search;
358 int argIndex;
359
360 static CONST char *opts[] = {
361 "cancel", "create", "delete", "genid", "jobs", "joblist",
362 "threadlist", "queue", "queues", "queuelist", "wait",
363 "waitany", NULL
364 };
365
366 enum {
367 JCancelIdx, JCreateIdx, JDeleteIdx, JGenIDIdx, JJobsIdx, JJobsListIdx,
368 JThreadListIdx, JQueueIdx, JQueuesIdx, JQueueListIdx, JWaitIdx, JWaitAnyIdx
369 } _nsmayalias opt;
370
371 if (objc < 2) {
372 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?");
373 return TCL_ERROR;
374 }
375 if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0,
376 (int *) &opt) != TCL_OK) {
377 return TCL_ERROR;
378 }
379
380 code = TCL_OK;
381 switch (opt) {
382 case JCreateIdx:
383 {
384 /*
385 * ns_job create
386 *
387 * Create a new thread pool queue.
388 */
389 Tcl_Obj *queueIdObj = NULL;
390 char *queueDesc = "";
391
392 argIndex = 2;
393 if ((objc < 3) || (objc > 6)) {
394 Tcl_WrongNumArgs(interp, 2, objv,
395 "?-desc description? queueId ?maxThreads?");
396 return TCL_ERROR;
397 }
398 if (objc > 3) {
399 if (strncmp(Tcl_GetString(objv[argIndex]), "-desc", strlen("-desc")) == 0) {
400 ++argIndex;
401 queueDesc = Tcl_GetString(objv[argIndex++]);
402 }
403 }
404
405 queueIdObj = objv[argIndex++];
406 queueId = Tcl_GetString(queueIdObj);
407
408 max = NS_JOB_DEFAULT_MAXTHREADS;
409 if ((objc > argIndex) &&
410 Tcl_GetIntFromObj(interp, objv[argIndex++], &max) != TCL_OK) {
411 return TCL_ERROR;
412 }
413
414 Ns_MutexLock(&tp.queuelock);
415 hPtr = Tcl_CreateHashEntry(&tp.queues, Tcl_GetString(queueIdObj), &new);
416 if (new) {
417 queuePtr = <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NewQueue">NewQueue</a>(Tcl_GetHashKey(&tp.queues, hPtr),queueDesc, max);
418 Tcl_SetHashValue(hPtr, queuePtr);
419 }
420 Ns_MutexUnlock(&tp.queuelock);
421 if (!new) {
422 Tcl_AppendResult(interp, "queue already exists: ", queueId, NULL);
423 return TCL_ERROR;
424 }
425 Tcl_SetObjResult(interp, queueIdObj);
426 }
427 break;
428 case JDeleteIdx:
429 {
430 /*
431 * ns_job delete
432 *
433 * Request that the specified queue be deleted. The queue will
434 * only be deleted when all jobs are removed.
435 */
436 if (objc != 3) {
437 Tcl_WrongNumArgs(interp, 2, objv, "queueId");
438 return TCL_ERROR;
439 }
440
441 if (LookupQueue(interp, Tcl_GetString(objv[2]), &queuePtr, 0) != TCL_OK) {
442 return TCL_ERROR;
443 }
444 queuePtr->req = QUEUE_REQ_DELETE;
445 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 0);
446
447 Ns_CondBroadcast(&tp.cond);
448 Tcl_SetResult(interp, "", TCL_STATIC);
449 }
450 break;
451 case JQueueIdx:
452 {
453 /*
454 * ns_job queue
455 *
456 * Add a new job the specified queue.
457 */
458 int job_type = JOB_NON_DETACHED;
459
460 argIndex = 2;
461 if ((objc != 4) && (objc != 5)) {
462 Tcl_WrongNumArgs(interp, 2, objv, "?-detached? queueId script");
463 return TCL_ERROR;
464 }
465 if (objc > 4) {
466 if (strcmp(Tcl_GetString(objv[argIndex++]), "-detached") == 0) {
467 job_type = JOB_DETACHED;
468 } else {
469 Tcl_WrongNumArgs(interp, 2, objv, "?-detached? queueId script");
470 return TCL_ERROR;
471 }
472 }
473
474 Ns_MutexLock(&tp.queuelock);
475 if (LookupQueue(interp, Tcl_GetString(objv[argIndex++]),
476 &queuePtr, 1) != TCL_OK) {
477 Ns_MutexUnlock(&tp.queuelock);
478 return TCL_ERROR;
479 }
480
481 /*
482 * Create a new job and add to the Thread Pool's list of jobs.
483 */
484 jobPtr = <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NewJob">NewJob</a>(itPtr->servPtr->server,
485 queuePtr->name,
486 job_type,
487 objv[argIndex++]);
488 Ns_GetTime(&jobPtr->startTime);
489
490
491 if ((tp.req == THREADPOOL_REQ_STOP) ||
492 (queuePtr->req == QUEUE_REQ_DELETE))
493 {
494 Tcl_AppendResult(interp,
495 "The specified queue is being deleted or "
496 "the system is stopping.", NULL);
497 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_FreeJob">FreeJob</a>(jobPtr);
498 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 1);
499 Ns_MutexUnlock(&tp.queuelock);
500 return TCL_ERROR;
501 }
502
503 /*
504 * Add the job to the thread pool's job list.
505 */
506 nextPtrPtr = &tp.firstPtr;
507 while (*nextPtrPtr != NULL) {
508 nextPtrPtr = &((*nextPtrPtr)->nextPtr);
509 }
510 *nextPtrPtr = jobPtr;
511
512 /*
513 * Start a new thread if there are less than maxThreads currently
514 * running and there currently no idle threads.
515 */
516 if (tp.nidle == 0 && tp.nthreads < tp.maxThreads) {
517 create = 1;
518 ++tp.nthreads;
519 } else {
520 create = 0;
521 }
522
523 /*
524 * Add the job to queue.
525 */
526 jobId = buf;
527 do {
528 sprintf(jobId, "job%d", queuePtr->nextid++);
529 hPtr = Tcl_CreateHashEntry(&queuePtr->jobs, jobId, &new);
530 } while (!new);
531
532 Tcl_DStringAppend(&jobPtr->id, jobId, -1);
533 Tcl_SetHashValue(hPtr, jobPtr);
534 Ns_CondBroadcast(&tp.cond);
535
536 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 1);
537 Ns_MutexUnlock(&tp.queuelock);
538
539 if (create) {
540 Ns_ThreadCreate(<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_JobThread">JobThread</a>, 0, 0, NULL);
541 }
542
543 Tcl_SetResult(interp, jobId, TCL_VOLATILE);
544 }
545 break;
546 case JWaitIdx:
547 {
548 /*
549 * ns_job wait
550 *
551 * Wait for the specified job.
552 */
553 int timeoutFlag = 0;
554 Ns_Time timeout;
555 int timedOut = 0;
556 Ns_Time delta_timeout;
557
558 argIndex = 2;
559 if ((objc != 4) && (objc != 6)) {
560 Tcl_WrongNumArgs(interp, 2, objv,
561 "?-timeout seconds:microseconds? queueId jobId");
562 return TCL_ERROR;
563 }
564 if (objc > 4) {
565 if (strcmp(Tcl_GetString(objv[argIndex++]), "-timeout") == 0) {
566 timeoutFlag = 1;
567 if (<a href="/cvs/aolserver/aolserver/nsd/tclobj.c#A_Ns_TclGetTimeFromObj">Ns_TclGetTimeFromObj</a>(interp, objv[argIndex++],
568 &delta_timeout) != TCL_OK) {
569 return TCL_ERROR;
570 }
571
572 /*
573 * Set the timeout time. This is an absolute time.
574 */
575 Ns_GetTime(&timeout);
576 Ns_IncrTime(&timeout, delta_timeout.sec, delta_timeout.usec);
577 }
578 }
579
580 if (LookupQueue(interp, Tcl_GetString(objv[argIndex++]),
581 &queuePtr, 0) != TCL_OK) {
582 return TCL_ERROR;
583 }
584 jobId = Tcl_GetString(objv[argIndex++]);
585
586 hPtr = Tcl_FindHashEntry(&queuePtr->jobs, jobId);
587 if (hPtr == NULL) {
588
589 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 0);
590 Tcl_AppendResult(interp, "no such job: ", jobId, NULL);
591 return TCL_ERROR;
592 }
593
594 jobPtr = Tcl_GetHashValue(hPtr);
595
596 if ((jobPtr->type == JOB_DETACHED) ||
597 (jobPtr->req == JOB_CANCEL) ||
598 (jobPtr->req == JOB_WAIT)) {
599 Tcl_AppendResult(interp, "cannot wait on job: ",
600 Tcl_DStringValue(&jobPtr->id), NULL);
601 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 0);
602 return TCL_ERROR;
603 }
604
605 jobPtr->req = JOB_WAIT;
606
607 if (timeoutFlag) {
608 while (jobPtr->state != JOB_DONE) {
609 timedOut = Ns_CondTimedWait(&queuePtr->cond,
610 &queuePtr->lock, &timeout);
611 if (timedOut == NS_TIMEOUT) {
612 Tcl_SetResult(interp, "Wait timed out.", TCL_STATIC);
613 jobPtr->req = JOB_NONE;
614
615 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 0);
616
617 return TCL_ERROR;
618 }
619 }
620 } else {
621 while (jobPtr->state != JOB_DONE) {
622 Ns_CondWait(&queuePtr->cond, &queuePtr->lock);
623 }
624 }
625
626 /*
627 * At this point the job we were waiting on has completed, so we return
628 * the job's results and errorcodes, then clean up the job.
629 */
630
631 /*
632 * The following is a sanity check that ensures no other
633 * process removed this job's entry.
634 */
635 if (((hPtr = Tcl_FindHashEntry(&queuePtr->jobs, jobId)) == NULL) ||
636 (jobPtr == Tcl_GetHashValue(hPtr))) {
637 Tcl_SetResult(interp, "Internal ns_job error.", TCL_STATIC);
638 }
639
640 Tcl_DeleteHashEntry(hPtr);
641 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 0);
642
643 Tcl_DStringResult(interp, &jobPtr->results);
644 if (jobPtr->errorCode != NULL) {
645 Tcl_SetVar(interp, "errorCode", jobPtr->errorCode, TCL_GLOBAL_ONLY);
646 }
647 if (jobPtr->errorInfo != NULL) {
648 Tcl_SetVar(interp, "errorInfo", jobPtr->errorInfo, TCL_GLOBAL_ONLY);
649 }
650 code = jobPtr->code;
651 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_FreeJob">FreeJob</a>(jobPtr);
652 }
653 break;
654 case JCancelIdx:
655 {
656 /*
657 * ns_job cancel
658 *
659 * Cancel the specified job.
660 */
661 if (objc != 4) {
662 Tcl_WrongNumArgs(interp, 2, objv, "queueId jobId");
663 return TCL_ERROR;
664 }
665 if (LookupQueue(interp, Tcl_GetString(objv[2]), &queuePtr, 0) != TCL_OK) {
666 return TCL_ERROR;
667 }
668 jobId = Tcl_GetString(objv[3]);
669 jPtr = Tcl_FindHashEntry(&queuePtr->jobs, jobId);
670 if (jPtr == NULL) {
671
672 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 0);
673 Tcl_AppendResult(interp, "no such job: ", jobId, NULL);
674 return TCL_ERROR;
675 }
676
677 jobPtr = Tcl_GetHashValue(jPtr);
678
679 if (jobPtr->req == JOB_WAIT) {
680 Tcl_AppendResult(interp,
681 "Can not cancel this job because someone"
682 " is waiting on it. Job ID: ",
683 Tcl_DStringValue(&jobPtr->id), NULL);
684
685 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 0);
686
687 return TCL_ERROR;
688 }
689
690 jobPtr->req = JOB_CANCEL;
691
692 if (jobPtr->state == JOB_DONE) {
693 Tcl_DeleteHashEntry(jPtr);
694 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_FreeJob">FreeJob</a>(jobPtr);
695 }
696
697 Ns_CondBroadcast(&queuePtr->cond);
698
699 Ns_CondBroadcast(&tp.cond);
700
701 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (jobPtr->state == JOB_RUNNING));
702
703 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 0);
704 }
705 break;
706 case JWaitAnyIdx:
707 {
708 /*
709 * ns_job waitany
710 *
711 * Wait for any job on the queue complete.
712 */
713 int timeoutFlag = 0;
714 Ns_Time timeout;
715 int timedOut = 0;
716 Ns_Time delta_timeout;
717
718 argIndex = 2;
719 if ((objc != 3) && (objc != 5)) {
720 Tcl_WrongNumArgs(interp, 2, objv,
721 "?-timeout seconds:microseconds? queueId");
722 return TCL_ERROR;
723 }
724 if (objc > 3) {
725 if (strcmp(Tcl_GetString(objv[argIndex++]), "-timeout") == 0) {
726 timeoutFlag = 1;
727 if (<a href="/cvs/aolserver/aolserver/nsd/tclobj.c#A_Ns_TclGetTimeFromObj">Ns_TclGetTimeFromObj</a>(interp, objv[argIndex++],
728 &delta_timeout) != TCL_OK) {
729 return TCL_ERROR;
730 }
731 /*
732 * Set the timeout time. This is an absolute time.
733 */
734 Ns_GetTime(&timeout);
735 Ns_IncrTime(&timeout, delta_timeout.sec, delta_timeout.usec);
736 }
737 }
738
739 if (LookupQueue(interp, Tcl_GetString(objv[argIndex++]),
740 &queuePtr, 0) != TCL_OK) {
741 return TCL_ERROR;
742 }
743
744 /*
745 * While there are jobs in queue or no jobs are "done", wait
746 * on the queue condition variable.
747 */
748 if (timeoutFlag) {
749 while ((Tcl_FirstHashEntry(&queuePtr->jobs, &search) != NULL) &&
750 (!<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AnyDone">AnyDone</a>(queuePtr))) {
751
752 timedOut = Ns_CondTimedWait(&queuePtr->cond,
753 &queuePtr->lock, &timeout);
754 if (timedOut == NS_TIMEOUT) {
755 Tcl_SetResult(interp, "Wait timed out.", TCL_STATIC);
756
757 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 0);
758 return TCL_ERROR;
759 }
760 }
761 } else {
762 while ((Tcl_FirstHashEntry(&queuePtr->jobs, &search) != NULL) &&
763 (!<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AnyDone">AnyDone</a>(queuePtr))) {
764 Ns_CondWait(&queuePtr->cond, &queuePtr->lock);
765 }
766 }
767
768 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 0);
769 Tcl_SetResult(interp, "", TCL_STATIC);
770 }
771 break;
772 case JJobsIdx:
773 {
774 /*
775 * ns_job jobs
776 *
777 * Returns a list of job IDs in arbitrary order.
778 */
779 if (objc != 3) {
780 Tcl_WrongNumArgs(interp, 2, objv, "queueId");
781 return TCL_ERROR;
782 }
783 if (LookupQueue(interp, Tcl_GetString(objv[2]), &queuePtr, 0) != TCL_OK) {
784 return TCL_ERROR;
785 }
786 hPtr = Tcl_FirstHashEntry(&queuePtr->jobs, &search);
787 while (hPtr != NULL) {
788 jobId = Tcl_GetHashKey(&queuePtr->jobs, hPtr);
789 Tcl_AppendElement(interp, jobId);
790 hPtr = Tcl_NextHashEntry(&search);
791 }
792 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 0);
793 }
794 break;
795 case JQueuesIdx:
796 {
797 /*
798 * ns_job queues
799 *
800 * Returns a list of the current queues.
801 */
802 Ns_MutexLock(&tp.queuelock);
803 hPtr = Tcl_FirstHashEntry(&tp.queues, &search);
804 while (hPtr != NULL) {
805 queuePtr = Tcl_GetHashValue(hPtr);
806 Tcl_AppendElement(interp, queuePtr->name);
807 hPtr = Tcl_NextHashEntry(&search);
808 }
809 Ns_MutexUnlock(&tp.queuelock);
810 }
811 break;
812 case JJobsListIdx:
813 {
814 /*
815 * ns_job joblist
816 *
817 * Returns a list of all the jobs in the queue. The "job" consists of:
818 *
819 * Job ID
820 * Job State (Scheduled, Running, or Done)
821 * Job Results (or job script, if job has not yet completed).
822 * Job Code (TCL_OK, TCL_ERROR, TCL_RETURN, TCL_BREAK, TCL_CONTINE)
823 * Job Running Time (TBD)
824 */
825
826 Tcl_Obj *jobList, *jobFieldList;
827 char *jobId, *jobState, *jobCode, *jobType;
828 char *jobResults, *jobScript, *jobReq;
829 double delta;
830
831 if (objc != 3) {
832 Tcl_WrongNumArgs(interp, 2, objv, "queueId");
833 return TCL_ERROR;
834 }
835 if (LookupQueue(interp, Tcl_GetString(objv[2]), &queuePtr, 0) != TCL_OK) {
836 return TCL_ERROR;
837 }
838
839 /* Create a Tcl List to hold the list of jobs. */
840 jobList = Tcl_NewListObj(0, NULL);
841
842 hPtr = Tcl_FirstHashEntry(&queuePtr->jobs, &search);
843 while (hPtr != NULL) {
844
845 jobPtr = Tcl_GetHashValue(hPtr);
846
847 jobId = Tcl_GetHashKey(&queuePtr->jobs, hPtr);
848 jobCode = <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetJobCodeStr">GetJobCodeStr</a>(jobPtr->code);
849 jobState = <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetJobStateStr">GetJobStateStr</a>(jobPtr->state);
850 jobType = <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetJobTypeStr">GetJobTypeStr</a>(jobPtr->type);
851 jobReq = <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetJobReqStr">GetJobReqStr</a>(jobPtr->req);
852 jobResults = Tcl_DStringValue(&jobPtr->results);
853 jobScript = Tcl_DStringValue(&jobPtr->script);
854
855 if ((jobPtr->state == JOB_SCHEDULED) ||
856 (jobPtr->state == JOB_RUNNING)) {
857 Ns_GetTime(&jobPtr->endTime);
858 }
859 delta = ComputeDelta(&jobPtr->startTime, &jobPtr->endTime);
860
861 /* Create a Tcl List to hold the list of job fields. */
862 jobFieldList = Tcl_NewListObj(0, NULL);
863
864 /* Add Job ID */
865 if ((<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a>(interp, jobFieldList,
866 "id", jobId) != TCL_OK) ||
867 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a>(interp, jobFieldList,
868 "state", jobState) != TCL_OK) ||
869 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a>(interp, jobFieldList,
870 "results", jobResults) != TCL_OK) ||
871 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a>(interp, jobFieldList,
872 "script", jobScript) != TCL_OK) ||
873 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a>(interp, jobFieldList,
874 "code", jobCode) != TCL_OK) ||
875 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a>(interp, jobFieldList,
876 "type", jobType) != TCL_OK) ||
877 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a>(interp, jobFieldList,
878 "req", jobReq) != TCL_OK) ||
879 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendFieldDouble">AppendFieldDouble</a>(interp, jobFieldList,
880 "time", delta) != TCL_OK) ||
881 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendFieldLong">AppendFieldLong</a>(interp,
882 jobFieldList,
883 "starttime",
884 jobPtr->startTime.sec) != TCL_OK) ||
885 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendFieldLong">AppendFieldLong</a>(interp,
886 jobFieldList,
887 "endtime",
888 jobPtr->endTime.sec) != TCL_OK))
889 {
890 /* <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a> sets results if an error occurs. */
891 Tcl_DecrRefCount(jobList);
892 Tcl_DecrRefCount(jobFieldList);
893 Ns_MutexUnlock(&queuePtr->lock);
894 return TCL_ERROR;
895 }
896
897 /* Add the job to the job list */
898 if (Tcl_ListObjAppendElement(interp, jobList, jobFieldList) != TCL_OK) {
899 Tcl_DecrRefCount(jobList);
900 Tcl_DecrRefCount(jobFieldList);
901
902 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 0);
903 return TCL_ERROR;
904 }
905
906 hPtr = Tcl_NextHashEntry(&search);
907 }
908 Tcl_SetObjResult(interp, jobList);
909
910 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 0);
911 }
912 break;
913 case JQueueListIdx:
914 {
915 /*
916 * ns_job queuelist
917 *
918 * Returns a list of all the queues and the queue information.
919 */
920 Tcl_Obj *queueList, *queueFieldList;
921 char *queueReq;
922
923 /* Create a Tcl List to hold the list of jobs. */
924 queueList = Tcl_NewListObj(0, NULL);
925
926 Ns_MutexLock(&tp.queuelock);
927 hPtr = Tcl_FirstHashEntry(&tp.queues, &search);
928 while (hPtr != NULL) {
929
930 queuePtr = Tcl_GetHashValue(hPtr);
931
932 /* Create a Tcl List to hold the list of queue fields. */
933 queueFieldList = Tcl_NewListObj(0, NULL);
934
935 queueReq = <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetQueueReqStr">GetQueueReqStr</a>(queuePtr->req);
936
937 /* Add queue name */
938 if ((<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a>(interp, queueFieldList,
939 "name", queuePtr->name) != TCL_OK) ||
940 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a>(interp, queueFieldList,
941 "desc", queuePtr->desc) != TCL_OK) ||
942 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendFieldInt">AppendFieldInt</a>(interp, queueFieldList,
943 "maxthreads", queuePtr->maxThreads) != TCL_OK) ||
944 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendFieldInt">AppendFieldInt</a>(interp, queueFieldList,
945 "numrunning", queuePtr->nRunning) != TCL_OK) ||
946 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a>(interp, queueFieldList,
947 "req", queueReq) != TCL_OK))
948 {
949 /* <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a> sets results if an error occurs. */
950 Tcl_DecrRefCount(queueList);
951 Tcl_DecrRefCount(queueFieldList);
952 Ns_MutexUnlock(&tp.queuelock);
953 return TCL_ERROR;
954 }
955
956 /* Add the job to the job list */
957 if (Tcl_ListObjAppendElement(interp, queueList,
958 queueFieldList) != TCL_OK) {
959 Tcl_DecrRefCount(queueList);
960 Tcl_DecrRefCount(queueFieldList);
961 Ns_MutexUnlock(&tp.queuelock);
962 return TCL_ERROR;
963 }
964
965 hPtr = Tcl_NextHashEntry(&search);
966 }
967 Tcl_SetObjResult(interp, queueList);
968 Ns_MutexUnlock(&tp.queuelock);
969 }
970 break;
971 case JGenIDIdx:
972 {
973 /*
974 * ns_job genID
975 *
976 * Generate a unique queue name.
977 */
978 Ns_Time currentTime;
979 Ns_GetTime(&currentTime);
980 snprintf(buf, 100, "queue_id_%x_%x", tp.nextQueueId++, currentTime.sec);
981 Tcl_SetResult(interp, buf, TCL_VOLATILE);
982 }
983 break;
984 case JThreadListIdx:
985 {
986 /*
987 * ns_job threadlist
988 *
989 * Return a list of the thread pool's fields.
990 *
991 */
992
993 Tcl_Obj *tpFieldList;
994 char *tpReq;
995
996 /* Create a Tcl List to hold the list of thread fields. */
997 tpFieldList = Tcl_NewListObj(0, NULL);
998
999 tpReq = <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetTpReqStr">GetTpReqStr</a>(tp.req);
1000
1001 Ns_MutexLock(&tp.queuelock);
1002
1003 /* Add queue name */
1004 if ((<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendFieldInt">AppendFieldInt</a>(interp, tpFieldList,
1005 "maxthreads", tp.maxThreads) != TCL_OK) ||
1006 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendFieldInt">AppendFieldInt</a>(interp, tpFieldList,
1007 "numthreads", tp.nthreads) != TCL_OK) ||
1008 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendFieldInt">AppendFieldInt</a>(interp, tpFieldList,
1009 "numidle", tp.nidle) != TCL_OK) ||
1010 (<a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a>(interp, tpFieldList,
1011 "req", tpReq) != TCL_OK))
1012
1013 {
1014 /* <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a> sets results if an error occurs. */
1015 Tcl_DecrRefCount(tpFieldList);
1016 Ns_MutexUnlock(&tp.queuelock);
1017 return TCL_ERROR;
1018 }
1019 Ns_MutexUnlock(&tp.queuelock);
1020 Tcl_SetObjResult(interp, tpFieldList);
1021 }
1022 break;
1023 }
1024 return code;
1025 }
1026
1027
1028 /*
1029 *----------------------------------------------------------------------
1030 *
1031 * <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_JobThread">JobThread</a> --
1032 *
1033 * Background thread for the ns_job command.
1034 *
1035 * Results:
1036 * None.
1037 *
1038 * Side effects:
1039 * Jobs will be run from the queue.
1040 *
1041 *----------------------------------------------------------------------
1042 */
1043
1044 static void
1045 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_JobThread">JobThread</a>(void *arg)
1046 {
1047 Tcl_Interp *interp;
1048 Job *jobPtr;
1049 char buf[100];
1050 CONST char *err;
1051 JobQueue *queuePtr;
1052 Tcl_HashEntry *jPtr;
1053
1054 <a href="/cvs/aolserver/aolserver/nsd/nsmain.c#A_Ns_WaitForStartup">Ns_WaitForStartup</a>();
1055 Ns_MutexLock(&tp.queuelock);
1056 snprintf(buf, 100, "-ns_job_%x-", tp.nextThreadId++);
1057 Ns_ThreadSetName(buf);
1058 <a href="/cvs/aolserver/aolserver/nsd/log.c#A_Ns_Log">Ns_Log</a>(Notice, "Starting thread: %s", buf);
1059 while (1) {
1060 ++tp.nidle;
1061 while (((jobPtr = <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NextJob">NextJob</a>()) == NULL) &&
1062 !(tp.req == THREADPOOL_REQ_STOP)) {
1063 Ns_CondWait(&tp.cond, &tp.queuelock);
1064 }
1065 --tp.nidle;
1066
1067 if (tp.req == THREADPOOL_REQ_STOP) {
1068 break;
1069 }
1070
1071 /*
1072 * Run the job.
1073 */
1074 Ns_MutexUnlock(&tp.queuelock);
1075
1076 interp = <a href="/cvs/aolserver/aolserver/nsd/tclinit.c#A_Ns_TclAllocateInterp">Ns_TclAllocateInterp</a>(jobPtr->server);
1077 Ns_GetTime(&jobPtr->endTime);
1078 Ns_GetTime(&jobPtr->startTime);
1079 jobPtr->code = Tcl_EvalEx(interp, jobPtr->script.string, -1, 0);
1080
1081 /*
1082 * Save the results.
1083 */
1084 Tcl_DStringAppend(&jobPtr->results, Tcl_GetStringResult(interp), -1);
1085 err = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY);
1086 if (err != NULL) {
1087 jobPtr->errorCode = ns_strdup(err);
1088 }
1089 err = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
1090 if (err != NULL) {
1091 jobPtr->errorInfo = ns_strdup(err);
1092 }
1093 Ns_GetTime(&jobPtr->endTime);
1094 <a href="/cvs/aolserver/aolserver/nsd/tclinit.c#A_Ns_TclDeAllocateInterp">Ns_TclDeAllocateInterp</a>(interp);
1095
1096 Ns_MutexLock(&tp.queuelock);
1097
1098 LookupQueue(NULL, jobPtr->queueId, &queuePtr, 1);
1099
1100 --(queuePtr->nRunning);
1101 jobPtr->state = JOB_DONE;
1102
1103 /*
1104 * Clean any cancelled or detached jobs.
1105 */
1106 if ((jobPtr->req == JOB_CANCEL) || (jobPtr->type == JOB_DETACHED)) {
1107 jPtr = Tcl_FindHashEntry(&queuePtr->jobs, Tcl_DStringValue(&jobPtr->id));
1108 Tcl_DeleteHashEntry(jPtr);
1109 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_FreeJob">FreeJob</a>(jobPtr);
1110 }
1111
1112 Ns_CondBroadcast(&queuePtr->cond);
1113 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 1);
1114 }
1115
1116 --tp.nthreads;
1117
1118 Ns_CondBroadcast(&tp.cond);
1119 Ns_MutexUnlock(&tp.queuelock);
1120
1121 <a href="/cvs/aolserver/aolserver/nsd/log.c#A_Ns_Log">Ns_Log</a>(Notice, "exiting");
1122 }
1123
1124
1125 /*
1126 *----------------------------------------------------------------------
1127 * Get the "next" job.
1128 *
1129 * Queues have a "maxThreads" so if the queue is already at
1130 * "maxThreads" jobs of that queue will be skipped.
1131 *
1132 * Note: the "queuelock" should be locked when calling this function.
1133 *
1134 * Results:
1135 *
1136 * Side effects:
1137 *
1138 *----------------------------------------------------------------------
1139 */
1140
1141 static Job *
1142 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NextJob">NextJob</a>(void)
1143 {
1144 JobQueue *queuePtr;
1145 Tcl_HashEntry *jPtr;
1146 Job *prev = NULL;
1147 Job *tmp = NULL;
1148 Job *jobPtr = NULL;
1149 int done = 0;
1150
1151 jobPtr = tp.firstPtr;
1152 prev = tp.firstPtr;
1153 while ((!done) && (jobPtr != NULL)) {
1154 LookupQueue(NULL, jobPtr->queueId, &queuePtr, 1);
1155
1156 /*
1157 * Check if the job is not cancel and
1158 */
1159
1160 if (jobPtr->req == JOB_CANCEL) {
1161 /*
1162 * Remove job from the list.
1163 */
1164 tmp = jobPtr;
1165 if (jobPtr == tp.firstPtr) {
1166 tp.firstPtr = jobPtr->nextPtr;
1167 } else {
1168 prev->nextPtr = jobPtr->nextPtr;
1169 }
1170 jobPtr = jobPtr->nextPtr;
1171
1172 /*
1173 * Remove cancelled job from the queue and free it.
1174 */
1175 jPtr = Tcl_FindHashEntry(&queuePtr->jobs, Tcl_DStringValue(&tmp->id));
1176 Tcl_DeleteHashEntry(jPtr);
1177 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_FreeJob">FreeJob</a>(tmp);
1178 } else if (queuePtr->nRunning < queuePtr->maxThreads) {
1179 /*
1180 * Remove job from the list.
1181 */
1182 if (jobPtr == tp.firstPtr) {
1183 tp.firstPtr = jobPtr->nextPtr;
1184 } else {
1185 prev->nextPtr = jobPtr->nextPtr;
1186 }
1187
1188 done = 1;
1189 jobPtr->state = JOB_RUNNING;
1190 ++(queuePtr->nRunning);
1191 } else {
1192 /*
1193 * Advance the list pointer.
1194 */
1195 prev = jobPtr;
1196 jobPtr = jobPtr->nextPtr;
1197 }
1198
1199 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(queuePtr, 1);
1200 }
1201
1202 return jobPtr;
1203 }
1204
1205
1206 /*
1207 *----------------------------------------------------------------------
1208 *
1209 * <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NewQueue">NewQueue</a> --
1210 *
1211 * Create a thread pool queue.
1212 *
1213 * Results:
1214 * None.
1215 *
1216 * Side effects:
1217 * None.
1218 *
1219 *----------------------------------------------------------------------
1220 */
1221
1222 static JobQueue *
1223 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NewQueue">NewQueue</a>(CONST char* queueName, CONST char* queueDesc, int maxThreads)
1224 {
1225 JobQueue *queuePtr = NULL;
1226
1227 queuePtr = ns_calloc(1, sizeof(JobQueue));
1228 queuePtr->req = QUEUE_REQ_NONE;
1229
1230 queuePtr->name = ns_calloc(1, strlen(queueName) + 1);
1231 strcpy(queuePtr->name, queueName);
1232
1233 queuePtr->desc = ns_calloc(1, strlen(queueDesc) + 1);
1234 strcpy(queuePtr->desc, queueDesc);
1235 queuePtr->maxThreads = maxThreads;
1236
1237 queuePtr->refCount = 0;
1238
1239 Ns_MutexSetName2(&queuePtr->lock, "tcljob", queueName);
1240 Tcl_InitHashTable(&queuePtr->jobs, TCL_STRING_KEYS);
1241
1242 tp.maxThreads += maxThreads;
1243
1244 return queuePtr;
1245 }
1246
1247
1248 /*
1249 *----------------------------------------------------------------------
1250 *
1251 * <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_FreeQueue">FreeQueue</a> --
1252 *
1253 * Cleanup the
1254 *
1255 * Results:
1256 * None.
1257 *
1258 * Side effects:
1259 * None.
1260 *
1261 *----------------------------------------------------------------------
1262 */
1263
1264 static void
1265 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_FreeQueue">FreeQueue</a>(JobQueue *queuePtr)
1266 {
1267 Ns_MutexDestroy(&queuePtr->lock);
1268 Tcl_DeleteHashTable(&queuePtr->jobs);
1269 ns_free(queuePtr->desc);
1270 ns_free(queuePtr->name);
1271 ns_free(queuePtr);
1272 }
1273
1274
1275 /*
1276 *----------------------------------------------------------------------
1277 *
1278 * <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NewJob">NewJob</a> --
1279 *
1280 * Create a new job and initialize it.
1281 *
1282 * Results:
1283 * None.
1284 *
1285 * Side effects:
1286 * None.
1287 *
1288 *----------------------------------------------------------------------
1289 */
1290
1291 static Job *
1292 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_NewJob">NewJob</a>(CONST char* server, CONST char* queueId, int type, Tcl_Obj *script)
1293 {
1294 Job *jobPtr = NULL;
1295
1296 jobPtr = ns_malloc(sizeof(Job));
1297 jobPtr->nextPtr = NULL;
1298 jobPtr->server = server;
1299 jobPtr->state = JOB_SCHEDULED;
1300 jobPtr->code = TCL_OK;
1301 jobPtr->type = type;
1302 jobPtr->req = JOB_NONE;
1303 jobPtr->errorCode = jobPtr->errorInfo = NULL;
1304
1305 jobPtr->queueId = ns_calloc(1, strlen(queueId) + 1);
1306 strcpy(jobPtr->queueId, queueId);
1307
1308 Tcl_DStringInit(&jobPtr->id);
1309 Tcl_DStringInit(&jobPtr->script);
1310 Tcl_DStringAppend(&jobPtr->script, Tcl_GetString(script), -1);
1311 Tcl_DStringInit(&jobPtr->results);
1312
1313 return jobPtr;
1314 }
1315
1316
1317 /*
1318 *----------------------------------------------------------------------
1319 *
1320 * <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_FreeJob">FreeJob</a> --
1321 *
1322 * Destory a Job structure.
1323 *
1324 * Results:
1325 * None.
1326 *
1327 * Side effects:
1328 * None.
1329 *
1330 *----------------------------------------------------------------------
1331 */
1332
1333 void
1334 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_FreeJob">FreeJob</a>(Job *jobPtr)
1335 {
1336 Tcl_DStringFree(&jobPtr->results);
1337 Tcl_DStringFree(&jobPtr->script);
1338 Tcl_DStringFree(&jobPtr->id);
1339
1340 ns_free(jobPtr->queueId);
1341
1342 if (jobPtr->errorCode) {
1343 ns_free(jobPtr->errorCode);
1344 }
1345 if (jobPtr->errorInfo) {
1346 ns_free(jobPtr->errorInfo);
1347 }
1348 ns_free(jobPtr);
1349 }
1350
1351
1352 /*
1353 *----------------------------------------------------------------------
1354 * Find the specified queue and lock it if found.
1355 *
1356 * Specify "locked" true if the "queuelock" is already locked.
1357 *
1358 * PWM: With the new locking scheme refCount is not longer necessary.
1359 * However, if there is ever a case in the future where an unlocked
1360 * queue can be referenced then we will again need the refCount.
1361 *
1362 * Results:
1363 *
1364 * Side effects:
1365 *
1366 *----------------------------------------------------------------------
1367 */
1368 static int LookupQueue(Tcl_Interp *interp,
1369 CONST char *queueId,
1370 JobQueue **queuePtr,
1371 int locked)
1372 {
1373 Tcl_HashEntry *hPtr;
1374
1375 if (!locked)
1376 Ns_MutexLock(&tp.queuelock);
1377
1378 *queuePtr = NULL;
1379
1380 hPtr = Tcl_FindHashEntry(&tp.queues, queueId);
1381 if (hPtr != NULL) {
1382 *queuePtr = Tcl_GetHashValue(hPtr);
1383 Ns_MutexLock(&(*queuePtr)->lock);
1384 ++((*queuePtr)->refCount);
1385 }
1386
1387 if (!locked)
1388 Ns_MutexUnlock(&tp.queuelock);
1389
1390 if (*queuePtr == NULL) {
1391 if (interp != NULL) {
1392 Tcl_AppendResult(interp, "no such queue: ", queueId, NULL);
1393 }
1394 return TCL_ERROR;
1395 }
1396 return TCL_OK;
1397 }
1398
1399
1400 /*
1401 *----------------------------------------------------------------------
1402 * Release queue
1403 *
1404 * Specify "locked" true if the queuelock is already locked.
1405 *
1406 * Results:
1407 *
1408 * Side effects:
1409 *
1410 *----------------------------------------------------------------------
1411 */
1412 static int
1413 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_ReleaseQueue">ReleaseQueue</a>(JobQueue *queuePtr, int locked)
1414 {
1415 Tcl_HashEntry *qPtr;
1416 Tcl_HashSearch search;
1417 int deleted = 0;
1418
1419 --(queuePtr->refCount);
1420
1421 /*
1422 * If user requested that the queue be deleted and
1423 * no other thread is referencing the queueu and
1424 * the queue is emtpy, then delete it.
1425 *
1426 */
1427 if ((queuePtr->req == QUEUE_REQ_DELETE) &&
1428 (queuePtr->refCount <= 0) &&
1429 (Tcl_FirstHashEntry(&queuePtr->jobs, &search) == NULL)) {
1430
1431 if (!locked)
1432 Ns_MutexLock(&tp.queuelock);
1433
1434 /*
1435 * Remove the queue from the list.
1436 */
1437 qPtr = Tcl_FindHashEntry(&tp.queues, queuePtr->name);
1438 if (qPtr != NULL) {
1439 Tcl_DeleteHashEntry(qPtr);
1440 tp.maxThreads -= queuePtr->maxThreads;
1441 deleted = 1;
1442 }
1443
1444 Ns_MutexUnlock(&queuePtr->lock);
1445 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_FreeQueue">FreeQueue</a>(queuePtr);
1446
1447 if (!locked)
1448 Ns_MutexUnlock(&tp.queuelock);
1449 } else {
1450 Ns_MutexUnlock(&queuePtr->lock);
1451 }
1452
1453 return deleted;
1454 }
1455
1456
1457 /*
1458 *----------------------------------------------------------------------
1459 * Check if any jobs on the queue are "done".
1460 *
1461 * 1 (true) there is at least one job done.
1462 * 0 (false) there are no jobs done.
1463 *
1464 * Results:
1465 *
1466 * Side effects:
1467 *
1468 *----------------------------------------------------------------------
1469 */
1470
1471 static int
1472 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AnyDone">AnyDone</a>(JobQueue *queue)
1473 {
1474 Tcl_HashEntry *hPtr;
1475 Job *jobPtr;
1476 Tcl_HashSearch search;
1477
1478 hPtr = Tcl_FirstHashEntry(&queue->jobs, &search);
1479
1480 while (hPtr != NULL) {
1481 jobPtr = Tcl_GetHashValue(hPtr);
1482 if (jobPtr->state == JOB_DONE) {
1483 return 1;
1484 }
1485 hPtr = Tcl_NextHashEntry(&search);
1486 }
1487 return 0;
1488 }
1489
1490
1491 /*
1492 *----------------------------------------------------------------------
1493 * Convert the job code into a string.
1494 *
1495 * Results:
1496 *
1497 * Side effects:
1498 *
1499 *----------------------------------------------------------------------
1500 */
1501
1502 static CONST char*
1503 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetJobCodeStr">GetJobCodeStr</a>(int code)
1504 {
1505 static CONST char *codeArr[] = {
1506 "TCL_OK", /* 0 */
1507 "TCL_ERROR", /* 1 */
1508 "TCL_RETURN", /* 2 */
1509 "TCL_BREAK", /* 3 */
1510 "TCL_CONTINUE", /* 4 */
1511 "UNKNOWN_CODE" /* 5 */
1512 };
1513 static int max_code_index = 5;
1514
1515 /* Check the caller's input. */
1516 if (code > (max_code_index)) {
1517 code = max_code_index;
1518 }
1519
1520 return codeArr[code];
1521 }
1522
1523
1524 /*
1525 *----------------------------------------------------------------------
1526 * Convert the job states into a string.
1527 *
1528 * Results:
1529 *
1530 * Side effects:
1531 *
1532 *----------------------------------------------------------------------
1533 */
1534
1535 static CONST char*
1536 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetJobStateStr">GetJobStateStr</a>(JobStates state)
1537 {
1538 static CONST char *stateArr[] = {
1539 "scheduled", /* 0 */
1540 "running", /* 1 */
1541 "done", /* 2 */
1542 "unknown" /* 3 */
1543 };
1544 static int max_state_index = 3;
1545
1546 /* Check the caller's input. */
1547 if (state > (max_state_index)) {
1548 state = max_state_index;
1549 }
1550
1551 return stateArr[state];
1552 }
1553
1554
1555 /*
1556 *----------------------------------------------------------------------
1557 * Convert the job states into a string.
1558 *
1559 * Results:
1560 *
1561 * Side effects:
1562 *
1563 *----------------------------------------------------------------------
1564 */
1565 static CONST char*
1566 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetJobTypeStr">GetJobTypeStr</a>(JobTypes type)
1567 {
1568 static CONST char *typeArr[] = {
1569 "nondetached", /* 0 */
1570 "detached", /* 1 */
1571 "unknown" /* 2 */
1572 };
1573 static int max_type_index = 2;
1574
1575 /* Check the caller's input. */
1576 if (type > (max_type_index)) {
1577 type = max_type_index;
1578 }
1579
1580 return typeArr[type];
1581 }
1582
1583
1584 /*
1585 *----------------------------------------------------------------------
1586 * Convert the job req into a string.
1587 *
1588 * Results:
1589 *
1590 * Side effects:
1591 *
1592 *----------------------------------------------------------------------
1593 */
1594 static CONST char*
1595 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetJobReqStr">GetJobReqStr</a>(JobRequests req)
1596 {
1597 static CONST char *reqArr[] = {
1598 "none", /* 0 */
1599 "wait", /* 1 */
1600 "cancel", /* 2 */
1601 "unknown" /* 3 */
1602 };
1603 static int req_max_index = 3;
1604
1605 /* Check the caller's input. */
1606 if (req > (req_max_index)) {
1607 req = req_max_index;
1608 }
1609
1610 return reqArr[req];
1611 }
1612
1613
1614 /*
1615 *----------------------------------------------------------------------
1616 * Convert the queue req into a string.
1617 *
1618 * Results:
1619 *
1620 * Side effects:
1621 *
1622 *----------------------------------------------------------------------
1623 */
1624 static CONST char*
1625 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetQueueReqStr">GetQueueReqStr</a>(QueueRequests req)
1626 {
1627 static CONST char *reqArr[] = {
1628 "none", /* 0 */
1629 "delete" /* 1 */
1630 };
1631 static int req_max_index = 1;
1632
1633 /* Check the caller's input. */
1634 if (req > (req_max_index)) {
1635 req = req_max_index;
1636 }
1637
1638 return reqArr[req];
1639 }
1640
1641
1642 /*
1643 *----------------------------------------------------------------------
1644 * Convert the thread pool req into a string.
1645 *
1646 * Results:
1647 *
1648 * Side effects:
1649 *
1650 *----------------------------------------------------------------------
1651 */
1652 static CONST char*
1653 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_GetTpReqStr">GetTpReqStr</a>(ThreadPoolRequests req)
1654 {
1655 static CONST char *reqArr[] = {
1656 "none", /* 0 */
1657 "stop" /* 1 */
1658 };
1659 static int req_max_index = 1;
1660
1661 /* Check the caller's input. */
1662 if (req > (req_max_index)) {
1663 req = req_max_index;
1664 }
1665
1666 return reqArr[req];
1667 }
1668
1669
1670 /*
1671 *----------------------------------------------------------------------
1672 * Append job field to the job field list.
1673 *
1674 * Results:
1675 *
1676 * Side effects:
1677 *
1678 *----------------------------------------------------------------------
1679 */
1680
1681 static int
1682 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendField">AppendField</a>(Tcl_Interp *interp,
1683 Tcl_Obj *list,
1684 CONST char *name,
1685 CONST char *value)
1686 {
1687 /* Add Job ID */
1688 if (Tcl_ListObjAppendElement(interp, list,
1689 Tcl_NewStringObj(name,
1690 (int)strlen(name))) == TCL_ERROR) {
1691 /*
1692 * Note: If there is an error occurs within Tcl_ListObjAppendElement
1693 * it will set the result.
1694 */
1695 return TCL_ERROR;
1696 }
1697
1698 if (Tcl_ListObjAppendElement(interp, list,
1699 Tcl_NewStringObj(value,
1700 (int)strlen(value))) == TCL_ERROR) {
1701 return TCL_ERROR;
1702 }
1703
1704 return TCL_OK;
1705 }
1706
1707
1708 /*
1709 *----------------------------------------------------------------------
1710 * Append job field to the job field list.
1711 *
1712 * Results:
1713 *
1714 * Side effects:
1715 *
1716 *----------------------------------------------------------------------
1717 */
1718
1719 static int
1720 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendFieldInt">AppendFieldInt</a>(Tcl_Interp *interp,
1721 Tcl_Obj *list,
1722 CONST char *name,
1723 int value)
1724 {
1725 /* Add Job ID */
1726 if (Tcl_ListObjAppendElement(interp, list,
1727 Tcl_NewStringObj(name,
1728 (int)strlen(name))) == TCL_ERROR) {
1729 /*
1730 * Note: If there is an error occurs within Tcl_ListObjAppendElement
1731 * it will set the result.
1732 */
1733 return TCL_ERROR;
1734 }
1735
1736 if (Tcl_ListObjAppendElement(interp, list, Tcl_NewIntObj(value)) == TCL_ERROR) {
1737 return TCL_ERROR;
1738 }
1739
1740 return TCL_OK;
1741 }
1742
1743
1744 /*
1745 *----------------------------------------------------------------------
1746 * Append job field to the job field list.
1747 *
1748 * Results:
1749 *
1750 * Side effects:
1751 *
1752 *----------------------------------------------------------------------
1753 */
1754
1755 static int
1756 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendFieldLong">AppendFieldLong</a>(Tcl_Interp *interp,
1757 Tcl_Obj *list,
1758 CONST char *name,
1759 long value)
1760 {
1761 /* Add Job ID */
1762 if (Tcl_ListObjAppendElement(interp, list,
1763 Tcl_NewStringObj(name,
1764 (int)strlen(name))) == TCL_ERROR) {
1765 /*
1766 * Note: If there is an error occurs within Tcl_ListObjAppendElement
1767 * it will set the result.
1768 */
1769 return TCL_ERROR;
1770 }
1771
1772 if (Tcl_ListObjAppendElement(interp, list, Tcl_NewLongObj(value)) == TCL_ERROR) {
1773 return TCL_ERROR;
1774 }
1775
1776 return TCL_OK;
1777 }
1778
1779
1780 /*
1781 *----------------------------------------------------------------------
1782 * Append the job field to the job field list.
1783 *
1784 * Results:
1785 *
1786 * Side effects:
1787 *
1788 *----------------------------------------------------------------------
1789 */
1790
1791 static int
1792 <a href="/cvs/aolserver/aolserver/nsd/tcljob.c#A_AppendFieldDouble">AppendFieldDouble</a>(Tcl_Interp *interp,
1793 Tcl_Obj *list,
1794 CONST char *name,
1795 double value)
1796 {
1797 /* Add Job ID */
1798 if (Tcl_ListObjAppendElement(interp, list,
1799 Tcl_NewStringObj(name,
1800 (int)strlen(name))) == TCL_ERROR) {
1801 /*
1802 * Note: If there is an error occurs within Tcl_ListObjAppendElement
1803 * it will set the result.
1804 */
1805 return TCL_ERROR;
1806 }
1807
1808 if (Tcl_ListObjAppendElement(interp, list, Tcl_NewDoubleObj(value)) == TCL_ERROR) {
1809 return TCL_ERROR;
1810 }
1811
1812 return TCL_OK;
1813 }
1814
1815
1816 /*
1817 *----------------------------------------------------------------------
1818 * Compute the time difference and return the result in milliseconds.
1819 *----------------------------------------------------------------------
1820 */
1821
1822 static double ComputeDelta(Ns_Time *start, Ns_Time *end) {
1823 Ns_Time diff;
1824 Ns_DiffTime(end, start, &diff);
1825 return ((double)diff.sec * 1000.0) + ((double)diff.usec / 1000.0);
1826 }