From ac672933cc2154f0e86feca424d5ef39137aed5b Mon Sep 17 00:00:00 2001 From: dkf Date: Sun, 30 May 2004 12:18:23 +0000 Subject: Limits documentation and style improvements Added C docs, cleaned up C code, added note on behaviour of limited master interps --- ChangeLog | 10 + doc/Limit.3 | 196 +++++++++++++++++++ doc/interp.n | 11 +- generic/tclBasic.c | 4 +- generic/tclInt.h | 6 +- generic/tclInterp.c | 532 ++++++++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 712 insertions(+), 47 deletions(-) create mode 100644 doc/Limit.3 diff --git a/ChangeLog b/ChangeLog index c96b405..9af1998 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2004-05-30 Donal K. Fellows + + * generic/tclInterp.c: Added comments describing the purposes of + each function in the limit implementation and rewrote the names of + some non-public functions for greater clarity of purpose. + * doc/interp.n: Added note about what happens when a limited + interpreter creates a slave interpreter. + * doc/Limit.3: Added manual page for the resource limit + subsystem's C API. [Bug 953903] + 2004-05-29 Joe English * doc/global.n, doc/interp.n, doc/lrange.n: diff --git a/doc/Limit.3 b/doc/Limit.3 new file mode 100644 index 0000000..6502919 --- /dev/null +++ b/doc/Limit.3 @@ -0,0 +1,196 @@ +'\" +'\" Copyright (c) 2004 Donal K. Fellows +'\" +'\" See the file "license.terms" for information on usage and redistribution +'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. +'\" +'\" RCS: @(#) $Id: Limit.3,v 1.1 2004/05/30 12:18:25 dkf Exp $ +'\" +.so man.macros +.TH Tcl_LimitCheck 3 8.5 Tcl "Tcl Library Procedures" +.BS +.SH NAME +Tcl_LimitAddHandler, Tcl_LimitCheck, Tcl_LimitExceeded, Tcl_LimitGetCommands, Tcl_LimitGetGranularity, Tcl_LimitGetTime, Tcl_LimitReady, Tcl_LimitRemoveHandler, Tcl_LimitSetCommands, Tcl_LimitSetGranularity, Tcl_LimitSetTime, Tcl_LimitTypeEnabled, Tcl_LimitTypeExceeded, Tcl_LimitTypeReset, Tcl_LimitTypeSet \- manage and check resource limits on interpreters +.SH SYNOPSIS +.nf +\fB#include \fR +.sp +int +\fBTcl_LimitCheck\fR(\fIinterp\fR) +.sp +int +\fBTcl_LimitReady\fR(\fIinterp\fR) +.sp +int +\fBTcl_LimitExceeded\fR(\fIinterp\fR) +.sp +int +\fBTcl_LimitTypeExceeded\fR(\fIinterp, type\fR) +.sp +int +\fBTcl_LimitTypeEnabled\fR(\fIinterp, type\fR) +.sp +void +\fBTcl_LimitTypeSet\fR(\fIinterp, type\fR) +.sp +void +\fBTcl_LimitTypeReset\fR(\fIinterp, type\fR) +.sp +int +\fBTcl_LimitGetCommands\fR(\fIinterp\fR) +.sp +void +\fBTcl_LimitSetCommands\fR(\fIinterp, commandLimit\fR) +.sp +void +\fBTcl_LimitGetTime\fR(\fIinterp, timeLimitPtr\fR) +.sp +void +\fBTcl_LimitSetTime\fR(\fIinterp, timeLimitPtr\fR) +.sp +int +\fBTcl_LimitGetGranularity\fR(\fIinterp, type\fR) +.sp +void +\fBTcl_LimitSetGranularity\fR(\fIinterp, type, granularity\fR) +.sp +void +\fBTcl_LimitAddHandler\fR(\fIinterp, type, handlerProc, clientData, deleteProc\fR) +.sp +void +\fBTcl_LimitRemoveHandler\fR(\fIinterp, type, handlerProc, clientData\fR) +.SH ARGUMENTS +.AS Tcl_LimitHandlerDeleteProc commandLimit +.AP Tcl_Interp *interp in +Interpreter that the limit being managed applies to or that will have +its limits checked. +.AP int type in +The type of limit that the operation refers to. Must be either +TCL_LIMIT_COMMANDS or TCL_LIMIT_TIME. +.AP int commandLimit in +The maximum number of commands (as reported by \fBinfo cmdcount\fR) +that may be executed in the interpreter. +.AP Tcl_Time *timeLimitPtr in/out +A pointer to a structure that will either have the new time limit read +from (\fBTcl_LimitSetTime\fR) or the current time limit written to +(\fBTcl_LimitGetTime\fR). +.AP int granularity in +Divisor that indicates how often a particular limit should really be +checked. Must be at least 1. +.AP Tcl_LimitHandlerProc *handlerProc in +Function to call when a particular limit is exceeded. If the +\fIhandlerProc\fR removes or raises the limit during its processing, +the limited interpreter will be permitted to continue to process after +the handler returns. Many handlers may be attached to the same +interpreter limit; their order of execution is not defined, and they +must be identified by \fIhandlerProc\fR and \fIclientData\fR when they +are deleted. +.AP ClientData clientData in +Arbitrary pointer-sized word used to pass some context to the +\fIhandlerProc\fR function. +.AP Tcl_LimitHandlerDeleteProc *deleteProc in +Function to call whenever a handler is deleted. May be NULL if the +\fIclientData\fR requires no deletion. +.BE + +.SH DESCRIPTION +.PP +Tcl's interpreter resource limit subsystem allows for close control +over how much computation time a script may use, and is useful for +cases where a program is divided into multiple pieces where some parts +are more trusted than others (e.g. web application servers). +.PP +Every interpreter may have a limit on the wall-time for execution, and +a limit on the number of commands that the interpreter may execute. +Since checking of these limits is potentially expensive (especially +the time limit), each limit also has a checking granularity, which is +a divisor for an internal count of the number of points in the core +where a check may be performed (which is immediately before executing +a command and at an unspecified frequency between running commands, +which can happen in empty-bodied \fBwhile\fR loops). +.PP +The final component of the limit engine is a callback scheme which +allows for notifications of when a limit has been exceeded. These +callbacks can just provide logging, or may allocate more resources to +the interpreter to permit it to continue processing longer. +.PP +When a limit is exceeded (and the callbacks have run; the order of +execution of the callbacks is unspecified) execution in the limited +interpreter is stopped by raising an error and setting a flag that +prevents the \fBcatch\fR command in that interpreter from trapping +that error. It is up to the context that started execution in that +interpreter (typically a master interpreter) to handle the error. +.SH "LIMIT CHECKING API" +.PP +To check the resource limits for an interpreter, call +\fBTcl_LimitCheck\fR, which returns TCL_OK if the limit was not +exceeded (after processing callbacks) and TCL_ERROR if the limit was +exceeded (in which case an error message is also placed in the +interpreter result). That function should only be called when +\fBTcl_LimitReady\fR returns non-zero so that granularity policy is +enforced. This API is designed to be similar in usage to +\fBTcl_AsyncReady\fR and \fBTcl_AsyncInvoke\fR. +.PP +When writing code that may behave like \fBcatch\fR in respect of +errors, you should only trap an error if \fBTcl_LimitExceeded\fR +returns zero. If it returns non-zero, the interpreter is in a +limit-exceeded state and errors should be allowed to propagate to the +calling context. You can also check whether a particular type of +limit has been exceeded using \fBTcl_LimitTypeExceeded\fR. +.SH "LIMIT CONFIGURATION" +.PP +To check whether a limit has been set (but not whether it has actually +been exceeded) on an interpreter, call \fTcl_LimitTypeEnabled\fR with +the type of limit you want to check. To enable a particular limit +call \fBTcl_LimitTypeSet\fR, and to disable a limit call +\fBTcl_LimitTypeReset\fR. +.PP +The level of a command limit may be set using +\fBTcl_LimitSetCommands\fR, and retrieved using +\fBTcl_LimitGetCommands\fR. Similarly for a time limit with +\fRTcl_LimitSetTime\fR and \fBTcl_LimitGetTime\fR respectively, but +with that API the time limit is copied from and to the Tcl_Time +structure that the \fItimeLimitPtr\fR argument points to. +.PP +The checking granularity for a particular limit may be set using +\fBTcl_LimitSetGranularity\fR and retrieved using +\fBTcl_LimitGetGranularity\fR. Note that granularities must always be +positive. +.SS "LIMIT CALLBACKS" +.PP +To add a handler callback to be invoked when a limit is exceeded, call +\fBTcl_LimitAddHandler\fR. The \fIhandlerProc\fR argument describes +the function that will actually be called; it should have the +following prototype: +.PP +.CS +typedef void Tcl_LimitHandlerProc( + ClientData \fIclientData\fR, + Tcl_Interp *\fIinterp\fR); +.CE +.PP +The \fIclientData\fR argument to the handler will be whatever is +passed to the \fIclientData\fR argment to \fBTcl_LimitAddHandler\fR, +and the \fIinterp\fR is the interpreter that had its limit exceeded. +.PP +The \fIdeleteProc\fR argument to \fBTcl_LimitAddHandler\fR is a +function to call to delete the \fIclientData\fR value. It may be +TCL_STATIC or NULL if no deletion action is necessary, or TCL_DYNAMIC +if all that is necessary is to free the structure with +\fBTcl_Free\fR. Otherwise, it should refer to a function with the +following prototype: +.PP +.CS +typedef void Tcl_LimitHandlerDeleteProc( + ClientData \fIclientData\fR); +.CE +.PP +A limit handler may be deleted using \fBTcl_LimitRemoveHandler\fR; the +handler removed will be the first one found (out of the handlers added +with \fBTcl_LimitAddHandler\fR) with exactly matching \fItype\fR, +\fIhandlerProc\fR and \fIclientData\fR arguments. This function +always invokes the \fIdeleteProc\fR on the \fIclientData\fR (unless +the \fIdeleteProc\fR was NULL or TCL_STATIC). + +.SH KEYWORDS +interpreter, resource, limit, commands, time, callback diff --git a/doc/interp.n b/doc/interp.n index f645f99..0f5aba4 100644 --- a/doc/interp.n +++ b/doc/interp.n @@ -5,7 +5,7 @@ '\" See the file "license.terms" for information on usage and redistribution '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. '\" -'\" RCS: @(#) $Id: interp.n,v 1.12 2004/05/29 18:50:33 jenglish Exp $ +'\" RCS: @(#) $Id: interp.n,v 1.13 2004/05/30 12:18:25 dkf Exp $ '\" .so man.macros .TH interp n 7.6 Tcl "Tcl Built-In Commands" @@ -671,6 +671,15 @@ This option specifies the number of commands that the interpreter may execute before triggering the command limit. This option may be the empty string, which indicates that a command limit is not set for the interpreter. +.PP +Where an interpreter with a resource limit set on it creates a slave +interpreter, that slave interpreter will have resource limits imposed +on it that are at least as restrictive as the limits on the creating +master interpreter. If the master interpreter of the limited master +wishes to relax these conditions, it should hide the \fBinterp\fR +command in the child and then use aliases and the \fBinterp +invokehidden\fR subcommand to provide such access as it chooses to the +\fBinterp\fR command to the limited master as necessary. .VE 8.5 .SH CREDITS This mechanism is based on the Safe-Tcl prototype implemented diff --git a/generic/tclBasic.c b/generic/tclBasic.c index ca89219..cf68241 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -13,7 +13,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclBasic.c,v 1.103 2004/05/25 08:37:31 dkf Exp $ + * RCS: @(#) $Id: tclBasic.c,v 1.104 2004/05/30 12:18:25 dkf Exp $ */ #include "tclInt.h" @@ -989,7 +989,7 @@ DeleteInterpProc(interp) * this interpreter. */ - TclDecommissionLimitCallbacks(interp); + TclRemoveScriptLimitCallbacks(interp); TclLimitRemoveAllHandlers(interp); /* diff --git a/generic/tclInt.h b/generic/tclInt.h index c007b74..ebf5b57 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclInt.h,v 1.161 2004/05/20 13:04:11 dkf Exp $ + * RCS: @(#) $Id: tclInt.h,v 1.162 2004/05/30 12:18:25 dkf Exp $ */ #ifndef _TCLINT @@ -1714,8 +1714,6 @@ EXTERN int TclArraySet _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *arrayNameObj, Tcl_Obj *arrayElemObj)); EXTERN int TclCheckBadOctal _ANSI_ARGS_((Tcl_Interp *interp, CONST char *value)); -EXTERN void TclDecommissionLimitCallbacks _ANSI_ARGS_(( - Tcl_Interp *interp)); EXTERN void TclExpandTokenArray _ANSI_ARGS_(( Tcl_Parse *parsePtr)); EXTERN int TclFileAttrsCmd _ANSI_ARGS_((Tcl_Interp *interp, @@ -1901,6 +1899,8 @@ EXTERN void TclRememberCondition _ANSI_ARGS_((Tcl_Condition *mutex)); EXTERN void TclRememberDataKey _ANSI_ARGS_((Tcl_ThreadDataKey *mutex)); EXTERN VOID TclRememberJoinableThread _ANSI_ARGS_((Tcl_ThreadId id)); EXTERN void TclRememberMutex _ANSI_ARGS_((Tcl_Mutex *mutex)); +EXTERN void TclRemoveScriptLimitCallbacks _ANSI_ARGS_(( + Tcl_Interp *interp)); EXTERN VOID TclSignalExitThread _ANSI_ARGS_((Tcl_ThreadId id, int result)); EXTERN int TclSubstTokens _ANSI_ARGS_((Tcl_Interp *interp, diff --git a/generic/tclInterp.c b/generic/tclInterp.c index d2d2cea..fff810a 100644 --- a/generic/tclInterp.c +++ b/generic/tclInterp.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclInterp.c,v 1.34 2004/05/25 22:23:01 dkf Exp $ + * RCS: @(#) $Id: tclInterp.c,v 1.35 2004/05/30 12:18:26 dkf Exp $ */ #include "tclInt.h" @@ -216,15 +216,16 @@ static void SlaveObjCmdDeleteProc _ANSI_ARGS_(( static int SlaveRecursionLimit _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Interp *slaveInterp, int objc, Tcl_Obj *CONST objv[])); -static int SlaveCommandLimit _ANSI_ARGS_((Tcl_Interp *interp, +static int SlaveCommandLimitCmd _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Interp *slaveInterp, int consumedObjc, int objc, Tcl_Obj *CONST objv[])); -static int SlaveTimeLimit _ANSI_ARGS_((Tcl_Interp *interp, +static int SlaveTimeLimitCmd _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Interp *slaveInterp, int consumedObjc, int objc, Tcl_Obj *CONST objv[])); -static void InheritLimits _ANSI_ARGS_((Tcl_Interp *slaveInterp, +static void InheritLimitsFromMaster _ANSI_ARGS_(( + Tcl_Interp *slaveInterp, Tcl_Interp *masterInterp)); -static void SetLimitCallback _ANSI_ARGS_((Tcl_Interp *interp, +static void SetScriptLimitCallback _ANSI_ARGS_((Tcl_Interp *interp, int type, Tcl_Interp *targetInterp, Tcl_Obj *scriptObj)); static void CallScriptLimitCallback _ANSI_ARGS_(( @@ -689,9 +690,9 @@ Tcl_InterpObjCmd(clientData, interp, objc, objv) } switch ((enum LimitTypes) limitType) { case LIMIT_TYPE_COMMANDS: - return SlaveCommandLimit(interp, slaveInterp, 4, objc, objv); + return SlaveCommandLimitCmd(interp, slaveInterp, 4, objc,objv); case LIMIT_TYPE_TIME: - return SlaveTimeLimit(interp, slaveInterp, 4, objc, objv); + return SlaveTimeLimitCmd(interp, slaveInterp, 4, objc, objv); } } case OPT_MARKTRUSTED: { @@ -1908,7 +1909,7 @@ SlaveCreate(interp, pathPtr, safe) /* * Inherit the TIP#143 limits. */ - InheritLimits(slaveInterp, masterInterp); + InheritLimitsFromMaster(slaveInterp, masterInterp); return slaveInterp; @@ -2083,9 +2084,9 @@ SlaveObjCmd(clientData, interp, objc, objv) } switch ((enum LimitTypes) limitType) { case LIMIT_TYPE_COMMANDS: - return SlaveCommandLimit(interp, slaveInterp, 3, objc, objv); + return SlaveCommandLimitCmd(interp, slaveInterp, 3, objc,objv); case LIMIT_TYPE_TIME: - return SlaveTimeLimit(interp, slaveInterp, 3, objc, objv); + return SlaveTimeLimitCmd(interp, slaveInterp, 3, objc, objv); } } case OPT_MARKTRUSTED: { @@ -2583,6 +2584,24 @@ Tcl_MakeSafe(interp) return TCL_OK; } +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitExceeded -- + * + * Tests whether any limit has been exceededin the given + * interpreter (i.e. whether the interpreter is currently unable + * to process further scripts). + * + * Results: + * A boolean value. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + int Tcl_LimitExceeded(interp) Tcl_Interp *interp; @@ -2592,6 +2611,24 @@ Tcl_LimitExceeded(interp) return iPtr->limit.exceeded != 0; } +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitReady -- + * + * Find out whether any limit has been set on the interpreter, + * and if so check whether the granularity of that limit is such + * that the full limit check should be carried out. + * + * Results: + * A boolean value that indicates whether to call Tcl_LimitCheck. + * + * Side effects: + * Increments the limit granularity counter. + * + *---------------------------------------------------------------------- + */ + int Tcl_LimitReady(interp) Tcl_Interp *interp; @@ -2615,6 +2652,29 @@ Tcl_LimitReady(interp) return 0; } +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitCheck -- + * + * Check all currently set limits in the interpreter (where + * permitted by granularity). If a limit is exceeded, call its + * callbacks and, if the limit is still exceeded after the + * callbacks have run, make the interpreter generate an error + * that cannot be caught within the limited interpreter. + * + * Results: + * A Tcl result value (TCL_OK if no limit is exceeded, and + * TCL_ERROR if a limit has been exceeded). + * + * Side effects: + * May invoke system calls. May invoke other interpreters. May + * be reentrant. May put the interpreter into a state where it + * can no longer execute commands without outside intervention. + * + *---------------------------------------------------------------------- + */ + int Tcl_LimitCheck(interp) Tcl_Interp *interp; @@ -2673,6 +2733,24 @@ Tcl_LimitCheck(interp) return TCL_OK; } +/* + *---------------------------------------------------------------------- + * + * RunLimitHandlers -- + * + * Invoke all the limit handlers in a list (for a particular + * limit). Note that no particular limit handler callback will + * be invoked reentrantly. + * + * Results: + * None. + * + * Side effects: + * Depends on the limit handlers. + * + *---------------------------------------------------------------------- + */ + static void RunLimitHandlers(handlerPtr, interp) LimitHandler *handlerPtr; @@ -2723,6 +2801,22 @@ RunLimitHandlers(handlerPtr, interp) } } +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitAddHandler -- + * + * Add a callback handler for a particular resource limit. + * + * Results: + * None. + * + * Side effects: + * Extends the internal linked list of handlers for a limit. + * + *---------------------------------------------------------------------- + */ + void Tcl_LimitAddHandler(interp, type, handlerProc, clientData, deleteProc) Tcl_Interp *interp; @@ -2734,22 +2828,34 @@ Tcl_LimitAddHandler(interp, type, handlerProc, clientData, deleteProc) Interp *iPtr = (Interp *) interp; LimitHandler *handlerPtr; + /* + * Convert everything into a real deletion callback. + */ + if (deleteProc == (Tcl_LimitHandlerDeleteProc *) TCL_DYNAMIC) { - deleteProc = (Tcl_LimitHandlerDeleteProc *) Tcl_Alloc; + deleteProc = (Tcl_LimitHandlerDeleteProc *) Tcl_Free; } if (deleteProc == (Tcl_LimitHandlerDeleteProc *) TCL_STATIC) { deleteProc = (Tcl_LimitHandlerDeleteProc *) NULL; } + /* + * Allocate a handler record. + */ + + handlerPtr = (LimitHandler *) ckalloc(sizeof(LimitHandler)); + handlerPtr->flags = 0; + handlerPtr->handlerProc = handlerProc; + handlerPtr->clientData = clientData; + handlerPtr->deleteProc = deleteProc; + handlerPtr->prevPtr = NULL; + + /* + * Prepend onto the front of the correct linked list. + */ + switch (type) { case TCL_LIMIT_COMMANDS: - handlerPtr = (LimitHandler *) ckalloc(sizeof(LimitHandler)); - - handlerPtr->flags = 0; - handlerPtr->handlerProc = handlerProc; - handlerPtr->clientData = clientData; - handlerPtr->deleteProc = deleteProc; - handlerPtr->prevPtr = NULL; handlerPtr->nextPtr = iPtr->limit.cmdHandlers; if (handlerPtr->nextPtr != NULL) { handlerPtr->nextPtr->prevPtr = handlerPtr; @@ -2758,13 +2864,6 @@ Tcl_LimitAddHandler(interp, type, handlerProc, clientData, deleteProc) return; case TCL_LIMIT_TIME: - handlerPtr = (LimitHandler *) ckalloc(sizeof(LimitHandler)); - - handlerPtr->flags = 0; - handlerPtr->handlerProc = handlerProc; - handlerPtr->clientData = clientData; - handlerPtr->deleteProc = deleteProc; - handlerPtr->prevPtr = NULL; handlerPtr->nextPtr = iPtr->limit.timeHandlers; if (handlerPtr->nextPtr != NULL) { handlerPtr->nextPtr->prevPtr = handlerPtr; @@ -2776,6 +2875,25 @@ Tcl_LimitAddHandler(interp, type, handlerProc, clientData, deleteProc) Tcl_Panic("unknown type of resource limit"); } +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitRemoveHandler -- + * + * Remove a callback handler for a particular resource limit. + * + * Results: + * None. + * + * Side effects: + * The handler is spliced out of the internal linked list for the + * limit, and if not currently being invoked, deleted. Otherwise + * it is just marked for deletion and removed when the limit + * handler has finished executing. + * + *---------------------------------------------------------------------- + */ + void Tcl_LimitRemoveHandler(interp, type, handlerProc, clientData) Tcl_Interp *interp; @@ -2850,6 +2968,24 @@ Tcl_LimitRemoveHandler(interp, type, handlerProc, clientData) } } +/* + *---------------------------------------------------------------------- + * + * TclLimitRemoveAllHandlers -- + * + * Remove all limit callback handlers for an interpreter. This + * is invoked as part of deleting the interpreter. + * + * Results: + * None. + * + * Side effects: + * Limit handlers are deleted or marked for deletion (as with + * Tcl_LimitRemoveHandler). + * + *---------------------------------------------------------------------- + */ + void TclLimitRemoveAllHandlers(interp) Tcl_Interp *interp; @@ -2924,6 +3060,23 @@ TclLimitRemoveAllHandlers(interp) } } +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitTypeEnabled -- + * + * Check whether a particular limit has been enabled for an + * interpreter. + * + * Results: + * A boolean value. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + int Tcl_LimitTypeEnabled(interp, type) Tcl_Interp *interp; @@ -2933,6 +3086,24 @@ Tcl_LimitTypeEnabled(interp, type) return (iPtr->limit.active & type) != 0; } + +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitTypeExceeded -- + * + * Check whether a particular limit has been exceeded for an + * interpreter. + * + * Results: + * A boolean value (note that Tcl_LimitExceeded will always + * return non-zero when this function returns non-zero). + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ int Tcl_LimitTypeExceeded(interp, type) @@ -2943,6 +3114,24 @@ Tcl_LimitTypeExceeded(interp, type) return (iPtr->limit.exceeded & type) != 0; } + +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitTypeSet -- + * + * Enable a particular limit for an interpreter. + * + * Results: + * None. + * + * Side effects: + * The limit is turned on and will be checked in future at an + * interval determined by the frequency of calling of + * Tcl_LimitReady and the granularity of the limit in question. + * + *---------------------------------------------------------------------- + */ void Tcl_LimitTypeSet(interp, type) @@ -2953,6 +3142,25 @@ Tcl_LimitTypeSet(interp, type) iPtr->limit.active |= type; } + +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitTypeReset -- + * + * Disable a particular limit for an interpreter. + * + * Results: + * None. + * + * Side effects: + * The limit is disabled. If the limit was exceeded when this + * function was called, the limit will no longer be exceeded + * afterwards and the interpreter will be free to execute further + * scripts (assuming it isn't also deleted, of course). + * + *---------------------------------------------------------------------- + */ void Tcl_LimitTypeReset(interp, type) @@ -2965,6 +3173,25 @@ Tcl_LimitTypeReset(interp, type) iPtr->limit.exceeded &= ~type; } +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitSetCommands -- + * + * Set the command limit for an interpreter. + * + * Results: + * None. + * + * Side effects: + * Also resets whether the command limit was exceeded. This + * might permit a small amount of further execution in the + * interpreter even if the limit itself is theoretically + * exceeded. + * + *---------------------------------------------------------------------- + */ + void Tcl_LimitSetCommands(interp, commandLimit) Tcl_Interp *interp; @@ -2975,6 +3202,23 @@ Tcl_LimitSetCommands(interp, commandLimit) iPtr->limit.cmdCount = commandLimit; iPtr->limit.exceeded &= ~TCL_LIMIT_COMMANDS; } + +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitGetCommands -- + * + * Get the number of commands that may be executed in the + * interpreter before the command-limit is reached. + * + * Results: + * An upper bound on the number of commands. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ int Tcl_LimitGetCommands(interp) @@ -2985,6 +3229,25 @@ Tcl_LimitGetCommands(interp) return iPtr->limit.cmdCount; } +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitSetTime -- + * + * Set the time limit for an interpreter by copying it from the + * value pointed to by the timeLimitPtr argument. + * + * Results: + * None. + * + * Side effects: + * Also resets whether the time limit was exceeded. This might + * permit a small amount of further execution in the interpreter + * even if the limit itself is theoretically exceeded. + * + *---------------------------------------------------------------------- + */ + void Tcl_LimitSetTime(interp, timeLimitPtr) Tcl_Interp *interp; @@ -2995,6 +3258,23 @@ Tcl_LimitSetTime(interp, timeLimitPtr) memcpy(&iPtr->limit.time, timeLimitPtr, sizeof(Tcl_Time)); iPtr->limit.exceeded &= ~TCL_LIMIT_COMMANDS; } + +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitGetTime -- + * + * Get the current time limit. + * + * Results: + * The time limit (by it being copied into the variable pointed + * to by the timeLimitPtr). + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ void Tcl_LimitGetTime(interp, timeLimitPtr) @@ -3006,6 +3286,23 @@ Tcl_LimitGetTime(interp, timeLimitPtr) memcpy(timeLimitPtr, &iPtr->limit.time, sizeof(Tcl_Time)); } +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitSetGranularity -- + * + * Set the granularity divisor (which must be positive) for a + * particular limit. + * + * Results: + * None. + * + * Side effects: + * The granularity is updated. + * + *---------------------------------------------------------------------- + */ + void Tcl_LimitSetGranularity(interp, type, granularity) Tcl_Interp *interp; @@ -3027,6 +3324,22 @@ Tcl_LimitSetGranularity(interp, type, granularity) } Tcl_Panic("unknown type of resource limit"); } + +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitGetGranularity -- + * + * Get the granularity divisor for a particular limit. + * + * Results: + * The granularity divisor for the given limit. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ int Tcl_LimitGetGranularity(interp, type) @@ -3046,8 +3359,24 @@ Tcl_LimitGetGranularity(interp, type) } /* - * Callback for when a script limit is deleted. + *---------------------------------------------------------------------- + * + * DeleteScriptLimitCallback -- + * + * Callback for when a script limit (a limit callback implemented + * as a Tcl script in a master interpreter, as set up from Tcl) + * is deleted. + * + * Results: + * None. + * + * Side effects: + * The reference to the script callback from the controlling + * interpreter is removed. + * + *---------------------------------------------------------------------- */ + static void DeleteScriptLimitCallback(clientData) ClientData clientData; @@ -3059,10 +3388,25 @@ DeleteScriptLimitCallback(clientData) Tcl_DeleteHashEntry(limitCBPtr->entryPtr); ckfree((char *) limitCBPtr); } - + /* - * Callback for when a script limit is invoked. + *---------------------------------------------------------------------- + * + * CallScriptLimitCallback -- + * + * Invoke a script limit callback. Used to implement limit + * callbacks set at the Tcl level on child interpreters. + * + * Results: + * None. + * + * Side effects: + * Depends on the callback script. Errors are reported as + * background errors. + * + *---------------------------------------------------------------------- */ + static void CallScriptLimitCallback(clientData, interp) ClientData clientData; @@ -3083,14 +3427,31 @@ CallScriptLimitCallback(clientData, interp) } Tcl_Release(limitCBPtr->interp); } - + /* - * Install (or remove, if scriptObj is NULL) a limit callback script - * that is called when the target interpreter exceeds the type of - * limit specified. + *---------------------------------------------------------------------- + * + * SetScriptLimitCallback -- + * + * Install (or remove, if scriptObj is NULL) a limit callback + * script that is called when the target interpreter exceeds the + * type of limit specified. Each interpreter may only have one + * callback set on another interpreter through this mechanism + * (though as many interpreters may be limited as the programmer + * chooses overall). + * + * Results: + * None. + * + * Side effects: + * A limit callback implemented as an invokation of a Tcl script + * in another interpreter is either installed or removed. + * + *---------------------------------------------------------------------- */ + static void -SetLimitCallback(interp, type, targetInterp, scriptObj) +SetScriptLimitCallback(interp, type, targetInterp, scriptObj) Tcl_Interp *interp; int type; Tcl_Interp *targetInterp; @@ -3139,12 +3500,26 @@ SetLimitCallback(interp, type, targetInterp, scriptObj) } /* - * Remove all limit callback scripts that make callbacks into the - * given interpreter. + *---------------------------------------------------------------------- + * + * TclRemoveScriptLimitCallbacks -- + * + * Remove all script-implemented limit callbacks that make calls + * back into the given interpreter. This invoked as part of + * deleting an interpreter. + * + * Results: + * None. + * + * Side effects: + * The script limit callbacks are removed or marked for later + * removal. + * + *---------------------------------------------------------------------- */ void -TclDecommissionLimitCallbacks(interp) +TclRemoveScriptLimitCallbacks(interp) Tcl_Interp *interp; { Interp *iPtr = (Interp *) interp; @@ -3163,6 +3538,25 @@ TclDecommissionLimitCallbacks(interp) Tcl_DeleteHashTable(&iPtr->limit.callbacks); } +/* + *---------------------------------------------------------------------- + * + * TclInitLimitSupport -- + * + * Initialise all the parts of the interpreter relating to + * resource limit management. This allows an interpreter to both + * have limits set upon itself and set limits upon other + * interpreters. + * + * Results: + * None. + * + * Side effects: + * The resource limit subsystem is initialised for the interpreter. + * + *---------------------------------------------------------------------- + */ + void TclInitLimitSupport(interp) Tcl_Interp *interp; @@ -3182,8 +3576,28 @@ TclInitLimitSupport(interp) sizeof(struct ScriptLimitCallbackKey)/sizeof(int)); } +/* + *---------------------------------------------------------------------- + * + * InheritLimitsFromMaster -- + * + * Derive the interpreter limit configuration for a slave + * interpreter from the limit config for the master. + * + * Results: + * None. + * + * Side effects: + * The slave interpreter limits are set so that if the master has + * a limit, it may not exceed it by handing off work to slave + * interpreters. Note that this does not transfer limit + * callbacks from the master to the slave. + * + *---------------------------------------------------------------------- + */ + static void -InheritLimits(slaveInterp, masterInterp) +InheritLimitsFromMaster(slaveInterp, masterInterp) Tcl_Interp *slaveInterp, *masterInterp; { Interp *slavePtr = (Interp *) slaveInterp; @@ -3202,8 +3616,26 @@ InheritLimits(slaveInterp, masterInterp) } } +/* + *---------------------------------------------------------------------- + * + * SlaveCommandLimitCmd -- + * + * Implementation of the [interp limit $i commands] and [$i limit + * commands] subcommands. See the interp manual page for a full + * description. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Depends on the arguments. + * + *---------------------------------------------------------------------- + */ + static int -SlaveCommandLimit(interp, slaveInterp, consumedObjc, objc, objv) +SlaveCommandLimitCmd(interp, slaveInterp, consumedObjc, objc, objv) Tcl_Interp *interp; /* Current interpreter. */ Tcl_Interp *slaveInterp; /* Interpreter being adjusted. */ int consumedObjc; /* Number of args already parsed. */ @@ -3339,7 +3771,7 @@ SlaveCommandLimit(interp, slaveInterp, consumedObjc, objc, objv) } } if (scriptObj != NULL) { - SetLimitCallback(interp, TCL_LIMIT_COMMANDS, slaveInterp, + SetScriptLimitCallback(interp, TCL_LIMIT_COMMANDS, slaveInterp, (scriptLen > 0 ? scriptObj : NULL)); } if (granObj != NULL) { @@ -3357,8 +3789,26 @@ SlaveCommandLimit(interp, slaveInterp, consumedObjc, objc, objv) } } +/* + *---------------------------------------------------------------------- + * + * SlaveTimeLimitCmd -- + * + * Implementation of the [interp limit $i time] and [$i limit + * time] subcommands. See the interp manual page for a full + * description. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Depends on the arguments. + * + *---------------------------------------------------------------------- + */ + static int -SlaveTimeLimit(interp, slaveInterp, consumedObjc, objc, objv) +SlaveTimeLimitCmd(interp, slaveInterp, consumedObjc, objc, objv) Tcl_Interp *interp; /* Current interpreter. */ Tcl_Interp *slaveInterp; /* Interpreter being adjusted. */ int consumedObjc; /* Number of args already parsed. */ @@ -3568,7 +4018,7 @@ SlaveTimeLimit(interp, slaveInterp, consumedObjc, objc, objv) } } if (scriptObj != NULL) { - SetLimitCallback(interp, TCL_LIMIT_TIME, slaveInterp, + SetScriptLimitCallback(interp, TCL_LIMIT_TIME, slaveInterp, (scriptLen > 0 ? scriptObj : NULL)); } if (granObj != NULL) { -- cgit v0.12