Tcl Source Code

Artifact [5bb7a6cd7e]
Login

Artifact 5bb7a6cd7ebcb3251969c7b382a00e81d80ce87a:

Attachment "newatfork.diff" to ticket [923072ffff] added by davidw 2004-06-17 03:59:59.
Index: generic/tclThread.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclThread.c,v
retrieving revision 1.7
diff -u -r1.7 tclThread.c
--- generic/tclThread.c	23 Apr 2004 07:21:36 -0000	1.7
+++ generic/tclThread.c	17 May 2004 20:58:28 -0000
@@ -494,7 +494,128 @@
     keyRecord.num = 0;
 #endif
 }
+#if 1 && defined(TCL_THREADS)
 
+/*
+ *-----------------------------------------------------------------------------
+ *
+ *  TclpAtforkLock --
+ *
+ * 	pthread_atfork callback called just before fork() in the
+ * 	parent process.  Locks mutexes which are subsequently unlocked
+ * 	in both the parent and child.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Locks mutexes.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+TclpAtforkLock(void)
+{
+    Tcl_Mutex *mutexPtr;
+    int i;
+
+    /* locks must be held in locking order (if any) */
+    TclpMasterLock();
+    TclpInitLock();
+    for (i=0 ; i<mutexRecord.num ; i++) {
+	mutexPtr = (Tcl_Mutex *)mutexRecord.list[i];
+	if (mutexPtr != NULL) {
+	    Tcl_MutexLock(mutexPtr);
+	}
+    }
+    Tcl_MutexLock(Tcl_GetAllocMutex());
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ *  TclpAtForkParent --
+ *
+ * 	This is the pthread_atfork callback in the parent process,
+ * 	after the fork.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Calls TclpAtforkUnlock to unlock mutexes and initialize the
+ *	notifier system.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void TclpAtForkParent(void)
+{
+    TclpAtforkUnlock();
+    /* FIXME Hooks can go here. */
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ *  TclpAtForkChild --
+ *
+ * 	This is the pthread_atfork callback in the child process,
+ * 	after the fork.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	Calls TclpAtforkUnlock to unlock mutexes and initialize the
+ *	notifier system.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void TclpAtForkChild(void)
+{
+    TclpAtforkUnlock();
+    /* FIXME Hooks can go here. */
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ *  TclpAtforkUnlock --
+ *
+ * 	Unlocks mutexes locked in TclpAtforkLock, and starts the
+ * 	notifier subsystem with TclInitNotifier.
+ *
+ * Results:
+ *	None.
+ *
+ * Side Effects:
+ *	TclInitNotifier creates a notifier thread.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+TclpAtforkUnlock(void)
+{
+    Tcl_Mutex *mutexPtr;
+    int i;
+
+    /* locks must be released in same order as in atfork_lock() */
+    TclpMasterUnlock();
+    TclpInitUnlock();
+    for (i=0; i<mutexRecord.num ; i++) {
+	mutexPtr = (Tcl_Mutex *)mutexRecord.list[i];
+	if (mutexPtr != NULL) {
+	    Tcl_MutexUnlock(mutexPtr);
+	}
+    }
+    Tcl_MutexUnlock(Tcl_GetAllocMutex());
+    TclInitNotifier();
+}
+#endif
 
 /*
  *----------------------------------------------------------------------
Index: tests/thread.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/thread.test,v
retrieving revision 1.10
diff -u -r1.10 thread.test
--- tests/thread.test	2 May 2000 22:02:36 -0000	1.10
+++ tests/thread.test	17 May 2004 20:58:31 -0000
@@ -19,10 +19,12 @@
 
 # Some tests require the testthread command
 
-set ::tcltest::testConstraints(testthread) \
-	[expr {[info commands testthread] != {}}]
+#set ::tcltest::testConstraints(testthread) \
+#	[expr {[info commands testthread] != {}}]
 
-if {$::tcltest::testConstraints(testthread)} {
+tcltest::constraints::cset testthread [expr {[info commands testthread] != {}}]
+
+if {[::tcltest::constraints::cset testthread]} {
 
     testthread errorproc ThreadError
 
Index: unix/tcl.m4
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tcl.m4,v
retrieving revision 1.114
diff -u -r1.114 tcl.m4
--- unix/tcl.m4	18 Mar 2004 19:01:43 -0000	1.114
+++ unix/tcl.m4	17 May 2004 20:58:48 -0000
@@ -469,6 +469,7 @@
 	ac_saved_libs=$LIBS
 	LIBS="$LIBS $THREADS_LIBS"
 	AC_CHECK_FUNCS(pthread_attr_setstacksize)
+	AC_CHECK_FUNCS(pthread_atfork)
 	LIBS=$ac_saved_libs
 	AC_CHECK_FUNCS(readdir_r)
     else
Index: unix/tclAppInit.c
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclAppInit.c,v
retrieving revision 1.11
diff -u -r1.11 tclAppInit.c
--- unix/tclAppInit.c	31 May 2002 22:20:22 -0000	1.11
+++ unix/tclAppInit.c	17 May 2004 20:58:48 -0000
@@ -15,6 +15,7 @@
  */
 
 #include "tcl.h"
+#include "tclPort.h"
 
 #ifdef TCL_TEST
 
Index: unix/tclUnixInit.c
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclUnixInit.c,v
retrieving revision 1.41
diff -u -r1.41 tclUnixInit.c
--- unix/tclUnixInit.c	7 Apr 2004 22:04:30 -0000	1.41
+++ unix/tclUnixInit.c	17 May 2004 20:58:49 -0000
@@ -937,6 +937,15 @@
     return result;
 }
 
+#if defined(TCL_THREADS) && defined(HAVE_PTHREAD_ATFORK)
+static pthread_once_t	once = PTHREAD_ONCE_INIT;
+ 
+static void
+InitAtfork(void)
+{
+    pthread_atfork(TclpAtforkLock, TclpAtForkParent, TclpAtForkChild);
+}
+#endif
 /*
  *----------------------------------------------------------------------
  *
@@ -967,12 +976,17 @@
 	    return (TCL_ERROR);
 	};
     }
-    
+
+#if defined(TCL_THREADS) && defined(HAVE_PTHREAD_ATFORK)
+    pthread_once(&once, InitAtfork);
+#endif
+
     pathPtr = TclGetLibraryPath();
     if (pathPtr == NULL) {
 	pathPtr = Tcl_NewObj();
     }
     Tcl_SetVar2Ex(interp, "tcl_libPath", NULL, pathPtr, TCL_GLOBAL_ONLY);
+
     return Tcl_Eval(interp, initScript);
 }
 
Index: unix/tclUnixPort.h
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclUnixPort.h,v
retrieving revision 1.35
diff -u -r1.35 tclUnixPort.h
--- unix/tclUnixPort.h	6 Apr 2004 22:25:57 -0000	1.35
+++ unix/tclUnixPort.h	17 May 2004 20:58:50 -0000
@@ -574,6 +574,10 @@
  */
 #define NO_REALPATH
 #endif
+EXTERN void		TclpAtforkLock(void);
+EXTERN void		TclpAtforkUnlock(void);
+EXTERN void		TclpAtForkChild(void);
+EXTERN void		TclpAtForkParent(void);
 #else
 typedef int TclpMutex;
 #define	TclpMutexInit(a)
Index: unix/tclUnixTest.c
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclUnixTest.c,v
retrieving revision 1.16
diff -u -r1.16 tclUnixTest.c
--- unix/tclUnixTest.c	6 Apr 2004 22:25:57 -0000	1.16
+++ unix/tclUnixTest.c	17 May 2004 20:58:50 -0000
@@ -81,6 +81,10 @@
 			    Tcl_Interp *interp, int argc, CONST char **argv));
 static int		TestgotsigCmd _ANSI_ARGS_((ClientData dummy,
 			    Tcl_Interp *interp, int argc, CONST char **argv));
+static int		TestforkCmd _ANSI_ARGS_((ClientData dummy,
+			    Tcl_Interp *interp, int argc, CONST char **argv));
+static int		TestwaitpidCmd _ANSI_ARGS_((ClientData dummy,
+			    Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
 static void 		AlarmHandler _ANSI_ARGS_(());
 
 /*
@@ -120,6 +124,10 @@
             (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
     Tcl_CreateCommand(interp, "testgotsig", TestgotsigCmd,
             (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+    Tcl_CreateCommand(interp, "testfork", TestforkCmd,
+            (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+    Tcl_CreateObjCommand(interp, "testwaitpid", TestwaitpidCmd,
+			 (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
     return TCL_OK;
 }
 
@@ -702,3 +710,80 @@
     gotsig = "0";
     return TCL_OK;
 }
+
+/*
+ *----------------------------------------------------------------------
+ * TestforkCmd --
+ *
+ * 	Implementation of fork().
+ *
+ * Results:
+ *	In the parent process, the child process' pid, in the child, 0.
+ *
+ * Side Effects:
+ *	Creates a new process.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TestforkCmd(clientData, interp, argc, argv)
+    ClientData clientData;		/* Not used. */
+    Tcl_Interp *interp;			/* Current interpreter. */
+    int argc;				/* Number of arguments. */
+    CONST char **argv;			/* Argument strings. */
+{
+    pid_t pid;
+
+    pid = fork();
+    if (pid < 0) {
+	Tcl_AppendResult(interp, "fork failed: ",
+			 Tcl_PosixError(interp),
+			 (char *)NULL);
+	return TCL_ERROR;
+    }
+    Tcl_SetIntObj (Tcl_GetObjResult (interp), (int)pid);
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ * TestwaitpidCmd --
+ *
+ * 	Implementation of waitpid().
+ *
+ * Results:
+ *	Returns the PID of the process we waited on.
+ *
+ * Side Effects:
+ *	Waits untill the process in question exits.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TestwaitpidCmd(clientData, interp, objc, objv)
+    ClientData clientData;		/* Not used. */
+    Tcl_Interp *interp;			/* Current interpreter. */
+    int objc;				/* Number of arguments. */
+    Tcl_Obj *CONST objv[];			/* Argument strings. */
+{
+    pid_t pid = 0;
+    pid_t returnPid = 0;
+    int status = 0;
+
+    if (Tcl_GetIntFromObj(interp, objv[1], &pid) != TCL_OK) {
+	return TCL_ERROR;
+    }
+
+    returnPid = waitpid(pid, &status, 0);
+    if (returnPid < 0) {
+	Tcl_AppendResult(interp, "wait failed: ",
+			 Tcl_PosixError(interp),
+			 (char *)NULL);
+	return TCL_ERROR;
+    }
+    Tcl_SetObjResult(interp, Tcl_NewIntObj(returnPid));
+
+    return TCL_OK;
+}
Index: unix/tclUnixThrd.c
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclUnixThrd.c,v
retrieving revision 1.28
diff -u -r1.28 tclUnixThrd.c
--- unix/tclUnixThrd.c	23 Apr 2004 07:21:37 -0000	1.28
+++ unix/tclUnixThrd.c	17 May 2004 20:58:51 -0000
@@ -928,6 +928,35 @@
 
 #ifdef TCL_THREADS
 /*
+ * Additional functions to handle broken pthread behavior on fork().
+ * From the pthread_atfork man page:
+ *	To  understand  the purpose of pthread_atfork, recall that
+ *	fork(2)  duplicates  the  whole  memory  space,  including
+ *	mutexes in their current locking state, but only the call­
+ *	ing thread: other threads are not  running  in  the  child
+ *	process.   The  mutexes  are not usable after the fork and
+ *	must be initialized with pthread_mutex_init in  the  child
+ *	process.   This is a limitation of the current implementa­
+ *	tion and might or might not be present in future versions.
+ */
+#if 0
+/* this is called in parent before the fork() */
+void
+TclpAtforkLock(void)
+{
+    /* locks must be held in locking order (if any) */
+    Tcl_MutexLock((Tcl_Mutex *)&allocLockPtr);
+}
+
+/* this is called in both parent and child after the fork() */
+void
+TclpAtforkUnlock(void)
+{
+    /* locks must be released in same order as in atfork_lock() */
+    Tcl_MutexUnlock((Tcl_Mutex *)&allocLockPtr);
+}
+#endif
+/*
  * Additions by AOL for specialized thread memory allocator.
  */
 #ifdef USE_THREAD_ALLOC