tclThreadTest.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:27k
源码类别:

通讯编程

开发平台:

Visual C++

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