> It would be useful if these workers were automatically killed if the
> master process dies abruptly (SIGKILL and the like).
If you make your master process be a session leader (i.e., call setsid)
then when it dies all the other processes in the session will get
SIGHUP. However, that doesn't help much with Go since there is no very
good way for a Go program to call setsid correctly.
> We can already use this system call without help from StartProcess if
> we are starting Go worker processes, but it would be quite useful to
> have a general solution whereby StartProcess takes care of calling
> prctl(PR_SET_PDEATHSIG) before calling exec, similar to the ptrace
> code that is already there in exec_unix.go.
>
> However, one would probably want to achieve this without increasing
> the size of the StartProcess parameter list, especially if the
> functionality is only supported on certain operating systems.
The Go library is missing a general approach to daemonizing processes.
Perhaps this could be handled by whatever mechanism we adopt there.
One perhaps over-general approach would be to pass a function which runs
in the child context after the fork but before the exec. That function
could daemonize the process, change process priority, change signal
handling, etc. That is a Unix-specific concept, though, and as such
should probably not be in os.StartProcess itself. Of course
os.StartProcess could simply call some operating system specific
function.
Ian
the problem with passing an arbitrary function is that the function
can have arbitrary side-effects, including memory allocation and file
opening, which will break things.
i wonder if we can't take a tip from haskell's monads, and have
an object that, *when applied* results in the process changes taking
place before the child process is run.
it might be implemented as a function pointer, but a function pointer
that can only run a certain set of primitives. an alternative could be
a symbolic representation of the available operations.
ProcAttrs would be defined in syscall and redefined in os with
only the portable methods.
for example:
type ProcAttrs struct {
setup func(*procAttrs) os.Error
}
func StartProcess(name string, argv []string, attrs *ProcAttrs) (p
*Process, err Error) {
... fork
if p.setup != nil {
err = p.setup()
if err != nil {
return nil, err
}
}
... exec
}
func (p *ProcAttrs) addFunc(f func(*procAttrs) os.Error) {
setup := p.setup
p.setup = func(attrs *procAttrs) (err os.Error) {
if exec != nil {
err = exec(attrs)
}
if err == nil {
err = f(attrs)
}
return err
}
}
func (p *ProcAttrs) SetSid() {
p.addFunc(func(attrs *procAttrs) (err os.Error) {
_, _, err = Syscall(SETSID)
return
})
}
func (p *ProcAttrs) Close(fd int) {
p.addFunc(func(attrs *procAttrs) os.Error {
return Close(fd)
})
}
func (p *ProcAttrs) Dup(fd1, fd2 int) {
p.addFunc(func(attrs *procAttrs) os.Error {
return Dup(fd1, fd2)
})
}
func (p *ProcAttrs) Chdir(dir string) {
p.addFunc(func(attrs *procAttrs) os.Error {
return Chdir(dir)
})
}
func (p *ProcAttrs) SetEnv(attr, val string) {...}
etc
of course, given that we can't safely call any functions in
forkExec, the symbolic representation is the only option.
the API could remain the same though.
> On 1 March 2011 15:21, Ian Lance Taylor <ia...@google.com> wrote:
>> One perhaps over-general approach would be to pass a function which runs
>> in the child context after the fork but before the exec. That function
>> could daemonize the process, change process priority, change signal
>> handling, etc. That is a Unix-specific concept, though, and as such
>> should probably not be in os.StartProcess itself. Of course
>> os.StartProcess could simply call some operating system specific
>> function.
>
> the problem with passing an arbitrary function is that the function
> can have arbitrary side-effects, including memory allocation and file
> opening, which will break things.
I agree that having arbitrary side-effects is troubling and probably
enough to doom the idea. However, the function would run between the
fork and the exec, so I think there is a relatively limited number of
things it could break. Neither memory allocation nor file opening seem
like a real problem to me.
> ProcAttrs would be defined in syscall and redefined in os with
> only the portable methods.
That seems like a reasonable approach to me.
Ian
The typical issue here is that, at the point of forking, another
thread in the parent process can be holding a lock (say a lock in the
memory allocation code). Now, in the child process and code which
tries to allocate memory will try the grab the lock and deadlock
forever.
AGL
BTW i tried it and it seems to work ok.
i'll produce a CL if others think it's a good idea.
I don't think that we can run any Go code between fork and exec. The
compiler is free to lift values to the heap at any time and that risks
deadlocking if a thread in the parent process had the memory lock. We
could write code that shouldn't cause values to be lifted, but that
strikes me as leaving landmines.
I think all code between fork and exec needs to be C code, which means
describing any operations in a structure which it can process.
And even fd mapping is complex. See [1] and [2] for the code that
Chrome uses. It's probably over-engineered, but it's never failed
which is more than can be said for the previous couple of attempts,
which resulted in nasty bugs.
Cheers
AGL
[1] http://src.chromium.org/viewvc/chrome/trunk/src/base/file_descriptor_shuffle.h?view=markup
[2] http://src.chromium.org/viewvc/chrome/trunk/src/base/file_descriptor_shuffle.cc?view=markup
yes. my function-based suggestion was a red herring.
> I think all code between fork and exec needs to be C code, which means
> describing any operations in a structure which it can process.
yes. this doesn't mean that my suggested API won't work though.
it just changes the intermediate format.
in my little trial, the ProcAttrs structure was defined as:
type ProcAttributes struct {
setsid bool
traceme bool
dir *byte
}
more stuff could be added as needed, including sequenced operations
if desired.
thanks for the response, BTW.
With clearer names I think that this is probably the best approach.
AGL
everything you said is true except it needing to be C code.
this is code in the base library, which is already tied in other
ways to the specific compiler being used (gccgo has a
different package os). so it's okay for this to be written
in (careful) Go, as it is now. we just can't execute arbitrary Go.
russ