os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclEvent.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 /* 
     2  * tclEvent.c --
     3  *
     4  *	This file implements some general event related interfaces including
     5  *	background errors, exit handlers, and the "vwait" and "update"
     6  *	command procedures. 
     7  *
     8  * Copyright (c) 1990-1994 The Regents of the University of California.
     9  * Copyright (c) 1994-1998 Sun Microsystems, Inc.
    10  * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.  
    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.28.2.15 2007/03/19 17:06:25 dgp Exp $
    16  */
    17 
    18 #include "tclInt.h"
    19 #include "tclPort.h"
    20 #if defined(__SYMBIAN32__) && defined(__WINSCW__)
    21 #include "tclSymbianGlobals.h"
    22 #define dataKey getdataKey(0)
    23 #endif 
    24 
    25 /*
    26  * The data structure below is used to report background errors.  One
    27  * such structure is allocated for each error;  it holds information
    28  * about the interpreter and the error until bgerror can be invoked
    29  * later as an idle handler.
    30  */
    31 
    32 typedef struct BgError {
    33     Tcl_Interp *interp;		/* Interpreter in which error occurred.  NULL
    34 				 * means this error report has been cancelled
    35 				 * (a previous report generated a break). */
    36     char *errorMsg;		/* Copy of the error message (the interp's
    37 				 * result when the error occurred).
    38 				 * Malloc-ed. */
    39     char *errorInfo;		/* Value of the errorInfo variable
    40 				 * (malloc-ed). */
    41     char *errorCode;		/* Value of the errorCode variable
    42 				 * (malloc-ed). */
    43     struct BgError *nextPtr;	/* Next in list of all pending error
    44 				 * reports for this interpreter, or NULL
    45 				 * for end of list. */
    46 } BgError;
    47 
    48 /*
    49  * One of the structures below is associated with the "tclBgError"
    50  * assoc data for each interpreter.  It keeps track of the head and
    51  * tail of the list of pending background errors for the interpreter.
    52  */
    53 
    54 typedef struct ErrAssocData {
    55     BgError *firstBgPtr;	/* First in list of all background errors
    56 				 * waiting to be processed for this
    57 				 * interpreter (NULL if none). */
    58     BgError *lastBgPtr;		/* Last in list of all background errors
    59 				 * waiting to be processed for this
    60 				 * interpreter (NULL if none). */
    61 } ErrAssocData;
    62 
    63 /*
    64  * For each exit handler created with a call to Tcl_CreateExitHandler
    65  * there is a structure of the following type:
    66  */
    67 
    68 typedef struct ExitHandler {
    69     Tcl_ExitProc *proc;		/* Procedure to call when process exits. */
    70     ClientData clientData;	/* One word of information to pass to proc. */
    71     struct ExitHandler *nextPtr;/* Next in list of all exit handlers for
    72 				 * this application, or NULL for end of list. */
    73 } ExitHandler;
    74 
    75 /*
    76  * There is both per-process and per-thread exit handlers.
    77  * The first list is controlled by a mutex.  The other is in
    78  * thread local storage.
    79  */
    80 
    81 static ExitHandler *firstExitPtr = NULL;
    82 				/* First in list of all exit handlers for
    83 				 * application. */
    84 TCL_DECLARE_MUTEX(exitMutex)
    85 
    86 /*
    87  * This variable is set to 1 when Tcl_Finalize is called, and at the end of
    88  * its work, it is reset to 0. The variable is checked by TclInExit() to
    89  * allow different behavior for exit-time processing, e.g. in closing of
    90  * files and pipes.
    91  */
    92 #if !defined(__SYMBIAN32__) || !defined(__WINSCW__)
    93 static int inFinalize = 0;
    94 static int subsystemsInitialized = 0;
    95 #endif
    96 
    97 typedef struct ThreadSpecificData {
    98     ExitHandler *firstExitPtr;  /* First in list of all exit handlers for
    99 				 * this thread. */
   100     int inExit;			/* True when this thread is exiting. This
   101 				 * is used as a hack to decide to close
   102 				 * the standard channels. */
   103     Tcl_Obj *tclLibraryPath;	/* Path(s) to the Tcl library */
   104 } ThreadSpecificData;
   105 #if !defined(__SYMBIAN32__) || !defined(__WINSCW__)
   106 static Tcl_ThreadDataKey dataKey;
   107 
   108 /*
   109  * Common string for the library path for sharing across threads.
   110  * This is ckalloc'd and cleared in Tcl_Finalize.
   111  */
   112 static char *tclLibraryPathStr = NULL;
   113 #endif
   114 
   115 #ifdef TCL_THREADS
   116 
   117 typedef struct {
   118     Tcl_ThreadCreateProc *proc;	/* Main() function of the thread */
   119     ClientData clientData;	/* The one argument to Main() */
   120 } ThreadClientData;
   121 static Tcl_ThreadCreateType NewThreadProc _ANSI_ARGS_((
   122            ClientData clientData));
   123 #endif
   124 
   125 /*
   126  * Prototypes for procedures referenced only in this file:
   127  */
   128 
   129 static void		BgErrorDeleteProc _ANSI_ARGS_((ClientData clientData,
   130 			    Tcl_Interp *interp));
   131 static void		HandleBgErrors _ANSI_ARGS_((ClientData clientData));
   132 static char *		VwaitVarProc _ANSI_ARGS_((ClientData clientData,
   133 			    Tcl_Interp *interp, CONST char *name1, 
   134 			    CONST char *name2, int flags));
   135 
   136 /*
   137  *----------------------------------------------------------------------
   138  *
   139  * Tcl_BackgroundError --
   140  *
   141  *	This procedure is invoked to handle errors that occur in Tcl
   142  *	commands that are invoked in "background" (e.g. from event or
   143  *	timer bindings).
   144  *
   145  * Results:
   146  *	None.
   147  *
   148  * Side effects:
   149  *	The command "bgerror" is invoked later as an idle handler to
   150  *	process the error, passing it the error message.  If that fails,
   151  *	then an error message is output on stderr.
   152  *
   153  *----------------------------------------------------------------------
   154  */
   155 
   156 EXPORT_C void
   157 Tcl_BackgroundError(interp)
   158     Tcl_Interp *interp;		/* Interpreter in which an error has
   159 				 * occurred. */
   160 {
   161     BgError *errPtr;
   162     CONST char *errResult, *varValue;
   163     ErrAssocData *assocPtr;
   164     int length;
   165 
   166     /*
   167      * The Tcl_AddErrorInfo call below (with an empty string) ensures that
   168      * errorInfo gets properly set.  It's needed in cases where the error
   169      * came from a utility procedure like Tcl_GetVar instead of Tcl_Eval;
   170      * in these cases errorInfo still won't have been set when this
   171      * procedure is called.
   172      */
   173 
   174     Tcl_AddErrorInfo(interp, "");
   175 
   176     errResult = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &length);
   177 	
   178     errPtr = (BgError *) ckalloc(sizeof(BgError));
   179     errPtr->interp = interp;
   180     errPtr->errorMsg = (char *) ckalloc((unsigned) (length + 1));
   181     memcpy(errPtr->errorMsg, errResult, (size_t) (length + 1));
   182     varValue = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
   183     if (varValue == NULL) {
   184 	varValue = errPtr->errorMsg;
   185     }
   186     errPtr->errorInfo = (char *) ckalloc((unsigned) (strlen(varValue) + 1));
   187     strcpy(errPtr->errorInfo, varValue);
   188     varValue = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY);
   189     if (varValue == NULL) {
   190 	varValue = "";
   191     }
   192     errPtr->errorCode = (char *) ckalloc((unsigned) (strlen(varValue) + 1));
   193     strcpy(errPtr->errorCode, varValue);
   194     errPtr->nextPtr = NULL;
   195 
   196     assocPtr = (ErrAssocData *) Tcl_GetAssocData(interp, "tclBgError",
   197 	    (Tcl_InterpDeleteProc **) NULL);
   198     if (assocPtr == NULL) {
   199 
   200 	/*
   201 	 * This is the first time a background error has occurred in
   202 	 * this interpreter.  Create associated data to keep track of
   203 	 * pending error reports.
   204 	 */
   205 
   206 	assocPtr = (ErrAssocData *) ckalloc(sizeof(ErrAssocData));
   207 	assocPtr->firstBgPtr = NULL;
   208 	assocPtr->lastBgPtr = NULL;
   209 	Tcl_SetAssocData(interp, "tclBgError", BgErrorDeleteProc,
   210 		(ClientData) assocPtr);
   211     }
   212     if (assocPtr->firstBgPtr == NULL) {
   213 	assocPtr->firstBgPtr = errPtr;
   214 	Tcl_DoWhenIdle(HandleBgErrors, (ClientData) assocPtr);
   215     } else {
   216 	assocPtr->lastBgPtr->nextPtr = errPtr;
   217     }
   218     assocPtr->lastBgPtr = errPtr;
   219     Tcl_ResetResult(interp);
   220 }
   221 
   222 /*
   223  *----------------------------------------------------------------------
   224  *
   225  * HandleBgErrors --
   226  *
   227  *	This procedure is invoked as an idle handler to process all of
   228  *	the accumulated background errors.
   229  *
   230  * Results:
   231  *	None.
   232  *
   233  * Side effects:
   234  *	Depends on what actions "bgerror" takes for the errors.
   235  *
   236  *----------------------------------------------------------------------
   237  */
   238 
   239 static void
   240 HandleBgErrors(clientData)
   241     ClientData clientData;	/* Pointer to ErrAssocData structure. */
   242 {
   243     Tcl_Interp *interp;
   244     CONST char *argv[2];
   245     int code;
   246     BgError *errPtr;
   247     ErrAssocData *assocPtr = (ErrAssocData *) clientData;
   248     Tcl_Channel errChannel;
   249 
   250     Tcl_Preserve((ClientData) assocPtr);
   251     
   252     while (assocPtr->firstBgPtr != NULL) {
   253 	interp = assocPtr->firstBgPtr->interp;
   254 	if (interp == NULL) {
   255 	    goto doneWithInterp;
   256 	}
   257 
   258 	/*
   259 	 * Restore important state variables to what they were at
   260 	 * the time the error occurred.
   261 	 */
   262 
   263 	Tcl_SetVar(interp, "errorInfo", assocPtr->firstBgPtr->errorInfo,
   264 		TCL_GLOBAL_ONLY);
   265 	Tcl_SetVar(interp, "errorCode", assocPtr->firstBgPtr->errorCode,
   266 		TCL_GLOBAL_ONLY);
   267 
   268 	/*
   269 	 * Create and invoke the bgerror command.
   270 	 */
   271 
   272 	argv[0] = "bgerror";
   273 	argv[1] = assocPtr->firstBgPtr->errorMsg;
   274 	
   275 	Tcl_AllowExceptions(interp);
   276         Tcl_Preserve((ClientData) interp);
   277 	code = TclGlobalInvoke(interp, 2, argv, 0);
   278 	if (code == TCL_ERROR) {
   279 
   280             /*
   281              * If the interpreter is safe, we look for a hidden command
   282              * named "bgerror" and call that with the error information.
   283              * Otherwise, simply ignore the error. The rationale is that
   284              * this could be an error caused by a malicious applet trying
   285              * to cause an infinite barrage of error messages. The hidden
   286              * "bgerror" command can be used by a security policy to
   287              * interpose on such attacks and e.g. kill the applet after a
   288              * few attempts.
   289              */
   290 
   291             if (Tcl_IsSafe(interp)) {
   292 		Tcl_SavedResult save;
   293 		
   294 		Tcl_SaveResult(interp, &save);
   295                 TclGlobalInvoke(interp, 2, argv, TCL_INVOKE_HIDDEN);
   296 		Tcl_RestoreResult(interp, &save);
   297 
   298                 goto doneWithInterp;
   299             } 
   300 
   301             /*
   302              * We have to get the error output channel at the latest possible
   303              * time, because the eval (above) might have changed the channel.
   304              */
   305             
   306             errChannel = Tcl_GetStdChannel(TCL_STDERR);
   307             if (errChannel != (Tcl_Channel) NULL) {
   308 		char *string;
   309 		int len;
   310 
   311 		string = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &len);
   312 		if (Tcl_FindCommand(interp, "bgerror", NULL, TCL_GLOBAL_ONLY) == NULL) {
   313                     Tcl_WriteChars(errChannel, assocPtr->firstBgPtr->errorInfo, -1);
   314                     Tcl_WriteChars(errChannel, "\n", -1);
   315                 } else {
   316                     Tcl_WriteChars(errChannel,
   317                             "bgerror failed to handle background error.\n",
   318                             -1);
   319                     Tcl_WriteChars(errChannel, "    Original error: ", -1);
   320                     Tcl_WriteChars(errChannel, assocPtr->firstBgPtr->errorMsg,
   321                             -1);
   322                     Tcl_WriteChars(errChannel, "\n", -1);
   323                     Tcl_WriteChars(errChannel, "    Error in bgerror: ", -1);
   324                     Tcl_WriteChars(errChannel, string, len);
   325                     Tcl_WriteChars(errChannel, "\n", -1);
   326                 }
   327                 Tcl_Flush(errChannel);
   328             }
   329 	} else if (code == TCL_BREAK) {
   330 
   331 	    /*
   332 	     * Break means cancel any remaining error reports for this
   333 	     * interpreter.
   334 	     */
   335 
   336 	    for (errPtr = assocPtr->firstBgPtr; errPtr != NULL;
   337 		    errPtr = errPtr->nextPtr) {
   338 		if (errPtr->interp == interp) {
   339 		    errPtr->interp = NULL;
   340 		}
   341 	    }
   342 	}
   343 
   344 	/*
   345 	 * Discard the command and the information about the error report.
   346 	 */
   347 
   348 doneWithInterp:
   349 
   350 	if (assocPtr->firstBgPtr) {
   351 	    ckfree(assocPtr->firstBgPtr->errorMsg);
   352 	    ckfree(assocPtr->firstBgPtr->errorInfo);
   353 	    ckfree(assocPtr->firstBgPtr->errorCode);
   354 	    errPtr = assocPtr->firstBgPtr->nextPtr;
   355 	    ckfree((char *) assocPtr->firstBgPtr);
   356 	    assocPtr->firstBgPtr = errPtr;
   357 	}
   358         
   359         if (interp != NULL) {
   360             Tcl_Release((ClientData) interp);
   361         }
   362     }
   363     assocPtr->lastBgPtr = NULL;
   364 
   365     Tcl_Release((ClientData) assocPtr);
   366 }
   367 
   368 /*
   369  *----------------------------------------------------------------------
   370  *
   371  * BgErrorDeleteProc --
   372  *
   373  *	This procedure is associated with the "tclBgError" assoc data
   374  *	for an interpreter;  it is invoked when the interpreter is
   375  *	deleted in order to free the information assoicated with any
   376  *	pending error reports.
   377  *
   378  * Results:
   379  *	None.
   380  *
   381  * Side effects:
   382  *	Background error information is freed: if there were any
   383  *	pending error reports, they are cancelled.
   384  *
   385  *----------------------------------------------------------------------
   386  */
   387 
   388 static void
   389 BgErrorDeleteProc(clientData, interp)
   390     ClientData clientData;	/* Pointer to ErrAssocData structure. */
   391     Tcl_Interp *interp;		/* Interpreter being deleted. */
   392 {
   393     ErrAssocData *assocPtr = (ErrAssocData *) clientData;
   394     BgError *errPtr;
   395 
   396     while (assocPtr->firstBgPtr != NULL) {
   397 	errPtr = assocPtr->firstBgPtr;
   398 	assocPtr->firstBgPtr = errPtr->nextPtr;
   399 	ckfree(errPtr->errorMsg);
   400 	ckfree(errPtr->errorInfo);
   401 	ckfree(errPtr->errorCode);
   402 	ckfree((char *) errPtr);
   403     }
   404     Tcl_CancelIdleCall(HandleBgErrors, (ClientData) assocPtr);
   405     Tcl_EventuallyFree((ClientData) assocPtr, TCL_DYNAMIC);
   406 }
   407 
   408 /*
   409  *----------------------------------------------------------------------
   410  *
   411  * Tcl_CreateExitHandler --
   412  *
   413  *	Arrange for a given procedure to be invoked just before the
   414  *	application exits.
   415  *
   416  * Results:
   417  *	None.
   418  *
   419  * Side effects:
   420  *	Proc will be invoked with clientData as argument when the
   421  *	application exits.
   422  *
   423  *----------------------------------------------------------------------
   424  */
   425 
   426 EXPORT_C void
   427 Tcl_CreateExitHandler(proc, clientData)
   428     Tcl_ExitProc *proc;		/* Procedure to invoke. */
   429     ClientData clientData;	/* Arbitrary value to pass to proc. */
   430 {
   431     ExitHandler *exitPtr;
   432 
   433     exitPtr = (ExitHandler *) ckalloc(sizeof(ExitHandler));
   434     exitPtr->proc = proc;
   435     exitPtr->clientData = clientData;
   436     Tcl_MutexLock(&exitMutex);
   437     exitPtr->nextPtr = firstExitPtr;
   438     firstExitPtr = exitPtr;
   439     Tcl_MutexUnlock(&exitMutex);
   440 }
   441 
   442 /*
   443  *----------------------------------------------------------------------
   444  *
   445  * Tcl_DeleteExitHandler --
   446  *
   447  *	This procedure cancels an existing exit handler matching proc
   448  *	and clientData, if such a handler exits.
   449  *
   450  * Results:
   451  *	None.
   452  *
   453  * Side effects:
   454  *	If there is an exit handler corresponding to proc and clientData
   455  *	then it is cancelled;  if no such handler exists then nothing
   456  *	happens.
   457  *
   458  *----------------------------------------------------------------------
   459  */
   460 
   461 EXPORT_C void
   462 Tcl_DeleteExitHandler(proc, clientData)
   463     Tcl_ExitProc *proc;		/* Procedure that was previously registered. */
   464     ClientData clientData;	/* Arbitrary value to pass to proc. */
   465 {
   466     ExitHandler *exitPtr, *prevPtr;
   467 
   468     Tcl_MutexLock(&exitMutex);
   469     for (prevPtr = NULL, exitPtr = firstExitPtr; exitPtr != NULL;
   470 	    prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) {
   471 	if ((exitPtr->proc == proc)
   472 		&& (exitPtr->clientData == clientData)) {
   473 	    if (prevPtr == NULL) {
   474 		firstExitPtr = exitPtr->nextPtr;
   475 	    } else {
   476 		prevPtr->nextPtr = exitPtr->nextPtr;
   477 	    }
   478 	    ckfree((char *) exitPtr);
   479 	    break;
   480 	}
   481     }
   482     Tcl_MutexUnlock(&exitMutex);
   483     return;
   484 }
   485 
   486 /*
   487  *----------------------------------------------------------------------
   488  *
   489  * Tcl_CreateThreadExitHandler --
   490  *
   491  *	Arrange for a given procedure to be invoked just before the
   492  *	current thread exits.
   493  *
   494  * Results:
   495  *	None.
   496  *
   497  * Side effects:
   498  *	Proc will be invoked with clientData as argument when the
   499  *	application exits.
   500  *
   501  *----------------------------------------------------------------------
   502  */
   503 
   504 EXPORT_C void
   505 Tcl_CreateThreadExitHandler(proc, clientData)
   506     Tcl_ExitProc *proc;		/* Procedure to invoke. */
   507     ClientData clientData;	/* Arbitrary value to pass to proc. */
   508 {
   509     ExitHandler *exitPtr;
   510     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   511 
   512     exitPtr = (ExitHandler *) ckalloc(sizeof(ExitHandler));
   513     exitPtr->proc = proc;
   514     exitPtr->clientData = clientData;
   515     exitPtr->nextPtr = tsdPtr->firstExitPtr;
   516     tsdPtr->firstExitPtr = exitPtr;
   517 }
   518 
   519 /*
   520  *----------------------------------------------------------------------
   521  *
   522  * Tcl_DeleteThreadExitHandler --
   523  *
   524  *	This procedure cancels an existing exit handler matching proc
   525  *	and clientData, if such a handler exits.
   526  *
   527  * Results:
   528  *	None.
   529  *
   530  * Side effects:
   531  *	If there is an exit handler corresponding to proc and clientData
   532  *	then it is cancelled;  if no such handler exists then nothing
   533  *	happens.
   534  *
   535  *----------------------------------------------------------------------
   536  */
   537 
   538 EXPORT_C void
   539 Tcl_DeleteThreadExitHandler(proc, clientData)
   540     Tcl_ExitProc *proc;		/* Procedure that was previously registered. */
   541     ClientData clientData;	/* Arbitrary value to pass to proc. */
   542 {
   543     ExitHandler *exitPtr, *prevPtr;
   544     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   545 
   546     for (prevPtr = NULL, exitPtr = tsdPtr->firstExitPtr; exitPtr != NULL;
   547 	    prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) {
   548 	if ((exitPtr->proc == proc)
   549 		&& (exitPtr->clientData == clientData)) {
   550 	    if (prevPtr == NULL) {
   551 		tsdPtr->firstExitPtr = exitPtr->nextPtr;
   552 	    } else {
   553 		prevPtr->nextPtr = exitPtr->nextPtr;
   554 	    }
   555 	    ckfree((char *) exitPtr);
   556 	    return;
   557 	}
   558     }
   559 }
   560 
   561 /*
   562  *----------------------------------------------------------------------
   563  *
   564  * Tcl_Exit --
   565  *
   566  *	This procedure is called to terminate the application.
   567  *
   568  * Results:
   569  *	None.
   570  *
   571  * Side effects:
   572  *	All existing exit handlers are invoked, then the application
   573  *	ends.
   574  *
   575  *----------------------------------------------------------------------
   576  */
   577 
   578 EXPORT_C void
   579 Tcl_Exit(status)
   580     int status;			/* Exit status for application;  typically
   581 				 * 0 for normal return, 1 for error return. */
   582 {
   583     Tcl_Finalize();
   584     TclpExit(status);
   585 }
   586 
   587 /*
   588  *-------------------------------------------------------------------------
   589  * 
   590  * TclSetLibraryPath --
   591  *
   592  *	Set the path that will be used for searching for init.tcl and 
   593  *	encodings when an interp is being created.
   594  *
   595  * Results:
   596  *	None.
   597  *
   598  * Side effects:
   599  *	Changing the library path will affect what directories are
   600  *	examined when looking for encodings for all interps from that
   601  *	point forward.
   602  *
   603  *	The refcount of the new library path is incremented and the 
   604  *	refcount of the old path is decremented.
   605  *
   606  *-------------------------------------------------------------------------
   607  */
   608 
   609 void
   610 TclSetLibraryPath(pathPtr)
   611     Tcl_Obj *pathPtr;		/* A Tcl list object whose elements are
   612 				 * the new library path. */
   613 {
   614     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   615     const char *toDupe;
   616     int size;
   617 
   618     if (pathPtr != NULL) {
   619 	Tcl_IncrRefCount(pathPtr);
   620     }
   621     if (tsdPtr->tclLibraryPath != NULL) {
   622 	Tcl_DecrRefCount(tsdPtr->tclLibraryPath);
   623     }
   624     tsdPtr->tclLibraryPath = pathPtr;
   625 
   626     /*
   627      *  No mutex locking is needed here as up the stack we're within
   628      *  TclpInitLock().
   629      */
   630     if (tclLibraryPathStr != NULL) {
   631 	ckfree(tclLibraryPathStr);
   632     }
   633     toDupe = Tcl_GetStringFromObj(pathPtr, &size);
   634     tclLibraryPathStr = ckalloc((unsigned)size+1);
   635     memcpy(tclLibraryPathStr, toDupe, (unsigned)size+1);
   636 }
   637 
   638 /*
   639  *-------------------------------------------------------------------------
   640  *
   641  * TclGetLibraryPath --
   642  *
   643  *	Return a Tcl list object whose elements are the library path.
   644  *	The caller should not modify the contents of the returned object.
   645  *
   646  * Results:
   647  *	As above.
   648  *
   649  * Side effects:
   650  *	None.
   651  *
   652  *-------------------------------------------------------------------------
   653  */
   654 
   655 Tcl_Obj *
   656 TclGetLibraryPath()
   657 {
   658     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   659 
   660     if (tsdPtr->tclLibraryPath == NULL) {
   661 	/*
   662 	 * Grab the shared string and place it into a new thread specific
   663 	 * Tcl_Obj.
   664 	 */
   665 	tsdPtr->tclLibraryPath = Tcl_NewStringObj(tclLibraryPathStr, -1);
   666 
   667 	/* take ownership */
   668 	Tcl_IncrRefCount(tsdPtr->tclLibraryPath);
   669     }
   670     return tsdPtr->tclLibraryPath;
   671 }
   672 
   673 /*
   674  *-------------------------------------------------------------------------
   675  *
   676  * TclInitSubsystems --
   677  *
   678  *	Initialize various subsytems in Tcl.  This should be called the
   679  *	first time an interp is created, or before any of the subsystems
   680  *	are used.  This function ensures an order for the initialization 
   681  *	of subsystems:
   682  *
   683  *	1. that cannot be initialized in lazy order because they are 
   684  *	mutually dependent.
   685  *
   686  *	2. so that they can be finalized in a known order w/o causing
   687  *	the subsequent re-initialization of a subsystem in the act of
   688  *	shutting down another.
   689  *
   690  * Results:
   691  *	None.
   692  *
   693  * Side effects:
   694  *	Varied, see the respective initialization routines.
   695  *
   696  *-------------------------------------------------------------------------
   697  */
   698 
   699 void
   700 TclInitSubsystems(argv0)
   701     CONST char *argv0;		/* Name of executable from argv[0] to main()
   702 				 * in native multi-byte encoding. */
   703 {
   704     ThreadSpecificData *tsdPtr;
   705 
   706     if (inFinalize != 0) {
   707 	panic("TclInitSubsystems called while finalizing");
   708     }
   709 
   710     /*
   711      * Grab the thread local storage pointer before doing anything because
   712      * the initialization routines will be registering exit handlers.
   713      * We use this pointer to detect if this is the first time this
   714      * thread has created an interpreter.
   715      */
   716 
   717     tsdPtr = (ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);
   718 
   719     if (subsystemsInitialized == 0) {
   720 	/* 
   721 	 * Double check inside the mutex.  There are definitly calls
   722 	 * back into this routine from some of the procedures below.
   723 	 */
   724 
   725 	TclpInitLock();
   726 	if (subsystemsInitialized == 0) {
   727 	    /*
   728 	     * Have to set this bit here to avoid deadlock with the
   729 	     * routines below us that call into TclInitSubsystems.
   730 	     */
   731 
   732 	    subsystemsInitialized = 1;
   733 
   734 	    tclExecutableName = NULL;
   735 
   736 	    /*
   737 	     * Initialize locks used by the memory allocators before anything
   738 	     * interesting happens so we can use the allocators in the
   739 	     * implementation of self-initializing locks.
   740 	     */
   741 
   742 #if USE_TCLALLOC
   743 	    TclInitAlloc(); /* process wide mutex init */
   744 #endif
   745 #ifdef TCL_MEM_DEBUG
   746 	    TclInitDbCkalloc(); /* process wide mutex init */
   747 #endif
   748 
   749 	    TclpInitPlatform(); /* creates signal handler(s) */
   750 	    TclInitObjSubsystem(); /* register obj types, create mutexes */
   751 	    TclInitIOSubsystem(); /* inits a tsd key (noop) */
   752 	    TclInitEncodingSubsystem(); /* process wide encoding init */
   753 	    TclInitNamespaceSubsystem(); /* register ns obj type (mutexed) */
   754 	}
   755 	TclpInitUnlock();
   756     }
   757 
   758     if (tsdPtr == NULL) {
   759 	/*
   760 	 * First time this thread has created an interpreter.
   761 	 * We fetch the key again just in case no exit handlers were
   762 	 * registered by this point.
   763 	 */
   764 
   765 	(void) TCL_TSD_INIT(&dataKey);
   766 	TclInitNotifier();
   767      }
   768 }
   769 
   770 /*
   771  *----------------------------------------------------------------------
   772  *
   773  * Tcl_Finalize --
   774  *
   775  *	Shut down Tcl.  First calls registered exit handlers, then
   776  *	carefully shuts down various subsystems.
   777  *	Called by Tcl_Exit or when the Tcl shared library is being 
   778  *	unloaded.
   779  *
   780  * Results:
   781  *	None.
   782  *
   783  * Side effects:
   784  *	Varied, see the respective finalization routines.
   785  *
   786  *----------------------------------------------------------------------
   787  */
   788 
   789 EXPORT_C void
   790 Tcl_Finalize()
   791 {
   792     ExitHandler *exitPtr;
   793     
   794     /*
   795      * Invoke exit handlers first.
   796      */
   797 
   798     Tcl_MutexLock(&exitMutex);
   799     inFinalize = 1;
   800     for (exitPtr = firstExitPtr; exitPtr != NULL; exitPtr = firstExitPtr) {
   801 	/*
   802 	 * Be careful to remove the handler from the list before
   803 	 * invoking its callback.  This protects us against
   804 	 * double-freeing if the callback should call
   805 	 * Tcl_DeleteExitHandler on itself.
   806 	 */
   807 
   808 	firstExitPtr = exitPtr->nextPtr;
   809 	Tcl_MutexUnlock(&exitMutex);
   810 	(*exitPtr->proc)(exitPtr->clientData);
   811 	ckfree((char *) exitPtr);
   812 	Tcl_MutexLock(&exitMutex);
   813     }    
   814     firstExitPtr = NULL;
   815     Tcl_MutexUnlock(&exitMutex);
   816 
   817     TclpInitLock();
   818     if (subsystemsInitialized != 0) {
   819 	subsystemsInitialized = 0;
   820 
   821 	/*
   822 	 * Ensure the thread-specific data is initialised as it is
   823 	 * used in Tcl_FinalizeThread()
   824 	 */
   825 
   826 	(void) TCL_TSD_INIT(&dataKey);
   827 
   828 	/*
   829 	 * Clean up after the current thread now, after exit handlers.
   830 	 * In particular, the testexithandler command sets up something
   831 	 * that writes to standard output, which gets closed.
   832 	 * Note that there is no thread-local storage after this call.
   833 	 */
   834 
   835 	Tcl_FinalizeThread();
   836 
   837 	/*
   838 	 * Now finalize the Tcl execution environment.  Note that this
   839 	 * must be done after the exit handlers, because there are
   840 	 * order dependencies.
   841 	 */
   842 
   843 	TclFinalizeCompilation();
   844 	TclFinalizeExecution();
   845 	TclFinalizeEnvironment();
   846 
   847 	/* 
   848 	 * Finalizing the filesystem must come after anything which
   849 	 * might conceivably interact with the 'Tcl_FS' API. 
   850 	 */
   851 
   852 	TclFinalizeFilesystem();
   853 
   854 	/*
   855 	 * Undo all the Tcl_ObjType registrations, and reset the master list
   856 	 * of free Tcl_Obj's.  After this returns, no more Tcl_Obj's should
   857 	 * be allocated or freed.
   858 	 *
   859 	 * Note in particular that TclFinalizeObjects() must follow
   860 	 * TclFinalizeFilesystem() because TclFinalizeFilesystem free's
   861 	 * the Tcl_Obj that holds the path of the current working directory.
   862 	 */
   863 
   864 	TclFinalizeObjects();
   865 
   866 	/* 
   867 	 * We must be sure the encoding finalization doesn't need
   868 	 * to examine the filesystem in any way.  Since it only
   869 	 * needs to clean up internal data structures, this is
   870 	 * fine.
   871 	 */
   872 	TclFinalizeEncodingSubsystem();
   873 
   874 	if (tclExecutableName != NULL) {
   875 	    ckfree(tclExecutableName);
   876 	    tclExecutableName = NULL;
   877 	}
   878 	if (tclNativeExecutableName != NULL) {
   879 	    ckfree(tclNativeExecutableName);
   880 	    tclNativeExecutableName = NULL;
   881 	}
   882 	if (tclDefaultEncodingDir != NULL) {
   883 	    ckfree(tclDefaultEncodingDir);
   884 	    tclDefaultEncodingDir = NULL;
   885 	}
   886 	if (tclLibraryPathStr != NULL) {
   887 	    ckfree(tclLibraryPathStr);
   888 	    tclLibraryPathStr = NULL;
   889 	}
   890 	
   891 	Tcl_SetPanicProc(NULL);
   892 
   893 	/*
   894 	 * There have been several bugs in the past that cause
   895 	 * exit handlers to be established during Tcl_Finalize
   896 	 * processing.  Such exit handlers leave malloc'ed memory,
   897 	 * and Tcl_FinalizeThreadAlloc or Tcl_FinalizeMemorySubsystem
   898 	 * will result in a corrupted heap.  The result can be a
   899 	 * mysterious crash on process exit.  Check here that
   900 	 * nobody's done this.
   901 	 */
   902 
   903 #ifdef TCL_MEM_DEBUG
   904 	if ( firstExitPtr != NULL ) {
   905 	    Tcl_Panic( "exit handlers were created during Tcl_Finalize" );
   906 	}
   907 #endif
   908 
   909 	TclFinalizePreserve();
   910 
   911 	/*
   912 	 * Free synchronization objects.  There really should only be one
   913 	 * thread alive at this moment.
   914 	 */
   915 
   916 	TclFinalizeSynchronization();
   917 
   918 #if defined(TCL_THREADS) && defined(USE_THREAD_ALLOC) && !defined(TCL_MEM_DEBUG) && !defined(PURIFY)
   919 	TclFinalizeThreadAlloc();
   920 #endif
   921 
   922 	/*
   923 	 * We defer unloading of packages until very late 
   924 	 * to avoid memory access issues.  Both exit callbacks and
   925 	 * synchronization variables may be stored in packages.
   926 	 * 
   927 	 * Note that TclFinalizeLoad unloads packages in the reverse
   928 	 * of the order they were loaded in (i.e. last to be loaded
   929 	 * is the first to be unloaded).  This can be important for
   930 	 * correct unloading when dependencies exist.
   931 	 * 
   932 	 * Once load has been finalized, we will have deleted any
   933 	 * temporary copies of shared libraries and can therefore
   934 	 * reset the filesystem to its original state.
   935 	 */
   936 
   937 	TclFinalizeLoad();
   938 	TclResetFilesystem();
   939 	
   940 	/*
   941 	 * At this point, there should no longer be any ckalloc'ed memory.
   942 	 */
   943 
   944 	TclFinalizeMemorySubsystem();
   945 	inFinalize = 0;
   946     }
   947     TclFinalizeLock();
   948 }
   949 
   950 /*
   951  *----------------------------------------------------------------------
   952  *
   953  * Tcl_FinalizeThread --
   954  *
   955  *	Runs the exit handlers to allow Tcl to clean up its state
   956  *	about a particular thread.
   957  *
   958  * Results:
   959  *	None.
   960  *
   961  * Side effects:
   962  *	Varied, see the respective finalization routines.
   963  *
   964  *----------------------------------------------------------------------
   965  */
   966 
   967 EXPORT_C void
   968 Tcl_FinalizeThread()
   969 {
   970     ExitHandler *exitPtr;
   971     ThreadSpecificData *tsdPtr;
   972 
   973     tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
   974     if (tsdPtr != NULL) {
   975 	tsdPtr->inExit = 1;
   976 
   977 	/*
   978 	 * Clean up the library path now, before we invalidate thread-local
   979 	 * storage or calling thread exit handlers.
   980 	 */
   981 
   982 	if (tsdPtr->tclLibraryPath != NULL) {
   983 	    Tcl_DecrRefCount(tsdPtr->tclLibraryPath);
   984 	    tsdPtr->tclLibraryPath = NULL;
   985 	}
   986 
   987 	for (exitPtr = tsdPtr->firstExitPtr; exitPtr != NULL;
   988 		exitPtr = tsdPtr->firstExitPtr) {
   989 	    /*
   990 	     * Be careful to remove the handler from the list before invoking
   991 	     * its callback.  This protects us against double-freeing if the
   992 	     * callback should call Tcl_DeleteThreadExitHandler on itself.
   993 	     */
   994 
   995 	    tsdPtr->firstExitPtr = exitPtr->nextPtr;
   996 	    (*exitPtr->proc)(exitPtr->clientData);
   997 	    ckfree((char *) exitPtr);
   998 	}
   999 	TclFinalizeIOSubsystem();
  1000 	TclFinalizeNotifier();
  1001 	TclFinalizeAsync();
  1002     }
  1003 
  1004     /*
  1005      * Blow away all thread local storage blocks.
  1006      *
  1007      * Note that Tcl API allows creation of threads which do not use any
  1008      * Tcl interp or other Tcl subsytems. Those threads might, however,
  1009      * use thread local storage, so we must unconditionally finalize it.
  1010      *
  1011      * Fix [Bug #571002]
  1012      */
  1013 
  1014     TclFinalizeThreadData();
  1015 }
  1016 
  1017 /*
  1018  *----------------------------------------------------------------------
  1019  *
  1020  * TclInExit --
  1021  *
  1022  *	Determines if we are in the middle of exit-time cleanup.
  1023  *
  1024  * Results:
  1025  *	If we are in the middle of exiting, 1, otherwise 0.
  1026  *
  1027  * Side effects:
  1028  *	None.
  1029  *
  1030  *----------------------------------------------------------------------
  1031  */
  1032 
  1033 int
  1034 TclInExit()
  1035 {
  1036     return inFinalize;
  1037 }
  1038 
  1039 /*
  1040  *----------------------------------------------------------------------
  1041  *
  1042  * TclInThreadExit --
  1043  *
  1044  *	Determines if we are in the middle of thread exit-time cleanup.
  1045  *
  1046  * Results:
  1047  *	If we are in the middle of exiting this thread, 1, otherwise 0.
  1048  *
  1049  * Side effects:
  1050  *	None.
  1051  *
  1052  *----------------------------------------------------------------------
  1053  */
  1054 
  1055 int
  1056 TclInThreadExit()
  1057 {
  1058     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
  1059 	    TclThreadDataKeyGet(&dataKey);
  1060     if (tsdPtr == NULL) {
  1061 	return 0;
  1062     } else {
  1063 	return tsdPtr->inExit;
  1064     }
  1065 }
  1066 
  1067 /*
  1068  *----------------------------------------------------------------------
  1069  *
  1070  * Tcl_VwaitObjCmd --
  1071  *
  1072  *	This procedure is invoked to process the "vwait" Tcl command.
  1073  *	See the user documentation for details on what it does.
  1074  *
  1075  * Results:
  1076  *	A standard Tcl result.
  1077  *
  1078  * Side effects:
  1079  *	See the user documentation.
  1080  *
  1081  *----------------------------------------------------------------------
  1082  */
  1083 
  1084 	/* ARGSUSED */
  1085 int
  1086 Tcl_VwaitObjCmd(clientData, interp, objc, objv)
  1087     ClientData clientData;	/* Not used. */
  1088     Tcl_Interp *interp;		/* Current interpreter. */
  1089     int objc;			/* Number of arguments. */
  1090     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1091 {
  1092     int done, foundEvent;
  1093     char *nameString;
  1094 
  1095     if (objc != 2) {
  1096         Tcl_WrongNumArgs(interp, 1, objv, "name");
  1097 	return TCL_ERROR;
  1098     }
  1099     nameString = Tcl_GetString(objv[1]);
  1100     if (Tcl_TraceVar(interp, nameString,
  1101 	    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1102 	    VwaitVarProc, (ClientData) &done) != TCL_OK) {
  1103 	return TCL_ERROR;
  1104     };
  1105     done = 0;
  1106     foundEvent = 1;
  1107     while (!done && foundEvent) {
  1108 	foundEvent = Tcl_DoOneEvent(TCL_ALL_EVENTS);
  1109     }
  1110     Tcl_UntraceVar(interp, nameString,
  1111 	    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1112 	    VwaitVarProc, (ClientData) &done);
  1113 
  1114     /*
  1115      * Clear out the interpreter's result, since it may have been set
  1116      * by event handlers.
  1117      */
  1118 
  1119     Tcl_ResetResult(interp);
  1120     if (!foundEvent) {
  1121 	Tcl_AppendResult(interp, "can't wait for variable \"", nameString,
  1122 		"\":  would wait forever", (char *) NULL);
  1123 	return TCL_ERROR;
  1124     }
  1125     return TCL_OK;
  1126 }
  1127 
  1128 	/* ARGSUSED */
  1129 static char *
  1130 VwaitVarProc(clientData, interp, name1, name2, flags)
  1131     ClientData clientData;	/* Pointer to integer to set to 1. */
  1132     Tcl_Interp *interp;		/* Interpreter containing variable. */
  1133     CONST char *name1;		/* Name of variable. */
  1134     CONST char *name2;		/* Second part of variable name. */
  1135     int flags;			/* Information about what happened. */
  1136 {
  1137     int *donePtr = (int *) clientData;
  1138 
  1139     *donePtr = 1;
  1140     return (char *) NULL;
  1141 }
  1142 
  1143 /*
  1144  *----------------------------------------------------------------------
  1145  *
  1146  * Tcl_UpdateObjCmd --
  1147  *
  1148  *	This procedure is invoked to process the "update" Tcl command.
  1149  *	See the user documentation for details on what it does.
  1150  *
  1151  * Results:
  1152  *	A standard Tcl result.
  1153  *
  1154  * Side effects:
  1155  *	See the user documentation.
  1156  *
  1157  *----------------------------------------------------------------------
  1158  */
  1159 
  1160 	/* ARGSUSED */
  1161 int
  1162 Tcl_UpdateObjCmd(clientData, interp, objc, objv)
  1163     ClientData clientData;	/* Not used. */
  1164     Tcl_Interp *interp;		/* Current interpreter. */
  1165     int objc;			/* Number of arguments. */
  1166     Tcl_Obj *CONST objv[];	/* Argument objects. */
  1167 {
  1168     int optionIndex;
  1169     int flags = 0;		/* Initialized to avoid compiler warning. */
  1170     static CONST char *updateOptions[] = {"idletasks", (char *) NULL};
  1171     enum updateOptions {REGEXP_IDLETASKS};
  1172 
  1173     if (objc == 1) {
  1174 	flags = TCL_ALL_EVENTS|TCL_DONT_WAIT;
  1175     } else if (objc == 2) {
  1176 	if (Tcl_GetIndexFromObj(interp, objv[1], updateOptions,
  1177 		"option", 0, &optionIndex) != TCL_OK) {
  1178 	    return TCL_ERROR;
  1179 	}
  1180 	switch ((enum updateOptions) optionIndex) {
  1181 	    case REGEXP_IDLETASKS: {
  1182 		flags = TCL_WINDOW_EVENTS|TCL_IDLE_EVENTS|TCL_DONT_WAIT;
  1183 		break;
  1184 	    }
  1185 	    default: {
  1186 		panic("Tcl_UpdateObjCmd: bad option index to UpdateOptions");
  1187 	    }
  1188 	}
  1189     } else {
  1190         Tcl_WrongNumArgs(interp, 1, objv, "?idletasks?");
  1191 	return TCL_ERROR;
  1192     }
  1193     
  1194     while (Tcl_DoOneEvent(flags) != 0) {
  1195 	/* Empty loop body */
  1196     }
  1197 
  1198     /*
  1199      * Must clear the interpreter's result because event handlers could
  1200      * have executed commands.
  1201      */
  1202 
  1203     Tcl_ResetResult(interp);
  1204     return TCL_OK;
  1205 }
  1206 
  1207 #ifdef TCL_THREADS
  1208 /*
  1209  *-----------------------------------------------------------------------------
  1210  *
  1211  *  NewThreadProc --
  1212  *
  1213  * 	Bootstrap function of a new Tcl thread.
  1214  *
  1215  * Results:
  1216  *	None.
  1217  *
  1218  * Side Effects:
  1219  *	Initializes Tcl notifier for the current thread.
  1220  *
  1221  *-----------------------------------------------------------------------------
  1222  */
  1223 
  1224 static Tcl_ThreadCreateType
  1225 NewThreadProc(ClientData clientData)
  1226 {
  1227     ThreadClientData *cdPtr;
  1228     ClientData threadClientData;
  1229     Tcl_ThreadCreateProc *threadProc;
  1230 
  1231     cdPtr = (ThreadClientData*)clientData;
  1232     threadProc = cdPtr->proc;
  1233     threadClientData = cdPtr->clientData;
  1234     ckfree((char*)clientData); /* Allocated in Tcl_CreateThread() */
  1235 
  1236     (*threadProc)(threadClientData);
  1237 
  1238     TCL_THREAD_CREATE_RETURN;
  1239 }
  1240 #endif
  1241 /*
  1242  *----------------------------------------------------------------------
  1243  *
  1244  * Tcl_CreateThread --
  1245  *
  1246  *	This procedure creates a new thread. This actually belongs
  1247  *	to the tclThread.c file but since we use some private 
  1248  *	data structures local to this file, it is placed here.
  1249  *
  1250  * Results:
  1251  *	TCL_OK if the thread could be created.  The thread ID is
  1252  *	returned in a parameter.
  1253  *
  1254  * Side effects:
  1255  *	A new thread is created.
  1256  *
  1257  *----------------------------------------------------------------------
  1258  */
  1259 
  1260 EXPORT_C int
  1261 Tcl_CreateThread(idPtr, proc, clientData, stackSize, flags)
  1262     Tcl_ThreadId *idPtr;		/* Return, the ID of the thread */
  1263     Tcl_ThreadCreateProc proc;		/* Main() function of the thread */
  1264     ClientData clientData;		/* The one argument to Main() */
  1265     int stackSize;			/* Size of stack for the new thread */
  1266     int flags;				/* Flags controlling behaviour of
  1267 					 * the new thread */
  1268 {
  1269 #ifdef TCL_THREADS
  1270     ThreadClientData *cdPtr;
  1271 
  1272     cdPtr = (ThreadClientData*)ckalloc(sizeof(ThreadClientData));
  1273     cdPtr->proc = proc;
  1274     cdPtr->clientData = clientData;
  1275 
  1276     return TclpThreadCreate(idPtr, NewThreadProc, (ClientData)cdPtr,
  1277                            stackSize, flags);
  1278 #else
  1279     return TCL_ERROR;
  1280 #endif /* TCL_THREADS */
  1281 }