tclthreadtest.c
来自「tcl是工具命令语言」· C语言 代码 · 共 1,030 行 · 第 1/2 页
C
1,030 行
/* * tclThreadTest.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. * * 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.16 2002/01/26 01:10:08 dgp Exp $ */#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. */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" */} ThreadSpecificData;static Tcl_ThreadDataKey dataKey;/* * This list is used to list all threads that have interpreters. * This is protected by threadMutex. */static struct ThreadSpecificData *threadList;/* * The following bit-values are legal for the "flags" field of the * ThreadSpecificData structure. */#define TP_Dying 0x001 /* This thread is being cancelled *//* * 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. */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. */} ThreadCtrl;/* * This is the event used to send scripts to other threads. */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. */} ThreadEvent;typedef struct ThreadEventResult { Tcl_Condition done; /* Signaled when the script completes */ int code; /* Return value of Tcl_Eval */ char *result; /* Result from the script */ char *errorInfo; /* Copy of errorInfo variable */ char *errorCode; /* Copy of errorCode variable */ Tcl_ThreadId srcThreadId; /* Id of sending thread, in case it dies */ Tcl_ThreadId dstThreadId; /* Id of target thread, in case it dies */ struct ThreadEvent *eventPtr; /* Back pointer */ struct ThreadEventResult *nextPtr; /* List for cleanup */ struct ThreadEventResult *prevPtr;} ThreadEventResult;static ThreadEventResult *resultList;/* * This is for simple error handling when a thread script exits badly. */static Tcl_ThreadId errorThreadId;static char *errorProcString;/* * 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 DLLEXPORTEXTERN 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 DLLIMPORTTcl_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));/* *---------------------------------------------------------------------- * * TclThread_Init -- * * Initialize the test thread command. * * Results: * TCL_OK if the package was properly initialized. * * Side effects: * Add the "testthread" command to the interp. * *---------------------------------------------------------------------- */intTclThread_Init(interp) 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; } return TCL_OK;}/* *---------------------------------------------------------------------- * * Tcl_ThreadObjCmd -- * * This procedure is invoked to process the "testthread" Tcl command. * See the user documentation for details on what it does. * * thread create ?-joinable? ?script? * thread send id ?-async? script * thread exit * thread info id * thread names * thread wait * thread errorproc proc * thread join id * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */ /* ARGSUSED */intTcl_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. */{ 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}; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option ?args?"); return TCL_ERROR; } 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. */ if (tsdPtr->interp == NULL) { Tcl_MutexLock(&threadMutex); tsdPtr->interp = interp; ListUpdateInner(tsdPtr); Tcl_CreateThreadExitHandler(ThreadExitProc, NULL); Tcl_MutexUnlock(&threadMutex); } switch ((enum options)option) { case THREAD_CREATE: { char *script; int joinable, len; if (objc == 2) { /* Neither joinable nor special script */ joinable = 0; script = "testthread wait"; /* Just enter the event loop */ } else if (objc == 3) { /* Possibly -joinable, then no special script, * no joinable, then its a script. */ 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 ? */ 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; } return TclCreateThread(interp, script, joinable); } case THREAD_EXIT: { if (objc > 2) { Tcl_WrongNumArgs(interp, 1, objv, NULL); return TCL_ERROR; } ListRemove(NULL); Tcl_ExitThread(0); return TCL_OK; } case THREAD_ID: if (objc == 2) { Tcl_Obj *idObj = Tcl_NewLongObj((long)Tcl_GetCurrentThread()); Tcl_SetObjResult(interp, idObj); return TCL_OK; } 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; } 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; } case THREAD_NAMES: { if (objc > 2) { Tcl_WrongNumArgs(interp, 2, objv, NULL); return TCL_ERROR; } return TclThreadList(interp); } 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) { return TCL_ERROR; } arg++; script = Tcl_GetString(objv[arg]); return TclThreadSend(interp, (Tcl_ThreadId) id, script, wait); } case THREAD_WAIT: { while (1) { (void) Tcl_DoOneEvent(TCL_ALL_EVENTS); } } case THREAD_ERRORPROC: { /* * Arrange for this proc to handle thread death errors. */ char *proc; if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "errorproc 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; } } return TCL_OK;}/* *---------------------------------------------------------------------- * * TclCreateThread -- * * This procedure is invoked to create a thread containing an interp to * run a script. This returns after the thread has started executing. * * Results: * A standard Tcl result, which is the thread ID. * * Side effects: * Create a thread. * *---------------------------------------------------------------------- */ /* ARGSUSED */intTclCreateThread(interp, script, joinable) Tcl_Interp *interp; /* Current interpreter. */ char *script; /* Script to execute */ int joinable; /* Flag, joinable thread or not */{ ThreadCtrl ctrl; Tcl_ThreadId id; ctrl.script = script; ctrl.condWait = NULL; ctrl.flags = 0; joinable = joinable ? TCL_THREAD_JOINABLE : TCL_THREAD_NOFLAGS; Tcl_MutexLock(&threadMutex); if (Tcl_CreateThread(&id, NewTestThread, (ClientData) &ctrl, TCL_THREAD_STACK_DEFAULT, joinable) != TCL_OK) { Tcl_MutexUnlock(&threadMutex); Tcl_AppendResult(interp,"can't create a new thread",0); ckfree((void*)ctrl.script); return TCL_ERROR; } /* * Wait for the thread to start because it is using something on our stack! */ Tcl_ConditionWait(&ctrl.condWait, &threadMutex, NULL); Tcl_MutexUnlock(&threadMutex); Tcl_ConditionFinalize(&ctrl.condWait); Tcl_SetObjResult(interp, Tcl_NewLongObj((long)id)); return TCL_OK;}/* *------------------------------------------------------------------------ * * 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. * * Results: * none * * Side effects: * A TCL script is executed in a new thread. * *------------------------------------------------------------------------ */Tcl_ThreadCreateTypeNewTestThread(clientData) ClientData clientData;{ ThreadCtrl *ctrlPtr = (ThreadCtrl*)clientData; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); int result; char *threadEvalScript; /* * Initialize the interpreter. This should be more general. */ tsdPtr->interp = Tcl_CreateInterp(); result = Tcl_Init(tsdPtr->interp); result = TclThread_Init(tsdPtr->interp); /* * Update the list of threads. */ 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 */ threadEvalScript = (char *) ckalloc(strlen(ctrlPtr->script)+1); strcpy(threadEvalScript, ctrlPtr->script); Tcl_CreateThreadExitHandler(ThreadExitProc, (ClientData) threadEvalScript); /* * Notify the parent we are alive. */ Tcl_ConditionNotify(&ctrlPtr->condWait); Tcl_MutexUnlock(&threadMutex); /* * Run the script. */ Tcl_Preserve((ClientData) tsdPtr->interp); result = Tcl_Eval(tsdPtr->interp, threadEvalScript); if (result != TCL_OK) { ThreadErrorProc(tsdPtr->interp); }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?