From efe824c8141a780f310fc5aef29a013f53fe541c Mon Sep 17 00:00:00 2001 From: kupries Date: Sun, 9 Apr 2000 16:04:16 +0000 Subject: 2000-04-08 Andreas Kupries * Overall change: Definition of a public API for the creation of new threads. * generic/tclInt.h (line 1802f): Removed the definition of 'TclpThreadCreate'. (line 793f) Removed the definition of 'Tcl_ThreadCreateProc'. * generic/tcl.h (line 388f): Readded the definition of 'Tcl_ThreadCreateProc'. Added Win32 stuff send in by David Graveraux to that too (__stdcall, ...). Added macros for the default stacksize and allowed flags. * generic/tcl.decls (line 1356f): Added definition of 'Tcl_CreateThread', slot 393 of the stub table. Two new arguments in the public API, for stacksize and flags. * win/tclWinThrd.c: * mac/tclMacThrd.c: Renamed TclpThreadCreate to Tcl_CreateThread, added handling of the stacksize. Flags are currently ignored. * unix/tclUnixThrd.c: See above, but handles joinable flag. Ignores the specified stacksize if the macro HAVE_PTHREAD_ATTR_SETSTACKSIZE is not defined. * generic/tclThreadTest.c (line 363): See below. * unix/tclUnixNotfy.c (line 210): Adapted to the changes above. Uses default stacksize and no flags now. * unic/tcl.m4 (line 382f): Added a check for 'pthread_attr_setstacksize' to detect platforms not implementing this feature of pthreads. If it is implemented, configure will define the macro HAVE_PTHREAD_ATTR_SETSTACKSIZE (See unix/tclUnixThrd.c too). * doc/Thread.3: Added Tcl_CreateThread and its arguments to the list of described functions. Removed stuff about not providing a public C-API for thread-creation. --- ChangeLog | 41 ++++++++++++++++++++++++++++++++++++++++ doc/Thread.3 | 41 +++++++++++++++++++++++++++++++++++----- generic/tcl.decls | 6 +++++- generic/tcl.h | 26 ++++++++++++++++++++++++- generic/tclDecls.h | 12 +++++++++++- generic/tclInt.h | 10 +--------- generic/tclStubInit.c | 3 ++- generic/tclThreadTest.c | 5 +++-- mac/tclMacThrd.c | 17 ++++++++++++----- unix/tcl.m4 | 5 +++++ unix/tclUnixNotfy.c | 6 +++--- unix/tclUnixThrd.c | 50 ++++++++++++++++++++++++++++++------------------- win/tclWinThrd.c | 9 ++++++--- 13 files changed, 181 insertions(+), 50 deletions(-) diff --git a/ChangeLog b/ChangeLog index bc27e53..e37d797 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,44 @@ +2000-04-08 Andreas Kupries + + * Overall change: Definition of a public API for the creation of + new threads. + + * generic/tclInt.h (line 1802f): Removed the definition of + 'TclpThreadCreate'. (line 793f) Removed the definition of + 'Tcl_ThreadCreateProc'. + + * generic/tcl.h (line 388f): Readded the definition of + 'Tcl_ThreadCreateProc'. Added Win32 stuff send in by David + Graveraux to that too (__stdcall, + ...). Added macros for the default stacksize and allowed flags. + + * generic/tcl.decls (line 1356f): Added definition of + 'Tcl_CreateThread', slot 393 of the stub table. Two new + arguments in the public API, for stacksize and flags. + + * win/tclWinThrd.c: + * mac/tclMacThrd.c: Renamed TclpThreadCreate to Tcl_CreateThread, + added handling of the stacksize. Flags are currently ignored. + + * unix/tclUnixThrd.c: See above, but handles joinable + flag. Ignores the specified stacksize if the macro + HAVE_PTHREAD_ATTR_SETSTACKSIZE is not defined. + + * generic/tclThreadTest.c (line 363): See below. + + * unix/tclUnixNotfy.c (line 210): Adapted to the changes + above. Uses default stacksize and no flags now. + + * unic/tcl.m4 (line 382f): Added a check for + 'pthread_attr_setstacksize' to detect platforms not implementing + this feature of pthreads. If it is implemented, configure will + define the macro HAVE_PTHREAD_ATTR_SETSTACKSIZE (See + unix/tclUnixThrd.c too). + + * doc/Thread.3: Added Tcl_CreateThread and its arguments to the + list of described functions. Removed stuff about not providing a + public C-API for thread-creation. + 2000-04-07 Jeff Hobbs * doc/binary.n: clarified docs on sign extension in binary scan diff --git a/doc/Thread.3 b/doc/Thread.3 index cd50dbc..3ba95b2 100644 --- a/doc/Thread.3 +++ b/doc/Thread.3 @@ -5,7 +5,7 @@ '\" See the file "license.terms" for information on usage and redistribution '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. '\" -'\" RCS: @(#) $Id: Thread.3,v 1.8 2000/04/04 20:28:40 kupries Exp $ +'\" RCS: @(#) $Id: Thread.3,v 1.9 2000/04/09 16:04:17 kupries Exp $ '\" .so man.macros .TH Threads 3 "8.1" Tcl "Tcl Library Procedures" @@ -38,6 +38,9 @@ void .sp void \fBTcl_MutexFinalize\fR(\fImutexPtr\fR) +.sp +int +\fBTcl_CreateThread\fR(\fIidPtr, threadProc, clientData, stackSize, flags\fR) .SH ARGUMENTS .AS Tcl_ThreadDataKey *keyPtr .AP Tcl_Condition *condPtr in @@ -55,6 +58,19 @@ a different block of storage with this key. The size of the thread local storage block. This amount of data is allocated and initialized to zero the first time each thread calls \fBTcl_GetThreadData\fR. +.AP Tcl_ThreadId *idPtr out +The refered storage will contain the id of the newly created thread as +returned by the operating system. +.AP Tcl_ThreadCreateProc threadProc in +This procedure will act as the \fBmain()\fR of the newly created +thread. The specified \fIclientData\fR will be its sole argument. +.AP ClientData clientData in +Arbitrary information. Passed as sole argument to the \fIthreadProc\fR. +.AP int stackSize in +The size of the stack given to the new thread. +.AP int flags in +Bitmask containing flags allowing the caller to modify behaviour of +the new thread. .BE .SH INTRODUCTION Beginning with the 8.1 release, the Tcl core is thread safe, which @@ -70,10 +86,25 @@ the same Tcl interpreter. (However, as was the case in previous releases, a single thread can safely create and use multiple interpreters.) .PP -Tcl provides no special API for creating -threads. When writing multithreaded applications incorporating Tcl, -use the standard POSIX threads APIs on Unix systems and the standard -Win32 threads APIs on Windows systems. +.VB +Tcl does provide \fBTcl_CreateThread\fR for creating threads. The +caller can determine the size of the stack given to the new thread and +modify the behaviour through the supplied \fIflags\fR. The value +\fBTCL_THREAD_STACK_DEFAULT\fR for the \fIstackSize\fR indicates that +the default size as specified by the operating system is to be used +for the new thread. As for the flags, currently are only the values +\fBTCL_THREAD_NOFLAGS\fR and \fBTCL_THREAD_JOINABLE\fR defined. The +first of them invokes the default behaviour with no +specialities. Using the second value marks the new thread as +\fIjoinable\fR. This means that another thread can wait for the such +marked thread to exit and join it. + +Restrictions: On some unix systems the pthread-library does not +contain the functionality to specify the stacksize of a thread. The +specified value for the stacksize is ignored on these systems. Both +Windows and Macintosh currently do not support joinable threads. This +flag value is therefore ignored on these platforms. +.VE .PP Tcl does provide \fBTcl_ExitThread\fR and \fBTcl_FinalizeThread\fR for terminating threads and invoking optional per-thread exit diff --git a/generic/tcl.decls b/generic/tcl.decls index 7c2837d..9d75c87 100644 --- a/generic/tcl.decls +++ b/generic/tcl.decls @@ -10,7 +10,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -# RCS: @(#) $Id: tcl.decls,v 1.32 2000/04/04 20:28:40 kupries Exp $ +# RCS: @(#) $Id: tcl.decls,v 1.33 2000/04/09 16:04:17 kupries Exp $ library tcl @@ -1353,6 +1353,10 @@ declare 391 generic { declare 392 generic { void Tcl_MutexFinalize (Tcl_Mutex *mutex) } +declare 393 generic { + int Tcl_CreateThread (Tcl_ThreadId *idPtr, Tcl_ThreadCreateProc proc, \ + ClientData clientData, int stackSize, int flags) +} ############################################################################## diff --git a/generic/tcl.h b/generic/tcl.h index 8ecd7d6..8bc298a 100644 --- a/generic/tcl.h +++ b/generic/tcl.h @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tcl.h,v 1.64 2000/04/04 20:28:41 kupries Exp $ + * RCS: @(#) $Id: tcl.h,v 1.65 2000/04/09 16:04:17 kupries Exp $ */ #ifndef _TCL @@ -387,6 +387,30 @@ typedef struct Tcl_Trace_ *Tcl_Trace; typedef struct Tcl_Var_ *Tcl_Var; /* + * Definition of the interface to procedures implementing threads. + * A procedure following this definition is given to each call of + * 'Tcl_CreateThread' and will be called as the main fuction of + * the new thread created by that call. + */ + +#ifdef MAC_TCL +typedef pascal void *(Tcl_ThreadCreateProc) _ANSI_ARGS_((ClientData clientData)); +#elif defined __WIN32__ +typedef unsigned __stdcall (Tcl_ThreadCreateProc) _ANSI_ARGS_((ClientData clientData)); +#else +typedef void (Tcl_ThreadCreateProc) _ANSI_ARGS_((ClientData clientData)); +#endif + +/* + * Definition of values for default stacksize and the possible flags to be + * given to Tcl_CreateThread. + */ + +#define TCL_THREAD_STACK_DEFAULT (0) /* Use default size for stack */ +#define TCL_THREAD_NOFLAGS (0000) /* Standard flags, default behaviour */ +#define TCL_THREAD_JOINABLE (0001) /* Mark the thread as joinable */ + +/* * Flag values passed to Tcl_GetRegExpFromObj. */ diff --git a/generic/tclDecls.h b/generic/tclDecls.h index 22c6af4..8cef5c4 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -8,7 +8,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclDecls.h,v 1.33 2000/04/05 00:42:19 welch Exp $ + * RCS: @(#) $Id: tclDecls.h,v 1.34 2000/04/09 16:04:17 kupries Exp $ */ #ifndef _TCLDECLS @@ -1223,6 +1223,11 @@ EXTERN void Tcl_ConditionFinalize _ANSI_ARGS_(( Tcl_Condition * condPtr)); /* 392 */ EXTERN void Tcl_MutexFinalize _ANSI_ARGS_((Tcl_Mutex * mutex)); +/* 393 */ +EXTERN int Tcl_CreateThread _ANSI_ARGS_((Tcl_ThreadId * idPtr, + Tcl_ThreadCreateProc proc, + ClientData clientData, int stackSize, + int flags)); typedef struct TclStubHooks { struct TclPlatStubs *tclPlatStubs; @@ -1683,6 +1688,7 @@ typedef struct TclStubs { int (*tcl_ProcObjCmd) _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[])); /* 390 */ void (*tcl_ConditionFinalize) _ANSI_ARGS_((Tcl_Condition * condPtr)); /* 391 */ void (*tcl_MutexFinalize) _ANSI_ARGS_((Tcl_Mutex * mutex)); /* 392 */ + int (*tcl_CreateThread) _ANSI_ARGS_((Tcl_ThreadId * idPtr, Tcl_ThreadCreateProc proc, ClientData clientData, int stackSize, int flags)); /* 393 */ } TclStubs; #ifdef __cplusplus @@ -3300,6 +3306,10 @@ extern TclStubs *tclStubsPtr; #define Tcl_MutexFinalize \ (tclStubsPtr->tcl_MutexFinalize) /* 392 */ #endif +#ifndef Tcl_CreateThread +#define Tcl_CreateThread \ + (tclStubsPtr->tcl_CreateThread) /* 393 */ +#endif #endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */ diff --git a/generic/tclInt.h b/generic/tclInt.h index 66a0cc1..bd6a314 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclInt.h,v 1.41 2000/04/04 20:28:42 kupries Exp $ + * RCS: @(#) $Id: tclInt.h,v 1.42 2000/04/09 16:04:18 kupries Exp $ */ #ifndef _TCLINT @@ -790,12 +790,6 @@ EXTERN void TclThreadDataKeySet _ANSI_ARGS_((Tcl_ThreadDataKey *keyPtr, VOID *da #define TCL_TSD_INIT(keyPtr) (ThreadSpecificData *)Tcl_GetThreadData((keyPtr), sizeof(ThreadSpecificData)) -#ifdef MAC_TCL -typedef pascal void *(Tcl_ThreadCreateProc) _ANSI_ARGS_((ClientData clientData)); -#else -typedef void (Tcl_ThreadCreateProc) _ANSI_ARGS_((ClientData clientData)); -#endif - /* *---------------------------------------------------------------- * Data structures related to bytecode compilation and execution. @@ -1799,8 +1793,6 @@ EXTERN int TclProcCompileProc _ANSI_ARGS_((Tcl_Interp *interp, EXTERN void TclProcDeleteProc _ANSI_ARGS_((ClientData clientData)); EXTERN int TclProcInterpProc _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, int argc, char **argv)); -EXTERN int TclpThreadCreate _ANSI_ARGS_((Tcl_ThreadId *idPtr, - Tcl_ThreadCreateProc proc, ClientData clientData)); EXTERN VOID * TclpThreadDataKeyGet _ANSI_ARGS_(( Tcl_ThreadDataKey *keyPtr)); EXTERN void TclpThreadDataKeyInit _ANSI_ARGS_(( diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c index 13180f8..78f57ea 100644 --- a/generic/tclStubInit.c +++ b/generic/tclStubInit.c @@ -8,7 +8,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclStubInit.c,v 1.34 2000/04/05 00:42:19 welch Exp $ + * RCS: @(#) $Id: tclStubInit.c,v 1.35 2000/04/09 16:04:18 kupries Exp $ */ #include "tclInt.h" @@ -790,6 +790,7 @@ TclStubs tclStubs = { Tcl_ProcObjCmd, /* 390 */ Tcl_ConditionFinalize, /* 391 */ Tcl_MutexFinalize, /* 392 */ + Tcl_CreateThread, /* 393 */ }; /* !END!: Do not edit above this line. */ diff --git a/generic/tclThreadTest.c b/generic/tclThreadTest.c index d924d4f..d548423 100644 --- a/generic/tclThreadTest.c +++ b/generic/tclThreadTest.c @@ -11,7 +11,7 @@ * 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.7 2000/04/04 20:28:43 kupries Exp $ + * RCS: @(#) $Id: tclThreadTest.c,v 1.8 2000/04/09 16:04:19 kupries Exp $ */ #include "tclInt.h" @@ -359,7 +359,8 @@ TclCreateThread(interp, script) ctrl.flags = 0; Tcl_MutexLock(&threadMutex); - if (TclpThreadCreate(&id, NewThread, (ClientData) &ctrl) != TCL_OK) { + if (Tcl_CreateThread(&id, NewThread, (ClientData) &ctrl, + TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS) != TCL_OK) { Tcl_MutexUnlock(&threadMutex); Tcl_AppendResult(interp,"can't create a new thread",0); ckfree((void*)ctrl.script); diff --git a/mac/tclMacThrd.c b/mac/tclMacThrd.c index 75f5e35..eb58865 100644 --- a/mac/tclMacThrd.c +++ b/mac/tclMacThrd.c @@ -89,7 +89,7 @@ TclMacHaveThreads(void) /* *---------------------------------------------------------------------- * - * TclpThreadCreate -- + * Tcl_CreateThread -- * * This procedure creates a new thread. * @@ -104,27 +104,34 @@ TclMacHaveThreads(void) */ int -TclpThreadCreate(idPtr, proc, clientData) +Tcl_CreateThread(idPtr, proc, clientData, stackSize, flags) Tcl_ThreadId *idPtr; /* Return, the ID of the thread */ Tcl_ThreadCreateProc proc; /* Main() function of the thread */ ClientData clientData; /* The one argument to Main() */ + int stackSize; /* Size of stack for the new thread */ + int flags; /* Flags controlling behaviour of + * the new thread */ { if (!TclMacHaveThreads()) { return TCL_ERROR; } - + + if (stackSize == TCL_THREAD_STACK_DEFAULT) { + stackSize = TCL_MAC_THRD_DEFAULT_STACK; + } + #if TARGET_CPU_68K && TARGET_RT_MAC_CFM { ThreadEntryProcPtr entryProc; entryProc = NewThreadEntryProc(proc); NewThread(kCooperativeThread, entryProc, (void *) clientData, - TCL_MAC_THRD_DEFAULT_STACK, kCreateIfNeeded, NULL, (ThreadID *) idPtr); + stackSize, kCreateIfNeeded, NULL, (ThreadID *) idPtr); } #else NewThread(kCooperativeThread, proc, (void *) clientData, - TCL_MAC_THRD_DEFAULT_STACK, kCreateIfNeeded, NULL, (ThreadID *) idPtr); + stackSize, kCreateIfNeeded, NULL, (ThreadID *) idPtr); #endif if ((ThreadID) *idPtr == kNoThreadID) { return TCL_ERROR; diff --git a/unix/tcl.m4 b/unix/tcl.m4 index 0939379..f5a5488 100644 --- a/unix/tcl.m4 +++ b/unix/tcl.m4 @@ -379,6 +379,11 @@ AC_DEFUN(SC_ENABLE_THREADS, [ fi fi fi + + # Does the pthread-implementation provide + # 'pthread_attr_setstacksize' ? + + AC_CHECK_FUNCS(pthread_attr_setstacksize) else TCL_THREADS=0 AC_MSG_RESULT(no (default)) diff --git a/unix/tclUnixNotfy.c b/unix/tclUnixNotfy.c index 5098f93..7d63228 100644 --- a/unix/tclUnixNotfy.c +++ b/unix/tclUnixNotfy.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclUnixNotfy.c,v 1.8 2000/04/04 20:28:43 kupries Exp $ + * RCS: @(#) $Id: tclUnixNotfy.c,v 1.9 2000/04/09 16:04:20 kupries Exp $ */ #include "tclInt.h" @@ -207,8 +207,8 @@ Tcl_InitNotifier() Tcl_MutexLock(¬ifierMutex); if (notifierCount == 0) { - if (TclpThreadCreate(¬ifierThread, NotifierThreadProc, NULL) - != TCL_OK) { + if (Tcl_CreateThread(¬ifierThread, NotifierThreadProc, NULL, + TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS) != TCL_OK) { panic("Tcl_InitNotifier: unable to start notifier thread"); } } diff --git a/unix/tclUnixThrd.c b/unix/tclUnixThrd.c index 53489bc..ca3e592 100644 --- a/unix/tclUnixThrd.c +++ b/unix/tclUnixThrd.c @@ -59,7 +59,7 @@ static pthread_mutex_t *allocLockPtr = &allocLock; /* *---------------------------------------------------------------------- * - * TclpThreadCreate -- + * Tcl_CreateThread -- * * This procedure creates a new thread. * @@ -74,37 +74,49 @@ static pthread_mutex_t *allocLockPtr = &allocLock; */ int -TclpThreadCreate(idPtr, proc, clientData) +Tcl_CreateThread(idPtr, proc, clientData, stackSize, flags) Tcl_ThreadId *idPtr; /* Return, the ID of the thread */ Tcl_ThreadCreateProc proc; /* Main() function of the thread */ ClientData clientData; /* The one argument to Main() */ + int stackSize; /* Size of stack for the new thread */ + int flags; /* Flags controlling behaviour of + * the new thread */ { pthread_attr_t attr; int result; -#ifdef TCL_THREAD_STACK_MIN - size_t size; -#endif pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); +#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE + if (stackSize != TCL_THREAD_STACK_DEFAULT) { + pthread_attr_setstacksize(&attr, (size_t) stackSize); #ifdef TCL_THREAD_STACK_MIN - /* - * Certain systems define a thread stack size that by default is - * too small for many operations. The user has the option of - * defining TCL_THREAD_STACK_MIN to a value large enough to work - * for their needs. This would look like (for 128K min stack): - * make MEM_DEBUG_FLAGS=-DTCL_THREAD_STACK_MIN=131072L - * - * This solution is not optimal, as we should allow the user to - * specify a size at runtime, but we don't want to slow this function - * down, and that would still leave the main thread at the default. - */ - result = pthread_attr_getstacksize(&attr, &size); - if (!result && (size < TCL_THREAD_STACK_MIN)) { - pthread_attr_setstacksize(&attr, (size_t) TCL_THREAD_STACK_MIN); + } else { + /* + * Certain systems define a thread stack size that by default is + * too small for many operations. The user has the option of + * defining TCL_THREAD_STACK_MIN to a value large enough to work + * for their needs. This would look like (for 128K min stack): + * make MEM_DEBUG_FLAGS=-DTCL_THREAD_STACK_MIN=131072L + * + * This solution is not optimal, as we should allow the user to + * specify a size at runtime, but we don't want to slow this function + * down, and that would still leave the main thread at the default. + */ + + size_t size; + result = pthread_attr_getstacksize(&attr, &size); + if (!result && (size < TCL_THREAD_STACK_MIN)) { + pthread_attr_setstacksize(&attr, (size_t) TCL_THREAD_STACK_MIN); + } +#endif } #endif + if (! (flags & TCL_THREAD_JOINABLE)) { + pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); + } + if (pthread_create((pthread_t *)idPtr, &attr, (void * (*)())proc, (void *)clientData) && diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c index e449731..5f430b2 100644 --- a/win/tclWinThrd.c +++ b/win/tclWinThrd.c @@ -100,7 +100,7 @@ static void FinalizeConditionEvent(ClientData data); /* *---------------------------------------------------------------------- * - * TclpThreadCreate -- + * Tcl_CreateThread -- * * This procedure creates a new thread. * @@ -115,14 +115,17 @@ static void FinalizeConditionEvent(ClientData data); */ int -TclpThreadCreate(idPtr, proc, clientData) +Tcl_CreateThread(idPtr, proc, clientData, stackSize, flags) Tcl_ThreadId *idPtr; /* Return, the ID of the thread */ Tcl_ThreadCreateProc proc; /* Main() function of the thread */ ClientData clientData; /* The one argument to Main() */ + int stackSize; /* Size of stack for the new thread */ + int flags; /* Flags controlling behaviour of + * the new thread */ { unsigned long code; - code = _beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE) proc, + code = _beginthreadex(NULL, stackSize, (LPTHREAD_START_ROUTINE) proc, (void *)clientData, 0, (unsigned *)idPtr); if (code == 0) { return TCL_ERROR; -- cgit v0.12