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.4 - (show annotations) (download) (as text)
Thu Dec 24 19:50:08 2009 UTC (8 years ago) by dvrsn
Branch: MAIN
CVS Tags: aolserver_v45_r2_rc0, HEAD
Branch point for: aolserver_v45_r2
Changes since 1.3: +4 -4 lines
File MIME type: text/x-chdr
fixes for tcl8.6 compatibility
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 * tclloop.c --
32 *
33 * Replacements for the "for", "while", and "foreach" commands to be
34 * monitored and managed by "ns_loop_ctl" command.
35 */
36
37 static const char *RCSID = "@(#) $Header: /cvsroot-fuse/aolserver/aolserver/nsd/tclloop.c,v 1.4 2009/12/24 19:50:08 dvrsn Exp $, compiled: " __DATE__ " " __TIME__;
38
39 #include "nsd.h"
40
41 /*
42 * The following structure supports sending a script to a loop to eval.
43 */
44
45 typedef struct EvalData {
46 enum {
47 EVAL_WAIT,
48 EVAL_DONE,
49 EVAL_DROP
50 } state; /* Eval request state. */
51 int code; /* Script result code. */
52 Tcl_DString script; /* Script buffer. */
53 Tcl_DString result; /* <a href="/cvs/aolserver/aolserver/nsd/tclresp.c#A_Result">Result</a> buffer. */
54 } EvalData;
55
56 /*
57 * The following structure is allocated for the "while"
58 * and "for" commands to maintain a copy of the current
59 * args and provide a cancel flag.
60 */
61
62 typedef struct LoopData {
63 enum {
64 LOOP_RUN,
65 LOOP_PAUSE,
66 LOOP_CANCEL
67 } control; /* Loop control commands. */
68 unsigned int lid; /* Unique loop id. */
69 int tid; /* Thread id of script. */
70 unsigned int spins; /* Loop iterations. */
71 Ns_Time etime; /* Loop entry time. */
72 Tcl_HashEntry *hPtr; /* Entry in active loop table. */
73 Tcl_DString args; /* Copy of command args. */
74 EvalData *evalPtr; /* Eval request pending. */
75 } LoopData;
76
77 /*
78 * Static procedures defined in this file.
79 */
80
81 static int <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_CheckControl">CheckControl</a>(NsServer *servPtr, Tcl_Interp *interp, LoopData *dataPtr);
82 static void <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_EnterLoop">EnterLoop</a>(NsServer *servPtr, LoopData *dataPtr, int objc,
83 Tcl_Obj **objv);
84 static void <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_LeaveLoop">LeaveLoop</a>(NsServer *servPtr, LoopData *dataPtr);
85
86
87 /*
88 *----------------------------------------------------------------------
89 *
90 * <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_NsTclForObjCmd">NsTclForObjCmd</a> --
91 *
92 * This procedure is invoked to process the "for" Tcl command.
93 * See the user documentation for details on what it does.
94 *
95 * With the bytecode compiler, this procedure is only called when
96 * a command name is computed at runtime, and is "for" or the name
97 * to which "for" was renamed: e.g.,
98 * "set z for; $z {set i 0} {$i<100} {incr i} {puts $i}"
99 *
100 * Copied from the Tcl source with additional calls to the
101 * loop control facility.
102 *
103 * Results:
104 * A standard Tcl result.
105 *
106 * Side effects:
107 * See the user documentation.
108 *
109 *----------------------------------------------------------------------
110 */
111
112 int
113 <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_NsTclForObjCmd">NsTclForObjCmd</a>(arg, interp, objc, objv)
114 ClientData arg; /* Pointer to NsInterp. */
115 Tcl_Interp *interp; /* Current interpreter. */
116 int objc; /* Number of arguments. */
117 Tcl_Obj *CONST objv[]; /* Argument objects. */
118 {
119 NsInterp *itPtr = arg;
120 NsServer *servPtr = itPtr->servPtr;
121 LoopData data;
122 int result, value;
123
124 if (objc != 5) {
125 Tcl_WrongNumArgs(interp, 1, objv, "start test next command");
126 return TCL_ERROR;
127 }
128
129 result = Tcl_EvalObjEx(interp, objv[1], 0);
130 if (result != TCL_OK) {
131 if (result == TCL_ERROR) {
132 Tcl_AddErrorInfo(interp, "\n (\"for\" initial command)");
133 }
134 return result;
135 }
136 <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_EnterLoop">EnterLoop</a>(servPtr, &data, objc, objv);
137 while (1) {
138 /*
139 * We need to reset the result before passing it off to
140 * Tcl_ExprBooleanObj. Otherwise, any error message will be appended
141 * to the result of the last evaluation.
142 */
143
144 Tcl_ResetResult(interp);
145 result = Tcl_ExprBooleanObj(interp, objv[2], &value);
146 if (result != TCL_OK) {
147 goto done;
148 }
149 if (!value) {
150 break;
151 }
152 result = <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_CheckControl">CheckControl</a>(servPtr, interp, &data);
153 if (result == TCL_OK) {
154 result = Tcl_EvalObjEx(interp, objv[4], 0);
155 }
156 if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
157 if (result == TCL_ERROR) {
158 char msg[32 + TCL_INTEGER_SPACE];
159
160 sprintf(msg, "\n (\"for\" body line %d)",Tcl_GetErrorLine(interp));
161 Tcl_AddErrorInfo(interp, msg);
162 }
163 break;
164 }
165 result = Tcl_EvalObjEx(interp, objv[3], 0);
166 if (result == TCL_BREAK) {
167 break;
168 } else if (result != TCL_OK) {
169 if (result == TCL_ERROR) {
170 Tcl_AddErrorInfo(interp, "\n (\"for\" loop-end command)");
171 }
172 goto done;
173 }
174 }
175 if (result == TCL_BREAK) {
176 result = TCL_OK;
177 }
178 if (result == TCL_OK) {
179 Tcl_ResetResult(interp);
180 }
181 done:
182 <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_LeaveLoop">LeaveLoop</a>(servPtr, &data);
183 return result;
184 }
185
186
187 /*
188 *----------------------------------------------------------------------
189 *
190 * <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_NsTclWhileObjCmd">NsTclWhileObjCmd</a> --
191 *
192 * This procedure is invoked to process the "while" Tcl command.
193 * See the user documentation for details on what it does.
194 *
195 * With the bytecode compiler, this procedure is only called when
196 * a command name is computed at runtime, and is "while" or the name
197 * to which "while" was renamed: e.g., "set z while; $z {$i<100} {}"
198 *
199 * Copied from the Tcl source with additional calls to the
200 * loop control facility.
201 *
202 * Results:
203 * A standard Tcl result.
204 *
205 * Side effects:
206 * See the user documentation.
207 *
208 *----------------------------------------------------------------------
209 */
210
211 int
212 <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_NsTclWhileObjCmd">NsTclWhileObjCmd</a>(arg, interp, objc, objv)
213 ClientData arg; /* Pointer to NsInterp. */
214 Tcl_Interp *interp; /* Current interpreter. */
215 int objc; /* Number of arguments. */
216 Tcl_Obj *CONST objv[]; /* Argument objects. */
217 {
218 NsInterp *itPtr = arg;
219 NsServer *servPtr = itPtr->servPtr;
220 LoopData data;
221 int result, value;
222
223 if (objc != 3) {
224 Tcl_WrongNumArgs(interp, 1, objv, "test command");
225 return TCL_ERROR;
226 }
227 <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_EnterLoop">EnterLoop</a>(servPtr, &data, objc, objv);
228 while (1) {
229 result = Tcl_ExprBooleanObj(interp, objv[1], &value);
230 if (result != TCL_OK) {
231 goto done;
232 }
233 if (!value) {
234 break;
235 }
236 result = <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_CheckControl">CheckControl</a>(servPtr, interp, &data);
237 if (result == TCL_OK) {
238 result = Tcl_EvalObjEx(interp, objv[2], 0);
239 }
240 if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
241 if (result == TCL_ERROR) {
242 char msg[32 + TCL_INTEGER_SPACE];
243
244 sprintf(msg, "\n (\"while\" body line %d)",
245 Tcl_GetErrorLine(interp));
246 Tcl_AddErrorInfo(interp, msg);
247 }
248 break;
249 }
250 }
251 if (result == TCL_BREAK) {
252 result = TCL_OK;
253 }
254 if (result == TCL_OK) {
255 Tcl_ResetResult(interp);
256 }
257 done:
258 <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_LeaveLoop">LeaveLoop</a>(servPtr, &data);
259 return result;
260 }
261
262
263 /*
264 *----------------------------------------------------------------------
265 *
266 * <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_NsTclForeachObjCmd">NsTclForeachObjCmd</a> --
267 *
268 * This object-based procedure is invoked to process the "foreach" Tcl
269 * command. See the user documentation for details on what it does.
270 *
271 * Results:
272 * A standard Tcl object result.
273 *
274 * Side effects:
275 * See the user documentation.
276 *
277 *----------------------------------------------------------------------
278 */
279
280 int
281 <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_NsTclForeachObjCmd">NsTclForeachObjCmd</a>(arg, interp, objc, objv)
282 ClientData arg; /* Pointer to NsInterp. */
283 Tcl_Interp *interp; /* Current interpreter. */
284 int objc; /* Number of arguments. */
285 Tcl_Obj *CONST objv[]; /* Argument objects. */
286 {
287 NsInterp *itPtr = arg;
288 NsServer *servPtr = itPtr->servPtr;
289 LoopData data;
290 int result = TCL_OK;
291 int i; /* i selects a value list */
292 int j, maxj; /* Number of loop iterations */
293 int v; /* v selects a loop variable */
294 int numLists; /* Count of value lists */
295 Tcl_Obj *bodyPtr;
296
297 /*
298 * We copy the argument object pointers into a local array to avoid
299 * the problem that "objv" might become invalid. It is a pointer into
300 * the evaluation stack and that stack might be grown and reallocated
301 * if the loop body requires a large amount of stack space.
302 */
303
304 #define NUM_ARGS 9
305 Tcl_Obj *(argObjStorage[NUM_ARGS]);
306 Tcl_Obj **argObjv = argObjStorage;
307
308 #define STATIC_LIST_SIZE 4
309 int indexArray[STATIC_LIST_SIZE];
310 int varcListArray[STATIC_LIST_SIZE];
311 Tcl_Obj **varvListArray[STATIC_LIST_SIZE];
312 int argcListArray[STATIC_LIST_SIZE];
313 Tcl_Obj **argvListArray[STATIC_LIST_SIZE];
314
315 int *index = indexArray; /* Array of value list indices */
316 int *varcList = varcListArray; /* # loop variables per list */
317 Tcl_Obj ***varvList = varvListArray; /* Array of var name lists */
318 int *argcList = argcListArray; /* Array of value list sizes */
319 Tcl_Obj ***argvList = argvListArray; /* Array of value lists */
320
321 if (objc < 4 || (objc%2 != 0)) {
322 Tcl_WrongNumArgs(interp, 1, objv,
323 "varList list ?varList list ...? command");
324 return TCL_ERROR;
325 }
326 <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_EnterLoop">EnterLoop</a>(servPtr, &data, objc, objv);
327
328 /*
329 * Create the object argument array "argObjv". Make sure argObjv is
330 * large enough to hold the objc arguments.
331 */
332
333 if (objc > NUM_ARGS) {
334 argObjv = (Tcl_Obj **) ckalloc(objc * sizeof(Tcl_Obj *));
335 }
336 for (i = 0; i < objc; i++) {
337 argObjv[i] = objv[i];
338 }
339
340 /*
341 * Manage numList parallel value lists.
342 * argvList[i] is a value list counted by argcList[i]
343 * varvList[i] is the list of variables associated with the value list
344 * varcList[i] is the number of variables associated with the value list
345 * index[i] is the current pointer into the value list argvList[i]
346 */
347
348 numLists = (objc-2)/2;
349 if (numLists > STATIC_LIST_SIZE) {
350 index = (int *) ckalloc(numLists * sizeof(int));
351 varcList = (int *) ckalloc(numLists * sizeof(int));
352 varvList = (Tcl_Obj ***) ckalloc(numLists * sizeof(Tcl_Obj **));
353 argcList = (int *) ckalloc(numLists * sizeof(int));
354 argvList = (Tcl_Obj ***) ckalloc(numLists * sizeof(Tcl_Obj **));
355 }
356 for (i = 0; i < numLists; i++) {
357 index[i] = 0;
358 varcList[i] = 0;
359 varvList[i] = (Tcl_Obj **) NULL;
360 argcList[i] = 0;
361 argvList[i] = (Tcl_Obj **) NULL;
362 }
363
364 /*
365 * Break up the value lists and variable lists into elements
366 */
367
368 maxj = 0;
369 for (i = 0; i < numLists; i++) {
370 result = Tcl_ListObjGetElements(interp, argObjv[1+i*2],
371 &varcList[i], &varvList[i]);
372 if (result != TCL_OK) {
373 goto done;
374 }
375 if (varcList[i] < 1) {
376 Tcl_AppendToObj(Tcl_GetObjResult(interp),
377 "foreach varlist is empty", -1);
378 result = TCL_ERROR;
379 goto done;
380 }
381
382 result = Tcl_ListObjGetElements(interp, argObjv[2+i*2],
383 &argcList[i], &argvList[i]);
384 if (result != TCL_OK) {
385 goto done;
386 }
387
388 j = argcList[i] / varcList[i];
389 if ((argcList[i] % varcList[i]) != 0) {
390 j++;
391 }
392 if (j > maxj) {
393 maxj = j;
394 }
395 }
396
397 /*
398 * Iterate maxj times through the lists in parallel
399 * If some value lists run out of values, set loop vars to ""
400 */
401
402 bodyPtr = argObjv[objc-1];
403 for (j = 0; j < maxj; j++) {
404 for (i = 0; i < numLists; i++) {
405 /*
406 * Refetch the list members; we assume that the sizes are
407 * the same, but the array of elements might be different
408 * if the internal rep of the objects has been lost and
409 * recreated (it is too difficult to accurately tell when
410 * this happens, which can lead to some wierd crashes,
411 * like Bug #494348...)
412 */
413
414 result = Tcl_ListObjGetElements(interp, argObjv[1+i*2],
415 &varcList[i], &varvList[i]);
416 if (result != TCL_OK) {
417 panic("Tcl_ForeachObjCmd: could not reconvert variable list %d to a list object\n", i);
418 }
419 result = Tcl_ListObjGetElements(interp, argObjv[2+i*2],
420 &argcList[i], &argvList[i]);
421 if (result != TCL_OK) {
422 panic("Tcl_ForeachObjCmd: could not reconvert value list %d to a list object\n", i);
423 }
424
425 for (v = 0; v < varcList[i]; v++) {
426 int k = index[i]++;
427 Tcl_Obj *valuePtr, *varValuePtr;
428
429 if (k < argcList[i]) {
430 valuePtr = argvList[i][k];
431 } else {
432 valuePtr = Tcl_NewObj(); /* empty string */
433 }
434 Tcl_IncrRefCount(valuePtr);
435 varValuePtr = Tcl_ObjSetVar2(interp, varvList[i][v],
436 NULL, valuePtr, 0);
437 Tcl_DecrRefCount(valuePtr);
438 if (varValuePtr == NULL) {
439 Tcl_ResetResult(interp);
440 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
441 "couldn't set loop variable: \"",
442 Tcl_GetString(varvList[i][v]), "\"", (char *) NULL);
443 result = TCL_ERROR;
444 goto done;
445 }
446
447 }
448 }
449 result = <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_CheckControl">CheckControl</a>(servPtr, interp, &data);
450 if (result == TCL_OK) {
451 result = Tcl_EvalObjEx(interp, bodyPtr, 0);
452 }
453 if (result != TCL_OK) {
454 if (result == TCL_CONTINUE) {
455 result = TCL_OK;
456 } else if (result == TCL_BREAK) {
457 result = TCL_OK;
458 break;
459 } else if (result == TCL_ERROR) {
460 char msg[32 + TCL_INTEGER_SPACE];
461
462 sprintf(msg, "\n (\"foreach\" body line %d)",
463 Tcl_GetErrorLine(interp));
464 Tcl_AddObjErrorInfo(interp, msg, -1);
465 break;
466 } else {
467 break;
468 }
469 }
470 }
471 if (result == TCL_OK) {
472 Tcl_ResetResult(interp);
473 }
474
475 done:
476 if (numLists > STATIC_LIST_SIZE) {
477 ckfree((char *) index);
478 ckfree((char *) varcList);
479 ckfree((char *) argcList);
480 ckfree((char *) varvList);
481 ckfree((char *) argvList);
482 }
483 if (argObjv != argObjStorage) {
484 ckfree((char *) argObjv);
485 }
486 <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_LeaveLoop">LeaveLoop</a>(servPtr, &data);
487 return result;
488 #undef STATIC_LIST_SIZE
489 #undef NUM_ARGS
490 }
491
492
493 /*
494 *----------------------------------------------------------------------
495 *
496 * <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_NsTclLoopCtlObjCmd">NsTclLoopCtlObjCmd</a> --
497 *
498 * Control command to list all active for or while loops in
499 * any thread, get info (thread id and args) for an active
500 * loop, or signal cancel of a loop.
501 *
502 * Results:
503 * A standard Tcl result.
504 *
505 * Side effects:
506 * May cancel an active loop. Not cancel results in a
507 * TCL_ERROR result for the "for" or "while" command,
508 * an exception which can possibly be caught.
509 *
510 *----------------------------------------------------------------------
511 */
512
513 int
514 <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_NsTclLoopCtlObjCmd">NsTclLoopCtlObjCmd</a>(arg, interp, objc, objv)
515 ClientData arg; /* Pointer to NsInterp. */
516 Tcl_Interp *interp; /* Current interpreter. */
517 int objc; /* Number of arguments. */
518 Tcl_Obj *CONST objv[]; /* Argument objects. */
519 {
520 NsInterp *itPtr = arg;
521 NsServer *servPtr = itPtr->servPtr;
522 LoopData *dataPtr;
523 EvalData eval;
524 Tcl_HashEntry *hPtr;
525 Tcl_HashSearch search;
526 Ns_Time timeout;
527 int lid, result, len, status;
528 char *str = "";
529 Tcl_Obj *objPtr, *listPtr;
530 static CONST char *opts[] = {
531 "list", "info", "pause", "resume", "cancel", "eval",
532 "install", NULL
533 };
534 enum {
535 LListIdx, LInfoIdx, LPauseIdx, LResumeIdx, LCancelIdx, LEvalIdx,
536 LInstallIdx
537 } opt;
538 static CONST char *copts[] = {
539 "for", "foreach", "while", NULL
540 };
541 enum {
542 CForIdx, CForeachIdx, CWhileIdx
543 } copt;
544 static Tcl_ObjCmdProc *procs[] = {
545 <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_NsTclForObjCmd">NsTclForObjCmd</a>, <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_NsTclForeachObjCmd">NsTclForeachObjCmd</a>, <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_NsTclWhileObjCmd">NsTclWhileObjCmd</a>
546 };
547 if (objc < 2) {
548 Tcl_WrongNumArgs(interp, 1, objv, "option ?id?");
549 return TCL_ERROR;
550 }
551 if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0,
552 (int *) &opt) != TCL_OK) {
553 return TCL_ERROR;
554 }
555
556 /*
557 * Handle the list and install commands and verify arguments first.
558 */
559
560 switch (opt) {
561 case LListIdx:
562 if (objc != 2) {
563 Tcl_WrongNumArgs(interp, 2, objv, NULL);
564 return TCL_ERROR;
565 }
566 listPtr = Tcl_NewObj();
567 Ns_MutexLock(&servPtr->tcl.llock);
568 hPtr = Tcl_FirstHashEntry(&servPtr->tcl.loops, &search);
569 while (hPtr != NULL) {
570 lid = (int) Tcl_GetHashKey(&servPtr->tcl.loops, hPtr);
571 objPtr = Tcl_NewIntObj(lid);
572 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
573 hPtr = Tcl_NextHashEntry(&search);
574 }
575 Ns_MutexUnlock(&servPtr->tcl.llock);
576 Tcl_SetObjResult(interp, listPtr);
577 return TCL_OK;
578 break;
579
580 case LInstallIdx:
581 if (objc != 3) {
582 Tcl_WrongNumArgs(interp, 2, objv, "command");
583 return TCL_ERROR;
584 }
585 if (Tcl_GetIndexFromObj(interp, objv[2], copts, "command", 0,
586 (int *) &copt) != TCL_OK) {
587 return TCL_ERROR;
588 }
589 Tcl_CreateObjCommand(interp, copts[copt], procs[copt], arg, NULL);
590 return TCL_OK;
591 break;
592
593 case LEvalIdx:
594 if (objc != 4) {
595 Tcl_WrongNumArgs(interp, 2, objv, "id script");
596 return TCL_ERROR;
597 }
598 break;
599
600 default:
601 if (objc != 3) {
602 Tcl_WrongNumArgs(interp, 2, objv, "id");
603 return TCL_ERROR;
604 }
605 break;
606 }
607
608 /*
609 * All other commands require a loop id arg.
610 */
611
612 if (Tcl_GetIntFromObj(interp, objv[2], (int *) &lid) != TCL_OK) {
613 return TCL_ERROR;
614 }
615 result = TCL_OK;
616 Ns_MutexLock(&servPtr->tcl.llock);
617 hPtr = Tcl_FindHashEntry(&servPtr->tcl.loops, (char *) lid);
618 if (hPtr == NULL) {
619 switch (opt) {
620 case LInfoIdx:
621 case LEvalIdx:
622 Tcl_AppendResult(interp, "no such loop id: ",
623 Tcl_GetString(objv[2]), NULL);
624 result = TCL_ERROR;
625 break;
626 default:
627 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 0);
628 break;
629 }
630 goto done;
631 }
632
633 dataPtr = Tcl_GetHashValue(hPtr);
634 switch (opt) {
635 case LInstallIdx:
636 case LListIdx:
637 /* NB: Silence warning. */
638 break;
639
640 case LInfoIdx:
641 /*
642 * Info format is:
643 * {loop id} {thread id} {start time} {status} {command args}
644 */
645
646 listPtr = Tcl_NewObj();
647 objPtr = Tcl_NewIntObj(lid);
648 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
649 objPtr = Tcl_NewIntObj(dataPtr->tid);
650 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
651 objPtr = Tcl_NewObj();
652 <a href="/cvs/aolserver/aolserver/nsd/tclobj.c#A_Ns_TclSetTimeObj">Ns_TclSetTimeObj</a>(objPtr, &dataPtr->etime);
653 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
654 objPtr = Tcl_NewIntObj(dataPtr->spins);
655 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
656 switch (dataPtr->control) {
657 case LOOP_RUN:
658 str = "running";
659 break;
660 case LOOP_PAUSE:
661 str = "paused";
662 break;
663 case LOOP_CANCEL:
664 str = "canceled";
665 break;
666 }
667 objPtr = Tcl_NewStringObj(str, -1);
668 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
669 objPtr = Tcl_NewStringObj(dataPtr->args.string, dataPtr->args.length);
670 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
671 Tcl_SetObjResult(interp, listPtr);
672 break;
673
674 case LEvalIdx:
675 if (dataPtr->evalPtr != NULL) {
676 Tcl_SetResult(interp, "eval pending", TCL_STATIC);
677 result = TCL_ERROR;
678 goto done;
679 }
680
681 /*
682 * <a href="/cvs/aolserver/aolserver/nsd/sockcallback.c#A_Queue">Queue</a> new script to eval.
683 */
684
685 eval.state = EVAL_WAIT;
686 eval.code = TCL_OK;
687 Tcl_DStringInit(&eval.result);
688 Tcl_DStringInit(&eval.script);
689 str = Tcl_GetStringFromObj(objv[3], &len);
690 Tcl_DStringAppend(&eval.script, str, len);
691 dataPtr->evalPtr = &eval;
692
693 /*
694 * Wait for result.
695 */
696
697 Ns_GetTime(&timeout);
698 Ns_IncrTime(&timeout, 2, 0);
699 Ns_CondBroadcast(&servPtr->tcl.lcond);
700 status = NS_OK;
701 while (status == NS_OK && eval.state == EVAL_WAIT) {
702 status = Ns_CondTimedWait(&servPtr->tcl.lcond,
703 &servPtr->tcl.llock, &timeout);
704 }
705 switch (eval.state) {
706 case EVAL_WAIT:
707 Tcl_SetResult(interp, "timeout: result dropped", TCL_STATIC);
708 dataPtr->evalPtr = NULL;
709 result = TCL_ERROR;
710 break;
711 case EVAL_DROP:
712 Tcl_SetResult(interp, "dropped: loop exited", TCL_STATIC);
713 result = TCL_ERROR;
714 break;
715 case EVAL_DONE:
716 Tcl_DStringResult(interp, &eval.result);
717 result = eval.code;
718 }
719 Tcl_DStringFree(&eval.script);
720 Tcl_DStringFree(&eval.result);
721 break;
722
723 case LResumeIdx:
724 case LPauseIdx:
725 case LCancelIdx:
726 if (opt == LCancelIdx) {
727 dataPtr->control = LOOP_CANCEL;
728 } else if (opt == LPauseIdx) {
729 dataPtr->control = LOOP_PAUSE;
730 } else {
731 dataPtr->control = LOOP_RUN;
732 }
733 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 1);
734 Ns_CondBroadcast(&servPtr->tcl.lcond);
735 break;
736 }
737 done:
738 Ns_MutexUnlock(&servPtr->tcl.llock);
739 return result;
740 }
741
742
743 /*
744 *----------------------------------------------------------------------
745 *
746 * <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_EnterLoop">EnterLoop</a> --
747 *
748 * Add entry for the LoopData structure when a "for" or
749 * "while" command starts.
750 *
751 * Results:
752 * None.
753 *
754 * Side effects:
755 * Loop can be monitored and possibly canceled by "loop.ctl".
756 *
757 *----------------------------------------------------------------------
758 */
759
760 static void
761 <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_EnterLoop">EnterLoop</a>(NsServer *servPtr, LoopData *dataPtr, int objc, Tcl_Obj **objv)
762 {
763 static unsigned int next = 0;
764 int i, new;
765
766 dataPtr->control = LOOP_RUN;
767 dataPtr->spins = 0;
768 dataPtr->tid = Ns_ThreadId();
769 dataPtr->evalPtr = NULL;
770 Ns_GetTime(&dataPtr->etime);
771 /* NB: Must copy strings in case loop body updates or invalidates them. */
772 Tcl_DStringInit(&dataPtr->args);
773 for (i = 0; i < objc; ++i) {
774 Tcl_DStringAppendElement(&dataPtr->args, Tcl_GetString(objv[i]));
775 }
776 Ns_MutexLock(&servPtr->tcl.llock);
777 do {
778 dataPtr->lid = next++;
779 dataPtr->hPtr = Tcl_CreateHashEntry(&servPtr->tcl.loops,
780 (char *) dataPtr->lid, &new);
781 } while (!new);
782 Tcl_SetHashValue(dataPtr->hPtr, dataPtr);
783 Ns_MutexUnlock(&servPtr->tcl.llock);
784 }
785
786
787 /*
788 *----------------------------------------------------------------------
789 *
790 * <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_LeaveLoop">LeaveLoop</a> --
791 *
792 * Remove entry for the LoopData structure when a "for" or
793 * "while" command exits.
794 *
795 * Results:
796 * None.
797 *
798 * Side effects:
799 * None.
800 *
801 *----------------------------------------------------------------------
802 */
803
804 static void
805 <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_LeaveLoop">LeaveLoop</a>(NsServer *servPtr, LoopData *dataPtr)
806 {
807 Ns_MutexLock(&servPtr->tcl.llock);
808 if (dataPtr->evalPtr != NULL) {
809 dataPtr->evalPtr->state = EVAL_DROP;
810 Ns_CondBroadcast(&servPtr->tcl.lcond);
811 }
812 Tcl_DeleteHashEntry(dataPtr->hPtr);
813 Ns_MutexUnlock(&servPtr->tcl.llock);
814 Tcl_DStringFree(&dataPtr->args);
815 }
816
817
818 /*
819 *----------------------------------------------------------------------
820 *
821 * <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_CheckControl">CheckControl</a> --
822 *
823 * Check for control flag within a loop of a cancel or pause.
824 *
825 * Results:
826 * TCL_OK if not canceled, TCL_ERROR otherwise.
827 *
828 * Side effects:
829 * Leave cancel message as interp result.
830 *
831 *----------------------------------------------------------------------
832 */
833
834 static int
835 <a href="/cvs/aolserver/aolserver/nsd/tclloop.c#A_CheckControl">CheckControl</a>(NsServer *servPtr, Tcl_Interp *interp, LoopData *dataPtr)
836 {
837 Tcl_DString script;
838 char *str;
839 int result, len;
840
841 Ns_MutexLock(&servPtr->tcl.llock);
842 ++dataPtr->spins;
843 while (dataPtr->evalPtr != NULL || dataPtr->control == LOOP_PAUSE) {
844 if (dataPtr->evalPtr != NULL) {
845 Tcl_DStringInit(&script);
846 Tcl_DStringAppend(&script, dataPtr->evalPtr->script.string,
847 dataPtr->evalPtr->script.length);
848 Ns_MutexUnlock(&servPtr->tcl.llock);
849 result = Tcl_EvalEx(interp, script.string, script.length, 0);
850 Tcl_DStringFree(&script);
851 if (result != TCL_OK) {
852 <a href="/cvs/aolserver/aolserver/nsd/tclinit.c#A_Ns_TclLogError">Ns_TclLogError</a>(interp);
853 }
854 Ns_MutexLock(&servPtr->tcl.llock);
855 if (dataPtr->evalPtr == NULL) {
856 <a href="/cvs/aolserver/aolserver/nsd/log.c#A_Ns_Log">Ns_Log</a>(Error, "loopctl: dropped result: %s", Tcl_GetStringResult(interp));
857 } else {
858 str = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &len);
859 Tcl_DStringAppend(&dataPtr->evalPtr->result, str, len);
860 dataPtr->evalPtr->state = EVAL_DONE;
861 dataPtr->evalPtr = NULL;
862 Ns_CondBroadcast(&servPtr->tcl.lcond);
863 }
864 }
865 if (dataPtr->control == LOOP_PAUSE) {
866 Ns_CondWait(&servPtr->tcl.lcond, &servPtr->tcl.llock);
867 }
868 }
869 if (dataPtr->control == LOOP_CANCEL) {
870 Tcl_SetResult(interp, "loop canceled", TCL_STATIC);
871 result = TCL_ERROR;
872 } else {
873 result = TCL_OK;
874 }
875 Ns_MutexUnlock(&servPtr->tcl.llock);
876 return result;
877 }