Experiment after suggestion by Zoran Vasiljevic at comp.lang.tcl to send a script from *any* thread to a thread with a Tcl interpreter. This can be useful for extension developers in C that uses a library that executes callbacks in other threads than the main thread where the Tcl interpreter lives. Most code duplicated from the thread package.
/*
*
*
*
*
*
*/ #if TARGET_API_MAC_CARBON # include <Tcl/tcl.h> #else # include "tcl.h" #endif #ifndef TCL_TSD_INIT #define TCL_TSD_INIT(keyPtr) \ (ThreadSpecificData*)Tcl_GetThreadData((keyPtr),sizeof(ThreadSpecificData)) #endif /*
*/ typedef struct ThreadSpecificData { Tcl_Interp *interp; /* Interp to evaluate scripts */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; /*
*/ typedef struct ThreadEvent { Tcl_Event event; /* Must be first */ struct ThreadSendData *sendData; /* See below */ } ThreadEvent; typedef int (ThreadSendProc) _ANSI_ARGS_((Tcl_Interp*, ClientData)); typedef void (ThreadSendFree) _ANSI_ARGS_((ClientData)); static ThreadSendProc ThreadSendEval; /* Does a regular Tcl_Eval */ /*
*/ typedef struct ThreadSendData { ThreadSendProc *execProc; /* Func to exec in remote thread */ ClientData clientData; /* Ptr to pass to send function */ ThreadSendFree *freeProc; /* Function to free client data */ } ThreadSendData; static void ThreadSend _ANSI_ARGS_((Tcl_ThreadId targetId, ThreadSendData *send)); static int ThreadEventProc _ANSI_ARGS_((Tcl_Event *evPtr, int mask)); static void ThreadFreeProc _ANSI_ARGS_((ClientData clientData)); /* *---------------------------------------------------------------------- *
*
*
*
* *---------------------------------------------------------------------- */ void XThread_RegisterThread(Tcl_Interp *interp) { ThreadSpecificData* tsdPtr = TCL_TSD_INIT(&dataKey); tsdPtr->interp = interp; Tcl_Preserve((ClientData)interp); } /* *---------------------------------------------------------------------- *
*
*
*
* *---------------------------------------------------------------------- */ void XThread_UnregisterThread() { ThreadSpecificData* tsdPtr = TCL_TSD_INIT(&dataKey); if (tsdPtr->interp) { Tcl_Release((ClientData)tsdPtr->interp); tsdPtr->interp = NULL; } } /* *---------------------------------------------------------------------- *
*
*
*
*
* *---------------------------------------------------------------------- */ void XThread_EvalInThread(Tcl_ThreadId threadId, const char *script, int flags) { ThreadSendData *sendPtr; int len = strlen(script); /*
*/ sendPtr = (ThreadSendData*)Tcl_Alloc(sizeof(ThreadSendData)); sendPtr->execProc = ThreadSendEval; sendPtr->freeProc = (ThreadSendFree*)Tcl_Free; sendPtr->clientData = (ClientData)strcpy(Tcl_Alloc(1+len), script); ThreadSend((Tcl_ThreadId)threadId, sendPtr); } /* *---------------------------------------------------------------------- *
*
*
*
* *---------------------------------------------------------------------- */ static void ThreadSend(targetId, send) Tcl_ThreadId targetId; /* Thread Id of other thread. */ ThreadSendData *send; /* Pointer to structure with work to do */ { ThreadEvent *eventPtr; /*
*/ eventPtr = (ThreadEvent*)Tcl_Alloc(sizeof(ThreadEvent)); eventPtr->sendData = send; eventPtr->event.proc = ThreadEventProc; /*
*/ Tcl_ThreadQueueEvent(targetId, (Tcl_Event*)eventPtr, TCL_QUEUE_TAIL); Tcl_ThreadAlert(targetId); return; } /* *---------------------------------------------------------------------- *
*
*
*
* *---------------------------------------------------------------------- */ static int ThreadSendEval(interp, clientData) Tcl_Interp *interp; ClientData clientData; { ThreadSendData *sendPtr = (ThreadSendData*)clientData; char *script = (char*)sendPtr->clientData; return Tcl_EvalEx(interp, script, -1, TCL_EVAL_GLOBAL); } /* *---------------------------------------------------------------------- *
*
*
*
* *---------------------------------------------------------------------- */ static int ThreadEventProc(evPtr, mask) Tcl_Event *evPtr; /* Really ThreadEvent */ int mask; { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); ThreadEvent *eventPtr = (ThreadEvent*)evPtr; ThreadSendData *sendPtr = eventPtr->sendData; if (tsdPtr->interp != NULL) { if (sendPtr) { Tcl_CreateThreadExitHandler(ThreadFreeProc, (ClientData)sendPtr); (*sendPtr->execProc)(tsdPtr->interp, (ClientData)sendPtr); Tcl_DeleteThreadExitHandler(ThreadFreeProc, (ClientData)sendPtr); } } ThreadFreeProc((ClientData)sendPtr); return 1; } /* *---------------------------------------------------------------------- *
*
*
*
* *---------------------------------------------------------------------- */ static void ThreadFreeProc(clientData) ClientData clientData; { ThreadSendData *anyPtr = (ThreadSendData*)clientData; if (anyPtr) { if (anyPtr->clientData) { (*anyPtr->freeProc)(anyPtr->clientData); } Tcl_Free((char*)anyPtr); } }