Tcl Source Code

Artifact [be5ac89d9a]
Login

Artifact be5ac89d9a71345ed85857e35970599c3efe50f1:

Attachment "unixStackCheck.v6.diff" to ticket [746378ffff] added by dkf 2004-06-23 07:28:15.
? unixStackCheck.v5.diff
? unixStackCheck.v6.diff
? unixStackCheckV4.diff
? unix/autom4te.cache
? unix/dltest.marker
Index: ChangeLog
===================================================================
RCS file: /cvsroot/tcl/tcl/ChangeLog,v
retrieving revision 1.2099
diff -b -u -r1.2099 ChangeLog
--- ChangeLog	22 Jun 2004 22:08:36 -0000	1.2099
+++ ChangeLog	23 Jun 2004 00:19:32 -0000
@@ -1,3 +1,10 @@
+2004-06-23  Donal K. Fellows  <[email protected]>
+
+	* unix/tclUnixInit.c (GetStackSize, TclpCheckStackSpace): 
+	* unix/tclUnixThrd.c (TclpThreadGetStackSize): Added code to check
+	whether the C stack is about to be exceeded, from [Patch 746378]
+	by Joe Mistachkin but with substantial revisions.
+
 2004-06-22  Kevin Kenny  <[email protected]>
 
 	* generic/tclEvent.c (NewThreadProc): Fixed broken build on
Index: generic/tclInt.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclInt.h,v
retrieving revision 1.166
diff -b -u -r1.166 tclInt.h
--- generic/tclInt.h	22 Jun 2004 13:08:59 -0000	1.166
+++ generic/tclInt.h	23 Jun 2004 00:19:34 -0000
@@ -1901,6 +1901,7 @@
 EXTERN void		TclpThreadDataKeySet _ANSI_ARGS_((
 			    Tcl_ThreadDataKey *keyPtr, VOID *data));
 EXTERN void		TclpThreadExit _ANSI_ARGS_((int status));
+EXTERN int		TclpThreadGetStackSize _ANSI_ARGS_((void));
 EXTERN void		TclRememberCondition _ANSI_ARGS_((Tcl_Condition *mutex));
 EXTERN void		TclRememberDataKey _ANSI_ARGS_((Tcl_ThreadDataKey *mutex));
 EXTERN VOID             TclRememberJoinableThread _ANSI_ARGS_((Tcl_ThreadId id));
Index: tests/stack.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/stack.test,v
retrieving revision 1.17
diff -b -u -r1.17 stack.test
--- tests/stack.test	22 Jun 2004 19:41:25 -0000	1.17
+++ tests/stack.test	23 Jun 2004 00:19:34 -0000
@@ -56,12 +56,10 @@
 } {too many nested evaluations (infinite loop?)}
 
 # Make sure that there is enough stack to run regexp even if we're
-# close to the recursion limit. [Bug 947070]
-
-test stack-3.1 {enough room for regexp near recursion limit} \
-    -constraints { win } \
-    -setup {
-	set ::limit [interp recursionlimit {} 10000]
+# close to the recursion limit. [Bug 947070] [Patch 746378]
+test stack-3.1 {enough room for regexp near recursion limit} -setup {
+    set limit [interp recursionlimit {} 10000]
+    set depth 0
 	proc a { max } {
 	    if { [info level] < $max } {
 		set ::depth [info level]
@@ -70,16 +68,14 @@
 		regexp {^ ?} x
 	    }
 	}
-    } -body {
-	set ::depth 0
+} -body {
 	catch { a 10001 }
 	set depth2 $depth
-	catch { a $::depth }
-	expr { $depth2 - $depth }
-    } -cleanup {
-	interp recursionlimit {} $::limit
+    list [a $depth] [expr { $depth2 - $depth }]
+} -cleanup {
+    interp recursionlimit {} $limit
 	rename a {}
-    } -result {1}
+} -result {1 1}
 
 # cleanup
 ::tcltest::cleanupTests
Index: unix/configure
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/configure,v
retrieving revision 1.113
diff -b -u -r1.113 configure
--- unix/configure	18 Jun 2004 15:29:23 -0000	1.113
+++ unix/configure	23 Jun 2004 00:19:34 -0000
@@ -3280,6 +3280,124 @@
 fi
 done
 
+
+
+for ac_func in pthread_attr_get_np pthread_getattr_np
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+#line $LINENO "configure"
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+         { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+	echo "$as_me:$LINENO: checking for pthread_getattr_np declaration" >&5
+echo $ECHO_N "checking for pthread_getattr_np declaration... $ECHO_C" >&6
+	if test "${tcl_cv_grep_pthread_getattr_np+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+#line $LINENO "configure"
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <pthread.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "pthread_getattr_np" >/dev/null 2>&1; then
+  tcl_cv_grep_pthread_getattr_np=present
+else
+  tcl_cv_grep_pthread_getattr_np=missing
+fi
+rm -f conftest*
+
+fi
+
+	echo "$as_me:$LINENO: result: $tcl_cv_grep_pthread_getattr_np" >&5
+echo "${ECHO_T}$tcl_cv_grep_pthread_getattr_np" >&6
+	if test $tcl_cv_grep_pthread_getattr_np = missing ; then
+	    cat >>confdefs.h <<\_ACEOF
+#define GETATTRNP_NOT_DECLARED 1
+_ACEOF
+
+	fi
 	LIBS=$ac_saved_libs
 
 for ac_func in readdir_r
Index: unix/tcl.m4
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tcl.m4,v
retrieving revision 1.116
diff -b -u -r1.116 tcl.m4
--- unix/tcl.m4	18 Jun 2004 15:29:23 -0000	1.116
+++ unix/tcl.m4	23 Jun 2004 00:19:34 -0000
@@ -469,6 +469,16 @@
 	ac_saved_libs=$LIBS
 	LIBS="$LIBS $THREADS_LIBS"
 	AC_CHECK_FUNCS(pthread_attr_setstacksize)
+	AC_CHECK_FUNCS(pthread_attr_get_np pthread_getattr_np)
+	AC_MSG_CHECKING([for pthread_getattr_np declaration])
+	AC_CACHE_VAL(tcl_cv_grep_pthread_getattr_np,
+	    AC_EGREP_HEADER(pthread_getattr_np, pthread.h,
+		tcl_cv_grep_pthread_getattr_np=present,
+		tcl_cv_grep_pthread_getattr_np=missing))
+	AC_MSG_RESULT($tcl_cv_grep_pthread_getattr_np)
+	if test $tcl_cv_grep_pthread_getattr_np = missing ; then
+	    AC_DEFINE(GETATTRNP_NOT_DECLARED)
+	fi
 	LIBS=$ac_saved_libs
 	AC_CHECK_FUNCS(readdir_r)
     else
Index: unix/tclUnixInit.c
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclUnixInit.c,v
retrieving revision 1.45
diff -b -u -r1.45 tclUnixInit.c
--- unix/tclUnixInit.c	18 Jun 2004 15:26:00 -0000	1.45
+++ unix/tclUnixInit.c	23 Jun 2004 00:19:34 -0000
@@ -14,10 +14,12 @@
 #include <CoreFoundation/CoreFoundation.h>
 #endif
 #include "tclInt.h"
+#include <stddef.h>
 #include <locale.h>
 #ifdef HAVE_LANGINFO
 #include <langinfo.h>
 #endif
+#include <sys/resource.h>
 #if defined(__FreeBSD__)
 #   include <floatingpoint.h>
 #endif
@@ -28,6 +30,69 @@
 #   endif
 #endif
 
+/*
+ * Define this if you want to revert to the old behavior of
+ * never checking the stack.
+ */
+#undef TCL_NO_STACK_CHECK
+
+/*
+ * Define this if you want to see a lot of output regarding
+ * stack checking.
+ */
+#undef TCL_DEBUG_STACK_CHECK
+
+/*
+ * Values used to compute how much space is really available for Tcl's
+ * use for the stack.
+ *
+ * NOTE: Now I have some idea why the maximum stack size must be
+ * divided by 64 on FreeBSD with threads enabled to get a reasonably
+ * correct value.
+ *
+ * The getrlimit() function is documented to return the maximum stack
+ * size in bytes. However, with threads enabled, the pthread library
+ * does bad things to the stack size limits.  First, the limits cannot
+ * be changed. Second, they appear to be reported incorrectly by a
+ * factor of about 64.
+ *
+ * The defines below may need to be adjusted if more platforms have
+ * this broken behavior with threads enabled.
+ */
+
+#if defined(__FreeBSD__)
+#   define TCL_MAGIC_STACK_DIVISOR	64
+#   define TCL_RESERVED_STACK_PAGES	3
+#endif
+
+#ifndef TCL_MAGIC_STACK_DIVISOR
+#define TCL_MAGIC_STACK_DIVISOR		1
+#endif
+#ifndef TCL_RESERVED_STACK_PAGES
+#define TCL_RESERVED_STACK_PAGES	8
+#endif
+
+/*
+ * Thread specific data for stack checking.
+ */
+
+#ifndef TCL_NO_STACK_CHECK
+typedef struct ThreadSpecificData {
+    int *outerVarPtr;		/* The "outermost" stack frame pointer for
+				 * this thread. */
+    int initialised;		/* Have we found what the stack size was? */
+    int stackDetermineResult;	/* What happened when we did that? */
+    size_t stackSize;		/* The size of the current stack. */
+} ThreadSpecificData;
+static Tcl_ThreadDataKey dataKey;
+#endif /* TCL_NO_STACK_CHECK */
+
+#ifdef TCL_DEBUG_STACK_CHECK
+#define STACK_DEBUG(args) printf args
+#else
+#define STACK_DEBUG(args) (void)0
+#endif /* TCL_DEBUG_STACK_CHECK */
+
 /* Used to store the encoding used for binary files */
 static Tcl_Encoding binaryEncoding = NULL;
 /* Has the basic library path encoding issue been fixed */
@@ -138,8 +203,13 @@
     {NULL, NULL}
 };
 
+#ifndef TCL_NO_STACK_CHECK
+static int		GetStackSize _ANSI_ARGS_((size_t *stackSizePtr));
+#endif /* TCL_NO_STACK_CHECK */
 #ifdef HAVE_CFBUNDLE
-static int Tcl_MacOSXGetLibraryPath(Tcl_Interp *interp, int maxPathLen, char *tclLibPath);
+static int		MacOSXGetLibraryPath _ANSI_ARGS((
+			    Tcl_Interp *interp, int maxPathLen,
+			    char *tclLibPath));
 #endif /* HAVE_CFBUNDLE */
 
 
@@ -201,7 +271,7 @@
 
 #ifdef __FreeBSD__
     fpsetround(FP_RN);
-    fpsetmask(0L);
+    (void) fpsetmask(0L);
 #endif
 
 #if defined(__bsdi__) && (_BSDI_VERSION > 199501)
@@ -348,7 +418,6 @@
      *	  (e.g. /usr/src/tcl8.4.0/unix/solaris-sparc/../../../tcl8.4.0/library)
      */
      
-
      /*
       * The variable path holds an absolute path.  Take care not to
       * overwrite pathv[0] since that might produce a relative path.
@@ -442,7 +511,7 @@
 #ifdef HAVE_CFBUNDLE
     char tclLibPath[MAXPATHLEN + 1];
     
-    if (Tcl_MacOSXGetLibraryPath(NULL, MAXPATHLEN, tclLibPath) == TCL_OK) {
+    if (MacOSXGetLibraryPath(NULL, MAXPATHLEN, tclLibPath) == TCL_OK) {
         str = tclLibPath;
     } else
 #endif /* HAVE_CFBUNDLE */
@@ -740,7 +809,7 @@
 #ifdef HAVE_CFBUNDLE
     char tclLibPath[MAXPATHLEN + 1];
     
-    if (Tcl_MacOSXGetLibraryPath(interp, MAXPATHLEN, tclLibPath) == TCL_OK) {
+    if (MacOSXGetLibraryPath(interp, MAXPATHLEN, tclLibPath) == TCL_OK) {
         CONST char *str;
         Tcl_DString ds;
         CFBundleRef bundleRef;
@@ -954,18 +1023,182 @@
 int
 TclpCheckStackSpace()
 {
+#ifdef TCL_NO_STACK_CHECK
+
     /*
-     * This function is unimplemented on Unix platforms.
+     * This function was normally unimplemented on Unix platforms and
+     * this implements old behavior, i.e. no stack checking performed.
      */
 
     return 1;
+
+#else
+
+    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+				/* Most variables are actually in a
+				 * thread-specific data block to minimise the
+				 * impact on the stack. */
+    register ptrdiff_t stackUsed;
+    int localVar;		/* Reference to somewhere on the local stack.
+				 * This is declared last so it's as "deep" as
+				 * possible. */
+
+    if (tsdPtr == NULL) {
+        /* this should probably be a panic(). */
+        Tcl_Panic("failed to get thread specific stack check data");
+    }
+
+    /*
+     * The first time through, we record the "outermost" stack frame.
+     */
+
+    if (tsdPtr->outerVarPtr == NULL) {
+	tsdPtr->outerVarPtr = &localVar;
+    }
+
+    if (tsdPtr->initialised == 0) {
+	/*
+	 * We appear to have not computed the stack size before.
+	 * Attempt to retrieve it from either the current thread or,
+	 * failing that, the process accounting limit.  Note that we
+	 * assume that stack sizes do not change throughout the
+	 * lifespan of the thread/process; this is almost always true.
+	 */
+
+	tsdPtr->stackDetermineResult = GetStackSize(&tsdPtr->stackSize);
+	tsdPtr->initialised = 1;
+    }
+
+    switch (tsdPtr->stackDetermineResult) {
+    case TCL_BREAK:
+	STACK_DEBUG(("skipping stack check with failure\n"));
+	return 0;
+    case TCL_CONTINUE:
+	STACK_DEBUG(("skipping stack check with success\n"));
+	return 1;
+    }
+
+    /*
+     * Sanity check to see if somehow the stack started going the
+     * other way.
+     */
+
+    if (&localVar > tsdPtr->outerVarPtr) {
+	stackUsed = (char *)&localVar - (char *)tsdPtr->outerVarPtr;
+    } else {
+	stackUsed = (char *)tsdPtr->outerVarPtr - (char *)&localVar;
+    }
+
+    /*
+     * Now we perform the actual check.  Are we about to blow
+     * our stack frame?
+     */
+
+    if (stackUsed < (ptrdiff_t) tsdPtr->stackSize) {
+	STACK_DEBUG(("stack OK\tin:%p\tout:%p\tuse:%04X\tmax:%04X\n",
+		&localVar, tsdPtr->outerVarPtr, stackUsed, tsdPtr->stackSize));
+	return 1;
+    } else {
+	STACK_DEBUG(("stack OVERFLOW\tin:%p\tout:%p\tuse:%04X\tmax:%04X\n",
+		&localVar, tsdPtr->outerVarPtr, stackUsed, tsdPtr->stackSize));
+	return 0;
+    }
+#endif /* TCL_NO_STACK_CHECK */
 }
 
-#ifdef HAVE_CFBUNDLE
 /*
  *----------------------------------------------------------------------
  *
- * Tcl_MacOSXGetLibraryPath --
+ * GetStackSize --
+ *
+ *	Discover what the stack size for the current thread/process
+ *	actually is.  Expects to only ever be called once per thread
+ *	and then only at a point when there is a reasonable amount of
+ *	space left on the current stack; TclpCheckStackSpace is called
+ *	sufficiently frequently that that is true.
+ *
+ * Results:
+ *	TCL_OK if the stack space was discovered, TCL_BREAK if the
+ *	stack space was undiscoverable in a way that stack checks
+ *	should fail, and TCL_CONTINUE if the stack space was
+ *	undiscoverable in a way that stack checks should succeed.
+ *
+ * Side effects:
+ *	None
+ *
+ *----------------------------------------------------------------------
+ */
+
+#ifndef TCL_NO_STACK_CHECK
+static int
+GetStackSize(stackSizePtr)
+    size_t *stackSizePtr;
+{
+    size_t rawStackSize;
+    struct rlimit rLimit;	/* The result from getrlimit(). */
+
+#ifdef TCL_THREADS
+    rawStackSize = (size_t) TclpThreadGetStackSize();
+    if (rawStackSize == (size_t) -1) {
+	/*
+	 * Some kind of confirmed error?!
+	 */
+	return TCL_BREAK;
+    }
+    if (rawStackSize > 0) {
+	goto finalSanityCheck;
+    }
+
+    /*
+     * If we have zero or an error, try the system limits
+     * instead. After all, the pthread documentation states that
+     * threads should always be bound by the system stack size limit
+     * in any case.
+     */
+#endif /* TCL_THREADS */
+
+    if (getrlimit(RLIMIT_STACK, &rLimit) != 0) {
+	/*
+	 * getrlimit() failed, just fail the whole thing.
+	 */
+	return TCL_BREAK;
+    }
+    if (rLimit.rlim_cur == RLIM_INFINITY) {
+	/*
+	 * Limit is "infinite"; there is no stack limit.
+	 */
+	return TCL_CONTINUE;
+    }
+    rawStackSize = rLimit.rlim_cur;
+
+    /*
+     * Final sanity check on the determined stack size.  If we fail
+     * this, assume there are bogus values about and that we can't
+     * actually figure out what the stack size really is.
+     */
+
+#ifdef TCL_THREADS /* Stop warning... */
+  finalSanityCheck:
+#endif
+    if (rawStackSize <= 0) {
+	return TCL_CONTINUE;
+    }
+
+    /*
+     * Calculate a stack size with a safety margin.
+     */
+
+    *stackSizePtr = (rawStackSize / TCL_MAGIC_STACK_DIVISOR)
+	    - (getpagesize() * TCL_RESERVED_STACK_PAGES);
+
+    return TCL_OK;
+}
+#endif /* TCL_NO_STACK_CHECK */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MacOSXGetLibraryPath --
  *
  *	If we have a bundle structure for the Tcl installation,
  *	then check there first to see if we can find the libraries
@@ -979,7 +1212,10 @@
  *
  *----------------------------------------------------------------------
  */
-static int Tcl_MacOSXGetLibraryPath(Tcl_Interp *interp, int maxPathLen, char *tclLibPath)
+
+#ifdef HAVE_CFBUNDLE
+static int
+MacOSXGetLibraryPath(Tcl_Interp *interp, int maxPathLen, char *tclLibPath)
 {
     int foundInFramework = TCL_ERROR;
     if (strcmp(defaultLibraryDir, "@TCL_IN_FRAMEWORK@") == 0) {
@@ -989,4 +1225,3 @@
     return foundInFramework;
 }
 #endif /* HAVE_CFBUNDLE */
-
Index: unix/tclUnixPort.h
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclUnixPort.h,v
retrieving revision 1.37
diff -b -u -r1.37 tclUnixPort.h
--- unix/tclUnixPort.h	27 May 2004 13:18:55 -0000	1.37
+++ unix/tclUnixPort.h	23 Jun 2004 00:19:34 -0000
@@ -553,7 +553,7 @@
  */
 
 #ifdef TCL_THREADS
-#include <pthread.h>
+#   include <pthread.h>
 typedef pthread_mutex_t TclpMutex;
 EXTERN void	TclpMutexInit _ANSI_ARGS_((TclpMutex *mPtr));
 EXTERN void	TclpMutexLock _ANSI_ARGS_((TclpMutex *mPtr));
@@ -562,25 +562,36 @@
 EXTERN struct tm *     	TclpLocaltime(CONST time_t *);
 EXTERN struct tm *     	TclpGmtime(CONST time_t *);
 EXTERN char *          	TclpInetNtoa(struct in_addr);
-#define readdir(x)	TclpReaddir(x)
+#   define readdir(x)	TclpReaddir(x)
 /* #define localtime(x)	TclpLocaltime(x)
  * #define gmtime(x)	TclpGmtime(x)    */
-#undef inet_ntoa
-#define inet_ntoa(x)	TclpInetNtoa(x)
-#undef TclOSreaddir
-#define TclOSreaddir(x) TclpReaddir(x)
-#ifdef MAC_OSX_TCL
+#   undef inet_ntoa
+#   define inet_ntoa(x)	TclpInetNtoa(x)
+#   undef TclOSreaddir
+#   define TclOSreaddir(x) TclpReaddir(x)
+#   ifdef MAC_OSX_TCL
 /* 
  * On Mac OS X, realpath is currently not
  * thread safe, c.f. SF bug # 711232.
  */
-#define NO_REALPATH
-#endif
+#	define NO_REALPATH
+#   endif
+#   ifdef HAVE_PTHREAD_ATTR_GET_NP
+#	include <pthread_np.h>
+#	define TclpPthreadGetAttrs	pthread_attr_get_np
+#   else
+#	ifdef HAVE_PTHREAD_GETATTR_NP
+#	    define TclpPthreadGetAttrs	pthread_getattr_np
+#	    ifdef GETATTRNP_NOT_DECLARED
+EXTERN int pthread_getattr_np _ANSI_ARGS_((pthread_t, pthread_attr_t *));
+#	    endif
+#	endif /* HAVE_PTHREAD_GETATTR_NP */
+#   endif /* HAVE_PTHREAD_ATTR_GET_NP */
 #else
 typedef int TclpMutex;
-#define	TclpMutexInit(a)
-#define	TclpMutexLock(a)
-#define	TclpMutexUnlock(a)
+#   define	TclpMutexInit(a)
+#   define	TclpMutexLock(a)
+#   define	TclpMutexUnlock(a)
 #endif /* TCL_THREADS */
 
 #endif /* _TCLUNIXPORT */
Index: unix/tclUnixThrd.c
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclUnixThrd.c,v
retrieving revision 1.31
diff -b -u -r1.31 tclUnixThrd.c
--- unix/tclUnixThrd.c	22 Jun 2004 13:09:01 -0000	1.31
+++ unix/tclUnixThrd.c	23 Jun 2004 00:19:34 -0000
@@ -199,6 +199,55 @@
 }
 #endif /* TCL_THREADS */
 
+#ifdef TCL_THREADS
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpThreadGetStackSize --
+ *
+ *	This procedure returns the size of the current thread's stack.
+ *
+ * Results:
+ *	Stack size (in bytes?) or -1 for error or 0 for undeterminable.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclpThreadGetStackSize()
+{
+#if defined(HAVE_PTHREAD_SETSTACKSIZE) && defined(TclpPthreadGetAttrs)
+    pthread_attr_t threadAttr;	/* This will hold the thread attributes for
+				 * the current thread. */
+    size_t stackSize;
+
+    if (pthread_attr_init(&threadAttr) != 0) {
+	return -1;
+    }
+    if (TclpPthreadGetAttrs(pthread_self(), &threadAttr) != 0) {
+	pthread_attr_destroy(&threadAttr);
+	return -1;
+    }
+    if (pthread_attr_getstacksize(&threadAttr, &stackSize) != 0) {
+	pthread_attr_destroy(&threadAttr);
+	return -1;
+    }
+    pthread_attr_destroy(&threadAttr);
+    return (int) stackSize;
+#else
+    /*
+     * Cannot determine the real stack size of this thread.  The
+     * caller might want to try looking at the process accounting
+     * limits instead.
+     */
+    return 0;
+#endif
+}
+#endif /* TCL_THREADS */
+
 /*
  *----------------------------------------------------------------------
  *