#include <tcl.h> int Tcl_LimitCheck(interp) int Tcl_LimitReady(interp) int Tcl_LimitExceeded(interp) int Tcl_LimitTypeExceeded(interp, type) int Tcl_LimitTypeEnabled(interp, type) void Tcl_LimitTypeSet(interp, type) void Tcl_LimitTypeReset(interp, type) int Tcl_LimitGetCommands(interp) void Tcl_LimitSetCommands(interp, commandLimit) void Tcl_LimitGetTime(interp, timeLimitPtr) void Tcl_LimitSetTime(interp, timeLimitPtr) int Tcl_LimitGetGranularity(interp, type) void Tcl_LimitSetGranularity(interp, type, granularity) void Tcl_LimitAddHandler(interp, type, handlerProc, clientData, deleteProc) void Tcl_LimitRemoveHandler(interp, type, handlerProc, clientData)
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).
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 while loops).
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.
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 catch 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.
To check the resource limits for an interpreter, call Tcl_LimitCheck, 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 Tcl_LimitReady returns non-zero so that granularity policy is enforced. This API is designed to be similar in usage to Tcl_AsyncReady and Tcl_AsyncInvoke.
When writing code that may behave like catch in respect of errors, you should only trap an error if Tcl_LimitExceeded 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 Tcl_LimitTypeExceeded.
To check whether a limit has been set (but not whether it has actually been exceeded) on an interpreter, call Tcl_LimitTypeEnabled with the type of limit you want to check. To enable a particular limit call Tcl_LimitTypeSet, and to disable a limit call Tcl_LimitTypeReset.
The level of a command limit may be set using Tcl_LimitSetCommands, and retrieved using Tcl_LimitGetCommands. Similarly for a time limit with Tcl_LimitSetTime and Tcl_LimitGetTime respectively, but with that API the time limit is copied from and to the Tcl_Time structure that the timeLimitPtr argument points to.
The checking granularity for a particular limit may be set using Tcl_LimitSetGranularity and retrieved using Tcl_LimitGetGranularity. Note that granularities must always be positive.
To add a handler callback to be invoked when a limit is exceeded, call Tcl_LimitAddHandler. The handlerProc argument describes the function that will actually be called; it should have the following prototype:
typedef void Tcl_LimitHandlerProc( ClientData clientData, Tcl_Interp *interp);
The clientData argument to the handler will be whatever is passed to the clientData argment to Tcl_LimitAddHandler, and the interp is the interpreter that had its limit exceeded.
The deleteProc argument to Tcl_LimitAddHandler is a function to call to delete the clientData 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 Tcl_Free. Otherwise, it should refer to a function with the following prototype:
typedef void Tcl_LimitHandlerDeleteProc( ClientData clientData);
A limit handler may be deleted using Tcl_LimitRemoveHandler; the handler removed will be the first one found (out of the handlers added with Tcl_LimitAddHandler) with exactly matching type, handlerProc and clientData arguments. This function always invokes the deleteProc on the clientData (unless the deleteProc was NULL or TCL_STATIC).