|
/* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tcl_base/tclevent.c,v 1.1.1.1 2001/06/13 04:38:05 dtashley Exp $ */ |
|
|
|
|
|
/* |
|
|
* tclEvent.c -- |
|
|
* |
|
|
* This file implements some general event related interfaces including |
|
|
* background errors, exit handlers, and the "vwait" and "update" |
|
|
* command procedures. |
|
|
* |
|
|
* Copyright (c) 1990-1994 The Regents of the University of California. |
|
|
* Copyright (c) 1994-1998 Sun Microsystems, Inc. |
|
|
* |
|
|
* See the file "license.terms" for information on usage and redistribution |
|
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
|
|
* |
|
|
* RCS: @(#) $Id: tclevent.c,v 1.1.1.1 2001/06/13 04:38:05 dtashley Exp $ |
|
|
*/ |
|
|
|
|
|
#include "tclInt.h" |
|
|
#include "tclPort.h" |
|
|
|
|
|
/* |
|
|
* The data structure below is used to report background errors. One |
|
|
* such structure is allocated for each error; it holds information |
|
|
* about the interpreter and the error until bgerror can be invoked |
|
|
* later as an idle handler. |
|
|
*/ |
|
|
|
|
|
typedef struct BgError { |
|
|
Tcl_Interp *interp; /* Interpreter in which error occurred. NULL |
|
|
* means this error report has been cancelled |
|
|
* (a previous report generated a break). */ |
|
|
char *errorMsg; /* Copy of the error message (the interp's |
|
|
* result when the error occurred). |
|
|
* Malloc-ed. */ |
|
|
char *errorInfo; /* Value of the errorInfo variable |
|
|
* (malloc-ed). */ |
|
|
char *errorCode; /* Value of the errorCode variable |
|
|
* (malloc-ed). */ |
|
|
struct BgError *nextPtr; /* Next in list of all pending error |
|
|
* reports for this interpreter, or NULL |
|
|
* for end of list. */ |
|
|
} BgError; |
|
|
|
|
|
/* |
|
|
* One of the structures below is associated with the "tclBgError" |
|
|
* assoc data for each interpreter. It keeps track of the head and |
|
|
* tail of the list of pending background errors for the interpreter. |
|
|
*/ |
|
|
|
|
|
typedef struct ErrAssocData { |
|
|
BgError *firstBgPtr; /* First in list of all background errors |
|
|
* waiting to be processed for this |
|
|
* interpreter (NULL if none). */ |
|
|
BgError *lastBgPtr; /* Last in list of all background errors |
|
|
* waiting to be processed for this |
|
|
* interpreter (NULL if none). */ |
|
|
} ErrAssocData; |
|
|
|
|
|
/* |
|
|
* For each exit handler created with a call to Tcl_CreateExitHandler |
|
|
* there is a structure of the following type: |
|
|
*/ |
|
|
|
|
|
typedef struct ExitHandler { |
|
|
Tcl_ExitProc *proc; /* Procedure to call when process exits. */ |
|
|
ClientData clientData; /* One word of information to pass to proc. */ |
|
|
struct ExitHandler *nextPtr;/* Next in list of all exit handlers for |
|
|
* this application, or NULL for end of list. */ |
|
|
} ExitHandler; |
|
|
|
|
|
/* |
|
|
* There is both per-process and per-thread exit handlers. |
|
|
* The first list is controlled by a mutex. The other is in |
|
|
* thread local storage. |
|
|
*/ |
|
|
|
|
|
static ExitHandler *firstExitPtr = NULL; |
|
|
/* First in list of all exit handlers for |
|
|
* application. */ |
|
|
TCL_DECLARE_MUTEX(exitMutex) |
|
|
|
|
|
/* |
|
|
* This variable is set to 1 when Tcl_Finalize is called, and at the end of |
|
|
* its work, it is reset to 0. The variable is checked by TclInExit() to |
|
|
* allow different behavior for exit-time processing, e.g. in closing of |
|
|
* files and pipes. |
|
|
*/ |
|
|
|
|
|
static int inFinalize = 0; |
|
|
static int subsystemsInitialized = 0; |
|
|
|
|
|
typedef struct ThreadSpecificData { |
|
|
ExitHandler *firstExitPtr; /* First in list of all exit handlers for |
|
|
* this thread. */ |
|
|
int inExit; /* True when this thread is exiting. This |
|
|
* is used as a hack to decide to close |
|
|
* the standard channels. */ |
|
|
Tcl_Obj *tclLibraryPath; /* Path(s) to the Tcl library */ |
|
|
} ThreadSpecificData; |
|
|
static Tcl_ThreadDataKey dataKey; |
|
|
|
|
|
/* |
|
|
* Prototypes for procedures referenced only in this file: |
|
|
*/ |
|
|
|
|
|
static void BgErrorDeleteProc _ANSI_ARGS_((ClientData clientData, |
|
|
Tcl_Interp *interp)); |
|
|
static void HandleBgErrors _ANSI_ARGS_((ClientData clientData)); |
|
|
static char * VwaitVarProc _ANSI_ARGS_((ClientData clientData, |
|
|
Tcl_Interp *interp, char *name1, char *name2, |
|
|
int flags)); |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* Tcl_BackgroundError -- |
|
|
* |
|
|
* This procedure is invoked to handle errors that occur in Tcl |
|
|
* commands that are invoked in "background" (e.g. from event or |
|
|
* timer bindings). |
|
|
* |
|
|
* Results: |
|
|
* None. |
|
|
* |
|
|
* Side effects: |
|
|
* The command "bgerror" is invoked later as an idle handler to |
|
|
* process the error, passing it the error message. If that fails, |
|
|
* then an error message is output on stderr. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
void |
|
|
Tcl_BackgroundError(interp) |
|
|
Tcl_Interp *interp; /* Interpreter in which an error has |
|
|
* occurred. */ |
|
|
{ |
|
|
BgError *errPtr; |
|
|
char *errResult, *varValue; |
|
|
ErrAssocData *assocPtr; |
|
|
int length; |
|
|
|
|
|
/* |
|
|
* The Tcl_AddErrorInfo call below (with an empty string) ensures that |
|
|
* errorInfo gets properly set. It's needed in cases where the error |
|
|
* came from a utility procedure like Tcl_GetVar instead of Tcl_Eval; |
|
|
* in these cases errorInfo still won't have been set when this |
|
|
* procedure is called. |
|
|
*/ |
|
|
|
|
|
Tcl_AddErrorInfo(interp, ""); |
|
|
|
|
|
errResult = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &length); |
|
|
|
|
|
errPtr = (BgError *) ckalloc(sizeof(BgError)); |
|
|
errPtr->interp = interp; |
|
|
errPtr->errorMsg = (char *) ckalloc((unsigned) (length + 1)); |
|
|
memcpy(errPtr->errorMsg, errResult, (size_t) (length + 1)); |
|
|
varValue = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); |
|
|
if (varValue == NULL) { |
|
|
varValue = errPtr->errorMsg; |
|
|
} |
|
|
errPtr->errorInfo = (char *) ckalloc((unsigned) (strlen(varValue) + 1)); |
|
|
strcpy(errPtr->errorInfo, varValue); |
|
|
varValue = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY); |
|
|
if (varValue == NULL) { |
|
|
varValue = ""; |
|
|
} |
|
|
errPtr->errorCode = (char *) ckalloc((unsigned) (strlen(varValue) + 1)); |
|
|
strcpy(errPtr->errorCode, varValue); |
|
|
errPtr->nextPtr = NULL; |
|
|
|
|
|
assocPtr = (ErrAssocData *) Tcl_GetAssocData(interp, "tclBgError", |
|
|
(Tcl_InterpDeleteProc **) NULL); |
|
|
if (assocPtr == NULL) { |
|
|
|
|
|
/* |
|
|
* This is the first time a background error has occurred in |
|
|
* this interpreter. Create associated data to keep track of |
|
|
* pending error reports. |
|
|
*/ |
|
|
|
|
|
assocPtr = (ErrAssocData *) ckalloc(sizeof(ErrAssocData)); |
|
|
assocPtr->firstBgPtr = NULL; |
|
|
assocPtr->lastBgPtr = NULL; |
|
|
Tcl_SetAssocData(interp, "tclBgError", BgErrorDeleteProc, |
|
|
(ClientData) assocPtr); |
|
|
} |
|
|
if (assocPtr->firstBgPtr == NULL) { |
|
|
assocPtr->firstBgPtr = errPtr; |
|
|
Tcl_DoWhenIdle(HandleBgErrors, (ClientData) assocPtr); |
|
|
} else { |
|
|
assocPtr->lastBgPtr->nextPtr = errPtr; |
|
|
} |
|
|
assocPtr->lastBgPtr = errPtr; |
|
|
Tcl_ResetResult(interp); |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* HandleBgErrors -- |
|
|
* |
|
|
* This procedure is invoked as an idle handler to process all of |
|
|
* the accumulated background errors. |
|
|
* |
|
|
* Results: |
|
|
* None. |
|
|
* |
|
|
* Side effects: |
|
|
* Depends on what actions "bgerror" takes for the errors. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
static void |
|
|
HandleBgErrors(clientData) |
|
|
ClientData clientData; /* Pointer to ErrAssocData structure. */ |
|
|
{ |
|
|
Tcl_Interp *interp; |
|
|
char *argv[2]; |
|
|
int code; |
|
|
BgError *errPtr; |
|
|
ErrAssocData *assocPtr = (ErrAssocData *) clientData; |
|
|
Tcl_Channel errChannel; |
|
|
|
|
|
Tcl_Preserve((ClientData) assocPtr); |
|
|
|
|
|
while (assocPtr->firstBgPtr != NULL) { |
|
|
interp = assocPtr->firstBgPtr->interp; |
|
|
if (interp == NULL) { |
|
|
goto doneWithInterp; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Restore important state variables to what they were at |
|
|
* the time the error occurred. |
|
|
*/ |
|
|
|
|
|
Tcl_SetVar(interp, "errorInfo", assocPtr->firstBgPtr->errorInfo, |
|
|
TCL_GLOBAL_ONLY); |
|
|
Tcl_SetVar(interp, "errorCode", assocPtr->firstBgPtr->errorCode, |
|
|
TCL_GLOBAL_ONLY); |
|
|
|
|
|
/* |
|
|
* Create and invoke the bgerror command. |
|
|
*/ |
|
|
|
|
|
argv[0] = "bgerror"; |
|
|
argv[1] = assocPtr->firstBgPtr->errorMsg; |
|
|
|
|
|
Tcl_AllowExceptions(interp); |
|
|
Tcl_Preserve((ClientData) interp); |
|
|
code = TclGlobalInvoke(interp, 2, argv, 0); |
|
|
if (code == TCL_ERROR) { |
|
|
|
|
|
/* |
|
|
* If the interpreter is safe, we look for a hidden command |
|
|
* named "bgerror" and call that with the error information. |
|
|
* Otherwise, simply ignore the error. The rationale is that |
|
|
* this could be an error caused by a malicious applet trying |
|
|
* to cause an infinite barrage of error messages. The hidden |
|
|
* "bgerror" command can be used by a security policy to |
|
|
* interpose on such attacks and e.g. kill the applet after a |
|
|
* few attempts. |
|
|
*/ |
|
|
|
|
|
if (Tcl_IsSafe(interp)) { |
|
|
Tcl_SavedResult save; |
|
|
|
|
|
Tcl_SaveResult(interp, &save); |
|
|
TclGlobalInvoke(interp, 2, argv, TCL_INVOKE_HIDDEN); |
|
|
Tcl_RestoreResult(interp, &save); |
|
|
|
|
|
goto doneWithInterp; |
|
|
} |
|
|
|
|
|
/* |
|
|
* We have to get the error output channel at the latest possible |
|
|
* time, because the eval (above) might have changed the channel. |
|
|
*/ |
|
|
|
|
|
errChannel = Tcl_GetStdChannel(TCL_STDERR); |
|
|
if (errChannel != (Tcl_Channel) NULL) { |
|
|
char *string; |
|
|
int len; |
|
|
|
|
|
string = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &len); |
|
|
if (strcmp(string, "\"bgerror\" is an invalid command name or ambiguous abbreviation") == 0) { |
|
|
Tcl_WriteChars(errChannel, assocPtr->firstBgPtr->errorInfo, -1); |
|
|
Tcl_WriteChars(errChannel, "\n", -1); |
|
|
} else { |
|
|
Tcl_WriteChars(errChannel, |
|
|
"bgerror failed to handle background error.\n", |
|
|
-1); |
|
|
Tcl_WriteChars(errChannel, " Original error: ", -1); |
|
|
Tcl_WriteChars(errChannel, assocPtr->firstBgPtr->errorMsg, |
|
|
-1); |
|
|
Tcl_WriteChars(errChannel, "\n", -1); |
|
|
Tcl_WriteChars(errChannel, " Error in bgerror: ", -1); |
|
|
Tcl_WriteChars(errChannel, string, len); |
|
|
Tcl_WriteChars(errChannel, "\n", -1); |
|
|
} |
|
|
Tcl_Flush(errChannel); |
|
|
} |
|
|
} else if (code == TCL_BREAK) { |
|
|
|
|
|
/* |
|
|
* Break means cancel any remaining error reports for this |
|
|
* interpreter. |
|
|
*/ |
|
|
|
|
|
for (errPtr = assocPtr->firstBgPtr; errPtr != NULL; |
|
|
errPtr = errPtr->nextPtr) { |
|
|
if (errPtr->interp == interp) { |
|
|
errPtr->interp = NULL; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
/* |
|
|
* Discard the command and the information about the error report. |
|
|
*/ |
|
|
|
|
|
doneWithInterp: |
|
|
|
|
|
if (assocPtr->firstBgPtr) { |
|
|
ckfree(assocPtr->firstBgPtr->errorMsg); |
|
|
ckfree(assocPtr->firstBgPtr->errorInfo); |
|
|
ckfree(assocPtr->firstBgPtr->errorCode); |
|
|
errPtr = assocPtr->firstBgPtr->nextPtr; |
|
|
ckfree((char *) assocPtr->firstBgPtr); |
|
|
assocPtr->firstBgPtr = errPtr; |
|
|
} |
|
|
|
|
|
if (interp != NULL) { |
|
|
Tcl_Release((ClientData) interp); |
|
|
} |
|
|
} |
|
|
assocPtr->lastBgPtr = NULL; |
|
|
|
|
|
Tcl_Release((ClientData) assocPtr); |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* BgErrorDeleteProc -- |
|
|
* |
|
|
* This procedure is associated with the "tclBgError" assoc data |
|
|
* for an interpreter; it is invoked when the interpreter is |
|
|
* deleted in order to free the information assoicated with any |
|
|
* pending error reports. |
|
|
* |
|
|
* Results: |
|
|
* None. |
|
|
* |
|
|
* Side effects: |
|
|
* Background error information is freed: if there were any |
|
|
* pending error reports, they are cancelled. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
static void |
|
|
BgErrorDeleteProc(clientData, interp) |
|
|
ClientData clientData; /* Pointer to ErrAssocData structure. */ |
|
|
Tcl_Interp *interp; /* Interpreter being deleted. */ |
|
|
{ |
|
|
ErrAssocData *assocPtr = (ErrAssocData *) clientData; |
|
|
BgError *errPtr; |
|
|
|
|
|
while (assocPtr->firstBgPtr != NULL) { |
|
|
errPtr = assocPtr->firstBgPtr; |
|
|
assocPtr->firstBgPtr = errPtr->nextPtr; |
|
|
ckfree(errPtr->errorMsg); |
|
|
ckfree(errPtr->errorInfo); |
|
|
ckfree(errPtr->errorCode); |
|
|
ckfree((char *) errPtr); |
|
|
} |
|
|
Tcl_CancelIdleCall(HandleBgErrors, (ClientData) assocPtr); |
|
|
Tcl_EventuallyFree((ClientData) assocPtr, TCL_DYNAMIC); |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* Tcl_CreateExitHandler -- |
|
|
* |
|
|
* Arrange for a given procedure to be invoked just before the |
|
|
* application exits. |
|
|
* |
|
|
* Results: |
|
|
* None. |
|
|
* |
|
|
* Side effects: |
|
|
* Proc will be invoked with clientData as argument when the |
|
|
* application exits. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
void |
|
|
Tcl_CreateExitHandler(proc, clientData) |
|
|
Tcl_ExitProc *proc; /* Procedure to invoke. */ |
|
|
ClientData clientData; /* Arbitrary value to pass to proc. */ |
|
|
{ |
|
|
ExitHandler *exitPtr; |
|
|
|
|
|
exitPtr = (ExitHandler *) ckalloc(sizeof(ExitHandler)); |
|
|
exitPtr->proc = proc; |
|
|
exitPtr->clientData = clientData; |
|
|
Tcl_MutexLock(&exitMutex); |
|
|
exitPtr->nextPtr = firstExitPtr; |
|
|
firstExitPtr = exitPtr; |
|
|
Tcl_MutexUnlock(&exitMutex); |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* Tcl_DeleteExitHandler -- |
|
|
* |
|
|
* This procedure cancels an existing exit handler matching proc |
|
|
* and clientData, if such a handler exits. |
|
|
* |
|
|
* Results: |
|
|
* None. |
|
|
* |
|
|
* Side effects: |
|
|
* If there is an exit handler corresponding to proc and clientData |
|
|
* then it is cancelled; if no such handler exists then nothing |
|
|
* happens. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
void |
|
|
Tcl_DeleteExitHandler(proc, clientData) |
|
|
Tcl_ExitProc *proc; /* Procedure that was previously registered. */ |
|
|
ClientData clientData; /* Arbitrary value to pass to proc. */ |
|
|
{ |
|
|
ExitHandler *exitPtr, *prevPtr; |
|
|
|
|
|
Tcl_MutexLock(&exitMutex); |
|
|
for (prevPtr = NULL, exitPtr = firstExitPtr; exitPtr != NULL; |
|
|
prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) { |
|
|
if ((exitPtr->proc == proc) |
|
|
&& (exitPtr->clientData == clientData)) { |
|
|
if (prevPtr == NULL) { |
|
|
firstExitPtr = exitPtr->nextPtr; |
|
|
} else { |
|
|
prevPtr->nextPtr = exitPtr->nextPtr; |
|
|
} |
|
|
ckfree((char *) exitPtr); |
|
|
break; |
|
|
} |
|
|
} |
|
|
Tcl_MutexUnlock(&exitMutex); |
|
|
return; |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* Tcl_CreateThreadExitHandler -- |
|
|
* |
|
|
* Arrange for a given procedure to be invoked just before the |
|
|
* current thread exits. |
|
|
* |
|
|
* Results: |
|
|
* None. |
|
|
* |
|
|
* Side effects: |
|
|
* Proc will be invoked with clientData as argument when the |
|
|
* application exits. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
void |
|
|
Tcl_CreateThreadExitHandler(proc, clientData) |
|
|
Tcl_ExitProc *proc; /* Procedure to invoke. */ |
|
|
ClientData clientData; /* Arbitrary value to pass to proc. */ |
|
|
{ |
|
|
ExitHandler *exitPtr; |
|
|
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
|
|
|
|
|
exitPtr = (ExitHandler *) ckalloc(sizeof(ExitHandler)); |
|
|
exitPtr->proc = proc; |
|
|
exitPtr->clientData = clientData; |
|
|
exitPtr->nextPtr = tsdPtr->firstExitPtr; |
|
|
tsdPtr->firstExitPtr = exitPtr; |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* Tcl_DeleteThreadExitHandler -- |
|
|
* |
|
|
* This procedure cancels an existing exit handler matching proc |
|
|
* and clientData, if such a handler exits. |
|
|
* |
|
|
* Results: |
|
|
* None. |
|
|
* |
|
|
* Side effects: |
|
|
* If there is an exit handler corresponding to proc and clientData |
|
|
* then it is cancelled; if no such handler exists then nothing |
|
|
* happens. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
void |
|
|
Tcl_DeleteThreadExitHandler(proc, clientData) |
|
|
Tcl_ExitProc *proc; /* Procedure that was previously registered. */ |
|
|
ClientData clientData; /* Arbitrary value to pass to proc. */ |
|
|
{ |
|
|
ExitHandler *exitPtr, *prevPtr; |
|
|
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
|
|
|
|
|
for (prevPtr = NULL, exitPtr = tsdPtr->firstExitPtr; exitPtr != NULL; |
|
|
prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) { |
|
|
if ((exitPtr->proc == proc) |
|
|
&& (exitPtr->clientData == clientData)) { |
|
|
if (prevPtr == NULL) { |
|
|
tsdPtr->firstExitPtr = exitPtr->nextPtr; |
|
|
} else { |
|
|
prevPtr->nextPtr = exitPtr->nextPtr; |
|
|
} |
|
|
ckfree((char *) exitPtr); |
|
|
return; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* Tcl_Exit -- |
|
|
* |
|
|
* This procedure is called to terminate the application. |
|
|
* |
|
|
* Results: |
|
|
* None. |
|
|
* |
|
|
* Side effects: |
|
|
* All existing exit handlers are invoked, then the application |
|
|
* ends. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
void |
|
|
Tcl_Exit(status) |
|
|
int status; /* Exit status for application; typically |
|
|
* 0 for normal return, 1 for error return. */ |
|
|
{ |
|
|
Tcl_Finalize(); |
|
|
TclpExit(status); |
|
|
} |
|
|
|
|
|
/* |
|
|
*------------------------------------------------------------------------- |
|
|
* |
|
|
* TclSetLibraryPath -- |
|
|
* |
|
|
* Set the path that will be used for searching for init.tcl and |
|
|
* encodings when an interp is being created. |
|
|
* |
|
|
* Results: |
|
|
* None. |
|
|
* |
|
|
* Side effects: |
|
|
* Changing the library path will affect what directories are |
|
|
* examined when looking for encodings for all interps from that |
|
|
* point forward. |
|
|
* |
|
|
* The refcount of the new library path is incremented and the |
|
|
* refcount of the old path is decremented. |
|
|
* |
|
|
*------------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
void |
|
|
TclSetLibraryPath(pathPtr) |
|
|
Tcl_Obj *pathPtr; /* A Tcl list object whose elements are |
|
|
* the new library path. */ |
|
|
{ |
|
|
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
|
|
|
|
|
if (pathPtr != NULL) { |
|
|
Tcl_IncrRefCount(pathPtr); |
|
|
} |
|
|
if (tsdPtr->tclLibraryPath != NULL) { |
|
|
Tcl_DecrRefCount(tsdPtr->tclLibraryPath); |
|
|
} |
|
|
tsdPtr->tclLibraryPath = pathPtr; |
|
|
} |
|
|
|
|
|
/* |
|
|
*------------------------------------------------------------------------- |
|
|
* |
|
|
* TclGetLibraryPath -- |
|
|
* |
|
|
* Return a Tcl list object whose elements are the library path. |
|
|
* The caller should not modify the contents of the returned object. |
|
|
* |
|
|
* Results: |
|
|
* As above. |
|
|
* |
|
|
* Side effects: |
|
|
* None. |
|
|
* |
|
|
*------------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
Tcl_Obj * |
|
|
TclGetLibraryPath() |
|
|
{ |
|
|
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
|
|
return tsdPtr->tclLibraryPath; |
|
|
} |
|
|
|
|
|
/* |
|
|
*------------------------------------------------------------------------- |
|
|
* |
|
|
* TclInitSubsystems -- |
|
|
* |
|
|
* Initialize various subsytems in Tcl. This should be called the |
|
|
* first time an interp is created, or before any of the subsystems |
|
|
* are used. This function ensures an order for the initialization |
|
|
* of subsystems: |
|
|
* |
|
|
* 1. that cannot be initialized in lazy order because they are |
|
|
* mutually dependent. |
|
|
* |
|
|
* 2. so that they can be finalized in a known order w/o causing |
|
|
* the subsequent re-initialization of a subsystem in the act of |
|
|
* shutting down another. |
|
|
* |
|
|
* Results: |
|
|
* None. |
|
|
* |
|
|
* Side effects: |
|
|
* Varied, see the respective initialization routines. |
|
|
* |
|
|
*------------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
void |
|
|
TclInitSubsystems(argv0) |
|
|
CONST char *argv0; /* Name of executable from argv[0] to main() |
|
|
* in native multi-byte encoding. */ |
|
|
{ |
|
|
ThreadSpecificData *tsdPtr; |
|
|
|
|
|
if (inFinalize != 0) { |
|
|
panic("TclInitSubsystems called while finalizing"); |
|
|
} |
|
|
|
|
|
/* |
|
|
* Grab the thread local storage pointer before doing anything because |
|
|
* the initialization routines will be registering exit handlers. |
|
|
* We use this pointer to detect if this is the first time this |
|
|
* thread has created an interpreter. |
|
|
*/ |
|
|
|
|
|
tsdPtr = (ThreadSpecificData *) TclThreadDataKeyGet(&dataKey); |
|
|
|
|
|
if (subsystemsInitialized == 0) { |
|
|
/* |
|
|
* Double check inside the mutex. There are definitly calls |
|
|
* back into this routine from some of the procedures below. |
|
|
*/ |
|
|
|
|
|
TclpInitLock(); |
|
|
if (subsystemsInitialized == 0) { |
|
|
/* |
|
|
* Have to set this bit here to avoid deadlock with the |
|
|
* routines below us that call into TclInitSubsystems. |
|
|
*/ |
|
|
|
|
|
subsystemsInitialized = 1; |
|
|
|
|
|
tclExecutableName = NULL; |
|
|
|
|
|
/* |
|
|
* Initialize locks used by the memory allocators before anything |
|
|
* interesting happens so we can use the allocators in the |
|
|
* implementation of self-initializing locks. |
|
|
*/ |
|
|
#if USE_TCLALLOC |
|
|
TclInitAlloc(); /* process wide mutex init */ |
|
|
#endif |
|
|
#ifdef TCL_MEM_DEBUG |
|
|
TclInitDbCkalloc(); /* process wide mutex init */ |
|
|
#endif |
|
|
|
|
|
TclpInitPlatform(); /* creates signal handler(s) */ |
|
|
TclInitObjSubsystem(); /* register obj types, create mutexes */ |
|
|
TclInitIOSubsystem(); /* inits a tsd key (noop) */ |
|
|
TclInitEncodingSubsystem(); /* process wide encoding init */ |
|
|
TclInitNamespaceSubsystem(); /* register ns obj type (mutexed) */ |
|
|
} |
|
|
TclpInitUnlock(); |
|
|
} |
|
|
|
|
|
if (tsdPtr == NULL) { |
|
|
/* |
|
|
* First time this thread has created an interpreter. |
|
|
* We fetch the key again just in case no exit handlers were |
|
|
* registered by this point. |
|
|
*/ |
|
|
|
|
|
(void) TCL_TSD_INIT(&dataKey); |
|
|
TclInitNotifier(); |
|
|
} |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* Tcl_Finalize -- |
|
|
* |
|
|
* Shut down Tcl. First calls registered exit handlers, then |
|
|
* carefully shuts down various subsystems. |
|
|
* Called by Tcl_Exit or when the Tcl shared library is being |
|
|
* unloaded. |
|
|
* |
|
|
* Results: |
|
|
* None. |
|
|
* |
|
|
* Side effects: |
|
|
* Varied, see the respective finalization routines. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
void |
|
|
Tcl_Finalize() |
|
|
{ |
|
|
ExitHandler *exitPtr; |
|
|
ThreadSpecificData *tsdPtr; |
|
|
|
|
|
TclpInitLock(); |
|
|
tsdPtr = TCL_TSD_INIT(&dataKey); |
|
|
if (subsystemsInitialized != 0) { |
|
|
subsystemsInitialized = 0; |
|
|
|
|
|
/* |
|
|
* Invoke exit handlers first. |
|
|
*/ |
|
|
|
|
|
Tcl_MutexLock(&exitMutex); |
|
|
inFinalize = 1; |
|
|
for (exitPtr = firstExitPtr; exitPtr != NULL; exitPtr = firstExitPtr) { |
|
|
/* |
|
|
* Be careful to remove the handler from the list before |
|
|
* invoking its callback. This protects us against |
|
|
* double-freeing if the callback should call |
|
|
* Tcl_DeleteExitHandler on itself. |
|
|
*/ |
|
|
|
|
|
firstExitPtr = exitPtr->nextPtr; |
|
|
Tcl_MutexUnlock(&exitMutex); |
|
|
(*exitPtr->proc)(exitPtr->clientData); |
|
|
ckfree((char *) exitPtr); |
|
|
Tcl_MutexLock(&exitMutex); |
|
|
} |
|
|
firstExitPtr = NULL; |
|
|
Tcl_MutexUnlock(&exitMutex); |
|
|
|
|
|
/* |
|
|
* Clean up the library path now, before we invalidate thread-local |
|
|
* storage. |
|
|
*/ |
|
|
if (tsdPtr->tclLibraryPath != NULL) { |
|
|
Tcl_DecrRefCount(tsdPtr->tclLibraryPath); |
|
|
tsdPtr->tclLibraryPath = NULL; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Clean up after the current thread now, after exit handlers. |
|
|
* In particular, the testexithandler command sets up something |
|
|
* that writes to standard output, which gets closed. |
|
|
* Note that there is no thread-local storage after this call. |
|
|
*/ |
|
|
|
|
|
Tcl_FinalizeThread(); |
|
|
|
|
|
/* |
|
|
* Now finalize the Tcl execution environment. Note that this |
|
|
* must be done after the exit handlers, because there are |
|
|
* order dependencies. |
|
|
*/ |
|
|
|
|
|
TclFinalizeCompExecEnv(); |
|
|
TclFinalizeEnvironment(); |
|
|
|
|
|
TclFinalizeEncodingSubsystem(); |
|
|
|
|
|
if (tclExecutableName != NULL) { |
|
|
ckfree(tclExecutableName); |
|
|
tclExecutableName = NULL; |
|
|
} |
|
|
if (tclNativeExecutableName != NULL) { |
|
|
ckfree(tclNativeExecutableName); |
|
|
tclNativeExecutableName = NULL; |
|
|
} |
|
|
if (tclDefaultEncodingDir != NULL) { |
|
|
ckfree(tclDefaultEncodingDir); |
|
|
tclDefaultEncodingDir = NULL; |
|
|
} |
|
|
|
|
|
Tcl_SetPanicProc(NULL); |
|
|
|
|
|
/* |
|
|
* Free synchronization objects. There really should only be one |
|
|
* thread alive at this moment. |
|
|
*/ |
|
|
|
|
|
TclFinalizeSynchronization(); |
|
|
|
|
|
/* |
|
|
* We defer unloading of packages until very late |
|
|
* to avoid memory access issues. Both exit callbacks and |
|
|
* synchronization variables may be stored in packages. |
|
|
*/ |
|
|
|
|
|
TclFinalizeLoad(); |
|
|
|
|
|
/* |
|
|
* There shouldn't be any malloc'ed memory after this. |
|
|
*/ |
|
|
|
|
|
TclFinalizeMemorySubsystem(); |
|
|
inFinalize = 0; |
|
|
} |
|
|
TclpInitUnlock(); |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* Tcl_FinalizeThread -- |
|
|
* |
|
|
* Runs the exit handlers to allow Tcl to clean up its state |
|
|
* about a particular thread. |
|
|
* |
|
|
* Results: |
|
|
* None. |
|
|
* |
|
|
* Side effects: |
|
|
* Varied, see the respective finalization routines. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
void |
|
|
Tcl_FinalizeThread() |
|
|
{ |
|
|
ExitHandler *exitPtr; |
|
|
ThreadSpecificData *tsdPtr = |
|
|
(ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); |
|
|
|
|
|
if (tsdPtr != NULL) { |
|
|
/* |
|
|
* Invoke thread exit handlers first. |
|
|
*/ |
|
|
|
|
|
tsdPtr->inExit = 1; |
|
|
for (exitPtr = tsdPtr->firstExitPtr; exitPtr != NULL; |
|
|
exitPtr = tsdPtr->firstExitPtr) { |
|
|
/* |
|
|
* Be careful to remove the handler from the list before invoking |
|
|
* its callback. This protects us against double-freeing if the |
|
|
* callback should call Tcl_DeleteThreadExitHandler on itself. |
|
|
*/ |
|
|
|
|
|
tsdPtr->firstExitPtr = exitPtr->nextPtr; |
|
|
(*exitPtr->proc)(exitPtr->clientData); |
|
|
ckfree((char *) exitPtr); |
|
|
} |
|
|
TclFinalizeIOSubsystem(); |
|
|
TclFinalizeNotifier(); |
|
|
|
|
|
/* |
|
|
* Blow away all thread local storage blocks. |
|
|
*/ |
|
|
|
|
|
TclFinalizeThreadData(); |
|
|
} |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* TclInExit -- |
|
|
* |
|
|
* Determines if we are in the middle of exit-time cleanup. |
|
|
* |
|
|
* Results: |
|
|
* If we are in the middle of exiting, 1, otherwise 0. |
|
|
* |
|
|
* Side effects: |
|
|
* None. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
int |
|
|
TclInExit() |
|
|
{ |
|
|
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
|
|
return tsdPtr->inExit; |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* Tcl_VwaitObjCmd -- |
|
|
* |
|
|
* This procedure is invoked to process the "vwait" Tcl command. |
|
|
* See the user documentation for details on what it does. |
|
|
* |
|
|
* Results: |
|
|
* A standard Tcl result. |
|
|
* |
|
|
* Side effects: |
|
|
* See the user documentation. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
/* ARGSUSED */ |
|
|
int |
|
|
Tcl_VwaitObjCmd(clientData, interp, objc, objv) |
|
|
ClientData clientData; /* Not used. */ |
|
|
Tcl_Interp *interp; /* Current interpreter. */ |
|
|
int objc; /* Number of arguments. */ |
|
|
Tcl_Obj *CONST objv[]; /* Argument objects. */ |
|
|
{ |
|
|
int done, foundEvent; |
|
|
char *nameString; |
|
|
|
|
|
if (objc != 2) { |
|
|
Tcl_WrongNumArgs(interp, 1, objv, "name"); |
|
|
return TCL_ERROR; |
|
|
} |
|
|
nameString = Tcl_GetString(objv[1]); |
|
|
if (Tcl_TraceVar(interp, nameString, |
|
|
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, |
|
|
VwaitVarProc, (ClientData) &done) != TCL_OK) { |
|
|
return TCL_ERROR; |
|
|
}; |
|
|
done = 0; |
|
|
foundEvent = 1; |
|
|
while (!done && foundEvent) { |
|
|
foundEvent = Tcl_DoOneEvent(TCL_ALL_EVENTS); |
|
|
} |
|
|
Tcl_UntraceVar(interp, nameString, |
|
|
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, |
|
|
VwaitVarProc, (ClientData) &done); |
|
|
|
|
|
/* |
|
|
* Clear out the interpreter's result, since it may have been set |
|
|
* by event handlers. |
|
|
*/ |
|
|
|
|
|
Tcl_ResetResult(interp); |
|
|
if (!foundEvent) { |
|
|
Tcl_AppendResult(interp, "can't wait for variable \"", nameString, |
|
|
"\": would wait forever", (char *) NULL); |
|
|
return TCL_ERROR; |
|
|
} |
|
|
return TCL_OK; |
|
|
} |
|
|
|
|
|
/* ARGSUSED */ |
|
|
static char * |
|
|
VwaitVarProc(clientData, interp, name1, name2, flags) |
|
|
ClientData clientData; /* Pointer to integer to set to 1. */ |
|
|
Tcl_Interp *interp; /* Interpreter containing variable. */ |
|
|
char *name1; /* Name of variable. */ |
|
|
char *name2; /* Second part of variable name. */ |
|
|
int flags; /* Information about what happened. */ |
|
|
{ |
|
|
int *donePtr = (int *) clientData; |
|
|
|
|
|
*donePtr = 1; |
|
|
return (char *) NULL; |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* Tcl_UpdateObjCmd -- |
|
|
* |
|
|
* This procedure is invoked to process the "update" Tcl command. |
|
|
* See the user documentation for details on what it does. |
|
|
* |
|
|
* Results: |
|
|
* A standard Tcl result. |
|
|
* |
|
|
* Side effects: |
|
|
* See the user documentation. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
/* ARGSUSED */ |
|
|
int |
|
|
Tcl_UpdateObjCmd(clientData, interp, objc, objv) |
|
|
ClientData clientData; /* Not used. */ |
|
|
Tcl_Interp *interp; /* Current interpreter. */ |
|
|
int objc; /* Number of arguments. */ |
|
|
Tcl_Obj *CONST objv[]; /* Argument objects. */ |
|
|
{ |
|
|
int optionIndex; |
|
|
int flags = 0; /* Initialized to avoid compiler warning. */ |
|
|
static char *updateOptions[] = {"idletasks", (char *) NULL}; |
|
|
enum updateOptions {REGEXP_IDLETASKS}; |
|
|
|
|
|
if (objc == 1) { |
|
|
flags = TCL_ALL_EVENTS|TCL_DONT_WAIT; |
|
|
} else if (objc == 2) { |
|
|
if (Tcl_GetIndexFromObj(interp, objv[1], updateOptions, |
|
|
"option", 0, &optionIndex) != TCL_OK) { |
|
|
return TCL_ERROR; |
|
|
} |
|
|
switch ((enum updateOptions) optionIndex) { |
|
|
case REGEXP_IDLETASKS: { |
|
|
flags = TCL_WINDOW_EVENTS|TCL_IDLE_EVENTS|TCL_DONT_WAIT; |
|
|
break; |
|
|
} |
|
|
default: { |
|
|
panic("Tcl_UpdateObjCmd: bad option index to UpdateOptions"); |
|
|
} |
|
|
} |
|
|
} else { |
|
|
Tcl_WrongNumArgs(interp, 1, objv, "?idletasks?"); |
|
|
return TCL_ERROR; |
|
|
} |
|
|
|
|
|
while (Tcl_DoOneEvent(flags) != 0) { |
|
|
/* Empty loop body */ |
|
|
} |
|
|
|
|
|
/* |
|
|
* Must clear the interpreter's result because event handlers could |
|
|
* have executed commands. |
|
|
*/ |
|
|
|
|
|
Tcl_ResetResult(interp); |
|
|
return TCL_OK; |
|
|
} |
|
|
|
|
|
|
|
|
/* $History: tclevent.c $ |
|
|
* |
|
|
* ***************** Version 1 ***************** |
|
|
* User: Dtashley Date: 1/02/01 Time: 1:26a |
|
|
* Created in $/IjuScripter, IjuConsole/Source/Tcl Base |
|
|
* Initial check-in. |
|
|
*/ |
|
|
|
|
|
/* End of TCLEVENT.C */ |
|
1 |
|
/* $Header$ */ |
2 |
|
/* |
3 |
|
* tclEvent.c -- |
4 |
|
* |
5 |
|
* This file implements some general event related interfaces including |
6 |
|
* background errors, exit handlers, and the "vwait" and "update" |
7 |
|
* command procedures. |
8 |
|
* |
9 |
|
* Copyright (c) 1990-1994 The Regents of the University of California. |
10 |
|
* Copyright (c) 1994-1998 Sun Microsystems, Inc. |
11 |
|
* |
12 |
|
* See the file "license.terms" for information on usage and redistribution |
13 |
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
14 |
|
* |
15 |
|
* RCS: @(#) $Id: tclevent.c,v 1.1.1.1 2001/06/13 04:38:05 dtashley Exp $ |
16 |
|
*/ |
17 |
|
|
18 |
|
#include "tclInt.h" |
19 |
|
#include "tclPort.h" |
20 |
|
|
21 |
|
/* |
22 |
|
* The data structure below is used to report background errors. One |
23 |
|
* such structure is allocated for each error; it holds information |
24 |
|
* about the interpreter and the error until bgerror can be invoked |
25 |
|
* later as an idle handler. |
26 |
|
*/ |
27 |
|
|
28 |
|
typedef struct BgError { |
29 |
|
Tcl_Interp *interp; /* Interpreter in which error occurred. NULL |
30 |
|
* means this error report has been cancelled |
31 |
|
* (a previous report generated a break). */ |
32 |
|
char *errorMsg; /* Copy of the error message (the interp's |
33 |
|
* result when the error occurred). |
34 |
|
* Malloc-ed. */ |
35 |
|
char *errorInfo; /* Value of the errorInfo variable |
36 |
|
* (malloc-ed). */ |
37 |
|
char *errorCode; /* Value of the errorCode variable |
38 |
|
* (malloc-ed). */ |
39 |
|
struct BgError *nextPtr; /* Next in list of all pending error |
40 |
|
* reports for this interpreter, or NULL |
41 |
|
* for end of list. */ |
42 |
|
} BgError; |
43 |
|
|
44 |
|
/* |
45 |
|
* One of the structures below is associated with the "tclBgError" |
46 |
|
* assoc data for each interpreter. It keeps track of the head and |
47 |
|
* tail of the list of pending background errors for the interpreter. |
48 |
|
*/ |
49 |
|
|
50 |
|
typedef struct ErrAssocData { |
51 |
|
BgError *firstBgPtr; /* First in list of all background errors |
52 |
|
* waiting to be processed for this |
53 |
|
* interpreter (NULL if none). */ |
54 |
|
BgError *lastBgPtr; /* Last in list of all background errors |
55 |
|
* waiting to be processed for this |
56 |
|
* interpreter (NULL if none). */ |
57 |
|
} ErrAssocData; |
58 |
|
|
59 |
|
/* |
60 |
|
* For each exit handler created with a call to Tcl_CreateExitHandler |
61 |
|
* there is a structure of the following type: |
62 |
|
*/ |
63 |
|
|
64 |
|
typedef struct ExitHandler { |
65 |
|
Tcl_ExitProc *proc; /* Procedure to call when process exits. */ |
66 |
|
ClientData clientData; /* One word of information to pass to proc. */ |
67 |
|
struct ExitHandler *nextPtr;/* Next in list of all exit handlers for |
68 |
|
* this application, or NULL for end of list. */ |
69 |
|
} ExitHandler; |
70 |
|
|
71 |
|
/* |
72 |
|
* There is both per-process and per-thread exit handlers. |
73 |
|
* The first list is controlled by a mutex. The other is in |
74 |
|
* thread local storage. |
75 |
|
*/ |
76 |
|
|
77 |
|
static ExitHandler *firstExitPtr = NULL; |
78 |
|
/* First in list of all exit handlers for |
79 |
|
* application. */ |
80 |
|
TCL_DECLARE_MUTEX(exitMutex) |
81 |
|
|
82 |
|
/* |
83 |
|
* This variable is set to 1 when Tcl_Finalize is called, and at the end of |
84 |
|
* its work, it is reset to 0. The variable is checked by TclInExit() to |
85 |
|
* allow different behavior for exit-time processing, e.g. in closing of |
86 |
|
* files and pipes. |
87 |
|
*/ |
88 |
|
|
89 |
|
static int inFinalize = 0; |
90 |
|
static int subsystemsInitialized = 0; |
91 |
|
|
92 |
|
typedef struct ThreadSpecificData { |
93 |
|
ExitHandler *firstExitPtr; /* First in list of all exit handlers for |
94 |
|
* this thread. */ |
95 |
|
int inExit; /* True when this thread is exiting. This |
96 |
|
* is used as a hack to decide to close |
97 |
|
* the standard channels. */ |
98 |
|
Tcl_Obj *tclLibraryPath; /* Path(s) to the Tcl library */ |
99 |
|
} ThreadSpecificData; |
100 |
|
static Tcl_ThreadDataKey dataKey; |
101 |
|
|
102 |
|
/* |
103 |
|
* Prototypes for procedures referenced only in this file: |
104 |
|
*/ |
105 |
|
|
106 |
|
static void BgErrorDeleteProc _ANSI_ARGS_((ClientData clientData, |
107 |
|
Tcl_Interp *interp)); |
108 |
|
static void HandleBgErrors _ANSI_ARGS_((ClientData clientData)); |
109 |
|
static char * VwaitVarProc _ANSI_ARGS_((ClientData clientData, |
110 |
|
Tcl_Interp *interp, char *name1, char *name2, |
111 |
|
int flags)); |
112 |
|
|
113 |
|
/* |
114 |
|
*---------------------------------------------------------------------- |
115 |
|
* |
116 |
|
* Tcl_BackgroundError -- |
117 |
|
* |
118 |
|
* This procedure is invoked to handle errors that occur in Tcl |
119 |
|
* commands that are invoked in "background" (e.g. from event or |
120 |
|
* timer bindings). |
121 |
|
* |
122 |
|
* Results: |
123 |
|
* None. |
124 |
|
* |
125 |
|
* Side effects: |
126 |
|
* The command "bgerror" is invoked later as an idle handler to |
127 |
|
* process the error, passing it the error message. If that fails, |
128 |
|
* then an error message is output on stderr. |
129 |
|
* |
130 |
|
*---------------------------------------------------------------------- |
131 |
|
*/ |
132 |
|
|
133 |
|
void |
134 |
|
Tcl_BackgroundError(interp) |
135 |
|
Tcl_Interp *interp; /* Interpreter in which an error has |
136 |
|
* occurred. */ |
137 |
|
{ |
138 |
|
BgError *errPtr; |
139 |
|
char *errResult, *varValue; |
140 |
|
ErrAssocData *assocPtr; |
141 |
|
int length; |
142 |
|
|
143 |
|
/* |
144 |
|
* The Tcl_AddErrorInfo call below (with an empty string) ensures that |
145 |
|
* errorInfo gets properly set. It's needed in cases where the error |
146 |
|
* came from a utility procedure like Tcl_GetVar instead of Tcl_Eval; |
147 |
|
* in these cases errorInfo still won't have been set when this |
148 |
|
* procedure is called. |
149 |
|
*/ |
150 |
|
|
151 |
|
Tcl_AddErrorInfo(interp, ""); |
152 |
|
|
153 |
|
errResult = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &length); |
154 |
|
|
155 |
|
errPtr = (BgError *) ckalloc(sizeof(BgError)); |
156 |
|
errPtr->interp = interp; |
157 |
|
errPtr->errorMsg = (char *) ckalloc((unsigned) (length + 1)); |
158 |
|
memcpy(errPtr->errorMsg, errResult, (size_t) (length + 1)); |
159 |
|
varValue = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); |
160 |
|
if (varValue == NULL) { |
161 |
|
varValue = errPtr->errorMsg; |
162 |
|
} |
163 |
|
errPtr->errorInfo = (char *) ckalloc((unsigned) (strlen(varValue) + 1)); |
164 |
|
strcpy(errPtr->errorInfo, varValue); |
165 |
|
varValue = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY); |
166 |
|
if (varValue == NULL) { |
167 |
|
varValue = ""; |
168 |
|
} |
169 |
|
errPtr->errorCode = (char *) ckalloc((unsigned) (strlen(varValue) + 1)); |
170 |
|
strcpy(errPtr->errorCode, varValue); |
171 |
|
errPtr->nextPtr = NULL; |
172 |
|
|
173 |
|
assocPtr = (ErrAssocData *) Tcl_GetAssocData(interp, "tclBgError", |
174 |
|
(Tcl_InterpDeleteProc **) NULL); |
175 |
|
if (assocPtr == NULL) { |
176 |
|
|
177 |
|
/* |
178 |
|
* This is the first time a background error has occurred in |
179 |
|
* this interpreter. Create associated data to keep track of |
180 |
|
* pending error reports. |
181 |
|
*/ |
182 |
|
|
183 |
|
assocPtr = (ErrAssocData *) ckalloc(sizeof(ErrAssocData)); |
184 |
|
assocPtr->firstBgPtr = NULL; |
185 |
|
assocPtr->lastBgPtr = NULL; |
186 |
|
Tcl_SetAssocData(interp, "tclBgError", BgErrorDeleteProc, |
187 |
|
(ClientData) assocPtr); |
188 |
|
} |
189 |
|
if (assocPtr->firstBgPtr == NULL) { |
190 |
|
assocPtr->firstBgPtr = errPtr; |
191 |
|
Tcl_DoWhenIdle(HandleBgErrors, (ClientData) assocPtr); |
192 |
|
} else { |
193 |
|
assocPtr->lastBgPtr->nextPtr = errPtr; |
194 |
|
} |
195 |
|
assocPtr->lastBgPtr = errPtr; |
196 |
|
Tcl_ResetResult(interp); |
197 |
|
} |
198 |
|
|
199 |
|
/* |
200 |
|
*---------------------------------------------------------------------- |
201 |
|
* |
202 |
|
* HandleBgErrors -- |
203 |
|
* |
204 |
|
* This procedure is invoked as an idle handler to process all of |
205 |
|
* the accumulated background errors. |
206 |
|
* |
207 |
|
* Results: |
208 |
|
* None. |
209 |
|
* |
210 |
|
* Side effects: |
211 |
|
* Depends on what actions "bgerror" takes for the errors. |
212 |
|
* |
213 |
|
*---------------------------------------------------------------------- |
214 |
|
*/ |
215 |
|
|
216 |
|
static void |
217 |
|
HandleBgErrors(clientData) |
218 |
|
ClientData clientData; /* Pointer to ErrAssocData structure. */ |
219 |
|
{ |
220 |
|
Tcl_Interp *interp; |
221 |
|
char *argv[2]; |
222 |
|
int code; |
223 |
|
BgError *errPtr; |
224 |
|
ErrAssocData *assocPtr = (ErrAssocData *) clientData; |
225 |
|
Tcl_Channel errChannel; |
226 |
|
|
227 |
|
Tcl_Preserve((ClientData) assocPtr); |
228 |
|
|
229 |
|
while (assocPtr->firstBgPtr != NULL) { |
230 |
|
interp = assocPtr->firstBgPtr->interp; |
231 |
|
if (interp == NULL) { |
232 |
|
goto doneWithInterp; |
233 |
|
} |
234 |
|
|
235 |
|
/* |
236 |
|
* Restore important state variables to what they were at |
237 |
|
* the time the error occurred. |
238 |
|
*/ |
239 |
|
|
240 |
|
Tcl_SetVar(interp, "errorInfo", assocPtr->firstBgPtr->errorInfo, |
241 |
|
TCL_GLOBAL_ONLY); |
242 |
|
Tcl_SetVar(interp, "errorCode", assocPtr->firstBgPtr->errorCode, |
243 |
|
TCL_GLOBAL_ONLY); |
244 |
|
|
245 |
|
/* |
246 |
|
* Create and invoke the bgerror command. |
247 |
|
*/ |
248 |
|
|
249 |
|
argv[0] = "bgerror"; |
250 |
|
argv[1] = assocPtr->firstBgPtr->errorMsg; |
251 |
|
|
252 |
|
Tcl_AllowExceptions(interp); |
253 |
|
Tcl_Preserve((ClientData) interp); |
254 |
|
code = TclGlobalInvoke(interp, 2, argv, 0); |
255 |
|
if (code == TCL_ERROR) { |
256 |
|
|
257 |
|
/* |
258 |
|
* If the interpreter is safe, we look for a hidden command |
259 |
|
* named "bgerror" and call that with the error information. |
260 |
|
* Otherwise, simply ignore the error. The rationale is that |
261 |
|
* this could be an error caused by a malicious applet trying |
262 |
|
* to cause an infinite barrage of error messages. The hidden |
263 |
|
* "bgerror" command can be used by a security policy to |
264 |
|
* interpose on such attacks and e.g. kill the applet after a |
265 |
|
* few attempts. |
266 |
|
*/ |
267 |
|
|
268 |
|
if (Tcl_IsSafe(interp)) { |
269 |
|
Tcl_SavedResult save; |
270 |
|
|
271 |
|
Tcl_SaveResult(interp, &save); |
272 |
|
TclGlobalInvoke(interp, 2, argv, TCL_INVOKE_HIDDEN); |
273 |
|
Tcl_RestoreResult(interp, &save); |
274 |
|
|
275 |
|
goto doneWithInterp; |
276 |
|
} |
277 |
|
|
278 |
|
/* |
279 |
|
* We have to get the error output channel at the latest possible |
280 |
|
* time, because the eval (above) might have changed the channel. |
281 |
|
*/ |
282 |
|
|
283 |
|
errChannel = Tcl_GetStdChannel(TCL_STDERR); |
284 |
|
if (errChannel != (Tcl_Channel) NULL) { |
285 |
|
char *string; |
286 |
|
int len; |
287 |
|
|
288 |
|
string = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &len); |
289 |
|
if (strcmp(string, "\"bgerror\" is an invalid command name or ambiguous abbreviation") == 0) { |
290 |
|
Tcl_WriteChars(errChannel, assocPtr->firstBgPtr->errorInfo, -1); |
291 |
|
Tcl_WriteChars(errChannel, "\n", -1); |
292 |
|
} else { |
293 |
|
Tcl_WriteChars(errChannel, |
294 |
|
"bgerror failed to handle background error.\n", |
295 |
|
-1); |
296 |
|
Tcl_WriteChars(errChannel, " Original error: ", -1); |
297 |
|
Tcl_WriteChars(errChannel, assocPtr->firstBgPtr->errorMsg, |
298 |
|
-1); |
299 |
|
Tcl_WriteChars(errChannel, "\n", -1); |
300 |
|
Tcl_WriteChars(errChannel, " Error in bgerror: ", -1); |
301 |
|
Tcl_WriteChars(errChannel, string, len); |
302 |
|
Tcl_WriteChars(errChannel, "\n", -1); |
303 |
|
} |
304 |
|
Tcl_Flush(errChannel); |
305 |
|
} |
306 |
|
} else if (code == TCL_BREAK) { |
307 |
|
|
308 |
|
/* |
309 |
|
* Break means cancel any remaining error reports for this |
310 |
|
* interpreter. |
311 |
|
*/ |
312 |
|
|
313 |
|
for (errPtr = assocPtr->firstBgPtr; errPtr != NULL; |
314 |
|
errPtr = errPtr->nextPtr) { |
315 |
|
if (errPtr->interp == interp) { |
316 |
|
errPtr->interp = NULL; |
317 |
|
} |
318 |
|
} |
319 |
|
} |
320 |
|
|
321 |
|
/* |
322 |
|
* Discard the command and the information about the error report. |
323 |
|
*/ |
324 |
|
|
325 |
|
doneWithInterp: |
326 |
|
|
327 |
|
if (assocPtr->firstBgPtr) { |
328 |
|
ckfree(assocPtr->firstBgPtr->errorMsg); |
329 |
|
ckfree(assocPtr->firstBgPtr->errorInfo); |
330 |
|
ckfree(assocPtr->firstBgPtr->errorCode); |
331 |
|
errPtr = assocPtr->firstBgPtr->nextPtr; |
332 |
|
ckfree((char *) assocPtr->firstBgPtr); |
333 |
|
assocPtr->firstBgPtr = errPtr; |
334 |
|
} |
335 |
|
|
336 |
|
if (interp != NULL) { |
337 |
|
Tcl_Release((ClientData) interp); |
338 |
|
} |
339 |
|
} |
340 |
|
assocPtr->lastBgPtr = NULL; |
341 |
|
|
342 |
|
Tcl_Release((ClientData) assocPtr); |
343 |
|
} |
344 |
|
|
345 |
|
/* |
346 |
|
*---------------------------------------------------------------------- |
347 |
|
* |
348 |
|
* BgErrorDeleteProc -- |
349 |
|
* |
350 |
|
* This procedure is associated with the "tclBgError" assoc data |
351 |
|
* for an interpreter; it is invoked when the interpreter is |
352 |
|
* deleted in order to free the information assoicated with any |
353 |
|
* pending error reports. |
354 |
|
* |
355 |
|
* Results: |
356 |
|
* None. |
357 |
|
* |
358 |
|
* Side effects: |
359 |
|
* Background error information is freed: if there were any |
360 |
|
* pending error reports, they are cancelled. |
361 |
|
* |
362 |
|
*---------------------------------------------------------------------- |
363 |
|
*/ |
364 |
|
|
365 |
|
static void |
366 |
|
BgErrorDeleteProc(clientData, interp) |
367 |
|
ClientData clientData; /* Pointer to ErrAssocData structure. */ |
368 |
|
Tcl_Interp *interp; /* Interpreter being deleted. */ |
369 |
|
{ |
370 |
|
ErrAssocData *assocPtr = (ErrAssocData *) clientData; |
371 |
|
BgError *errPtr; |
372 |
|
|
373 |
|
while (assocPtr->firstBgPtr != NULL) { |
374 |
|
errPtr = assocPtr->firstBgPtr; |
375 |
|
assocPtr->firstBgPtr = errPtr->nextPtr; |
376 |
|
ckfree(errPtr->errorMsg); |
377 |
|
ckfree(errPtr->errorInfo); |
378 |
|
ckfree(errPtr->errorCode); |
379 |
|
ckfree((char *) errPtr); |
380 |
|
} |
381 |
|
Tcl_CancelIdleCall(HandleBgErrors, (ClientData) assocPtr); |
382 |
|
Tcl_EventuallyFree((ClientData) assocPtr, TCL_DYNAMIC); |
383 |
|
} |
384 |
|
|
385 |
|
/* |
386 |
|
*---------------------------------------------------------------------- |
387 |
|
* |
388 |
|
* Tcl_CreateExitHandler -- |
389 |
|
* |
390 |
|
* Arrange for a given procedure to be invoked just before the |
391 |
|
* application exits. |
392 |
|
* |
393 |
|
* Results: |
394 |
|
* None. |
395 |
|
* |
396 |
|
* Side effects: |
397 |
|
* Proc will be invoked with clientData as argument when the |
398 |
|
* application exits. |
399 |
|
* |
400 |
|
*---------------------------------------------------------------------- |
401 |
|
*/ |
402 |
|
|
403 |
|
void |
404 |
|
Tcl_CreateExitHandler(proc, clientData) |
405 |
|
Tcl_ExitProc *proc; /* Procedure to invoke. */ |
406 |
|
ClientData clientData; /* Arbitrary value to pass to proc. */ |
407 |
|
{ |
408 |
|
ExitHandler *exitPtr; |
409 |
|
|
410 |
|
exitPtr = (ExitHandler *) ckalloc(sizeof(ExitHandler)); |
411 |
|
exitPtr->proc = proc; |
412 |
|
exitPtr->clientData = clientData; |
413 |
|
Tcl_MutexLock(&exitMutex); |
414 |
|
exitPtr->nextPtr = firstExitPtr; |
415 |
|
firstExitPtr = exitPtr; |
416 |
|
Tcl_MutexUnlock(&exitMutex); |
417 |
|
} |
418 |
|
|
419 |
|
/* |
420 |
|
*---------------------------------------------------------------------- |
421 |
|
* |
422 |
|
* Tcl_DeleteExitHandler -- |
423 |
|
* |
424 |
|
* This procedure cancels an existing exit handler matching proc |
425 |
|
* and clientData, if such a handler exits. |
426 |
|
* |
427 |
|
* Results: |
428 |
|
* None. |
429 |
|
* |
430 |
|
* Side effects: |
431 |
|
* If there is an exit handler corresponding to proc and clientData |
432 |
|
* then it is cancelled; if no such handler exists then nothing |
433 |
|
* happens. |
434 |
|
* |
435 |
|
*---------------------------------------------------------------------- |
436 |
|
*/ |
437 |
|
|
438 |
|
void |
439 |
|
Tcl_DeleteExitHandler(proc, clientData) |
440 |
|
Tcl_ExitProc *proc; /* Procedure that was previously registered. */ |
441 |
|
ClientData clientData; /* Arbitrary value to pass to proc. */ |
442 |
|
{ |
443 |
|
ExitHandler *exitPtr, *prevPtr; |
444 |
|
|
445 |
|
Tcl_MutexLock(&exitMutex); |
446 |
|
for (prevPtr = NULL, exitPtr = firstExitPtr; exitPtr != NULL; |
447 |
|
prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) { |
448 |
|
if ((exitPtr->proc == proc) |
449 |
|
&& (exitPtr->clientData == clientData)) { |
450 |
|
if (prevPtr == NULL) { |
451 |
|
firstExitPtr = exitPtr->nextPtr; |
452 |
|
} else { |
453 |
|
prevPtr->nextPtr = exitPtr->nextPtr; |
454 |
|
} |
455 |
|
ckfree((char *) exitPtr); |
456 |
|
break; |
457 |
|
} |
458 |
|
} |
459 |
|
Tcl_MutexUnlock(&exitMutex); |
460 |
|
return; |
461 |
|
} |
462 |
|
|
463 |
|
/* |
464 |
|
*---------------------------------------------------------------------- |
465 |
|
* |
466 |
|
* Tcl_CreateThreadExitHandler -- |
467 |
|
* |
468 |
|
* Arrange for a given procedure to be invoked just before the |
469 |
|
* current thread exits. |
470 |
|
* |
471 |
|
* Results: |
472 |
|
* None. |
473 |
|
* |
474 |
|
* Side effects: |
475 |
|
* Proc will be invoked with clientData as argument when the |
476 |
|
* application exits. |
477 |
|
* |
478 |
|
*---------------------------------------------------------------------- |
479 |
|
*/ |
480 |
|
|
481 |
|
void |
482 |
|
Tcl_CreateThreadExitHandler(proc, clientData) |
483 |
|
Tcl_ExitProc *proc; /* Procedure to invoke. */ |
484 |
|
ClientData clientData; /* Arbitrary value to pass to proc. */ |
485 |
|
{ |
486 |
|
ExitHandler *exitPtr; |
487 |
|
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
488 |
|
|
489 |
|
exitPtr = (ExitHandler *) ckalloc(sizeof(ExitHandler)); |
490 |
|
exitPtr->proc = proc; |
491 |
|
exitPtr->clientData = clientData; |
492 |
|
exitPtr->nextPtr = tsdPtr->firstExitPtr; |
493 |
|
tsdPtr->firstExitPtr = exitPtr; |
494 |
|
} |
495 |
|
|
496 |
|
/* |
497 |
|
*---------------------------------------------------------------------- |
498 |
|
* |
499 |
|
* Tcl_DeleteThreadExitHandler -- |
500 |
|
* |
501 |
|
* This procedure cancels an existing exit handler matching proc |
502 |
|
* and clientData, if such a handler exits. |
503 |
|
* |
504 |
|
* Results: |
505 |
|
* None. |
506 |
|
* |
507 |
|
* Side effects: |
508 |
|
* If there is an exit handler corresponding to proc and clientData |
509 |
|
* then it is cancelled; if no such handler exists then nothing |
510 |
|
* happens. |
511 |
|
* |
512 |
|
*---------------------------------------------------------------------- |
513 |
|
*/ |
514 |
|
|
515 |
|
void |
516 |
|
Tcl_DeleteThreadExitHandler(proc, clientData) |
517 |
|
Tcl_ExitProc *proc; /* Procedure that was previously registered. */ |
518 |
|
ClientData clientData; /* Arbitrary value to pass to proc. */ |
519 |
|
{ |
520 |
|
ExitHandler *exitPtr, *prevPtr; |
521 |
|
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
522 |
|
|
523 |
|
for (prevPtr = NULL, exitPtr = tsdPtr->firstExitPtr; exitPtr != NULL; |
524 |
|
prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) { |
525 |
|
if ((exitPtr->proc == proc) |
526 |
|
&& (exitPtr->clientData == clientData)) { |
527 |
|
if (prevPtr == NULL) { |
528 |
|
tsdPtr->firstExitPtr = exitPtr->nextPtr; |
529 |
|
} else { |
530 |
|
prevPtr->nextPtr = exitPtr->nextPtr; |
531 |
|
} |
532 |
|
ckfree((char *) exitPtr); |
533 |
|
return; |
534 |
|
} |
535 |
|
} |
536 |
|
} |
537 |
|
|
538 |
|
/* |
539 |
|
*---------------------------------------------------------------------- |
540 |
|
* |
541 |
|
* Tcl_Exit -- |
542 |
|
* |
543 |
|
* This procedure is called to terminate the application. |
544 |
|
* |
545 |
|
* Results: |
546 |
|
* None. |
547 |
|
* |
548 |
|
* Side effects: |
549 |
|
* All existing exit handlers are invoked, then the application |
550 |
|
* ends. |
551 |
|
* |
552 |
|
*---------------------------------------------------------------------- |
553 |
|
*/ |
554 |
|
|
555 |
|
void |
556 |
|
Tcl_Exit(status) |
557 |
|
int status; /* Exit status for application; typically |
558 |
|
* 0 for normal return, 1 for error return. */ |
559 |
|
{ |
560 |
|
Tcl_Finalize(); |
561 |
|
TclpExit(status); |
562 |
|
} |
563 |
|
|
564 |
|
/* |
565 |
|
*------------------------------------------------------------------------- |
566 |
|
* |
567 |
|
* TclSetLibraryPath -- |
568 |
|
* |
569 |
|
* Set the path that will be used for searching for init.tcl and |
570 |
|
* encodings when an interp is being created. |
571 |
|
* |
572 |
|
* Results: |
573 |
|
* None. |
574 |
|
* |
575 |
|
* Side effects: |
576 |
|
* Changing the library path will affect what directories are |
577 |
|
* examined when looking for encodings for all interps from that |
578 |
|
* point forward. |
579 |
|
* |
580 |
|
* The refcount of the new library path is incremented and the |
581 |
|
* refcount of the old path is decremented. |
582 |
|
* |
583 |
|
*------------------------------------------------------------------------- |
584 |
|
*/ |
585 |
|
|
586 |
|
void |
587 |
|
TclSetLibraryPath(pathPtr) |
588 |
|
Tcl_Obj *pathPtr; /* A Tcl list object whose elements are |
589 |
|
* the new library path. */ |
590 |
|
{ |
591 |
|
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
592 |
|
|
593 |
|
if (pathPtr != NULL) { |
594 |
|
Tcl_IncrRefCount(pathPtr); |
595 |
|
} |
596 |
|
if (tsdPtr->tclLibraryPath != NULL) { |
597 |
|
Tcl_DecrRefCount(tsdPtr->tclLibraryPath); |
598 |
|
} |
599 |
|
tsdPtr->tclLibraryPath = pathPtr; |
600 |
|
} |
601 |
|
|
602 |
|
/* |
603 |
|
*------------------------------------------------------------------------- |
604 |
|
* |
605 |
|
* TclGetLibraryPath -- |
606 |
|
* |
607 |
|
* Return a Tcl list object whose elements are the library path. |
608 |
|
* The caller should not modify the contents of the returned object. |
609 |
|
* |
610 |
|
* Results: |
611 |
|
* As above. |
612 |
|
* |
613 |
|
* Side effects: |
614 |
|
* None. |
615 |
|
* |
616 |
|
*------------------------------------------------------------------------- |
617 |
|
*/ |
618 |
|
|
619 |
|
Tcl_Obj * |
620 |
|
TclGetLibraryPath() |
621 |
|
{ |
622 |
|
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
623 |
|
return tsdPtr->tclLibraryPath; |
624 |
|
} |
625 |
|
|
626 |
|
/* |
627 |
|
*------------------------------------------------------------------------- |
628 |
|
* |
629 |
|
* TclInitSubsystems -- |
630 |
|
* |
631 |
|
* Initialize various subsytems in Tcl. This should be called the |
632 |
|
* first time an interp is created, or before any of the subsystems |
633 |
|
* are used. This function ensures an order for the initialization |
634 |
|
* of subsystems: |
635 |
|
* |
636 |
|
* 1. that cannot be initialized in lazy order because they are |
637 |
|
* mutually dependent. |
638 |
|
* |
639 |
|
* 2. so that they can be finalized in a known order w/o causing |
640 |
|
* the subsequent re-initialization of a subsystem in the act of |
641 |
|
* shutting down another. |
642 |
|
* |
643 |
|
* Results: |
644 |
|
* None. |
645 |
|
* |
646 |
|
* Side effects: |
647 |
|
* Varied, see the respective initialization routines. |
648 |
|
* |
649 |
|
*------------------------------------------------------------------------- |
650 |
|
*/ |
651 |
|
|
652 |
|
void |
653 |
|
TclInitSubsystems(argv0) |
654 |
|
CONST char *argv0; /* Name of executable from argv[0] to main() |
655 |
|
* in native multi-byte encoding. */ |
656 |
|
{ |
657 |
|
ThreadSpecificData *tsdPtr; |
658 |
|
|
659 |
|
if (inFinalize != 0) { |
660 |
|
panic("TclInitSubsystems called while finalizing"); |
661 |
|
} |
662 |
|
|
663 |
|
/* |
664 |
|
* Grab the thread local storage pointer before doing anything because |
665 |
|
* the initialization routines will be registering exit handlers. |
666 |
|
* We use this pointer to detect if this is the first time this |
667 |
|
* thread has created an interpreter. |
668 |
|
*/ |
669 |
|
|
670 |
|
tsdPtr = (ThreadSpecificData *) TclThreadDataKeyGet(&dataKey); |
671 |
|
|
672 |
|
if (subsystemsInitialized == 0) { |
673 |
|
/* |
674 |
|
* Double check inside the mutex. There are definitly calls |
675 |
|
* back into this routine from some of the procedures below. |
676 |
|
*/ |
677 |
|
|
678 |
|
TclpInitLock(); |
679 |
|
if (subsystemsInitialized == 0) { |
680 |
|
/* |
681 |
|
* Have to set this bit here to avoid deadlock with the |
682 |
|
* routines below us that call into TclInitSubsystems. |
683 |
|
*/ |
684 |
|
|
685 |
|
subsystemsInitialized = 1; |
686 |
|
|
687 |
|
tclExecutableName = NULL; |
688 |
|
|
689 |
|
/* |
690 |
|
* Initialize locks used by the memory allocators before anything |
691 |
|
* interesting happens so we can use the allocators in the |
692 |
|
* implementation of self-initializing locks. |
693 |
|
*/ |
694 |
|
#if USE_TCLALLOC |
695 |
|
TclInitAlloc(); /* process wide mutex init */ |
696 |
|
#endif |
697 |
|
#ifdef TCL_MEM_DEBUG |
698 |
|
TclInitDbCkalloc(); /* process wide mutex init */ |
699 |
|
#endif |
700 |
|
|
701 |
|
TclpInitPlatform(); /* creates signal handler(s) */ |
702 |
|
TclInitObjSubsystem(); /* register obj types, create mutexes */ |
703 |
|
TclInitIOSubsystem(); /* inits a tsd key (noop) */ |
704 |
|
TclInitEncodingSubsystem(); /* process wide encoding init */ |
705 |
|
TclInitNamespaceSubsystem(); /* register ns obj type (mutexed) */ |
706 |
|
} |
707 |
|
TclpInitUnlock(); |
708 |
|
} |
709 |
|
|
710 |
|
if (tsdPtr == NULL) { |
711 |
|
/* |
712 |
|
* First time this thread has created an interpreter. |
713 |
|
* We fetch the key again just in case no exit handlers were |
714 |
|
* registered by this point. |
715 |
|
*/ |
716 |
|
|
717 |
|
(void) TCL_TSD_INIT(&dataKey); |
718 |
|
TclInitNotifier(); |
719 |
|
} |
720 |
|
} |
721 |
|
|
722 |
|
/* |
723 |
|
*---------------------------------------------------------------------- |
724 |
|
* |
725 |
|
* Tcl_Finalize -- |
726 |
|
* |
727 |
|
* Shut down Tcl. First calls registered exit handlers, then |
728 |
|
* carefully shuts down various subsystems. |
729 |
|
* Called by Tcl_Exit or when the Tcl shared library is being |
730 |
|
* unloaded. |
731 |
|
* |
732 |
|
* Results: |
733 |
|
* None. |
734 |
|
* |
735 |
|
* Side effects: |
736 |
|
* Varied, see the respective finalization routines. |
737 |
|
* |
738 |
|
*---------------------------------------------------------------------- |
739 |
|
*/ |
740 |
|
|
741 |
|
void |
742 |
|
Tcl_Finalize() |
743 |
|
{ |
744 |
|
ExitHandler *exitPtr; |
745 |
|
ThreadSpecificData *tsdPtr; |
746 |
|
|
747 |
|
TclpInitLock(); |
748 |
|
tsdPtr = TCL_TSD_INIT(&dataKey); |
749 |
|
if (subsystemsInitialized != 0) { |
750 |
|
subsystemsInitialized = 0; |
751 |
|
|
752 |
|
/* |
753 |
|
* Invoke exit handlers first. |
754 |
|
*/ |
755 |
|
|
756 |
|
Tcl_MutexLock(&exitMutex); |
757 |
|
inFinalize = 1; |
758 |
|
for (exitPtr = firstExitPtr; exitPtr != NULL; exitPtr = firstExitPtr) { |
759 |
|
/* |
760 |
|
* Be careful to remove the handler from the list before |
761 |
|
* invoking its callback. This protects us against |
762 |
|
* double-freeing if the callback should call |
763 |
|
* Tcl_DeleteExitHandler on itself. |
764 |
|
*/ |
765 |
|
|
766 |
|
firstExitPtr = exitPtr->nextPtr; |
767 |
|
Tcl_MutexUnlock(&exitMutex); |
768 |
|
(*exitPtr->proc)(exitPtr->clientData); |
769 |
|
ckfree((char *) exitPtr); |
770 |
|
Tcl_MutexLock(&exitMutex); |
771 |
|
} |
772 |
|
firstExitPtr = NULL; |
773 |
|
Tcl_MutexUnlock(&exitMutex); |
774 |
|
|
775 |
|
/* |
776 |
|
* Clean up the library path now, before we invalidate thread-local |
777 |
|
* storage. |
778 |
|
*/ |
779 |
|
if (tsdPtr->tclLibraryPath != NULL) { |
780 |
|
Tcl_DecrRefCount(tsdPtr->tclLibraryPath); |
781 |
|
tsdPtr->tclLibraryPath = NULL; |
782 |
|
} |
783 |
|
|
784 |
|
/* |
785 |
|
* Clean up after the current thread now, after exit handlers. |
786 |
|
* In particular, the testexithandler command sets up something |
787 |
|
* that writes to standard output, which gets closed. |
788 |
|
* Note that there is no thread-local storage after this call. |
789 |
|
*/ |
790 |
|
|
791 |
|
Tcl_FinalizeThread(); |
792 |
|
|
793 |
|
/* |
794 |
|
* Now finalize the Tcl execution environment. Note that this |
795 |
|
* must be done after the exit handlers, because there are |
796 |
|
* order dependencies. |
797 |
|
*/ |
798 |
|
|
799 |
|
TclFinalizeCompExecEnv(); |
800 |
|
TclFinalizeEnvironment(); |
801 |
|
|
802 |
|
TclFinalizeEncodingSubsystem(); |
803 |
|
|
804 |
|
if (tclExecutableName != NULL) { |
805 |
|
ckfree(tclExecutableName); |
806 |
|
tclExecutableName = NULL; |
807 |
|
} |
808 |
|
if (tclNativeExecutableName != NULL) { |
809 |
|
ckfree(tclNativeExecutableName); |
810 |
|
tclNativeExecutableName = NULL; |
811 |
|
} |
812 |
|
if (tclDefaultEncodingDir != NULL) { |
813 |
|
ckfree(tclDefaultEncodingDir); |
814 |
|
tclDefaultEncodingDir = NULL; |
815 |
|
} |
816 |
|
|
817 |
|
Tcl_SetPanicProc(NULL); |
818 |
|
|
819 |
|
/* |
820 |
|
* Free synchronization objects. There really should only be one |
821 |
|
* thread alive at this moment. |
822 |
|
*/ |
823 |
|
|
824 |
|
TclFinalizeSynchronization(); |
825 |
|
|
826 |
|
/* |
827 |
|
* We defer unloading of packages until very late |
828 |
|
* to avoid memory access issues. Both exit callbacks and |
829 |
|
* synchronization variables may be stored in packages. |
830 |
|
*/ |
831 |
|
|
832 |
|
TclFinalizeLoad(); |
833 |
|
|
834 |
|
/* |
835 |
|
* There shouldn't be any malloc'ed memory after this. |
836 |
|
*/ |
837 |
|
|
838 |
|
TclFinalizeMemorySubsystem(); |
839 |
|
inFinalize = 0; |
840 |
|
} |
841 |
|
TclpInitUnlock(); |
842 |
|
} |
843 |
|
|
844 |
|
/* |
845 |
|
*---------------------------------------------------------------------- |
846 |
|
* |
847 |
|
* Tcl_FinalizeThread -- |
848 |
|
* |
849 |
|
* Runs the exit handlers to allow Tcl to clean up its state |
850 |
|
* about a particular thread. |
851 |
|
* |
852 |
|
* Results: |
853 |
|
* None. |
854 |
|
* |
855 |
|
* Side effects: |
856 |
|
* Varied, see the respective finalization routines. |
857 |
|
* |
858 |
|
*---------------------------------------------------------------------- |
859 |
|
*/ |
860 |
|
|
861 |
|
void |
862 |
|
Tcl_FinalizeThread() |
863 |
|
{ |
864 |
|
ExitHandler *exitPtr; |
865 |
|
ThreadSpecificData *tsdPtr = |
866 |
|
(ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); |
867 |
|
|
868 |
|
if (tsdPtr != NULL) { |
869 |
|
/* |
870 |
|
* Invoke thread exit handlers first. |
871 |
|
*/ |
872 |
|
|
873 |
|
tsdPtr->inExit = 1; |
874 |
|
for (exitPtr = tsdPtr->firstExitPtr; exitPtr != NULL; |
875 |
|
exitPtr = tsdPtr->firstExitPtr) { |
876 |
|
/* |
877 |
|
* Be careful to remove the handler from the list before invoking |
878 |
|
* its callback. This protects us against double-freeing if the |
879 |
|
* callback should call Tcl_DeleteThreadExitHandler on itself. |
880 |
|
*/ |
881 |
|
|
882 |
|
tsdPtr->firstExitPtr = exitPtr->nextPtr; |
883 |
|
(*exitPtr->proc)(exitPtr->clientData); |
884 |
|
ckfree((char *) exitPtr); |
885 |
|
} |
886 |
|
TclFinalizeIOSubsystem(); |
887 |
|
TclFinalizeNotifier(); |
888 |
|
|
889 |
|
/* |
890 |
|
* Blow away all thread local storage blocks. |
891 |
|
*/ |
892 |
|
|
893 |
|
TclFinalizeThreadData(); |
894 |
|
} |
895 |
|
} |
896 |
|
|
897 |
|
/* |
898 |
|
*---------------------------------------------------------------------- |
899 |
|
* |
900 |
|
* TclInExit -- |
901 |
|
* |
902 |
|
* Determines if we are in the middle of exit-time cleanup. |
903 |
|
* |
904 |
|
* Results: |
905 |
|
* If we are in the middle of exiting, 1, otherwise 0. |
906 |
|
* |
907 |
|
* Side effects: |
908 |
|
* None. |
909 |
|
* |
910 |
|
*---------------------------------------------------------------------- |
911 |
|
*/ |
912 |
|
|
913 |
|
int |
914 |
|
TclInExit() |
915 |
|
{ |
916 |
|
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
917 |
|
return tsdPtr->inExit; |
918 |
|
} |
919 |
|
|
920 |
|
/* |
921 |
|
*---------------------------------------------------------------------- |
922 |
|
* |
923 |
|
* Tcl_VwaitObjCmd -- |
924 |
|
* |
925 |
|
* This procedure is invoked to process the "vwait" Tcl command. |
926 |
|
* See the user documentation for details on what it does. |
927 |
|
* |
928 |
|
* Results: |
929 |
|
* A standard Tcl result. |
930 |
|
* |
931 |
|
* Side effects: |
932 |
|
* See the user documentation. |
933 |
|
* |
934 |
|
*---------------------------------------------------------------------- |
935 |
|
*/ |
936 |
|
|
937 |
|
/* ARGSUSED */ |
938 |
|
int |
939 |
|
Tcl_VwaitObjCmd(clientData, interp, objc, objv) |
940 |
|
ClientData clientData; /* Not used. */ |
941 |
|
Tcl_Interp *interp; /* Current interpreter. */ |
942 |
|
int objc; /* Number of arguments. */ |
943 |
|
Tcl_Obj *CONST objv[]; /* Argument objects. */ |
944 |
|
{ |
945 |
|
int done, foundEvent; |
946 |
|
char *nameString; |
947 |
|
|
948 |
|
if (objc != 2) { |
949 |
|
Tcl_WrongNumArgs(interp, 1, objv, "name"); |
950 |
|
return TCL_ERROR; |
951 |
|
} |
952 |
|
nameString = Tcl_GetString(objv[1]); |
953 |
|
if (Tcl_TraceVar(interp, nameString, |
954 |
|
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, |
955 |
|
VwaitVarProc, (ClientData) &done) != TCL_OK) { |
956 |
|
return TCL_ERROR; |
957 |
|
}; |
958 |
|
done = 0; |
959 |
|
foundEvent = 1; |
960 |
|
while (!done && foundEvent) { |
961 |
|
foundEvent = Tcl_DoOneEvent(TCL_ALL_EVENTS); |
962 |
|
} |
963 |
|
Tcl_UntraceVar(interp, nameString, |
964 |
|
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, |
965 |
|
VwaitVarProc, (ClientData) &done); |
966 |
|
|
967 |
|
/* |
968 |
|
* Clear out the interpreter's result, since it may have been set |
969 |
|
* by event handlers. |
970 |
|
*/ |
971 |
|
|
972 |
|
Tcl_ResetResult(interp); |
973 |
|
if (!foundEvent) { |
974 |
|
Tcl_AppendResult(interp, "can't wait for variable \"", nameString, |
975 |
|
"\": would wait forever", (char *) NULL); |
976 |
|
return TCL_ERROR; |
977 |
|
} |
978 |
|
return TCL_OK; |
979 |
|
} |
980 |
|
|
981 |
|
/* ARGSUSED */ |
982 |
|
static char * |
983 |
|
VwaitVarProc(clientData, interp, name1, name2, flags) |
984 |
|
ClientData clientData; /* Pointer to integer to set to 1. */ |
985 |
|
Tcl_Interp *interp; /* Interpreter containing variable. */ |
986 |
|
char *name1; /* Name of variable. */ |
987 |
|
char *name2; /* Second part of variable name. */ |
988 |
|
int flags; /* Information about what happened. */ |
989 |
|
{ |
990 |
|
int *donePtr = (int *) clientData; |
991 |
|
|
992 |
|
*donePtr = 1; |
993 |
|
return (char *) NULL; |
994 |
|
} |
995 |
|
|
996 |
|
/* |
997 |
|
*---------------------------------------------------------------------- |
998 |
|
* |
999 |
|
* Tcl_UpdateObjCmd -- |
1000 |
|
* |
1001 |
|
* This procedure is invoked to process the "update" Tcl command. |
1002 |
|
* See the user documentation for details on what it does. |
1003 |
|
* |
1004 |
|
* Results: |
1005 |
|
* A standard Tcl result. |
1006 |
|
* |
1007 |
|
* Side effects: |
1008 |
|
* See the user documentation. |
1009 |
|
* |
1010 |
|
*---------------------------------------------------------------------- |
1011 |
|
*/ |
1012 |
|
|
1013 |
|
/* ARGSUSED */ |
1014 |
|
int |
1015 |
|
Tcl_UpdateObjCmd(clientData, interp, objc, objv) |
1016 |
|
ClientData clientData; /* Not used. */ |
1017 |
|
Tcl_Interp *interp; /* Current interpreter. */ |
1018 |
|
int objc; /* Number of arguments. */ |
1019 |
|
Tcl_Obj *CONST objv[]; /* Argument objects. */ |
1020 |
|
{ |
1021 |
|
int optionIndex; |
1022 |
|
int flags = 0; /* Initialized to avoid compiler warning. */ |
1023 |
|
static char *updateOptions[] = {"idletasks", (char *) NULL}; |
1024 |
|
enum updateOptions {REGEXP_IDLETASKS}; |
1025 |
|
|
1026 |
|
if (objc == 1) { |
1027 |
|
flags = TCL_ALL_EVENTS|TCL_DONT_WAIT; |
1028 |
|
} else if (objc == 2) { |
1029 |
|
if (Tcl_GetIndexFromObj(interp, objv[1], updateOptions, |
1030 |
|
"option", 0, &optionIndex) != TCL_OK) { |
1031 |
|
return TCL_ERROR; |
1032 |
|
} |
1033 |
|
switch ((enum updateOptions) optionIndex) { |
1034 |
|
case REGEXP_IDLETASKS: { |
1035 |
|
flags = TCL_WINDOW_EVENTS|TCL_IDLE_EVENTS|TCL_DONT_WAIT; |
1036 |
|
break; |
1037 |
|
} |
1038 |
|
default: { |
1039 |
|
panic("Tcl_UpdateObjCmd: bad option index to UpdateOptions"); |
1040 |
|
} |
1041 |
|
} |
1042 |
|
} else { |
1043 |
|
Tcl_WrongNumArgs(interp, 1, objv, "?idletasks?"); |
1044 |
|
return TCL_ERROR; |
1045 |
|
} |
1046 |
|
|
1047 |
|
while (Tcl_DoOneEvent(flags) != 0) { |
1048 |
|
/* Empty loop body */ |
1049 |
|
} |
1050 |
|
|
1051 |
|
/* |
1052 |
|
* Must clear the interpreter's result because event handlers could |
1053 |
|
* have executed commands. |
1054 |
|
*/ |
1055 |
|
|
1056 |
|
Tcl_ResetResult(interp); |
1057 |
|
return TCL_OK; |
1058 |
|
} |
1059 |
|
|
1060 |
|
/* End of tclevent.c */ |