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