os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclThreadTest.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
sl@0
     1
/* 
sl@0
     2
 * tclThreadTest.c --
sl@0
     3
 *
sl@0
     4
 *	This file implements the testthread command.  Eventually this
sl@0
     5
 *	should be tclThreadCmd.c
sl@0
     6
 *	Some of this code is based on work done by Richard Hipp on behalf of
sl@0
     7
 *	Conservation Through Innovation, Limited, with their permission.
sl@0
     8
 *
sl@0
     9
 * Copyright (c) 1998 by Sun Microsystems, Inc.
sl@0
    10
 *
sl@0
    11
 * See the file "license.terms" for information on usage and redistribution
sl@0
    12
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
sl@0
    13
 *
sl@0
    14
 * RCS: @(#) $Id: tclThreadTest.c,v 1.16.2.2 2006/09/22 14:48:52 dkf Exp $
sl@0
    15
 */
sl@0
    16
sl@0
    17
#include "tclInt.h"
sl@0
    18
sl@0
    19
#ifdef TCL_THREADS
sl@0
    20
/*
sl@0
    21
 * Each thread has an single instance of the following structure.  There
sl@0
    22
 * is one instance of this structure per thread even if that thread contains
sl@0
    23
 * multiple interpreters.  The interpreter identified by this structure is
sl@0
    24
 * the main interpreter for the thread.  
sl@0
    25
 *
sl@0
    26
 * The main interpreter is the one that will process any messages 
sl@0
    27
 * received by a thread.  Any thread can send messages but only the
sl@0
    28
 * main interpreter can receive them.
sl@0
    29
 */
sl@0
    30
sl@0
    31
typedef struct ThreadSpecificData {
sl@0
    32
    Tcl_ThreadId  threadId;          /* Tcl ID for this thread */
sl@0
    33
    Tcl_Interp *interp;              /* Main interpreter for this thread */
sl@0
    34
    int flags;                       /* See the TP_ defines below... */
sl@0
    35
    struct ThreadSpecificData *nextPtr;	/* List for "thread names" */
sl@0
    36
    struct ThreadSpecificData *prevPtr;	/* List for "thread names" */
sl@0
    37
} ThreadSpecificData;
sl@0
    38
static Tcl_ThreadDataKey dataKey;
sl@0
    39
sl@0
    40
/*
sl@0
    41
 * This list is used to list all threads that have interpreters.
sl@0
    42
 * This is protected by threadMutex.
sl@0
    43
 */
sl@0
    44
sl@0
    45
static struct ThreadSpecificData *threadList;
sl@0
    46
sl@0
    47
/*
sl@0
    48
 * The following bit-values are legal for the "flags" field of the
sl@0
    49
 * ThreadSpecificData structure.
sl@0
    50
 */
sl@0
    51
#define TP_Dying               0x001 /* This thread is being cancelled */
sl@0
    52
sl@0
    53
/*
sl@0
    54
 * An instance of the following structure contains all information that is
sl@0
    55
 * passed into a new thread when the thread is created using either the
sl@0
    56
 * "thread create" Tcl command or the TclCreateThread() C function.
sl@0
    57
 */
sl@0
    58
sl@0
    59
typedef struct ThreadCtrl {
sl@0
    60
    char *script;    /* The TCL command this thread should execute */
sl@0
    61
    int flags;        /* Initial value of the "flags" field in the 
sl@0
    62
                       * ThreadSpecificData structure for the new thread.
sl@0
    63
                       * Might contain TP_Detached or TP_TclThread. */
sl@0
    64
    Tcl_Condition condWait;
sl@0
    65
    /* This condition variable is used to synchronize
sl@0
    66
     * the parent and child threads.  The child won't run
sl@0
    67
     * until it acquires threadMutex, and the parent function
sl@0
    68
     * won't complete until signaled on this condition
sl@0
    69
     * variable. */
sl@0
    70
} ThreadCtrl;
sl@0
    71
sl@0
    72
/*
sl@0
    73
 * This is the event used to send scripts to other threads.
sl@0
    74
 */
sl@0
    75
sl@0
    76
typedef struct ThreadEvent {
sl@0
    77
    Tcl_Event event;		/* Must be first */
sl@0
    78
    char *script;		/* The script to execute. */
sl@0
    79
    struct ThreadEventResult *resultPtr;
sl@0
    80
				/* To communicate the result.  This is
sl@0
    81
				 * NULL if we don't care about it. */
sl@0
    82
} ThreadEvent;
sl@0
    83
sl@0
    84
typedef struct ThreadEventResult {
sl@0
    85
    Tcl_Condition done;		/* Signaled when the script completes */
sl@0
    86
    int code;			/* Return value of Tcl_Eval */
sl@0
    87
    char *result;		/* Result from the script */
sl@0
    88
    char *errorInfo;		/* Copy of errorInfo variable */
sl@0
    89
    char *errorCode;		/* Copy of errorCode variable */
sl@0
    90
    Tcl_ThreadId srcThreadId;	/* Id of sending thread, in case it dies */
sl@0
    91
    Tcl_ThreadId dstThreadId;	/* Id of target thread, in case it dies */
sl@0
    92
    struct ThreadEvent *eventPtr;	/* Back pointer */
sl@0
    93
    struct ThreadEventResult *nextPtr;	/* List for cleanup */
sl@0
    94
    struct ThreadEventResult *prevPtr;
sl@0
    95
sl@0
    96
} ThreadEventResult;
sl@0
    97
sl@0
    98
static ThreadEventResult *resultList;
sl@0
    99
sl@0
   100
/*
sl@0
   101
 * This is for simple error handling when a thread script exits badly.
sl@0
   102
 */
sl@0
   103
sl@0
   104
static Tcl_ThreadId errorThreadId;
sl@0
   105
static char *errorProcString;
sl@0
   106
sl@0
   107
/* 
sl@0
   108
 * Access to the list of threads and to the thread send results is
sl@0
   109
 * guarded by this mutex. 
sl@0
   110
 */
sl@0
   111
sl@0
   112
TCL_DECLARE_MUTEX(threadMutex)
sl@0
   113
sl@0
   114
#undef TCL_STORAGE_CLASS
sl@0
   115
#define TCL_STORAGE_CLASS DLLEXPORT
sl@0
   116
sl@0
   117
EXTERN int	TclThread_Init _ANSI_ARGS_((Tcl_Interp *interp));
sl@0
   118
EXTERN int	Tcl_ThreadObjCmd _ANSI_ARGS_((ClientData clientData,
sl@0
   119
	Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
sl@0
   120
EXTERN int	TclCreateThread _ANSI_ARGS_((Tcl_Interp *interp,
sl@0
   121
	char *script, int joinable));
sl@0
   122
EXTERN int	TclThreadList _ANSI_ARGS_((Tcl_Interp *interp));
sl@0
   123
EXTERN int	TclThreadSend _ANSI_ARGS_((Tcl_Interp *interp, Tcl_ThreadId id,
sl@0
   124
	char *script, int wait));
sl@0
   125
sl@0
   126
#undef TCL_STORAGE_CLASS
sl@0
   127
#define TCL_STORAGE_CLASS DLLIMPORT
sl@0
   128
sl@0
   129
Tcl_ThreadCreateType	NewTestThread _ANSI_ARGS_((ClientData clientData));
sl@0
   130
static void	ListRemove _ANSI_ARGS_((ThreadSpecificData *tsdPtr));
sl@0
   131
static void	ListUpdateInner _ANSI_ARGS_((ThreadSpecificData *tsdPtr));
sl@0
   132
static int	ThreadEventProc _ANSI_ARGS_((Tcl_Event *evPtr, int mask));
sl@0
   133
static void	ThreadErrorProc _ANSI_ARGS_((Tcl_Interp *interp));
sl@0
   134
static void	ThreadFreeProc _ANSI_ARGS_((ClientData clientData));
sl@0
   135
static int	ThreadDeleteEvent _ANSI_ARGS_((Tcl_Event *eventPtr,
sl@0
   136
	ClientData clientData));
sl@0
   137
static void	ThreadExitProc _ANSI_ARGS_((ClientData clientData));
sl@0
   138
sl@0
   139

sl@0
   140
/*
sl@0
   141
 *----------------------------------------------------------------------
sl@0
   142
 *
sl@0
   143
 * TclThread_Init --
sl@0
   144
 *
sl@0
   145
 *	Initialize the test thread command.
sl@0
   146
 *
sl@0
   147
 * Results:
sl@0
   148
 *      TCL_OK if the package was properly initialized.
sl@0
   149
 *
sl@0
   150
 * Side effects:
sl@0
   151
 *	Add the "testthread" command to the interp.
sl@0
   152
 *
sl@0
   153
 *----------------------------------------------------------------------
sl@0
   154
 */
sl@0
   155
sl@0
   156
int
sl@0
   157
TclThread_Init(interp)
sl@0
   158
    Tcl_Interp *interp; /* The current Tcl interpreter */
sl@0
   159
{
sl@0
   160
    
sl@0
   161
    Tcl_CreateObjCommand(interp,"testthread", Tcl_ThreadObjCmd, 
sl@0
   162
	    (ClientData)NULL ,NULL);
sl@0
   163
    if (Tcl_PkgProvide(interp, "Thread", "1.0" ) != TCL_OK) {
sl@0
   164
	return TCL_ERROR;
sl@0
   165
    }
sl@0
   166
    return TCL_OK;
sl@0
   167
}
sl@0
   168
sl@0
   169

sl@0
   170
/*
sl@0
   171
 *----------------------------------------------------------------------
sl@0
   172
 *
sl@0
   173
 * Tcl_ThreadObjCmd --
sl@0
   174
 *
sl@0
   175
 *	This procedure is invoked to process the "testthread" Tcl command.
sl@0
   176
 *	See the user documentation for details on what it does.
sl@0
   177
 *
sl@0
   178
 *	thread create ?-joinable? ?script?
sl@0
   179
 *	thread send id ?-async? script
sl@0
   180
 *	thread exit
sl@0
   181
 *	thread info id
sl@0
   182
 *	thread names
sl@0
   183
 *	thread wait
sl@0
   184
 *	thread errorproc proc
sl@0
   185
 *	thread join id
sl@0
   186
 *
sl@0
   187
 * Results:
sl@0
   188
 *	A standard Tcl result.
sl@0
   189
 *
sl@0
   190
 * Side effects:
sl@0
   191
 *	See the user documentation.
sl@0
   192
 *
sl@0
   193
 *----------------------------------------------------------------------
sl@0
   194
 */
sl@0
   195
sl@0
   196
	/* ARGSUSED */
sl@0
   197
int
sl@0
   198
Tcl_ThreadObjCmd(dummy, interp, objc, objv)
sl@0
   199
    ClientData dummy;			/* Not used. */
sl@0
   200
    Tcl_Interp *interp;			/* Current interpreter. */
sl@0
   201
    int objc;				/* Number of arguments. */
sl@0
   202
    Tcl_Obj *CONST objv[];		/* Argument objects. */
sl@0
   203
{
sl@0
   204
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   205
    int option;
sl@0
   206
    static CONST char *threadOptions[] = {"create", "exit", "id", "join", "names",
sl@0
   207
				    "send", "wait", "errorproc",
sl@0
   208
				    (char *) NULL};
sl@0
   209
    enum options {THREAD_CREATE, THREAD_EXIT, THREAD_ID, THREAD_JOIN,
sl@0
   210
		  THREAD_NAMES, THREAD_SEND, THREAD_WAIT, THREAD_ERRORPROC};
sl@0
   211
sl@0
   212
    if (objc < 2) {
sl@0
   213
	Tcl_WrongNumArgs(interp, 1, objv, "option ?args?");
sl@0
   214
	return TCL_ERROR;
sl@0
   215
    }
sl@0
   216
    if (Tcl_GetIndexFromObj(interp, objv[1], threadOptions,
sl@0
   217
	    "option", 0, &option) != TCL_OK) {
sl@0
   218
	return TCL_ERROR;
sl@0
   219
    }
sl@0
   220
sl@0
   221
    /* 
sl@0
   222
     * Make sure the initial thread is on the list before doing anything.
sl@0
   223
     */
sl@0
   224
sl@0
   225
    if (tsdPtr->interp == NULL) {
sl@0
   226
	Tcl_MutexLock(&threadMutex);
sl@0
   227
	tsdPtr->interp = interp;
sl@0
   228
	ListUpdateInner(tsdPtr);
sl@0
   229
	Tcl_CreateThreadExitHandler(ThreadExitProc, NULL);
sl@0
   230
	Tcl_MutexUnlock(&threadMutex);
sl@0
   231
    }
sl@0
   232
sl@0
   233
    switch ((enum options)option) {
sl@0
   234
	case THREAD_CREATE: {
sl@0
   235
	    char *script;
sl@0
   236
	    int   joinable, len;
sl@0
   237
sl@0
   238
	    if (objc == 2) {
sl@0
   239
	        /* Neither joinable nor special script
sl@0
   240
		 */
sl@0
   241
sl@0
   242
	        joinable = 0;
sl@0
   243
		script   = "testthread wait";	/* Just enter the event loop */
sl@0
   244
sl@0
   245
	    } else if (objc == 3) {
sl@0
   246
	        /* Possibly -joinable, then no special script,
sl@0
   247
		 * no joinable, then its a script.
sl@0
   248
		 */
sl@0
   249
sl@0
   250
	        script = Tcl_GetString(objv[2]);
sl@0
   251
		len    = strlen (script);
sl@0
   252
sl@0
   253
		if ((len > 1) &&
sl@0
   254
		    (script [0] == '-') && (script [1] == 'j') &&
sl@0
   255
		    (0 == strncmp (script, "-joinable", (size_t) len))) {
sl@0
   256
		    joinable = 1;
sl@0
   257
		    script   = "testthread wait"; /* Just enter the event loop
sl@0
   258
						   */
sl@0
   259
		} else {
sl@0
   260
		    /* Remember the script */
sl@0
   261
		    joinable = 0;
sl@0
   262
		}
sl@0
   263
	    } else if (objc == 4) {
sl@0
   264
	        /* Definitely a script available, but is the flag
sl@0
   265
		 * -joinable ?
sl@0
   266
		 */
sl@0
   267
sl@0
   268
	        script = Tcl_GetString(objv[2]);
sl@0
   269
		len    = strlen (script);
sl@0
   270
sl@0
   271
		joinable = ((len > 1) &&
sl@0
   272
			    (script [0] == '-') && (script [1] == 'j') &&
sl@0
   273
			    (0 == strncmp (script, "-joinable", (size_t) len)));
sl@0
   274
sl@0
   275
		script = Tcl_GetString(objv[3]);
sl@0
   276
	    } else {
sl@0
   277
		Tcl_WrongNumArgs(interp, 2, objv, "?-joinable? ?script?");
sl@0
   278
		return TCL_ERROR;
sl@0
   279
	    }
sl@0
   280
	    return TclCreateThread(interp, script, joinable);
sl@0
   281
	}
sl@0
   282
	case THREAD_EXIT: {
sl@0
   283
	    if (objc > 2) {
sl@0
   284
		Tcl_WrongNumArgs(interp, 1, objv, NULL);
sl@0
   285
		return TCL_ERROR;
sl@0
   286
	    }
sl@0
   287
	    ListRemove(NULL);
sl@0
   288
	    Tcl_ExitThread(0);
sl@0
   289
	    return TCL_OK;
sl@0
   290
	}
sl@0
   291
	case THREAD_ID:
sl@0
   292
	    if (objc == 2) {
sl@0
   293
		Tcl_Obj *idObj = Tcl_NewLongObj((long)Tcl_GetCurrentThread());
sl@0
   294
		Tcl_SetObjResult(interp, idObj);
sl@0
   295
		return TCL_OK;
sl@0
   296
	    } else {
sl@0
   297
		Tcl_WrongNumArgs(interp, 2, objv, NULL);
sl@0
   298
		return TCL_ERROR;
sl@0
   299
	    }
sl@0
   300
        case THREAD_JOIN: {
sl@0
   301
	    long id;
sl@0
   302
	    int result, status;
sl@0
   303
sl@0
   304
	    if (objc != 3) {
sl@0
   305
		Tcl_WrongNumArgs(interp, 1, objv, "join id");
sl@0
   306
		return TCL_ERROR;
sl@0
   307
	    }
sl@0
   308
	    if (Tcl_GetLongFromObj(interp, objv[2], &id) != TCL_OK) {
sl@0
   309
		return TCL_ERROR;
sl@0
   310
	    }
sl@0
   311
sl@0
   312
	    result = Tcl_JoinThread ((Tcl_ThreadId) id, &status);
sl@0
   313
	    if (result == TCL_OK) {
sl@0
   314
	        Tcl_SetIntObj (Tcl_GetObjResult (interp), status);
sl@0
   315
	    } else {
sl@0
   316
	        char buf [20];
sl@0
   317
		sprintf (buf, "%ld", id);
sl@0
   318
		Tcl_AppendResult (interp, "cannot join thread ", buf, NULL);
sl@0
   319
	    }
sl@0
   320
	    return result;
sl@0
   321
	}
sl@0
   322
	case THREAD_NAMES: {
sl@0
   323
	    if (objc > 2) {
sl@0
   324
		Tcl_WrongNumArgs(interp, 2, objv, NULL);
sl@0
   325
		return TCL_ERROR;
sl@0
   326
	    }
sl@0
   327
	    return TclThreadList(interp);
sl@0
   328
	}
sl@0
   329
	case THREAD_SEND: {
sl@0
   330
	    long id;
sl@0
   331
	    char *script;
sl@0
   332
	    int wait, arg;
sl@0
   333
sl@0
   334
	    if ((objc != 4) && (objc != 5)) {
sl@0
   335
		Tcl_WrongNumArgs(interp, 1, objv, "send ?-async? id script");
sl@0
   336
		return TCL_ERROR;
sl@0
   337
	    }
sl@0
   338
	    if (objc == 5) {
sl@0
   339
		if (strcmp("-async", Tcl_GetString(objv[2])) != 0) {
sl@0
   340
		    Tcl_WrongNumArgs(interp, 1, objv, "send ?-async? id script");
sl@0
   341
		    return TCL_ERROR;
sl@0
   342
		}
sl@0
   343
		wait = 0;
sl@0
   344
		arg = 3;
sl@0
   345
	    } else {
sl@0
   346
		wait = 1;
sl@0
   347
		arg = 2;
sl@0
   348
	    }
sl@0
   349
	    if (Tcl_GetLongFromObj(interp, objv[arg], &id) != TCL_OK) {
sl@0
   350
		return TCL_ERROR;
sl@0
   351
	    }
sl@0
   352
	    arg++;
sl@0
   353
	    script = Tcl_GetString(objv[arg]);
sl@0
   354
	    return TclThreadSend(interp, (Tcl_ThreadId) id, script, wait);
sl@0
   355
	}
sl@0
   356
	case THREAD_WAIT: {
sl@0
   357
	    while (1) {
sl@0
   358
		(void) Tcl_DoOneEvent(TCL_ALL_EVENTS);
sl@0
   359
	    }
sl@0
   360
	}
sl@0
   361
	case THREAD_ERRORPROC: {
sl@0
   362
	    /*
sl@0
   363
	     * Arrange for this proc to handle thread death errors.
sl@0
   364
	     */
sl@0
   365
sl@0
   366
	    char *proc;
sl@0
   367
	    if (objc != 3) {
sl@0
   368
		Tcl_WrongNumArgs(interp, 1, objv, "errorproc proc");
sl@0
   369
		return TCL_ERROR;
sl@0
   370
	    }
sl@0
   371
	    Tcl_MutexLock(&threadMutex);
sl@0
   372
	    errorThreadId = Tcl_GetCurrentThread();
sl@0
   373
	    if (errorProcString) {
sl@0
   374
		ckfree(errorProcString);
sl@0
   375
	    }
sl@0
   376
	    proc = Tcl_GetString(objv[2]);
sl@0
   377
	    errorProcString = ckalloc(strlen(proc)+1);
sl@0
   378
	    strcpy(errorProcString, proc);
sl@0
   379
	    Tcl_MutexUnlock(&threadMutex);
sl@0
   380
	    return TCL_OK;
sl@0
   381
	}
sl@0
   382
    }
sl@0
   383
    return TCL_OK;
sl@0
   384
}
sl@0
   385
sl@0
   386

sl@0
   387
/*
sl@0
   388
 *----------------------------------------------------------------------
sl@0
   389
 *
sl@0
   390
 * TclCreateThread --
sl@0
   391
 *
sl@0
   392
 *	This procedure is invoked to create a thread containing an interp to
sl@0
   393
 *	run a script.  This returns after the thread has started executing.
sl@0
   394
 *
sl@0
   395
 * Results:
sl@0
   396
 *	A standard Tcl result, which is the thread ID.
sl@0
   397
 *
sl@0
   398
 * Side effects:
sl@0
   399
 *	Create a thread.
sl@0
   400
 *
sl@0
   401
 *----------------------------------------------------------------------
sl@0
   402
 */
sl@0
   403
sl@0
   404
	/* ARGSUSED */
sl@0
   405
int
sl@0
   406
TclCreateThread(interp, script, joinable)
sl@0
   407
    Tcl_Interp *interp;			/* Current interpreter. */
sl@0
   408
    char *script;			/* Script to execute */
sl@0
   409
    int         joinable;		/* Flag, joinable thread or not */
sl@0
   410
{
sl@0
   411
    ThreadCtrl ctrl;
sl@0
   412
    Tcl_ThreadId id;
sl@0
   413
sl@0
   414
    ctrl.script = script;
sl@0
   415
    ctrl.condWait = NULL;
sl@0
   416
    ctrl.flags = 0;
sl@0
   417
sl@0
   418
    joinable = joinable ? TCL_THREAD_JOINABLE : TCL_THREAD_NOFLAGS;
sl@0
   419
sl@0
   420
    Tcl_MutexLock(&threadMutex);
sl@0
   421
    if (Tcl_CreateThread(&id, NewTestThread, (ClientData) &ctrl,
sl@0
   422
		 TCL_THREAD_STACK_DEFAULT, joinable) != TCL_OK) {
sl@0
   423
	Tcl_MutexUnlock(&threadMutex);
sl@0
   424
        Tcl_AppendResult(interp,"can't create a new thread",NULL);
sl@0
   425
	ckfree((void*)ctrl.script);
sl@0
   426
	return TCL_ERROR;
sl@0
   427
    }
sl@0
   428
sl@0
   429
    /*
sl@0
   430
     * Wait for the thread to start because it is using something on our stack!
sl@0
   431
     */
sl@0
   432
sl@0
   433
    Tcl_ConditionWait(&ctrl.condWait, &threadMutex, NULL);
sl@0
   434
    Tcl_MutexUnlock(&threadMutex);
sl@0
   435
    Tcl_ConditionFinalize(&ctrl.condWait);
sl@0
   436
    Tcl_SetObjResult(interp, Tcl_NewLongObj((long)id));
sl@0
   437
    return TCL_OK;
sl@0
   438
}
sl@0
   439

sl@0
   440
/*
sl@0
   441
 *------------------------------------------------------------------------
sl@0
   442
 *
sl@0
   443
 * NewTestThread --
sl@0
   444
 *
sl@0
   445
 *    This routine is the "main()" for a new thread whose task is to
sl@0
   446
 *    execute a single TCL script.  The argument to this function is
sl@0
   447
 *    a pointer to a structure that contains the text of the TCL script
sl@0
   448
 *    to be executed.
sl@0
   449
 *
sl@0
   450
 *    Space to hold the script field of the ThreadControl structure passed 
sl@0
   451
 *    in as the only argument was obtained from malloc() and must be freed 
sl@0
   452
 *    by this function before it exits.  Space to hold the ThreadControl
sl@0
   453
 *    structure itself is released by the calling function, and the
sl@0
   454
 *    two condition variables in the ThreadControl structure are destroyed
sl@0
   455
 *    by the calling function.  The calling function will destroy the
sl@0
   456
 *    ThreadControl structure and the condition variable as soon as
sl@0
   457
 *    ctrlPtr->condWait is signaled, so this routine must make copies of
sl@0
   458
 *    any data it might need after that point.
sl@0
   459
 *
sl@0
   460
 * Results:
sl@0
   461
 *    none
sl@0
   462
 *
sl@0
   463
 * Side effects:
sl@0
   464
 *    A TCL script is executed in a new thread.
sl@0
   465
 *
sl@0
   466
 *------------------------------------------------------------------------
sl@0
   467
 */
sl@0
   468
Tcl_ThreadCreateType
sl@0
   469
NewTestThread(clientData)
sl@0
   470
    ClientData clientData;
sl@0
   471
{
sl@0
   472
    ThreadCtrl *ctrlPtr = (ThreadCtrl*)clientData;
sl@0
   473
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   474
    int result;
sl@0
   475
    char *threadEvalScript;
sl@0
   476
sl@0
   477
    /*
sl@0
   478
     * Initialize the interpreter.  This should be more general.
sl@0
   479
     */
sl@0
   480
sl@0
   481
    tsdPtr->interp = Tcl_CreateInterp();
sl@0
   482
    result = Tcl_Init(tsdPtr->interp);
sl@0
   483
    result = TclThread_Init(tsdPtr->interp);
sl@0
   484
sl@0
   485
    /*
sl@0
   486
     * Update the list of threads.
sl@0
   487
     */
sl@0
   488
sl@0
   489
    Tcl_MutexLock(&threadMutex);
sl@0
   490
    ListUpdateInner(tsdPtr);
sl@0
   491
    /*
sl@0
   492
     * We need to keep a pointer to the alloc'ed mem of the script
sl@0
   493
     * we are eval'ing, for the case that we exit during evaluation
sl@0
   494
     */
sl@0
   495
    threadEvalScript = (char *) ckalloc(strlen(ctrlPtr->script)+1);
sl@0
   496
    strcpy(threadEvalScript, ctrlPtr->script);
sl@0
   497
sl@0
   498
    Tcl_CreateThreadExitHandler(ThreadExitProc, (ClientData) threadEvalScript);
sl@0
   499
sl@0
   500
    /*
sl@0
   501
     * Notify the parent we are alive.
sl@0
   502
     */
sl@0
   503
sl@0
   504
    Tcl_ConditionNotify(&ctrlPtr->condWait);
sl@0
   505
    Tcl_MutexUnlock(&threadMutex);
sl@0
   506
sl@0
   507
    /*
sl@0
   508
     * Run the script.
sl@0
   509
     */
sl@0
   510
sl@0
   511
    Tcl_Preserve((ClientData) tsdPtr->interp);
sl@0
   512
    result = Tcl_Eval(tsdPtr->interp, threadEvalScript);
sl@0
   513
    if (result != TCL_OK) {
sl@0
   514
	ThreadErrorProc(tsdPtr->interp);
sl@0
   515
    }
sl@0
   516
sl@0
   517
    /*
sl@0
   518
     * Clean up.
sl@0
   519
     */
sl@0
   520
sl@0
   521
    ListRemove(tsdPtr);
sl@0
   522
    Tcl_Release((ClientData) tsdPtr->interp);
sl@0
   523
    Tcl_DeleteInterp(tsdPtr->interp);
sl@0
   524
    Tcl_ExitThread(result);
sl@0
   525
sl@0
   526
    TCL_THREAD_CREATE_RETURN;
sl@0
   527
}
sl@0
   528

sl@0
   529
/*
sl@0
   530
 *------------------------------------------------------------------------
sl@0
   531
 *
sl@0
   532
 * ThreadErrorProc --
sl@0
   533
 *
sl@0
   534
 *    Send a message to the thread willing to hear about errors.
sl@0
   535
 *
sl@0
   536
 * Results:
sl@0
   537
 *    none
sl@0
   538
 *
sl@0
   539
 * Side effects:
sl@0
   540
 *    Send an event.
sl@0
   541
 *
sl@0
   542
 *------------------------------------------------------------------------
sl@0
   543
 */
sl@0
   544
static void
sl@0
   545
ThreadErrorProc(interp)
sl@0
   546
    Tcl_Interp *interp;		/* Interp that failed */
sl@0
   547
{
sl@0
   548
    Tcl_Channel errChannel;
sl@0
   549
    CONST char *errorInfo, *argv[3];
sl@0
   550
    char *script;
sl@0
   551
    char buf[TCL_DOUBLE_SPACE+1];
sl@0
   552
    sprintf(buf, "%ld", (long) Tcl_GetCurrentThread());
sl@0
   553
sl@0
   554
    errorInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
sl@0
   555
    if (errorProcString == NULL) {
sl@0
   556
	errChannel = Tcl_GetStdChannel(TCL_STDERR);
sl@0
   557
	Tcl_WriteChars(errChannel, "Error from thread ", -1);
sl@0
   558
	Tcl_WriteChars(errChannel, buf, -1);
sl@0
   559
	Tcl_WriteChars(errChannel, "\n", 1);
sl@0
   560
	Tcl_WriteChars(errChannel, errorInfo, -1);
sl@0
   561
	Tcl_WriteChars(errChannel, "\n", 1);
sl@0
   562
    } else {
sl@0
   563
	argv[0] = errorProcString;
sl@0
   564
	argv[1] = buf;
sl@0
   565
	argv[2] = errorInfo;
sl@0
   566
	script = Tcl_Merge(3, argv);
sl@0
   567
	TclThreadSend(interp, errorThreadId, script, 0);
sl@0
   568
	ckfree(script);
sl@0
   569
    }
sl@0
   570
}
sl@0
   571
sl@0
   572

sl@0
   573
/*
sl@0
   574
 *------------------------------------------------------------------------
sl@0
   575
 *
sl@0
   576
 * ListUpdateInner --
sl@0
   577
 *
sl@0
   578
 *    Add the thread local storage to the list.  This assumes
sl@0
   579
 *	the caller has obtained the mutex.
sl@0
   580
 *
sl@0
   581
 * Results:
sl@0
   582
 *    none
sl@0
   583
 *
sl@0
   584
 * Side effects:
sl@0
   585
 *    Add the thread local storage to its list.
sl@0
   586
 *
sl@0
   587
 *------------------------------------------------------------------------
sl@0
   588
 */
sl@0
   589
static void
sl@0
   590
ListUpdateInner(tsdPtr)
sl@0
   591
    ThreadSpecificData *tsdPtr;
sl@0
   592
{
sl@0
   593
    if (tsdPtr == NULL) {
sl@0
   594
	tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   595
    }
sl@0
   596
    tsdPtr->threadId = Tcl_GetCurrentThread();
sl@0
   597
    tsdPtr->nextPtr = threadList;
sl@0
   598
    if (threadList) {
sl@0
   599
	threadList->prevPtr = tsdPtr;
sl@0
   600
    }
sl@0
   601
    tsdPtr->prevPtr = NULL;
sl@0
   602
    threadList = tsdPtr;
sl@0
   603
}
sl@0
   604

sl@0
   605
/*
sl@0
   606
 *------------------------------------------------------------------------
sl@0
   607
 *
sl@0
   608
 * ListRemove --
sl@0
   609
 *
sl@0
   610
 *    Remove the thread local storage from its list.  This grabs the
sl@0
   611
 *	mutex to protect the list.
sl@0
   612
 *
sl@0
   613
 * Results:
sl@0
   614
 *    none
sl@0
   615
 *
sl@0
   616
 * Side effects:
sl@0
   617
 *    Remove the thread local storage from its list.
sl@0
   618
 *
sl@0
   619
 *------------------------------------------------------------------------
sl@0
   620
 */
sl@0
   621
static void
sl@0
   622
ListRemove(tsdPtr)
sl@0
   623
    ThreadSpecificData *tsdPtr;
sl@0
   624
{
sl@0
   625
    if (tsdPtr == NULL) {
sl@0
   626
	tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   627
    }
sl@0
   628
    Tcl_MutexLock(&threadMutex);
sl@0
   629
    if (tsdPtr->prevPtr) {
sl@0
   630
	tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
sl@0
   631
    } else {
sl@0
   632
	threadList = tsdPtr->nextPtr;
sl@0
   633
    }
sl@0
   634
    if (tsdPtr->nextPtr) {
sl@0
   635
	tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
sl@0
   636
    }
sl@0
   637
    tsdPtr->nextPtr = tsdPtr->prevPtr = 0;
sl@0
   638
    Tcl_MutexUnlock(&threadMutex);
sl@0
   639
}
sl@0
   640
sl@0
   641

sl@0
   642
/*
sl@0
   643
 *------------------------------------------------------------------------
sl@0
   644
 *
sl@0
   645
 * TclThreadList --
sl@0
   646
 *
sl@0
   647
 *    Return a list of threads running Tcl interpreters.
sl@0
   648
 *
sl@0
   649
 * Results:
sl@0
   650
 *    A standard Tcl result.
sl@0
   651
 *
sl@0
   652
 * Side effects:
sl@0
   653
 *    None.
sl@0
   654
 *
sl@0
   655
 *------------------------------------------------------------------------
sl@0
   656
 */
sl@0
   657
int
sl@0
   658
TclThreadList(interp)
sl@0
   659
    Tcl_Interp *interp;
sl@0
   660
{
sl@0
   661
    ThreadSpecificData *tsdPtr;
sl@0
   662
    Tcl_Obj *listPtr;
sl@0
   663
sl@0
   664
    listPtr = Tcl_NewListObj(0, NULL);
sl@0
   665
    Tcl_MutexLock(&threadMutex);
sl@0
   666
    for (tsdPtr = threadList ; tsdPtr ; tsdPtr = tsdPtr->nextPtr) {
sl@0
   667
	Tcl_ListObjAppendElement(interp, listPtr,
sl@0
   668
		Tcl_NewLongObj((long)tsdPtr->threadId));
sl@0
   669
    }
sl@0
   670
    Tcl_MutexUnlock(&threadMutex);
sl@0
   671
    Tcl_SetObjResult(interp, listPtr);
sl@0
   672
    return TCL_OK;
sl@0
   673
}
sl@0
   674
sl@0
   675

sl@0
   676
/*
sl@0
   677
 *------------------------------------------------------------------------
sl@0
   678
 *
sl@0
   679
 * TclThreadSend --
sl@0
   680
 *
sl@0
   681
 *    Send a script to another thread.
sl@0
   682
 *
sl@0
   683
 * Results:
sl@0
   684
 *    A standard Tcl result.
sl@0
   685
 *
sl@0
   686
 * Side effects:
sl@0
   687
 *    None.
sl@0
   688
 *
sl@0
   689
 *------------------------------------------------------------------------
sl@0
   690
 */
sl@0
   691
int
sl@0
   692
TclThreadSend(interp, id, script, wait)
sl@0
   693
    Tcl_Interp *interp;		/* The current interpreter. */
sl@0
   694
    Tcl_ThreadId id;		/* Thread Id of other interpreter. */
sl@0
   695
    char *script;		/* The script to evaluate. */
sl@0
   696
    int wait;			/* If 1, we block for the result. */
sl@0
   697
{
sl@0
   698
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   699
    ThreadEvent *threadEventPtr;
sl@0
   700
    ThreadEventResult *resultPtr;
sl@0
   701
    int found, code;
sl@0
   702
    Tcl_ThreadId threadId = (Tcl_ThreadId) id;
sl@0
   703
sl@0
   704
    /* 
sl@0
   705
     * Verify the thread exists.
sl@0
   706
     */
sl@0
   707
sl@0
   708
    Tcl_MutexLock(&threadMutex);
sl@0
   709
    found = 0;
sl@0
   710
    for (tsdPtr = threadList ; tsdPtr ; tsdPtr = tsdPtr->nextPtr) {
sl@0
   711
	if (tsdPtr->threadId == threadId) {
sl@0
   712
	    found = 1;
sl@0
   713
	    break;
sl@0
   714
	}
sl@0
   715
    }
sl@0
   716
    if (!found) {
sl@0
   717
	Tcl_MutexUnlock(&threadMutex);
sl@0
   718
	Tcl_AppendResult(interp, "invalid thread id", NULL);
sl@0
   719
	return TCL_ERROR;
sl@0
   720
    }
sl@0
   721
sl@0
   722
    /*
sl@0
   723
     * Short circut sends to ourself.  Ought to do something with -async,
sl@0
   724
     * like run in an idle handler.
sl@0
   725
     */
sl@0
   726
sl@0
   727
    if (threadId == Tcl_GetCurrentThread()) {
sl@0
   728
        Tcl_MutexUnlock(&threadMutex);
sl@0
   729
	return Tcl_GlobalEval(interp, script);
sl@0
   730
    }
sl@0
   731
sl@0
   732
    /* 
sl@0
   733
     * Create the event for its event queue.
sl@0
   734
     */
sl@0
   735
sl@0
   736
    threadEventPtr = (ThreadEvent *) ckalloc(sizeof(ThreadEvent));
sl@0
   737
    threadEventPtr->script = ckalloc(strlen(script) + 1);
sl@0
   738
    strcpy(threadEventPtr->script, script);
sl@0
   739
    if (!wait) {
sl@0
   740
	resultPtr = threadEventPtr->resultPtr = NULL;
sl@0
   741
    } else {
sl@0
   742
	resultPtr = (ThreadEventResult *) ckalloc(sizeof(ThreadEventResult));
sl@0
   743
	threadEventPtr->resultPtr = resultPtr;
sl@0
   744
sl@0
   745
	/*
sl@0
   746
	 * Initialize the result fields.
sl@0
   747
	 */
sl@0
   748
sl@0
   749
	resultPtr->done = NULL;
sl@0
   750
	resultPtr->code = 0;
sl@0
   751
	resultPtr->result = NULL;
sl@0
   752
	resultPtr->errorInfo = NULL;
sl@0
   753
	resultPtr->errorCode = NULL;
sl@0
   754
sl@0
   755
	/* 
sl@0
   756
	 * Maintain the cleanup list.
sl@0
   757
	 */
sl@0
   758
sl@0
   759
	resultPtr->srcThreadId = Tcl_GetCurrentThread();
sl@0
   760
	resultPtr->dstThreadId = threadId;
sl@0
   761
	resultPtr->eventPtr = threadEventPtr;
sl@0
   762
	resultPtr->nextPtr = resultList;
sl@0
   763
	if (resultList) {
sl@0
   764
	    resultList->prevPtr = resultPtr;
sl@0
   765
	}
sl@0
   766
	resultPtr->prevPtr = NULL;
sl@0
   767
	resultList = resultPtr;
sl@0
   768
    }
sl@0
   769
sl@0
   770
    /*
sl@0
   771
     * Queue the event and poke the other thread's notifier.
sl@0
   772
     */
sl@0
   773
sl@0
   774
    threadEventPtr->event.proc = ThreadEventProc;
sl@0
   775
    Tcl_ThreadQueueEvent(threadId, (Tcl_Event *)threadEventPtr, 
sl@0
   776
	    TCL_QUEUE_TAIL);
sl@0
   777
    Tcl_ThreadAlert(threadId);
sl@0
   778
sl@0
   779
    if (!wait) {
sl@0
   780
	Tcl_MutexUnlock(&threadMutex);
sl@0
   781
	return TCL_OK;
sl@0
   782
    }
sl@0
   783
sl@0
   784
    /* 
sl@0
   785
     * Block on the results and then get them.
sl@0
   786
     */
sl@0
   787
sl@0
   788
    Tcl_ResetResult(interp);
sl@0
   789
    while (resultPtr->result == NULL) {
sl@0
   790
        Tcl_ConditionWait(&resultPtr->done, &threadMutex, NULL);
sl@0
   791
    }
sl@0
   792
sl@0
   793
    /*
sl@0
   794
     * Unlink result from the result list.
sl@0
   795
     */
sl@0
   796
sl@0
   797
    if (resultPtr->prevPtr) {
sl@0
   798
	resultPtr->prevPtr->nextPtr = resultPtr->nextPtr;
sl@0
   799
    } else {
sl@0
   800
	resultList = resultPtr->nextPtr;
sl@0
   801
    }
sl@0
   802
    if (resultPtr->nextPtr) {
sl@0
   803
	resultPtr->nextPtr->prevPtr = resultPtr->prevPtr;
sl@0
   804
    }
sl@0
   805
    resultPtr->eventPtr = NULL;
sl@0
   806
    resultPtr->nextPtr = NULL;
sl@0
   807
    resultPtr->prevPtr = NULL;
sl@0
   808
sl@0
   809
    Tcl_MutexUnlock(&threadMutex);
sl@0
   810
sl@0
   811
    if (resultPtr->code != TCL_OK) {
sl@0
   812
	if (resultPtr->errorCode) {
sl@0
   813
	    Tcl_SetErrorCode(interp, resultPtr->errorCode, NULL);
sl@0
   814
	    ckfree(resultPtr->errorCode);
sl@0
   815
	}
sl@0
   816
	if (resultPtr->errorInfo) {
sl@0
   817
	    Tcl_AddErrorInfo(interp, resultPtr->errorInfo);
sl@0
   818
	    ckfree(resultPtr->errorInfo);
sl@0
   819
	}
sl@0
   820
    }
sl@0
   821
    Tcl_SetResult(interp, resultPtr->result, TCL_DYNAMIC);
sl@0
   822
    Tcl_ConditionFinalize(&resultPtr->done);
sl@0
   823
    code = resultPtr->code;
sl@0
   824
sl@0
   825
    ckfree((char *) resultPtr);
sl@0
   826
sl@0
   827
    return code;
sl@0
   828
}
sl@0
   829
sl@0
   830

sl@0
   831
/*
sl@0
   832
 *------------------------------------------------------------------------
sl@0
   833
 *
sl@0
   834
 * ThreadEventProc --
sl@0
   835
 *
sl@0
   836
 *    Handle the event in the target thread.
sl@0
   837
 *
sl@0
   838
 * Results:
sl@0
   839
 *    Returns 1 to indicate that the event was processed.
sl@0
   840
 *
sl@0
   841
 * Side effects:
sl@0
   842
 *    Fills out the ThreadEventResult struct.
sl@0
   843
 *
sl@0
   844
 *------------------------------------------------------------------------
sl@0
   845
 */
sl@0
   846
static int
sl@0
   847
ThreadEventProc(evPtr, mask)
sl@0
   848
    Tcl_Event *evPtr;		/* Really ThreadEvent */
sl@0
   849
    int mask;
sl@0
   850
{
sl@0
   851
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   852
    ThreadEvent *threadEventPtr = (ThreadEvent *)evPtr;
sl@0
   853
    ThreadEventResult *resultPtr = threadEventPtr->resultPtr;
sl@0
   854
    Tcl_Interp *interp = tsdPtr->interp;
sl@0
   855
    int code;
sl@0
   856
    CONST char *result, *errorCode, *errorInfo;
sl@0
   857
sl@0
   858
    if (interp == NULL) {
sl@0
   859
	code = TCL_ERROR;
sl@0
   860
	result = "no target interp!";
sl@0
   861
	errorCode = "THREAD";
sl@0
   862
	errorInfo = "";
sl@0
   863
    } else {
sl@0
   864
	Tcl_Preserve((ClientData) interp);
sl@0
   865
	Tcl_ResetResult(interp);
sl@0
   866
	Tcl_CreateThreadExitHandler(ThreadFreeProc,
sl@0
   867
		(ClientData) threadEventPtr->script);
sl@0
   868
	code = Tcl_GlobalEval(interp, threadEventPtr->script);
sl@0
   869
	Tcl_DeleteThreadExitHandler(ThreadFreeProc,
sl@0
   870
		(ClientData) threadEventPtr->script);
sl@0
   871
	if (code != TCL_OK) {
sl@0
   872
	    errorCode = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY);
sl@0
   873
	    errorInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
sl@0
   874
	} else {
sl@0
   875
	    errorCode = errorInfo = NULL;
sl@0
   876
	}
sl@0
   877
	result = Tcl_GetStringResult(interp);
sl@0
   878
    }
sl@0
   879
    ckfree(threadEventPtr->script);
sl@0
   880
    if (resultPtr) {
sl@0
   881
	Tcl_MutexLock(&threadMutex);
sl@0
   882
	resultPtr->code = code;
sl@0
   883
	resultPtr->result = ckalloc(strlen(result) + 1);
sl@0
   884
	strcpy(resultPtr->result, result);
sl@0
   885
	if (errorCode != NULL) {
sl@0
   886
	    resultPtr->errorCode = ckalloc(strlen(errorCode) + 1);
sl@0
   887
	    strcpy(resultPtr->errorCode, errorCode);
sl@0
   888
	}
sl@0
   889
	if (errorInfo != NULL) {
sl@0
   890
	    resultPtr->errorInfo = ckalloc(strlen(errorInfo) + 1);
sl@0
   891
	    strcpy(resultPtr->errorInfo, errorInfo);
sl@0
   892
	}
sl@0
   893
	Tcl_ConditionNotify(&resultPtr->done);
sl@0
   894
	Tcl_MutexUnlock(&threadMutex);
sl@0
   895
    }
sl@0
   896
    if (interp != NULL) {
sl@0
   897
	Tcl_Release((ClientData) interp);
sl@0
   898
    }
sl@0
   899
    return 1;
sl@0
   900
}
sl@0
   901

sl@0
   902
/*
sl@0
   903
 *------------------------------------------------------------------------
sl@0
   904
 *
sl@0
   905
 * ThreadFreeProc --
sl@0
   906
 *
sl@0
   907
 *    This is called from when we are exiting and memory needs
sl@0
   908
 *    to be freed.
sl@0
   909
 *
sl@0
   910
 * Results:
sl@0
   911
 *    None.
sl@0
   912
 *
sl@0
   913
 * Side effects:
sl@0
   914
 *	Clears up mem specified in ClientData
sl@0
   915
 *
sl@0
   916
 *------------------------------------------------------------------------
sl@0
   917
 */
sl@0
   918
     /* ARGSUSED */
sl@0
   919
static void
sl@0
   920
ThreadFreeProc(clientData)
sl@0
   921
    ClientData clientData;
sl@0
   922
{
sl@0
   923
    if (clientData) {
sl@0
   924
	ckfree((char *) clientData);
sl@0
   925
    }
sl@0
   926
}
sl@0
   927

sl@0
   928
/*
sl@0
   929
 *------------------------------------------------------------------------
sl@0
   930
 *
sl@0
   931
 * ThreadDeleteEvent --
sl@0
   932
 *
sl@0
   933
 *    This is called from the ThreadExitProc to delete memory related
sl@0
   934
 *    to events that we put on the queue.
sl@0
   935
 *
sl@0
   936
 * Results:
sl@0
   937
 *    1 it was our event and we want it removed, 0 otherwise.
sl@0
   938
 *
sl@0
   939
 * Side effects:
sl@0
   940
 *	It cleans up our events in the event queue for this thread.
sl@0
   941
 *
sl@0
   942
 *------------------------------------------------------------------------
sl@0
   943
 */
sl@0
   944
     /* ARGSUSED */
sl@0
   945
static int
sl@0
   946
ThreadDeleteEvent(eventPtr, clientData)
sl@0
   947
    Tcl_Event *eventPtr;		/* Really ThreadEvent */
sl@0
   948
    ClientData clientData;		/* dummy */
sl@0
   949
{
sl@0
   950
    if (eventPtr->proc == ThreadEventProc) {
sl@0
   951
	ckfree((char *) ((ThreadEvent *) eventPtr)->script);
sl@0
   952
	return 1;
sl@0
   953
    }
sl@0
   954
    /*
sl@0
   955
     * If it was NULL, we were in the middle of servicing the event
sl@0
   956
     * and it should be removed
sl@0
   957
     */
sl@0
   958
    return (eventPtr->proc == NULL);
sl@0
   959
}
sl@0
   960

sl@0
   961
/*
sl@0
   962
 *------------------------------------------------------------------------
sl@0
   963
 *
sl@0
   964
 * ThreadExitProc --
sl@0
   965
 *
sl@0
   966
 *    This is called when the thread exits.  
sl@0
   967
 *
sl@0
   968
 * Results:
sl@0
   969
 *    None.
sl@0
   970
 *
sl@0
   971
 * Side effects:
sl@0
   972
 *	It unblocks anyone that is waiting on a send to this thread.
sl@0
   973
 *	It cleans up any events in the event queue for this thread.
sl@0
   974
 *
sl@0
   975
 *------------------------------------------------------------------------
sl@0
   976
 */
sl@0
   977
     /* ARGSUSED */
sl@0
   978
static void
sl@0
   979
ThreadExitProc(clientData)
sl@0
   980
    ClientData clientData;
sl@0
   981
{
sl@0
   982
    char *threadEvalScript = (char *) clientData;
sl@0
   983
    ThreadEventResult *resultPtr, *nextPtr;
sl@0
   984
    Tcl_ThreadId self = Tcl_GetCurrentThread();
sl@0
   985
sl@0
   986
    Tcl_MutexLock(&threadMutex);
sl@0
   987
sl@0
   988
    if (threadEvalScript) {
sl@0
   989
	ckfree((char *) threadEvalScript);
sl@0
   990
	threadEvalScript = NULL;
sl@0
   991
    }
sl@0
   992
    Tcl_DeleteEvents((Tcl_EventDeleteProc *)ThreadDeleteEvent, NULL);
sl@0
   993
sl@0
   994
    for (resultPtr = resultList ; resultPtr ; resultPtr = nextPtr) {
sl@0
   995
	nextPtr = resultPtr->nextPtr;
sl@0
   996
	if (resultPtr->srcThreadId == self) {
sl@0
   997
	    /*
sl@0
   998
	     * We are going away.  By freeing up the result we signal
sl@0
   999
	     * to the other thread we don't care about the result.
sl@0
  1000
	     */
sl@0
  1001
	    if (resultPtr->prevPtr) {
sl@0
  1002
		resultPtr->prevPtr->nextPtr = resultPtr->nextPtr;
sl@0
  1003
	    } else {
sl@0
  1004
		resultList = resultPtr->nextPtr;
sl@0
  1005
	    }
sl@0
  1006
	    if (resultPtr->nextPtr) {
sl@0
  1007
		resultPtr->nextPtr->prevPtr = resultPtr->prevPtr;
sl@0
  1008
	    }
sl@0
  1009
	    resultPtr->nextPtr = resultPtr->prevPtr = 0;
sl@0
  1010
	    resultPtr->eventPtr->resultPtr = NULL;
sl@0
  1011
	    ckfree((char *)resultPtr);
sl@0
  1012
	} else if (resultPtr->dstThreadId == self) {
sl@0
  1013
	    /*
sl@0
  1014
	     * Dang.  The target is going away.  Unblock the caller.
sl@0
  1015
	     * The result string must be dynamically allocated because
sl@0
  1016
	     * the main thread is going to call free on it.
sl@0
  1017
	     */
sl@0
  1018
sl@0
  1019
	    char *msg = "target thread died";
sl@0
  1020
	    resultPtr->result = ckalloc(strlen(msg)+1);
sl@0
  1021
	    strcpy(resultPtr->result, msg);
sl@0
  1022
	    resultPtr->code = TCL_ERROR;
sl@0
  1023
	    Tcl_ConditionNotify(&resultPtr->done);
sl@0
  1024
	}
sl@0
  1025
    }
sl@0
  1026
    Tcl_MutexUnlock(&threadMutex);
sl@0
  1027
}
sl@0
  1028
sl@0
  1029
#endif /* TCL_THREADS */