From: Richard M K. <kr...@pr...> - 2007-12-20 17:32:18
|
Richard M Kreuter writes: > Richard M Kreuter writes: > > > > So I think it's desirable to do something to support composition of > > independent child-creating programs here, but I'm not sure what's right. > > These options seem obvious to me... > > Another approach would be to make SB-POSIX:FORK not return pids, but > instances of SB-IMPL:PROCESS. This would be an incompatible breakage > for any program that calls SB-POSIX:FORK, however, though it would make > SIGCHLD handling invisible to the user. The more I think about this, the more I think having an SB-POSIX:FORK that worked like this would be the right thing. Suppose we had an SB-POSIX:FORK that returned a PROCESS to the parent and NIL to the child (and signaled an error when fork() fails), and had a "pid designator" to SB-POSIX with the following definition: * an integer designates itself, * an instance of SB-IMPL:PROCESS designates its PID slot. Then programs that used synchronous children could call SB-POSIX:WAITPID on the new PROCESS, and programs that used asynchronous children could employ the STATUS-HOOK slot for reacting to child state changes. Users wouldn't need to install SIGCHLD handlers, and the SIGCHLD machinery in RUN-PROGRAM would suffice for all asynchronous child processes. Changing SB-POSIX:FORK to return PROCESSes now, however, would break every program that uses it, since probably every program that calls SB-POSIX:FORK does something like this: (let ((pid (sb-posix:fork))) (if (zerop pid) (progn #| child |# ...) (progn #| parent |# ...))) Not all such programs are problematic: if the parent waits for the child immediately, it doesn't have to touch the SIGCHLD handler. And even those programs that clobber the SIGCHLD handler can work; they just won't work properly in a Lisp where any other programs also use the SIGCHLD handler. So here's a slightly fishy compromise that might suffice: have SB-POSIX:FORK create a PROCESS, stash it in SB-IMPL::*ACTIVE-PROCESSES*, return the PROCESS as a second return value, and have the bindings to the wait family of functions update the processes *ACTIVE-PROCESSES* and prune exited processes from *ACTIVE-PROCESSES* behind the scenes. This way, programs that now call fork() and wait() synchronously will work unchanged, and ones that now use install a SIGCHLD handler could instead be written as (let ((process (nth-value 1 (sb-posix:fork)))) (if (null process) (progn #| child |# ...) (progn (setf (process-status-hook proc) #'...)))) So user programs should never have to install a SIGCHLD handler. (Maybe we could signal warning when the user tries to install a SIGCHLD handler, even.) Or, if you wanted an API closer to RUN-PROGRAM's, we could have SB-POSIX:FORK take a STATUS-HOOK keyword argument, and set the slot when the process is constructed, so that user programs could be written as either (let ((pid (sb-posix:fork :status-hook #'...))) (when (zerop process) (progn #| child |# ...))) or (let ((process (nth-value 1 (sb-posix:fork :status-hook #'...)))) (when (null process) (progn #| child |# ...))) This does deviate from SB-POSIX's notional goal of being a low level interface, but if you grant that it's a flaw to give users a FORK that encourages clobbering a global resource in potentially destabilizing ways, then maybe you'll agree that this is one of those points where we shouldn't expose quite so low level an interface. Any opinions? -- RmK |