4 * This file implements the testthread command. Eventually this should be
6 * Some of this code is based on work done by Richard Hipp on behalf of
7 * Conservation Through Innovation, Limited, with their permission.
9 * Copyright (c) 1998 by Sun Microsystems, Inc.
10 * Copyright (c) 2006-2008 by Joe Mistachkin. All rights reserved.
12 * See the file "license.terms" for information on usage and redistribution of
13 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
17 # define USE_TCL_STUBS
23 * Each thread has an single instance of the following structure. There is one
24 * instance of this structure per thread even if that thread contains multiple
25 * interpreters. The interpreter identified by this structure is the main
26 * interpreter for the thread.
28 * The main interpreter is the one that will process any messages received by
29 * a thread. Any thread can send messages but only the main interpreter can
33 typedef struct ThreadSpecificData {
34 Tcl_ThreadId threadId; /* Tcl ID for this thread */
35 Tcl_Interp *interp; /* Main interpreter for this thread */
36 int flags; /* See the TP_ defines below... */
37 struct ThreadSpecificData *nextPtr;
38 /* List for "thread names" */
39 struct ThreadSpecificData *prevPtr;
40 /* List for "thread names" */
42 static Tcl_ThreadDataKey dataKey;
45 * This list is used to list all threads that have interpreters. This is
46 * protected by threadMutex.
49 static ThreadSpecificData *threadList = NULL;
52 * The following bit-values are legal for the "flags" field of the
53 * ThreadSpecificData structure.
56 #define TP_Dying 0x001 /* This thread is being canceled */
59 * An instance of the following structure contains all information that is
60 * passed into a new thread when the thread is created using either the
61 * "thread create" Tcl command or the ThreadCreate() C function.
64 typedef struct ThreadCtrl {
65 const char *script; /* The Tcl command this thread should
67 int flags; /* Initial value of the "flags" field in the
68 * ThreadSpecificData structure for the new
69 * thread. Might contain TP_Detached or
71 Tcl_Condition condWait; /* This condition variable is used to
72 * synchronize the parent and child threads.
73 * The child won't run until it acquires
74 * threadMutex, and the parent function won't
75 * complete until signaled on this condition
80 * This is the event used to send scripts to other threads.
83 typedef struct ThreadEvent {
84 Tcl_Event event; /* Must be first */
85 char *script; /* The script to execute. */
86 struct ThreadEventResult *resultPtr;
87 /* To communicate the result. This is NULL if
88 * we don't care about it. */
91 typedef struct ThreadEventResult {
92 Tcl_Condition done; /* Signaled when the script completes */
93 int code; /* Return value of Tcl_Eval */
94 char *result; /* Result from the script */
95 char *errorInfo; /* Copy of errorInfo variable */
96 char *errorCode; /* Copy of errorCode variable */
97 Tcl_ThreadId srcThreadId; /* Id of sending thread, in case it dies */
98 Tcl_ThreadId dstThreadId; /* Id of target thread, in case it dies */
99 struct ThreadEvent *eventPtr; /* Back pointer */
100 struct ThreadEventResult *nextPtr; /* List for cleanup */
101 struct ThreadEventResult *prevPtr;
105 static ThreadEventResult *resultList;
108 * This is for simple error handling when a thread script exits badly.
111 static Tcl_ThreadId mainThreadId;
112 static Tcl_ThreadId errorThreadId;
113 static char *errorProcString;
116 * Access to the list of threads and to the thread send results is guarded by
120 TCL_DECLARE_MUTEX(threadMutex)
122 static int ThreadObjCmd(ClientData clientData,
123 Tcl_Interp *interp, int objc,
124 Tcl_Obj *const objv[]);
125 static int ThreadCreate(Tcl_Interp *interp, const char *script,
127 static int ThreadList(Tcl_Interp *interp);
128 static int ThreadSend(Tcl_Interp *interp, Tcl_ThreadId id,
129 const char *script, int wait);
130 static int ThreadCancel(Tcl_Interp *interp, Tcl_ThreadId id,
131 const char *result, int flags);
133 static Tcl_ThreadCreateType NewTestThread(ClientData clientData);
134 static void ListRemove(ThreadSpecificData *tsdPtr);
135 static void ListUpdateInner(ThreadSpecificData *tsdPtr);
136 static int ThreadEventProc(Tcl_Event *evPtr, int mask);
137 static void ThreadErrorProc(Tcl_Interp *interp);
138 static void ThreadFreeProc(ClientData clientData);
139 static int ThreadDeleteEvent(Tcl_Event *eventPtr,
140 ClientData clientData);
141 static void ThreadExitProc(ClientData clientData);
142 extern int Tcltest_Init(Tcl_Interp *interp);
145 *----------------------------------------------------------------------
149 * Initialize the test thread command.
152 * TCL_OK if the package was properly initialized.
155 * Add the "testthread" command to the interp.
157 *----------------------------------------------------------------------
162 Tcl_Interp *interp) /* The current Tcl interpreter */
165 * If the main thread Id has not been set, do it now.
168 Tcl_MutexLock(&threadMutex);
169 if (mainThreadId == 0) {
170 mainThreadId = Tcl_GetCurrentThread();
172 Tcl_MutexUnlock(&threadMutex);
174 Tcl_CreateObjCommand(interp, "testthread", ThreadObjCmd, NULL, NULL);
179 *----------------------------------------------------------------------
183 * This procedure is invoked to process the "testthread" Tcl command. See
184 * the user documentation for details on what it does.
186 * thread cancel ?-unwind? id ?result?
187 * thread create ?-joinable? ?script?
188 * thread send ?-async? id script
194 * thread errorproc proc
198 * A standard Tcl result.
201 * See the user documentation.
203 *----------------------------------------------------------------------
209 ClientData dummy, /* Not used. */
210 Tcl_Interp *interp, /* Current interpreter. */
211 int objc, /* Number of arguments. */
212 Tcl_Obj *const objv[]) /* Argument objects. */
214 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
216 static const char *const threadOptions[] = {
217 "cancel", "create", "event", "exit", "id",
218 "join", "names", "send", "wait", "errorproc",
222 THREAD_CANCEL, THREAD_CREATE, THREAD_EVENT, THREAD_EXIT,
223 THREAD_ID, THREAD_JOIN, THREAD_NAMES, THREAD_SEND,
224 THREAD_WAIT, THREAD_ERRORPROC
228 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
231 if (Tcl_GetIndexFromObj(interp, objv[1], threadOptions, "option", 0,
232 &option) != TCL_OK) {
237 * Make sure the initial thread is on the list before doing anything.
240 if (tsdPtr->interp == NULL) {
241 Tcl_MutexLock(&threadMutex);
242 tsdPtr->interp = interp;
243 ListUpdateInner(tsdPtr);
244 Tcl_CreateThreadExitHandler(ThreadExitProc, NULL);
245 Tcl_MutexUnlock(&threadMutex);
248 switch ((enum options)option) {
249 case THREAD_CANCEL: {
254 if ((objc < 3) || (objc > 5)) {
255 Tcl_WrongNumArgs(interp, 2, objv, "?-unwind? id ?result?");
260 if ((objc == 4) || (objc == 5)) {
261 if (strcmp("-unwind", Tcl_GetString(objv[arg])) == 0) {
262 flags = TCL_CANCEL_UNWIND;
266 if (Tcl_GetWideIntFromObj(interp, objv[arg], &id) != TCL_OK) {
271 result = Tcl_GetString(objv[arg]);
275 return ThreadCancel(interp, (Tcl_ThreadId) (size_t) id, result, flags);
277 case THREAD_CREATE: {
283 * Neither joinable nor special script
287 script = "testthread wait"; /* Just enter event loop */
288 } else if (objc == 3) {
290 * Possibly -joinable, then no special script, no joinable, then
294 script = Tcl_GetStringFromObj(objv[2], &len);
296 if ((len > 1) && (script[0] == '-') && (script[1] == 'j') &&
297 (0 == strncmp(script, "-joinable", len))) {
299 script = "testthread wait"; /* Just enter event loop */
302 * Remember the script
307 } else if (objc == 4) {
309 * Definitely a script available, but is the flag -joinable?
312 script = Tcl_GetStringFromObj(objv[2], &len);
313 joinable = ((len > 1) && (script[0] == '-') && (script[1] == 'j')
314 && (0 == strncmp(script, "-joinable", len)));
315 script = Tcl_GetString(objv[3]);
317 Tcl_WrongNumArgs(interp, 2, objv, "?-joinable? ?script?");
320 return ThreadCreate(interp, script, joinable);
324 Tcl_WrongNumArgs(interp, 2, objv, NULL);
331 if (objc == 2 || objc == 3) {
335 * Check if they want the main thread id or the current thread id.
339 idObj = Tcl_NewWideIntObj((Tcl_WideInt)(size_t)Tcl_GetCurrentThread());
341 && strcmp("-main", Tcl_GetString(objv[2])) == 0) {
342 Tcl_MutexLock(&threadMutex);
343 idObj = Tcl_NewWideIntObj((Tcl_WideInt)(size_t)mainThreadId);
344 Tcl_MutexUnlock(&threadMutex);
346 Tcl_WrongNumArgs(interp, 2, objv, NULL);
350 Tcl_SetObjResult(interp, idObj);
353 Tcl_WrongNumArgs(interp, 2, objv, NULL);
361 Tcl_WrongNumArgs(interp, 2, objv, "id");
364 if (Tcl_GetWideIntFromObj(interp, objv[2], &id) != TCL_OK) {
368 result = Tcl_JoinThread((Tcl_ThreadId)(size_t)id, &status);
369 if (result == TCL_OK) {
370 Tcl_SetIntObj(Tcl_GetObjResult(interp), status);
374 sprintf(buf, "%" TCL_LL_MODIFIER "d", id);
375 Tcl_AppendResult(interp, "cannot join thread ", buf, NULL);
381 Tcl_WrongNumArgs(interp, 2, objv, NULL);
384 return ThreadList(interp);
390 if ((objc != 4) && (objc != 5)) {
391 Tcl_WrongNumArgs(interp, 2, objv, "?-async? id script");
395 if (strcmp("-async", Tcl_GetString(objv[2])) != 0) {
396 Tcl_WrongNumArgs(interp, 2, objv, "?-async? id script");
405 if (Tcl_GetWideIntFromObj(interp, objv[arg], &id) != TCL_OK) {
409 script = Tcl_GetString(objv[arg]);
410 return ThreadSend(interp, (Tcl_ThreadId)(size_t)id, script, wait);
414 Tcl_WrongNumArgs(interp, 2, objv, NULL);
417 Tcl_SetObjResult(interp, Tcl_NewIntObj(
418 Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT)));
421 case THREAD_ERRORPROC: {
423 * Arrange for this proc to handle thread death errors.
429 Tcl_WrongNumArgs(interp, 2, objv, "proc");
432 Tcl_MutexLock(&threadMutex);
433 errorThreadId = Tcl_GetCurrentThread();
434 if (errorProcString) {
435 ckfree(errorProcString);
437 proc = Tcl_GetString(objv[2]);
438 errorProcString = ckalloc(strlen(proc) + 1);
439 strcpy(errorProcString, proc);
440 Tcl_MutexUnlock(&threadMutex);
445 Tcl_WrongNumArgs(interp, 2, objv, "");
450 * If the script has been unwound, bail out immediately. This does
451 * not follow the recommended guidelines for how extensions should
452 * handle the script cancellation functionality because this is
453 * not a "normal" extension. Most extensions do not have a command
454 * that simply enters an infinite Tcl event loop. Normal
455 * extensions should not specify the TCL_CANCEL_UNWIND when
456 * calling Tcl_Canceled to check if the command has been canceled.
459 if (Tcl_Canceled(interp,
460 TCL_LEAVE_ERR_MSG | TCL_CANCEL_UNWIND) == TCL_ERROR) {
463 (void) Tcl_DoOneEvent(TCL_ALL_EVENTS);
467 * If we get to this point, we have been canceled by another thread,
468 * which is considered to be an "error".
471 ThreadErrorProc(interp);
478 *----------------------------------------------------------------------
482 * This procedure is invoked to create a thread containing an interp to
483 * run a script. This returns after the thread has started executing.
486 * A standard Tcl result, which is the thread ID.
491 *----------------------------------------------------------------------
497 Tcl_Interp *interp, /* Current interpreter. */
498 const char *script, /* Script to execute */
499 int joinable) /* Flag, joinable thread or not */
504 ctrl.script = script;
505 ctrl.condWait = NULL;
508 joinable = joinable ? TCL_THREAD_JOINABLE : TCL_THREAD_NOFLAGS;
510 Tcl_MutexLock(&threadMutex);
511 if (Tcl_CreateThread(&id, NewTestThread, (ClientData) &ctrl,
512 TCL_THREAD_STACK_DEFAULT, joinable) != TCL_OK) {
513 Tcl_MutexUnlock(&threadMutex);
514 Tcl_AppendResult(interp, "can't create a new thread", NULL);
519 * Wait for the thread to start because it is using something on our stack!
522 Tcl_ConditionWait(&ctrl.condWait, &threadMutex, NULL);
523 Tcl_MutexUnlock(&threadMutex);
524 Tcl_ConditionFinalize(&ctrl.condWait);
525 Tcl_SetObjResult(interp, Tcl_NewWideIntObj((Tcl_WideInt)(size_t)id));
530 *------------------------------------------------------------------------
534 * This routine is the "main()" for a new thread whose task is to execute
535 * a single Tcl script. The argument to this function is a pointer to a
536 * structure that contains the text of the TCL script to be executed.
538 * Space to hold the script field of the ThreadControl structure passed
539 * in as the only argument was obtained from malloc() and must be freed
540 * by this function before it exits. Space to hold the ThreadControl
541 * structure itself is released by the calling function, and the two
542 * condition variables in the ThreadControl structure are destroyed by
543 * the calling function. The calling function will destroy the
544 * ThreadControl structure and the condition variable as soon as
545 * ctrlPtr->condWait is signaled, so this routine must make copies of any
546 * data it might need after that point.
552 * A Tcl script is executed in a new thread.
554 *------------------------------------------------------------------------
559 ClientData clientData)
561 ThreadCtrl *ctrlPtr = clientData;
562 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
564 char *threadEvalScript;
567 * Initialize the interpreter. This should be more general.
570 tsdPtr->interp = Tcl_CreateInterp();
571 result = Tcl_Init(tsdPtr->interp);
572 if (result != TCL_OK) {
573 ThreadErrorProc(tsdPtr->interp);
577 * This is part of the test facility. Initialize _ALL_ test commands for
578 * use by the new thread.
581 result = Tcltest_Init(tsdPtr->interp);
582 if (result != TCL_OK) {
583 ThreadErrorProc(tsdPtr->interp);
587 * Update the list of threads.
590 Tcl_MutexLock(&threadMutex);
591 ListUpdateInner(tsdPtr);
594 * We need to keep a pointer to the alloc'ed mem of the script we are
595 * eval'ing, for the case that we exit during evaluation
598 threadEvalScript = ckalloc(strlen(ctrlPtr->script) + 1);
599 strcpy(threadEvalScript, ctrlPtr->script);
601 Tcl_CreateThreadExitHandler(ThreadExitProc, threadEvalScript);
604 * Notify the parent we are alive.
607 Tcl_ConditionNotify(&ctrlPtr->condWait);
608 Tcl_MutexUnlock(&threadMutex);
614 Tcl_Preserve(tsdPtr->interp);
615 result = Tcl_EvalEx(tsdPtr->interp, threadEvalScript, -1, 0);
616 if (result != TCL_OK) {
617 ThreadErrorProc(tsdPtr->interp);
624 Tcl_DeleteInterp(tsdPtr->interp);
625 Tcl_Release(tsdPtr->interp);
627 Tcl_ExitThread(result);
629 TCL_THREAD_CREATE_RETURN;
633 *------------------------------------------------------------------------
637 * Send a message to the thread willing to hear about errors.
645 *------------------------------------------------------------------------
650 Tcl_Interp *interp) /* Interp that failed */
652 Tcl_Channel errChannel;
653 const char *errorInfo, *argv[3];
655 char buf[TCL_DOUBLE_SPACE+1];
657 sprintf(buf, "%" TCL_LL_MODIFIER "d", (Tcl_WideInt)(size_t)Tcl_GetCurrentThread());
659 errorInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
660 if (errorProcString == NULL) {
661 errChannel = Tcl_GetStdChannel(TCL_STDERR);
662 Tcl_WriteChars(errChannel, "Error from thread ", -1);
663 Tcl_WriteChars(errChannel, buf, -1);
664 Tcl_WriteChars(errChannel, "\n", 1);
665 Tcl_WriteChars(errChannel, errorInfo, -1);
666 Tcl_WriteChars(errChannel, "\n", 1);
668 argv[0] = errorProcString;
671 script = Tcl_Merge(3, argv);
672 ThreadSend(interp, errorThreadId, script, 0);
679 *------------------------------------------------------------------------
683 * Add the thread local storage to the list. This assumes the caller has
684 * obtained the mutex.
690 * Add the thread local storage to its list.
692 *------------------------------------------------------------------------
697 ThreadSpecificData *tsdPtr)
699 if (tsdPtr == NULL) {
700 tsdPtr = TCL_TSD_INIT(&dataKey);
702 tsdPtr->threadId = Tcl_GetCurrentThread();
703 tsdPtr->nextPtr = threadList;
705 threadList->prevPtr = tsdPtr;
707 tsdPtr->prevPtr = NULL;
712 *------------------------------------------------------------------------
716 * Remove the thread local storage from its list. This grabs the mutex to
723 * Remove the thread local storage from its list.
725 *------------------------------------------------------------------------
730 ThreadSpecificData *tsdPtr)
732 if (tsdPtr == NULL) {
733 tsdPtr = TCL_TSD_INIT(&dataKey);
735 Tcl_MutexLock(&threadMutex);
736 if (tsdPtr->prevPtr) {
737 tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
739 threadList = tsdPtr->nextPtr;
741 if (tsdPtr->nextPtr) {
742 tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
744 tsdPtr->nextPtr = tsdPtr->prevPtr = 0;
745 tsdPtr->interp = NULL;
746 Tcl_MutexUnlock(&threadMutex);
750 *------------------------------------------------------------------------
754 * Return a list of threads running Tcl interpreters.
757 * A standard Tcl result.
762 *------------------------------------------------------------------------
768 ThreadSpecificData *tsdPtr;
771 listPtr = Tcl_NewListObj(0, NULL);
772 Tcl_MutexLock(&threadMutex);
773 for (tsdPtr = threadList ; tsdPtr ; tsdPtr = tsdPtr->nextPtr) {
774 Tcl_ListObjAppendElement(interp, listPtr,
775 Tcl_NewWideIntObj((Tcl_WideInt)(size_t)tsdPtr->threadId));
777 Tcl_MutexUnlock(&threadMutex);
778 Tcl_SetObjResult(interp, listPtr);
783 *------------------------------------------------------------------------
787 * Send a script to another thread.
790 * A standard Tcl result.
795 *------------------------------------------------------------------------
800 Tcl_Interp *interp, /* The current interpreter. */
801 Tcl_ThreadId id, /* Thread Id of other interpreter. */
802 const char *script, /* The script to evaluate. */
803 int wait) /* If 1, we block for the result. */
805 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
806 ThreadEvent *threadEventPtr;
807 ThreadEventResult *resultPtr;
809 Tcl_ThreadId threadId = (Tcl_ThreadId) id;
812 * Verify the thread exists.
815 Tcl_MutexLock(&threadMutex);
817 for (tsdPtr = threadList ; tsdPtr ; tsdPtr = tsdPtr->nextPtr) {
818 if (tsdPtr->threadId == threadId) {
824 Tcl_MutexUnlock(&threadMutex);
825 Tcl_AppendResult(interp, "invalid thread id", NULL);
830 * Short circut sends to ourself. Ought to do something with -async, like
831 * run in an idle handler.
834 if (threadId == Tcl_GetCurrentThread()) {
835 Tcl_MutexUnlock(&threadMutex);
836 return Tcl_EvalEx(interp, script,-1,TCL_EVAL_GLOBAL);
840 * Create the event for its event queue.
843 threadEventPtr = ckalloc(sizeof(ThreadEvent));
844 threadEventPtr->script = ckalloc(strlen(script) + 1);
845 strcpy(threadEventPtr->script, script);
847 resultPtr = threadEventPtr->resultPtr = NULL;
849 resultPtr = ckalloc(sizeof(ThreadEventResult));
850 threadEventPtr->resultPtr = resultPtr;
853 * Initialize the result fields.
856 resultPtr->done = NULL;
858 resultPtr->result = NULL;
859 resultPtr->errorInfo = NULL;
860 resultPtr->errorCode = NULL;
863 * Maintain the cleanup list.
866 resultPtr->srcThreadId = Tcl_GetCurrentThread();
867 resultPtr->dstThreadId = threadId;
868 resultPtr->eventPtr = threadEventPtr;
869 resultPtr->nextPtr = resultList;
871 resultList->prevPtr = resultPtr;
873 resultPtr->prevPtr = NULL;
874 resultList = resultPtr;
878 * Queue the event and poke the other thread's notifier.
881 threadEventPtr->event.proc = ThreadEventProc;
882 Tcl_ThreadQueueEvent(threadId, (Tcl_Event *) threadEventPtr,
884 Tcl_ThreadAlert(threadId);
887 Tcl_MutexUnlock(&threadMutex);
892 * Block on the results and then get them.
895 Tcl_ResetResult(interp);
896 while (resultPtr->result == NULL) {
897 Tcl_ConditionWait(&resultPtr->done, &threadMutex, NULL);
901 * Unlink result from the result list.
904 if (resultPtr->prevPtr) {
905 resultPtr->prevPtr->nextPtr = resultPtr->nextPtr;
907 resultList = resultPtr->nextPtr;
909 if (resultPtr->nextPtr) {
910 resultPtr->nextPtr->prevPtr = resultPtr->prevPtr;
912 resultPtr->eventPtr = NULL;
913 resultPtr->nextPtr = NULL;
914 resultPtr->prevPtr = NULL;
916 Tcl_MutexUnlock(&threadMutex);
918 if (resultPtr->code != TCL_OK) {
919 if (resultPtr->errorCode) {
920 Tcl_SetErrorCode(interp, resultPtr->errorCode, NULL);
921 ckfree(resultPtr->errorCode);
923 if (resultPtr->errorInfo) {
924 Tcl_AddErrorInfo(interp, resultPtr->errorInfo);
925 ckfree(resultPtr->errorInfo);
928 Tcl_AppendResult(interp, resultPtr->result, NULL);
929 Tcl_ConditionFinalize(&resultPtr->done);
930 code = resultPtr->code;
932 ckfree(resultPtr->result);
939 *------------------------------------------------------------------------
943 * Cancels a script in another thread.
946 * A standard Tcl result.
951 *------------------------------------------------------------------------
956 Tcl_Interp *interp, /* The current interpreter. */
957 Tcl_ThreadId id, /* Thread Id of other interpreter. */
958 const char *result, /* The result or NULL for default. */
959 int flags) /* Flags for Tcl_CancelEval. */
961 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
963 Tcl_ThreadId threadId = (Tcl_ThreadId) id;
966 * Verify the thread exists.
969 Tcl_MutexLock(&threadMutex);
971 for (tsdPtr = threadList ; tsdPtr ; tsdPtr = tsdPtr->nextPtr) {
972 if (tsdPtr->threadId == threadId) {
978 Tcl_MutexUnlock(&threadMutex);
979 Tcl_AppendResult(interp, "invalid thread id", NULL);
984 * Since Tcl_CancelEval can be safely called from any thread,
988 Tcl_MutexUnlock(&threadMutex);
989 Tcl_ResetResult(interp);
990 return Tcl_CancelEval(tsdPtr->interp,
991 (result != NULL) ? Tcl_NewStringObj(result, -1) : NULL, 0, flags);
995 *------------------------------------------------------------------------
999 * Handle the event in the target thread.
1002 * Returns 1 to indicate that the event was processed.
1005 * Fills out the ThreadEventResult struct.
1007 *------------------------------------------------------------------------
1012 Tcl_Event *evPtr, /* Really ThreadEvent */
1015 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1016 ThreadEvent *threadEventPtr = (ThreadEvent *) evPtr;
1017 ThreadEventResult *resultPtr = threadEventPtr->resultPtr;
1018 Tcl_Interp *interp = tsdPtr->interp;
1020 const char *result, *errorCode, *errorInfo;
1022 if (interp == NULL) {
1024 result = "no target interp!";
1025 errorCode = "THREAD";
1028 Tcl_Preserve(interp);
1029 Tcl_ResetResult(interp);
1030 Tcl_CreateThreadExitHandler(ThreadFreeProc, threadEventPtr->script);
1031 code = Tcl_EvalEx(interp, threadEventPtr->script,-1,TCL_EVAL_GLOBAL);
1032 Tcl_DeleteThreadExitHandler(ThreadFreeProc, threadEventPtr->script);
1033 if (code != TCL_OK) {
1034 errorCode = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY);
1035 errorInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
1037 errorCode = errorInfo = NULL;
1039 result = Tcl_GetStringResult(interp);
1041 ckfree(threadEventPtr->script);
1043 Tcl_MutexLock(&threadMutex);
1044 resultPtr->code = code;
1045 resultPtr->result = ckalloc(strlen(result) + 1);
1046 strcpy(resultPtr->result, result);
1047 if (errorCode != NULL) {
1048 resultPtr->errorCode = ckalloc(strlen(errorCode) + 1);
1049 strcpy(resultPtr->errorCode, errorCode);
1051 if (errorInfo != NULL) {
1052 resultPtr->errorInfo = ckalloc(strlen(errorInfo) + 1);
1053 strcpy(resultPtr->errorInfo, errorInfo);
1055 Tcl_ConditionNotify(&resultPtr->done);
1056 Tcl_MutexUnlock(&threadMutex);
1058 if (interp != NULL) {
1059 Tcl_Release(interp);
1065 *------------------------------------------------------------------------
1069 * This is called from when we are exiting and memory needs
1076 * Clears up mem specified in ClientData
1078 *------------------------------------------------------------------------
1084 ClientData clientData)
1092 *------------------------------------------------------------------------
1094 * ThreadDeleteEvent --
1096 * This is called from the ThreadExitProc to delete memory related
1097 * to events that we put on the queue.
1100 * 1 it was our event and we want it removed, 0 otherwise.
1103 * It cleans up our events in the event queue for this thread.
1105 *------------------------------------------------------------------------
1111 Tcl_Event *eventPtr, /* Really ThreadEvent */
1112 ClientData clientData) /* dummy */
1114 if (eventPtr->proc == ThreadEventProc) {
1115 ckfree(((ThreadEvent *) eventPtr)->script);
1120 * If it was NULL, we were in the middle of servicing the event and it
1124 return (eventPtr->proc == NULL);
1128 *------------------------------------------------------------------------
1132 * This is called when the thread exits.
1138 * It unblocks anyone that is waiting on a send to this thread. It cleans
1139 * up any events in the event queue for this thread.
1141 *------------------------------------------------------------------------
1147 ClientData clientData)
1149 char *threadEvalScript = clientData;
1150 ThreadEventResult *resultPtr, *nextPtr;
1151 Tcl_ThreadId self = Tcl_GetCurrentThread();
1152 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1154 if (tsdPtr->interp != NULL) {
1158 Tcl_MutexLock(&threadMutex);
1160 if (self == errorThreadId) {
1161 if (errorProcString) { /* Extra safety */
1162 ckfree(errorProcString);
1163 errorProcString = NULL;
1168 if (threadEvalScript) {
1169 ckfree(threadEvalScript);
1170 threadEvalScript = NULL;
1172 Tcl_DeleteEvents((Tcl_EventDeleteProc *) ThreadDeleteEvent, NULL);
1174 for (resultPtr = resultList ; resultPtr ; resultPtr = nextPtr) {
1175 nextPtr = resultPtr->nextPtr;
1176 if (resultPtr->srcThreadId == self) {
1178 * We are going away. By freeing up the result we signal to the
1179 * other thread we don't care about the result.
1182 if (resultPtr->prevPtr) {
1183 resultPtr->prevPtr->nextPtr = resultPtr->nextPtr;
1185 resultList = resultPtr->nextPtr;
1187 if (resultPtr->nextPtr) {
1188 resultPtr->nextPtr->prevPtr = resultPtr->prevPtr;
1190 resultPtr->nextPtr = resultPtr->prevPtr = 0;
1191 resultPtr->eventPtr->resultPtr = NULL;
1193 } else if (resultPtr->dstThreadId == self) {
1195 * Dang. The target is going away. Unblock the caller. The result
1196 * string must be dynamically allocated because the main thread is
1197 * going to call free on it.
1200 const char *msg = "target thread died";
1202 resultPtr->result = ckalloc(strlen(msg) + 1);
1203 strcpy(resultPtr->result, msg);
1204 resultPtr->code = TCL_ERROR;
1205 Tcl_ConditionNotify(&resultPtr->done);
1208 Tcl_MutexUnlock(&threadMutex);
1210 #endif /* TCL_THREADS */