diff options
Diffstat (limited to 'generic/tclThreadTest.c')
-rw-r--r-- | generic/tclThreadTest.c | 856 |
1 files changed, 519 insertions, 337 deletions
diff --git a/generic/tclThreadTest.c b/generic/tclThreadTest.c index 7ea39dc..02ee038 100644 --- a/generic/tclThreadTest.c +++ b/generic/tclThreadTest.c @@ -1,72 +1,79 @@ -/* +/* * tclThreadTest.c -- * - * This file implements the testthread command. Eventually this - * should be tclThreadCmd.c + * This file implements the testthread command. Eventually this should be + * tclThreadCmd.c * Some of this code is based on work done by Richard Hipp on behalf of * Conservation Through Innovation, Limited, with their permission. * * Copyright (c) 1998 by Sun Microsystems, Inc. + * Copyright (c) 2006-2008 by Joe Mistachkin. All rights reserved. * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclThreadTest.c,v 1.17 2004/10/20 05:28:39 dgp Exp $ + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ +#ifndef USE_TCL_STUBS +# define USE_TCL_STUBS +#endif #include "tclInt.h" #ifdef TCL_THREADS /* - * Each thread has an single instance of the following structure. There - * is one instance of this structure per thread even if that thread contains - * multiple interpreters. The interpreter identified by this structure is - * the main interpreter for the thread. - * - * The main interpreter is the one that will process any messages - * received by a thread. Any thread can send messages but only the - * main interpreter can receive them. + * Each thread has an single instance of the following structure. There is one + * instance of this structure per thread even if that thread contains multiple + * interpreters. The interpreter identified by this structure is the main + * interpreter for the thread. + * + * The main interpreter is the one that will process any messages received by + * a thread. Any thread can send messages but only the main interpreter can + * receive them. */ typedef struct ThreadSpecificData { - Tcl_ThreadId threadId; /* Tcl ID for this thread */ - Tcl_Interp *interp; /* Main interpreter for this thread */ - int flags; /* See the TP_ defines below... */ - struct ThreadSpecificData *nextPtr; /* List for "thread names" */ - struct ThreadSpecificData *prevPtr; /* List for "thread names" */ + Tcl_ThreadId threadId; /* Tcl ID for this thread */ + Tcl_Interp *interp; /* Main interpreter for this thread */ + int flags; /* See the TP_ defines below... */ + struct ThreadSpecificData *nextPtr; + /* List for "thread names" */ + struct ThreadSpecificData *prevPtr; + /* List for "thread names" */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; /* - * This list is used to list all threads that have interpreters. - * This is protected by threadMutex. + * This list is used to list all threads that have interpreters. This is + * protected by threadMutex. */ -static struct ThreadSpecificData *threadList; +static ThreadSpecificData *threadList = NULL; /* * The following bit-values are legal for the "flags" field of the * ThreadSpecificData structure. */ -#define TP_Dying 0x001 /* This thread is being cancelled */ + +#define TP_Dying 0x001 /* This thread is being canceled */ /* * An instance of the following structure contains all information that is * passed into a new thread when the thread is created using either the - * "thread create" Tcl command or the TclCreateThread() C function. + * "thread create" Tcl command or the ThreadCreate() C function. */ typedef struct ThreadCtrl { - char *script; /* The TCL command this thread should execute */ - int flags; /* Initial value of the "flags" field in the - * ThreadSpecificData structure for the new thread. - * Might contain TP_Detached or TP_TclThread. */ - Tcl_Condition condWait; - /* This condition variable is used to synchronize - * the parent and child threads. The child won't run - * until it acquires threadMutex, and the parent function - * won't complete until signaled on this condition - * variable. */ + const char *script; /* The Tcl command this thread should + * execute */ + int flags; /* Initial value of the "flags" field in the + * ThreadSpecificData structure for the new + * thread. Might contain TP_Detached or + * TP_TclThread. */ + Tcl_Condition condWait; /* This condition variable is used to + * synchronize the parent and child threads. + * The child won't run until it acquires + * threadMutex, and the parent function won't + * complete until signaled on this condition + * variable. */ } ThreadCtrl; /* @@ -77,8 +84,8 @@ typedef struct ThreadEvent { Tcl_Event event; /* Must be first */ char *script; /* The script to execute. */ struct ThreadEventResult *resultPtr; - /* To communicate the result. This is - * NULL if we don't care about it. */ + /* To communicate the result. This is NULL if + * we don't care about it. */ } ThreadEvent; typedef struct ThreadEventResult { @@ -101,41 +108,38 @@ static ThreadEventResult *resultList; * This is for simple error handling when a thread script exits badly. */ +static Tcl_ThreadId mainThreadId; static Tcl_ThreadId errorThreadId; static char *errorProcString; -/* - * Access to the list of threads and to the thread send results is - * guarded by this mutex. +/* + * Access to the list of threads and to the thread send results is guarded by + * this mutex. */ TCL_DECLARE_MUTEX(threadMutex) -#undef TCL_STORAGE_CLASS -#define TCL_STORAGE_CLASS DLLEXPORT - -EXTERN int TclThread_Init _ANSI_ARGS_((Tcl_Interp *interp)); -EXTERN int Tcl_ThreadObjCmd _ANSI_ARGS_((ClientData clientData, - Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); -EXTERN int TclCreateThread _ANSI_ARGS_((Tcl_Interp *interp, - char *script, int joinable)); -EXTERN int TclThreadList _ANSI_ARGS_((Tcl_Interp *interp)); -EXTERN int TclThreadSend _ANSI_ARGS_((Tcl_Interp *interp, Tcl_ThreadId id, - char *script, int wait)); - -#undef TCL_STORAGE_CLASS -#define TCL_STORAGE_CLASS DLLIMPORT - -Tcl_ThreadCreateType NewTestThread _ANSI_ARGS_((ClientData clientData)); -static void ListRemove _ANSI_ARGS_((ThreadSpecificData *tsdPtr)); -static void ListUpdateInner _ANSI_ARGS_((ThreadSpecificData *tsdPtr)); -static int ThreadEventProc _ANSI_ARGS_((Tcl_Event *evPtr, int mask)); -static void ThreadErrorProc _ANSI_ARGS_((Tcl_Interp *interp)); -static void ThreadFreeProc _ANSI_ARGS_((ClientData clientData)); -static int ThreadDeleteEvent _ANSI_ARGS_((Tcl_Event *eventPtr, - ClientData clientData)); -static void ThreadExitProc _ANSI_ARGS_((ClientData clientData)); - +static int ThreadObjCmd(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int ThreadCreate(Tcl_Interp *interp, const char *script, + int joinable); +static int ThreadList(Tcl_Interp *interp); +static int ThreadSend(Tcl_Interp *interp, Tcl_ThreadId id, + const char *script, int wait); +static int ThreadCancel(Tcl_Interp *interp, Tcl_ThreadId id, + const char *result, int flags); + +static Tcl_ThreadCreateType NewTestThread(ClientData clientData); +static void ListRemove(ThreadSpecificData *tsdPtr); +static void ListUpdateInner(ThreadSpecificData *tsdPtr); +static int ThreadEventProc(Tcl_Event *evPtr, int mask); +static void ThreadErrorProc(Tcl_Interp *interp); +static void ThreadFreeProc(ClientData clientData); +static int ThreadDeleteEvent(Tcl_Event *eventPtr, + ClientData clientData); +static void ThreadExitProc(ClientData clientData); +extern int Tcltest_Init(Tcl_Interp *interp); /* *---------------------------------------------------------------------- @@ -145,7 +149,7 @@ static void ThreadExitProc _ANSI_ARGS_((ClientData clientData)); * Initialize the test thread command. * * Results: - * TCL_OK if the package was properly initialized. + * TCL_OK if the package was properly initialized. * * Side effects: * Add the "testthread" command to the interp. @@ -154,15 +158,20 @@ static void ThreadExitProc _ANSI_ARGS_((ClientData clientData)); */ int -TclThread_Init(interp) - Tcl_Interp *interp; /* The current Tcl interpreter */ +TclThread_Init( + Tcl_Interp *interp) /* The current Tcl interpreter */ { - - Tcl_CreateObjCommand(interp,"testthread", Tcl_ThreadObjCmd, - (ClientData)NULL ,NULL); - if (Tcl_PkgProvide(interp, "Thread", "1.0" ) != TCL_OK) { - return TCL_ERROR; + /* + * If the main thread Id has not been set, do it now. + */ + + Tcl_MutexLock(&threadMutex); + if (mainThreadId == 0) { + mainThreadId = Tcl_GetCurrentThread(); } + Tcl_MutexUnlock(&threadMutex); + + Tcl_CreateObjCommand(interp, "testthread", ThreadObjCmd, NULL, NULL); return TCL_OK; } @@ -170,15 +179,17 @@ TclThread_Init(interp) /* *---------------------------------------------------------------------- * - * Tcl_ThreadObjCmd -- + * ThreadObjCmd -- * - * This procedure is invoked to process the "testthread" Tcl command. - * See the user documentation for details on what it does. + * This procedure is invoked to process the "testthread" Tcl command. See + * the user documentation for details on what it does. * + * thread cancel ?-unwind? id ?result? * thread create ?-joinable? ?script? - * thread send id ?-async? script + * thread send ?-async? id script + * thread event * thread exit - * thread info id + * thread id ?-main? * thread names * thread wait * thread errorproc proc @@ -194,31 +205,36 @@ TclThread_Init(interp) */ /* ARGSUSED */ -int -Tcl_ThreadObjCmd(dummy, interp, objc, objv) - ClientData dummy; /* Not used. */ - Tcl_Interp *interp; /* Current interpreter. */ - int objc; /* Number of arguments. */ - Tcl_Obj *CONST objv[]; /* Argument objects. */ +static int +ThreadObjCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); int option; - static CONST char *threadOptions[] = {"create", "exit", "id", "join", "names", - "send", "wait", "errorproc", - (char *) NULL}; - enum options {THREAD_CREATE, THREAD_EXIT, THREAD_ID, THREAD_JOIN, - THREAD_NAMES, THREAD_SEND, THREAD_WAIT, THREAD_ERRORPROC}; + static const char *const threadOptions[] = { + "cancel", "create", "event", "exit", "id", + "join", "names", "send", "wait", "errorproc", + NULL + }; + enum options { + THREAD_CANCEL, THREAD_CREATE, THREAD_EVENT, THREAD_EXIT, + THREAD_ID, THREAD_JOIN, THREAD_NAMES, THREAD_SEND, + THREAD_WAIT, THREAD_ERRORPROC + }; if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, "option ?args?"); + Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); return TCL_ERROR; } - if (Tcl_GetIndexFromObj(interp, objv[1], threadOptions, - "option", 0, &option) != TCL_OK) { + if (Tcl_GetIndexFromObj(interp, objv[1], threadOptions, "option", 0, + &option) != TCL_OK) { return TCL_ERROR; } - /* + /* * Make sure the initial thread is on the list before doing anything. */ @@ -231,166 +247,241 @@ Tcl_ThreadObjCmd(dummy, interp, objc, objv) } switch ((enum options)option) { - case THREAD_CREATE: { - char *script; - int joinable, len; + case THREAD_CANCEL: { + long id; + const char *result; + int flags, arg; + + if ((objc < 3) || (objc > 5)) { + Tcl_WrongNumArgs(interp, 2, objv, "?-unwind? id ?result?"); + return TCL_ERROR; + } + flags = 0; + arg = 2; + if ((objc == 4) || (objc == 5)) { + if (strcmp("-unwind", Tcl_GetString(objv[arg])) == 0) { + flags = TCL_CANCEL_UNWIND; + arg++; + } + } + if (Tcl_GetLongFromObj(interp, objv[arg], &id) != TCL_OK) { + return TCL_ERROR; + } + arg++; + if (arg < objc) { + result = Tcl_GetString(objv[arg]); + } else { + result = NULL; + } + return ThreadCancel(interp, (Tcl_ThreadId) (size_t) id, result, flags); + } + case THREAD_CREATE: { + const char *script; + int joinable, len; - if (objc == 2) { - /* Neither joinable nor special script - */ + if (objc == 2) { + /* + * Neither joinable nor special script + */ - joinable = 0; - script = "testthread wait"; /* Just enter the event loop */ + joinable = 0; + script = "testthread wait"; /* Just enter event loop */ + } else if (objc == 3) { + /* + * Possibly -joinable, then no special script, no joinable, then + * its a script. + */ - } else if (objc == 3) { - /* Possibly -joinable, then no special script, - * no joinable, then its a script. - */ + script = Tcl_GetStringFromObj(objv[2], &len); - script = Tcl_GetString(objv[2]); - len = strlen (script); - - if ((len > 1) && - (script [0] == '-') && (script [1] == 'j') && - (0 == strncmp (script, "-joinable", (size_t) len))) { - joinable = 1; - script = "testthread wait"; /* Just enter the event loop - */ - } else { - /* Remember the script */ - joinable = 0; - } - } else if (objc == 4) { - /* Definitely a script available, but is the flag - * -joinable ? + if ((len > 1) && (script[0] == '-') && (script[1] == 'j') && + (0 == strncmp(script, "-joinable", (size_t) len))) { + joinable = 1; + script = "testthread wait"; /* Just enter event loop */ + } else { + /* + * Remember the script */ - script = Tcl_GetString(objv[2]); - len = strlen (script); - - joinable = ((len > 1) && - (script [0] == '-') && (script [1] == 'j') && - (0 == strncmp (script, "-joinable", (size_t) len))); - - script = Tcl_GetString(objv[3]); - } else { - Tcl_WrongNumArgs(interp, 2, objv, "?-joinable? ?script?"); - return TCL_ERROR; + joinable = 0; } - return TclCreateThread(interp, script, joinable); + } else if (objc == 4) { + /* + * Definitely a script available, but is the flag -joinable? + */ + + script = Tcl_GetStringFromObj(objv[2], &len); + joinable = ((len > 1) && (script[0] == '-') && (script[1] == 'j') + && (0 == strncmp(script, "-joinable", (size_t) len))); + script = Tcl_GetString(objv[3]); + } else { + Tcl_WrongNumArgs(interp, 2, objv, "?-joinable? ?script?"); + return TCL_ERROR; } - case THREAD_EXIT: { - if (objc > 2) { - Tcl_WrongNumArgs(interp, 1, objv, NULL); - return TCL_ERROR; - } - ListRemove(NULL); - Tcl_ExitThread(0); - return TCL_OK; + return ThreadCreate(interp, script, joinable); + } + case THREAD_EXIT: + if (objc > 2) { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + return TCL_ERROR; } - case THREAD_ID: + ListRemove(NULL); + Tcl_ExitThread(0); + return TCL_OK; + case THREAD_ID: + if (objc == 2 || objc == 3) { + Tcl_Obj *idObj; + + /* + * Check if they want the main thread id or the current thread id. + */ + if (objc == 2) { - Tcl_Obj *idObj = Tcl_NewLongObj((long)Tcl_GetCurrentThread()); - Tcl_SetObjResult(interp, idObj); - return TCL_OK; + idObj = Tcl_NewWideIntObj((Tcl_WideInt)(size_t)Tcl_GetCurrentThread()); + } else if (objc == 3 + && strcmp("-main", Tcl_GetString(objv[2])) == 0) { + Tcl_MutexLock(&threadMutex); + idObj = Tcl_NewLongObj((long)(size_t)mainThreadId); + Tcl_MutexUnlock(&threadMutex); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); return TCL_ERROR; } - case THREAD_JOIN: { - long id; - int result, status; - if (objc != 3) { - Tcl_WrongNumArgs(interp, 1, objv, "join id"); - return TCL_ERROR; - } - if (Tcl_GetLongFromObj(interp, objv[2], &id) != TCL_OK) { - return TCL_ERROR; - } + Tcl_SetObjResult(interp, idObj); + return TCL_OK; + } else { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + return TCL_ERROR; + } + case THREAD_JOIN: { + Tcl_WideInt id; + int result, status; - result = Tcl_JoinThread ((Tcl_ThreadId) id, &status); - if (result == TCL_OK) { - Tcl_SetIntObj (Tcl_GetObjResult (interp), status); - } else { - char buf [20]; - sprintf (buf, "%ld", id); - Tcl_AppendResult (interp, "cannot join thread ", buf, NULL); - } - return result; + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "id"); + return TCL_ERROR; } - case THREAD_NAMES: { - if (objc > 2) { - Tcl_WrongNumArgs(interp, 2, objv, NULL); - return TCL_ERROR; - } - return TclThreadList(interp); + if (Tcl_GetWideIntFromObj(interp, objv[2], &id) != TCL_OK) { + return TCL_ERROR; } - case THREAD_SEND: { - long id; - char *script; - int wait, arg; - if ((objc != 4) && (objc != 5)) { - Tcl_WrongNumArgs(interp, 1, objv, "send ?-async? id script"); - return TCL_ERROR; - } - if (objc == 5) { - if (strcmp("-async", Tcl_GetString(objv[2])) != 0) { - Tcl_WrongNumArgs(interp, 1, objv, "send ?-async? id script"); - return TCL_ERROR; - } - wait = 0; - arg = 3; - } else { - wait = 1; - arg = 2; - } - if (Tcl_GetLongFromObj(interp, objv[arg], &id) != TCL_OK) { + result = Tcl_JoinThread((Tcl_ThreadId)(size_t)id, &status); + if (result == TCL_OK) { + Tcl_SetIntObj(Tcl_GetObjResult(interp), status); + } else { + char buf[20]; + + sprintf(buf, "%" TCL_LL_MODIFIER "d", id); + Tcl_AppendResult(interp, "cannot join thread ", buf, NULL); + } + return result; + } + case THREAD_NAMES: + if (objc > 2) { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + return TCL_ERROR; + } + return ThreadList(interp); + case THREAD_SEND: { + Tcl_WideInt id; + const char *script; + int wait, arg; + + if ((objc != 4) && (objc != 5)) { + Tcl_WrongNumArgs(interp, 2, objv, "?-async? id script"); + return TCL_ERROR; + } + if (objc == 5) { + if (strcmp("-async", Tcl_GetString(objv[2])) != 0) { + Tcl_WrongNumArgs(interp, 2, objv, "?-async? id script"); return TCL_ERROR; } - arg++; - script = Tcl_GetString(objv[arg]); - return TclThreadSend(interp, (Tcl_ThreadId) id, script, wait); + wait = 0; + arg = 3; + } else { + wait = 1; + arg = 2; } - case THREAD_WAIT: { - while (1) { - (void) Tcl_DoOneEvent(TCL_ALL_EVENTS); - } + if (Tcl_GetWideIntFromObj(interp, objv[arg], &id) != TCL_OK) { + return TCL_ERROR; } - case THREAD_ERRORPROC: { + arg++; + script = Tcl_GetString(objv[arg]); + return ThreadSend(interp, (Tcl_ThreadId)(size_t)id, script, wait); + } + case THREAD_EVENT: { + if (objc > 2) { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + return TCL_ERROR; + } + Tcl_SetObjResult(interp, Tcl_NewIntObj( + Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT))); + return TCL_OK; + } + case THREAD_ERRORPROC: { + /* + * Arrange for this proc to handle thread death errors. + */ + + const char *proc; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "proc"); + return TCL_ERROR; + } + Tcl_MutexLock(&threadMutex); + errorThreadId = Tcl_GetCurrentThread(); + if (errorProcString) { + ckfree(errorProcString); + } + proc = Tcl_GetString(objv[2]); + errorProcString = ckalloc(strlen(proc) + 1); + strcpy(errorProcString, proc); + Tcl_MutexUnlock(&threadMutex); + return TCL_OK; + } + case THREAD_WAIT: + if (objc > 2) { + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + while (1) { /* - * Arrange for this proc to handle thread death errors. + * If the script has been unwound, bail out immediately. This does + * not follow the recommended guidelines for how extensions should + * handle the script cancellation functionality because this is + * not a "normal" extension. Most extensions do not have a command + * that simply enters an infinite Tcl event loop. Normal + * extensions should not specify the TCL_CANCEL_UNWIND when + * calling Tcl_Canceled to check if the command has been canceled. */ - char *proc; - if (objc != 3) { - Tcl_WrongNumArgs(interp, 1, objv, "errorproc proc"); - return TCL_ERROR; + if (Tcl_Canceled(interp, + TCL_LEAVE_ERR_MSG | TCL_CANCEL_UNWIND) == TCL_ERROR) { + break; } - Tcl_MutexLock(&threadMutex); - errorThreadId = Tcl_GetCurrentThread(); - if (errorProcString) { - ckfree(errorProcString); - } - proc = Tcl_GetString(objv[2]); - errorProcString = ckalloc(strlen(proc)+1); - strcpy(errorProcString, proc); - Tcl_MutexUnlock(&threadMutex); - return TCL_OK; + (void) Tcl_DoOneEvent(TCL_ALL_EVENTS); } + + /* + * If we get to this point, we have been canceled by another thread, + * which is considered to be an "error". + */ + + ThreadErrorProc(interp); + return TCL_OK; } return TCL_OK; } - /* *---------------------------------------------------------------------- * - * TclCreateThread -- + * ThreadCreate -- * * This procedure is invoked to create a thread containing an interp to - * run a script. This returns after the thread has started executing. + * run a script. This returns after the thread has started executing. * * Results: * A standard Tcl result, which is the thread ID. @@ -402,11 +493,11 @@ Tcl_ThreadObjCmd(dummy, interp, objc, objv) */ /* ARGSUSED */ -int -TclCreateThread(interp, script, joinable) - Tcl_Interp *interp; /* Current interpreter. */ - char *script; /* Script to execute */ - int joinable; /* Flag, joinable thread or not */ +static int +ThreadCreate( + Tcl_Interp *interp, /* Current interpreter. */ + const char *script, /* Script to execute */ + int joinable) /* Flag, joinable thread or not */ { ThreadCtrl ctrl; Tcl_ThreadId id; @@ -419,10 +510,9 @@ TclCreateThread(interp, script, joinable) Tcl_MutexLock(&threadMutex); if (Tcl_CreateThread(&id, NewTestThread, (ClientData) &ctrl, - TCL_THREAD_STACK_DEFAULT, joinable) != TCL_OK) { + TCL_THREAD_STACK_DEFAULT, joinable) != TCL_OK) { Tcl_MutexUnlock(&threadMutex); - Tcl_AppendResult(interp,"can't create a new thread",0); - ckfree((void*)ctrl.script); + Tcl_AppendResult(interp, "can't create a new thread", NULL); return TCL_ERROR; } @@ -433,7 +523,7 @@ TclCreateThread(interp, script, joinable) Tcl_ConditionWait(&ctrl.condWait, &threadMutex, NULL); Tcl_MutexUnlock(&threadMutex); Tcl_ConditionFinalize(&ctrl.condWait); - Tcl_SetObjResult(interp, Tcl_NewLongObj((long)id)); + Tcl_SetObjResult(interp, Tcl_NewWideIntObj((Tcl_WideInt)(size_t)id)); return TCL_OK; } @@ -442,45 +532,57 @@ TclCreateThread(interp, script, joinable) * * NewTestThread -- * - * This routine is the "main()" for a new thread whose task is to - * execute a single TCL script. The argument to this function is - * a pointer to a structure that contains the text of the TCL script - * to be executed. - * - * Space to hold the script field of the ThreadControl structure passed - * in as the only argument was obtained from malloc() and must be freed - * by this function before it exits. Space to hold the ThreadControl - * structure itself is released by the calling function, and the - * two condition variables in the ThreadControl structure are destroyed - * by the calling function. The calling function will destroy the - * ThreadControl structure and the condition variable as soon as - * ctrlPtr->condWait is signaled, so this routine must make copies of - * any data it might need after that point. + * This routine is the "main()" for a new thread whose task is to execute + * a single Tcl script. The argument to this function is a pointer to a + * structure that contains the text of the TCL script to be executed. + * + * Space to hold the script field of the ThreadControl structure passed + * in as the only argument was obtained from malloc() and must be freed + * by this function before it exits. Space to hold the ThreadControl + * structure itself is released by the calling function, and the two + * condition variables in the ThreadControl structure are destroyed by + * the calling function. The calling function will destroy the + * ThreadControl structure and the condition variable as soon as + * ctrlPtr->condWait is signaled, so this routine must make copies of any + * data it might need after that point. * * Results: - * none + * None * * Side effects: - * A TCL script is executed in a new thread. + * A Tcl script is executed in a new thread. * *------------------------------------------------------------------------ */ + Tcl_ThreadCreateType -NewTestThread(clientData) - ClientData clientData; +NewTestThread( + ClientData clientData) { - ThreadCtrl *ctrlPtr = (ThreadCtrl*)clientData; + ThreadCtrl *ctrlPtr = clientData; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); int result; char *threadEvalScript; /* - * Initialize the interpreter. This should be more general. + * Initialize the interpreter. This should be more general. */ tsdPtr->interp = Tcl_CreateInterp(); result = Tcl_Init(tsdPtr->interp); - result = TclThread_Init(tsdPtr->interp); + if (result != TCL_OK) { + ThreadErrorProc(tsdPtr->interp); + } + + /* + * This is part of the test facility. Initialize _ALL_ test commands for + * use by the new thread. + */ + + result = Tcltest_Init(tsdPtr->interp); + if (result != TCL_OK) { + ThreadErrorProc(tsdPtr->interp); + } /* * Update the list of threads. @@ -488,14 +590,16 @@ NewTestThread(clientData) Tcl_MutexLock(&threadMutex); ListUpdateInner(tsdPtr); + /* - * We need to keep a pointer to the alloc'ed mem of the script - * we are eval'ing, for the case that we exit during evaluation + * We need to keep a pointer to the alloc'ed mem of the script we are + * eval'ing, for the case that we exit during evaluation */ - threadEvalScript = (char *) ckalloc(strlen(ctrlPtr->script)+1); + + threadEvalScript = ckalloc(strlen(ctrlPtr->script) + 1); strcpy(threadEvalScript, ctrlPtr->script); - Tcl_CreateThreadExitHandler(ThreadExitProc, (ClientData) threadEvalScript); + Tcl_CreateThreadExitHandler(ThreadExitProc, threadEvalScript); /* * Notify the parent we are alive. @@ -508,7 +612,7 @@ NewTestThread(clientData) * Run the script. */ - Tcl_Preserve((ClientData) tsdPtr->interp); + Tcl_Preserve(tsdPtr->interp); result = Tcl_Eval(tsdPtr->interp, threadEvalScript); if (result != TCL_OK) { ThreadErrorProc(tsdPtr->interp); @@ -518,9 +622,9 @@ NewTestThread(clientData) * Clean up. */ - ListRemove(tsdPtr); - Tcl_Release((ClientData) tsdPtr->interp); Tcl_DeleteInterp(tsdPtr->interp); + Tcl_Release(tsdPtr->interp); + ListRemove(tsdPtr); Tcl_ExitThread(result); TCL_THREAD_CREATE_RETURN; @@ -531,25 +635,27 @@ NewTestThread(clientData) * * ThreadErrorProc -- * - * Send a message to the thread willing to hear about errors. + * Send a message to the thread willing to hear about errors. * * Results: - * none + * None * * Side effects: - * Send an event. + * Send an event. * *------------------------------------------------------------------------ */ + static void -ThreadErrorProc(interp) - Tcl_Interp *interp; /* Interp that failed */ +ThreadErrorProc( + Tcl_Interp *interp) /* Interp that failed */ { Tcl_Channel errChannel; - CONST char *errorInfo, *argv[3]; + const char *errorInfo, *argv[3]; char *script; char buf[TCL_DOUBLE_SPACE+1]; - sprintf(buf, "%ld", (long) Tcl_GetCurrentThread()); + + sprintf(buf, "%" TCL_LL_MODIFIER "d", (Tcl_WideInt)(size_t)Tcl_GetCurrentThread()); errorInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); if (errorProcString == NULL) { @@ -564,7 +670,7 @@ ThreadErrorProc(interp) argv[1] = buf; argv[2] = errorInfo; script = Tcl_Merge(3, argv); - TclThreadSend(interp, errorThreadId, script, 0); + ThreadSend(interp, errorThreadId, script, 0); ckfree(script); } } @@ -575,20 +681,21 @@ ThreadErrorProc(interp) * * ListUpdateInner -- * - * Add the thread local storage to the list. This assumes - * the caller has obtained the mutex. + * Add the thread local storage to the list. This assumes the caller has + * obtained the mutex. * * Results: - * none + * None * * Side effects: - * Add the thread local storage to its list. + * Add the thread local storage to its list. * *------------------------------------------------------------------------ */ + static void -ListUpdateInner(tsdPtr) - ThreadSpecificData *tsdPtr; +ListUpdateInner( + ThreadSpecificData *tsdPtr) { if (tsdPtr == NULL) { tsdPtr = TCL_TSD_INIT(&dataKey); @@ -607,20 +714,21 @@ ListUpdateInner(tsdPtr) * * ListRemove -- * - * Remove the thread local storage from its list. This grabs the - * mutex to protect the list. + * Remove the thread local storage from its list. This grabs the mutex to + * protect the list. * * Results: - * none + * None * * Side effects: - * Remove the thread local storage from its list. + * Remove the thread local storage from its list. * *------------------------------------------------------------------------ */ + static void -ListRemove(tsdPtr) - ThreadSpecificData *tsdPtr; +ListRemove( + ThreadSpecificData *tsdPtr) { if (tsdPtr == NULL) { tsdPtr = TCL_TSD_INIT(&dataKey); @@ -635,14 +743,14 @@ ListRemove(tsdPtr) tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; } tsdPtr->nextPtr = tsdPtr->prevPtr = 0; + tsdPtr->interp = NULL; Tcl_MutexUnlock(&threadMutex); } - /* *------------------------------------------------------------------------ * - * TclThreadList -- + * ThreadList -- * * Return a list of threads running Tcl interpreters. * @@ -654,9 +762,9 @@ ListRemove(tsdPtr) * *------------------------------------------------------------------------ */ -int -TclThreadList(interp) - Tcl_Interp *interp; +static int +ThreadList( + Tcl_Interp *interp) { ThreadSpecificData *tsdPtr; Tcl_Obj *listPtr; @@ -665,18 +773,17 @@ TclThreadList(interp) Tcl_MutexLock(&threadMutex); for (tsdPtr = threadList ; tsdPtr ; tsdPtr = tsdPtr->nextPtr) { Tcl_ListObjAppendElement(interp, listPtr, - Tcl_NewLongObj((long)tsdPtr->threadId)); + Tcl_NewWideIntObj((Tcl_WideInt)(size_t)tsdPtr->threadId)); } Tcl_MutexUnlock(&threadMutex); Tcl_SetObjResult(interp, listPtr); return TCL_OK; } - /* *------------------------------------------------------------------------ * - * TclThreadSend -- + * ThreadSend -- * * Send a script to another thread. * @@ -688,12 +795,13 @@ TclThreadList(interp) * *------------------------------------------------------------------------ */ -int -TclThreadSend(interp, id, script, wait) - Tcl_Interp *interp; /* The current interpreter. */ - Tcl_ThreadId id; /* Thread Id of other interpreter. */ - char *script; /* The script to evaluate. */ - int wait; /* If 1, we block for the result. */ + +static int +ThreadSend( + Tcl_Interp *interp, /* The current interpreter. */ + Tcl_ThreadId id, /* Thread Id of other interpreter. */ + const char *script, /* The script to evaluate. */ + int wait) /* If 1, we block for the result. */ { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); ThreadEvent *threadEventPtr; @@ -701,7 +809,7 @@ TclThreadSend(interp, id, script, wait) int found, code; Tcl_ThreadId threadId = (Tcl_ThreadId) id; - /* + /* * Verify the thread exists. */ @@ -720,26 +828,26 @@ TclThreadSend(interp, id, script, wait) } /* - * Short circut sends to ourself. Ought to do something with -async, - * like run in an idle handler. + * Short circut sends to ourself. Ought to do something with -async, like + * run in an idle handler. */ if (threadId == Tcl_GetCurrentThread()) { - Tcl_MutexUnlock(&threadMutex); + Tcl_MutexUnlock(&threadMutex); return Tcl_GlobalEval(interp, script); } - /* + /* * Create the event for its event queue. */ - threadEventPtr = (ThreadEvent *) ckalloc(sizeof(ThreadEvent)); + threadEventPtr = ckalloc(sizeof(ThreadEvent)); threadEventPtr->script = ckalloc(strlen(script) + 1); strcpy(threadEventPtr->script, script); if (!wait) { resultPtr = threadEventPtr->resultPtr = NULL; } else { - resultPtr = (ThreadEventResult *) ckalloc(sizeof(ThreadEventResult)); + resultPtr = ckalloc(sizeof(ThreadEventResult)); threadEventPtr->resultPtr = resultPtr; /* @@ -752,7 +860,7 @@ TclThreadSend(interp, id, script, wait) resultPtr->errorInfo = NULL; resultPtr->errorCode = NULL; - /* + /* * Maintain the cleanup list. */ @@ -772,7 +880,7 @@ TclThreadSend(interp, id, script, wait) */ threadEventPtr->event.proc = ThreadEventProc; - Tcl_ThreadQueueEvent(threadId, (Tcl_Event *)threadEventPtr, + Tcl_ThreadQueueEvent(threadId, (Tcl_Event *) threadEventPtr, TCL_QUEUE_TAIL); Tcl_ThreadAlert(threadId); @@ -781,13 +889,13 @@ TclThreadSend(interp, id, script, wait) return TCL_OK; } - /* + /* * Block on the results and then get them. */ Tcl_ResetResult(interp); while (resultPtr->result == NULL) { - Tcl_ConditionWait(&resultPtr->done, &threadMutex, NULL); + Tcl_ConditionWait(&resultPtr->done, &threadMutex, NULL); } /* @@ -818,15 +926,71 @@ TclThreadSend(interp, id, script, wait) ckfree(resultPtr->errorInfo); } } - Tcl_SetResult(interp, resultPtr->result, TCL_DYNAMIC); + Tcl_AppendResult(interp, resultPtr->result, NULL); Tcl_ConditionFinalize(&resultPtr->done); code = resultPtr->code; - ckfree((char *) resultPtr); + ckfree(resultPtr->result); + ckfree(resultPtr); return code; } + +/* + *------------------------------------------------------------------------ + * + * ThreadCancel -- + * + * Cancels a script in another thread. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------ + */ + +static int +ThreadCancel( + Tcl_Interp *interp, /* The current interpreter. */ + Tcl_ThreadId id, /* Thread Id of other interpreter. */ + const char *result, /* The result or NULL for default. */ + int flags) /* Flags for Tcl_CancelEval. */ +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + int found; + Tcl_ThreadId threadId = (Tcl_ThreadId) id; + + /* + * Verify the thread exists. + */ + Tcl_MutexLock(&threadMutex); + found = 0; + for (tsdPtr = threadList ; tsdPtr ; tsdPtr = tsdPtr->nextPtr) { + if (tsdPtr->threadId == threadId) { + found = 1; + break; + } + } + if (!found) { + Tcl_MutexUnlock(&threadMutex); + Tcl_AppendResult(interp, "invalid thread id", NULL); + return TCL_ERROR; + } + + /* + * Since Tcl_CancelEval can be safely called from any thread, + * we do it now. + */ + + Tcl_MutexUnlock(&threadMutex); + Tcl_ResetResult(interp); + return Tcl_CancelEval(tsdPtr->interp, + (result != NULL) ? Tcl_NewStringObj(result, -1) : NULL, 0, flags); +} /* *------------------------------------------------------------------------ @@ -843,17 +1007,18 @@ TclThreadSend(interp, id, script, wait) * *------------------------------------------------------------------------ */ + static int -ThreadEventProc(evPtr, mask) - Tcl_Event *evPtr; /* Really ThreadEvent */ - int mask; +ThreadEventProc( + Tcl_Event *evPtr, /* Really ThreadEvent */ + int mask) { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - ThreadEvent *threadEventPtr = (ThreadEvent *)evPtr; + ThreadEvent *threadEventPtr = (ThreadEvent *) evPtr; ThreadEventResult *resultPtr = threadEventPtr->resultPtr; Tcl_Interp *interp = tsdPtr->interp; int code; - CONST char *result, *errorCode, *errorInfo; + const char *result, *errorCode, *errorInfo; if (interp == NULL) { code = TCL_ERROR; @@ -861,13 +1026,11 @@ ThreadEventProc(evPtr, mask) errorCode = "THREAD"; errorInfo = ""; } else { - Tcl_Preserve((ClientData) interp); + Tcl_Preserve(interp); Tcl_ResetResult(interp); - Tcl_CreateThreadExitHandler(ThreadFreeProc, - (ClientData) threadEventPtr->script); + Tcl_CreateThreadExitHandler(ThreadFreeProc, threadEventPtr->script); code = Tcl_GlobalEval(interp, threadEventPtr->script); - Tcl_DeleteThreadExitHandler(ThreadFreeProc, - (ClientData) threadEventPtr->script); + Tcl_DeleteThreadExitHandler(ThreadFreeProc, threadEventPtr->script); if (code != TCL_OK) { errorCode = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY); errorInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); @@ -894,7 +1057,7 @@ ThreadEventProc(evPtr, mask) Tcl_MutexUnlock(&threadMutex); } if (interp != NULL) { - Tcl_Release((ClientData) interp); + Tcl_Release(interp); } return 1; } @@ -915,13 +1078,14 @@ ThreadEventProc(evPtr, mask) * *------------------------------------------------------------------------ */ + /* ARGSUSED */ static void -ThreadFreeProc(clientData) - ClientData clientData; +ThreadFreeProc( + ClientData clientData) { if (clientData) { - ckfree((char *) clientData); + ckfree(clientData); } } @@ -941,20 +1105,23 @@ ThreadFreeProc(clientData) * *------------------------------------------------------------------------ */ + /* ARGSUSED */ static int -ThreadDeleteEvent(eventPtr, clientData) - Tcl_Event *eventPtr; /* Really ThreadEvent */ - ClientData clientData; /* dummy */ +ThreadDeleteEvent( + Tcl_Event *eventPtr, /* Really ThreadEvent */ + ClientData clientData) /* dummy */ { if (eventPtr->proc == ThreadEventProc) { - ckfree((char *) ((ThreadEvent *) eventPtr)->script); + ckfree(((ThreadEvent *) eventPtr)->script); return 1; } + /* - * If it was NULL, we were in the middle of servicing the event - * and it should be removed + * If it was NULL, we were in the middle of servicing the event and it + * should be removed */ + return (eventPtr->proc == NULL); } @@ -963,41 +1130,48 @@ ThreadDeleteEvent(eventPtr, clientData) * * ThreadExitProc -- * - * This is called when the thread exits. + * This is called when the thread exits. * * Results: * None. * * Side effects: - * It unblocks anyone that is waiting on a send to this thread. - * It cleans up any events in the event queue for this thread. + * It unblocks anyone that is waiting on a send to this thread. It cleans + * up any events in the event queue for this thread. * *------------------------------------------------------------------------ */ + /* ARGSUSED */ static void -ThreadExitProc(clientData) - ClientData clientData; +ThreadExitProc( + ClientData clientData) { - char *threadEvalScript = (char *) clientData; + char *threadEvalScript = clientData; ThreadEventResult *resultPtr, *nextPtr; Tcl_ThreadId self = Tcl_GetCurrentThread(); + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + if (tsdPtr->interp != NULL) { + ListRemove(tsdPtr); + } Tcl_MutexLock(&threadMutex); if (threadEvalScript) { - ckfree((char *) threadEvalScript); + ckfree(threadEvalScript); threadEvalScript = NULL; } - Tcl_DeleteEvents((Tcl_EventDeleteProc *)ThreadDeleteEvent, NULL); + Tcl_DeleteEvents((Tcl_EventDeleteProc *) ThreadDeleteEvent, NULL); for (resultPtr = resultList ; resultPtr ; resultPtr = nextPtr) { nextPtr = resultPtr->nextPtr; if (resultPtr->srcThreadId == self) { /* - * We are going away. By freeing up the result we signal - * to the other thread we don't care about the result. + * We are going away. By freeing up the result we signal to the + * other thread we don't care about the result. */ + if (resultPtr->prevPtr) { resultPtr->prevPtr->nextPtr = resultPtr->nextPtr; } else { @@ -1008,16 +1182,17 @@ ThreadExitProc(clientData) } resultPtr->nextPtr = resultPtr->prevPtr = 0; resultPtr->eventPtr->resultPtr = NULL; - ckfree((char *)resultPtr); + ckfree(resultPtr); } else if (resultPtr->dstThreadId == self) { /* - * Dang. The target is going away. Unblock the caller. - * The result string must be dynamically allocated because - * the main thread is going to call free on it. + * Dang. The target is going away. Unblock the caller. The result + * string must be dynamically allocated because the main thread is + * going to call free on it. */ - char *msg = "target thread died"; - resultPtr->result = ckalloc(strlen(msg)+1); + const char *msg = "target thread died"; + + resultPtr->result = ckalloc(strlen(msg) + 1); strcpy(resultPtr->result, msg); resultPtr->code = TCL_ERROR; Tcl_ConditionNotify(&resultPtr->done); @@ -1025,5 +1200,12 @@ ThreadExitProc(clientData) } Tcl_MutexUnlock(&threadMutex); } - #endif /* TCL_THREADS */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |