? 1 ? NT386.1 ? NT386.2 ? t.c ? src/Csupport/Common/t.c ? src/Csupport/Common/test_hand.cod ? src/Csupport/Common/test_hand.i ? src/Csupport/Common/test_hand.pdb ? src/Csupport/Common/vc90.pdb Index: src/runtime/common/RTCollector.m3 =================================================================== RCS file: /usr/cvs/cm3/m3-libs/m3core/src/runtime/common/RTCollector.m3,v retrieving revision 1.88 diff -u -r1.88 RTCollector.m3 --- src/runtime/common/RTCollector.m3 15 Dec 2009 19:52:08 -0000 1.88 +++ src/runtime/common/RTCollector.m3 17 Mar 2010 18:00:01 -0000 @@ -130,6 +130,7 @@ started *) VAR startedBackground := FALSE; +VAR backgroundThread: Thread.T; PROCEDURE StartBackgroundCollection () = VAR start := FALSE; @@ -144,7 +145,7 @@ RTOS.UnlockHeap(); END; IF start THEN - EVAL Thread.Fork(NEW(Thread.Closure, apply := BackgroundThread)); + backgroundThread := Thread.Fork(NEW(Thread.Closure, apply := BackgroundThread)); END; END StartBackgroundCollection; @@ -152,6 +153,7 @@ started *) VAR startedForeground := FALSE; +VAR foregroundThread: Thread.T; PROCEDURE StartForegroundCollection () = VAR start := FALSE; @@ -166,7 +168,7 @@ RTOS.UnlockHeap(); END; IF start THEN - EVAL Thread.Fork(NEW(Thread.Closure, apply := ForegroundThread)); + foregroundThread := Thread.Fork(NEW(Thread.Closure, apply := ForegroundThread)); END; END StartForegroundCollection; @@ -2032,6 +2034,7 @@ (* This is WeakRef.FromRef, which returns a new weak ref for an object. *) VAR startedWeakCleaner := FALSE; +VAR weakCleaner: Thread.T; PROCEDURE WeakRefFromRef (r: REFANY; p: WeakRefCleanUpProc := NIL): WeakRef = VAR @@ -2076,7 +2079,7 @@ RTOS.UnlockHeap(); END; IF start THEN - EVAL Thread.Fork(NEW(Thread.Closure, apply := WeakCleaner)); + weakCleaner := Thread.Fork(NEW(Thread.Closure, apply := WeakCleaner)); END; RETURN result; END WeakRefFromRef; @@ -2771,8 +2774,18 @@ (*** INITIALIZATION ***) +PROCEDURE ForkChild() = + BEGIN + Thread.ReforkThreadAfterProcessFork(weakCleaner); + Thread.ReforkThreadAfterProcessFork(backgroundThread); + Thread.ReforkThreadAfterProcessFork(foregroundThread); + END ForkChild; + PROCEDURE Init () = + VAR r: INTEGER; BEGIN + r := Thread.PThreadAtFork((*prepare*)NIL, (*parent*)NIL, ForkChild); + <* ASSERT r = 0 *> IF RTParams.IsPresent("paranoidgc") THEN InstallSanityCheck(); END; IF RTParams.IsPresent("nogc") THEN disableCount := 1; END; IF RTParams.IsPresent("noincremental") THEN incremental := FALSE; END; Index: src/thread/Common/Thread.i3 =================================================================== RCS file: /usr/cvs/cm3/m3-libs/m3core/src/thread/Common/Thread.i3,v retrieving revision 1.2 diff -u -r1.2 Thread.i3 --- src/thread/Common/Thread.i3 25 Oct 2009 04:06:35 -0000 1.2 +++ src/thread/Common/Thread.i3 17 Mar 2010 18:00:01 -0000 @@ -116,5 +116,14 @@ REQUIRES MEMBER(m, LL) ENSURES LL' = DELETE(LL, m) *> -END Thread. +TYPE PThreadForkHandler = PROCEDURE(); + +<*EXTERNAL "Thread__PThreadAtFork"*> +PROCEDURE PThreadAtFork(prepare, parent, child: PThreadForkHandler):INTEGER; +(* Only does anything with pthreads. *) + +PROCEDURE ForkProcessAndAllThreads(): INTEGER; +PROCEDURE ReforkThreadAfterProcessFork(VAR thread:T); + +END Thread. Index: src/thread/POSIX/ThreadPosix.m3 =================================================================== RCS file: /usr/cvs/cm3/m3-libs/m3core/src/thread/POSIX/ThreadPosix.m3,v retrieving revision 1.63 diff -u -r1.63 ThreadPosix.m3 --- src/thread/POSIX/ThreadPosix.m3 27 Nov 2009 21:13:15 -0000 1.63 +++ src/thread/POSIX/ThreadPosix.m3 17 Mar 2010 18:00:01 -0000 @@ -1418,6 +1418,23 @@ handlerStack := frame; END PopEFrame; +PROCEDURE ForkProcessAndAllThreads(): INTEGER + VAR pid: INTEGER; + BEGIN + LOOP + pid := Unix.fork(); + IF pid >= 0 THEN + RETURN pid; (* parent or child *) + ELSIF Cerrno.GetErrno() # Uerror.EAGAIN THEN + RETURN pid; (* error *) + END; + END; + END ForkProcessAndAllThreads; + +PROCEDURE ReforkThreadAfterProcessFork(VAR thread:T) = + BEGIN + END ReforkThreadAfterProcessFork; + VAR debug := RTParams.IsPresent ("debugthreads"); BEGIN Index: src/thread/POSIX/ThreadPosixC.c =================================================================== RCS file: /usr/cvs/cm3/m3-libs/m3core/src/thread/POSIX/ThreadPosixC.c,v retrieving revision 1.32 diff -u -r1.32 ThreadPosixC.c --- src/thread/POSIX/ThreadPosixC.c 16 Dec 2009 12:21:36 -0000 1.32 +++ src/thread/POSIX/ThreadPosixC.c 17 Mar 2010 18:00:01 -0000 @@ -278,6 +278,17 @@ p(&c[0], &c[1]); #endif } + +typedef void (*PThreadForkHandler)(void); + +ptrdiff_t +Thread__PThreadAtFork( + PThreadForkHandler prepare, + PThreadForkHandler parent, + PThreadForkHandler child) +{ + return 0; +} #ifdef __cplusplus } /* extern "C" */ Index: src/thread/PTHREAD/ThreadPThread.m3 =================================================================== RCS file: /usr/cvs/cm3/m3-libs/m3core/src/thread/PTHREAD/ThreadPThread.m3,v retrieving revision 1.232 diff -u -r1.232 ThreadPThread.m3 --- src/thread/PTHREAD/ThreadPThread.m3 25 Feb 2010 08:31:36 -0000 1.232 +++ src/thread/PTHREAD/ThreadPThread.m3 17 Mar 2010 18:00:01 -0000 @@ -9,7 +9,7 @@ RTCollectorSRC, RTError, RTHeapRep, RTIO, RTParams, RTPerfTool, RTProcess, ThreadEvent, Time, Unix, Utime, Word, Usched, - Uerror, Uexec; + Uerror, Uexec, Thread; FROM Compiler IMPORT ThisFile, ThisLine; FROM Ctypes IMPORT int; IMPORT RuntimeError AS RTE; @@ -55,7 +55,8 @@ stackbase: ADDRESS := NIL; (* LL = activeMu; stack base for GC *) context: ADDRESS := NIL; (* LL = activeMu *) state := ActState.Started; (* LL = activeMu *) - slot := 0; (* LL = slotMu; index in slots *) + slot: CARDINAL := 0; (* LL = slotMu; index in slots *) + processForked: BOOLEAN := FALSE; floatState : FloatMode.ThreadState; (* per-thread floating point state *) heapState : RTHeapRep.ThreadState; (* per-thread heap state *) END; @@ -260,8 +261,8 @@ (*------------------------------------------------------------------ Self ---*) VAR (* LL = slotMu *) - n_slotted := 0; - next_slot := 1; + n_slotted: CARDINAL; + next_slot: CARDINAL; (* NOTE: we don't use slots[0] *) slots: REF ARRAY OF T; (* NOTE: we don't use slots[0] *) PROCEDURE InitActivations (me: Activation) = @@ -270,10 +271,10 @@ me.next := me; me.prev := me; SetActivation(me); - <* ASSERT next_slot = 1 *> (* no threads created yet *) - <* ASSERT slots = NIL *> (* no threads created yet *) - <* ASSERT n_slotted = 0 *> (* no threads created yet *) - <* ASSERT allThreads = NIL *> (* no threads created yet *) + (* Explicitly (re)initialize to handle fork(). *) + next_slot := 1; (* no threads created yet *) + slots := NIL; (* no threads created yet *) + n_slotted := 0; (* no threads created yet *) allThreads := me; FloatMode.InitThread(me.floatState); END InitActivations; @@ -503,6 +504,95 @@ RETURN t; END Fork; +PROCEDURE ForkPrepare() = +(* This function is passed to pthread_atfork + * and is called in the parent process before fork(). + * + * It suspends all threads. + * It marks all threads as not "reforked", i.e. + * not existant in the child process. + * + * A marker is needed so that library owners + * of worker threads can decide to restart their + * threads or not, independent of if the application + * calls ForkProcessAndAllThreads or not (such as to restart its + * own threads that it might not have a convenient + * handle on). (e.g. cvsup). + *) + VAR me := GetActivation(); + act: Activation; + BEGIN + act := me.next; + WHILE act # me DO + act.processForked := TRUE; + act := act.next; + END; + me.processForked := FALSE; + SuspendOthers(); + END ForkPrepare; + +PROCEDURE ForkParent() = +(* This function is passed to pthread_atfork + * and is called in the parent process after fork(). + * It resumes all threads. + *) + BEGIN + ResumeOthers(); + END ForkParent; + +PROCEDURE ForkChild() = +(* This function is passed to pthread_atfork + * and carefully reinitializes our state. + * We are left with just one thread running, + * the current thread, and its stackbase + * is preserved from the parent. + *) + BEGIN + InitWithStackBase(GetActivation().stackbase); + ResumeOthers(); + END ForkChild; + +PROCEDURE ReforkThreadAfterProcessFork(VAR thread: T) = +(* This function is intended to relaunch threads in a + * child process after fork(). + *) + BEGIN + IF thread # NIL + AND thread.closure # NIL + AND thread.joined = FALSE + AND thread.act # NIL + AND thread.act.processForked = TRUE THEN + (* Note that we create a whole new thread. We could reuse. *) + thread.act.processForked := FALSE; (* Don't let it be reforked again. *) + thread := Fork(thread.closure); + END; + END ReforkThreadAfterProcessFork; + +PROCEDURE ForkProcessAndAllThreads(): INTEGER = +(* This function calls Unix.fork() and then + * recreates all the parent's (Modula-3) threads in the child. + * This is sometimes useful for programs that fork + * without exec, such as cvsup. + *) + VAR pid: INTEGER; + parentSlots := slots; (* capture before the reinitialize *) + BEGIN + LOOP + pid := Unix.fork(); + IF pid > 0 THEN + RETURN pid; (* parent *) + ELSIF pid = 0 THEN + EXIT; (* child *) + ELSIF Cerrno.GetErrno() # Uerror.EAGAIN THEN + RETURN pid; (* error *) + END; + END; + FOR i := 1 TO LAST(parentSlots^) DO + ReforkThreadAfterProcessFork(parentSlots[i]); + END; + RETURN 0; (* child *) + END ForkProcessAndAllThreads; + PROCEDURE XJoin (self: Activation; t: T; alertable: BOOLEAN): REFANY RAISES {Alerted} = BEGIN @@ -1294,18 +1384,18 @@ (*-------------------------------------------------------- Initialization ---*) -PROCEDURE Init ()= +PROCEDURE InitWithStackBase (stackbase: ADDRESS) = VAR self: T; me: Activation; BEGIN - InitC(ADR(self)); + InitC(stackbase); me := NEW(Activation, mutex := pthread_mutex_new(), cond := pthread_cond_new()); InitActivations(me); - me.stackbase := ADR(self); (* not quite accurate but hopefully ok *) + me.stackbase := stackbase; IF me.mutex = NIL OR me.cond = NIL THEN Die(ThisLine(), "Thread initialization failed."); END; @@ -1322,6 +1412,14 @@ IF RTParams.IsPresent("foregroundgc") THEN RTCollectorSRC.StartForegroundCollection(); END; + END InitWithStackBase; + +PROCEDURE Init ()= + VAR r: INTEGER; + BEGIN + r := Thread.PThreadAtFork(ForkPrepare, ForkParent, ForkChild); + IF r # 0 THEN DieI(ThisLine(), r) END; + InitWithStackBase(ADR(r)); (* not quite accurate but hopefully ok *) END Init; (*------------------------------------------------------------- collector ---*) Index: src/thread/PTHREAD/ThreadPThreadC.c =================================================================== RCS file: /usr/cvs/cm3/m3-libs/m3core/src/thread/PTHREAD/ThreadPThreadC.c,v retrieving revision 1.123 diff -u -r1.123 ThreadPThreadC.c --- src/thread/PTHREAD/ThreadPThreadC.c 25 Feb 2010 08:31:36 -0000 1.123 +++ src/thread/PTHREAD/ThreadPThreadC.c 17 Mar 2010 18:00:01 -0000 @@ -379,6 +379,17 @@ M3WRAP1(int, pthread_mutex_lock, pthread_mutex_t*) M3WRAP1(int, pthread_mutex_unlock, pthread_mutex_t*) +typedef void (*PThreadForkHandler)(void); + +ptrdiff_t +Thread__PThreadAtFork( + PThreadForkHandler prepare, + PThreadForkHandler parent, + PThreadForkHandler child) +{ + return pthread_atfork(prepare, parent, child); +} + void InitC(int *bottom) { Index: src/thread/WIN32/ThreadWin32.m3 =================================================================== RCS file: /usr/cvs/cm3/m3-libs/m3core/src/thread/WIN32/ThreadWin32.m3,v retrieving revision 1.209 diff -u -r1.209 ThreadWin32.m3 --- src/thread/WIN32/ThreadWin32.m3 16 Dec 2009 09:26:34 -0000 1.209 +++ src/thread/WIN32/ThreadWin32.m3 17 Mar 2010 18:00:01 -0000 @@ -1230,6 +1230,15 @@ GetActivation().frame := frame; END PopEFrame; +PROCEDURE ForkProcessAndAllThreads(): INTEGER = + BEGIN + RETURN -1; + END ForkProcessAndAllThreads; + +PROCEDURE ReforkThreadAfterProcessFork(<*UNUSED*>VAR thread:T) = + BEGIN + END ReforkThreadAfterProcessFork; + VAR DEBUG := RTParams.IsPresent("debugthreads"); BEGIN Index: src/thread/WIN32/ThreadWin32C.c =================================================================== RCS file: /usr/cvs/cm3/m3-libs/m3core/src/thread/WIN32/ThreadWin32C.c,v retrieving revision 1.64 diff -u -r1.64 ThreadWin32C.c --- src/thread/WIN32/ThreadWin32C.c 16 Jan 2010 12:44:56 -0000 1.64 +++ src/thread/WIN32/ThreadWin32C.c 17 Mar 2010 18:00:01 -0000 @@ -161,6 +161,17 @@ CRITICAL_SECTION ThreadWin32__heapLock; CRITICAL_SECTION ThreadWin32__perfLock; CRITICAL_SECTION ThreadWin32__initLock; + +typedef void (*PThreadForkHandler)(void); + +ptrdiff_t +Thread__PThreadAtFork( + PThreadForkHandler prepare, + PThreadForkHandler parent, + PThreadForkHandler child) +{ + return 0; +} /* widen to USHORT, etc. if needed */