[Openmcl-devel] creating a daemon
Gary Byers
gb at clozure.com
Mon Jul 5 17:49:21 PDT 2004
On Mon, 5 Jul 2004, Eric Blood wrote:
>
> I took this example a step further and combined the fork and IO
> redirection and created the following:
>
> (in-package :ccl)
>
> (defun daemonize ()
> (when (/= 0 (#_fork))
> (quit))
> (fd-close 0)
> (fd-open "/dev/null" #$O_RDONLY)
> (fd-close 1)
> (fd-open "/tmp/openmcl.log" (logior #$O_WRONLY #$O_CREAT
> #$O_APPEND))
> (fd-close 2)
> (fd-open "/tmp/openmcl.log" (logior #$O_WRONLY #$O_APPEND))
> (while t
> (print (get-universal-time))
> (force-output)
> (sleep 1)))
>
> This works... from the listener. However when I combine this with my
> earlier application (having DAEMONIZE return after the last FD-OPEN
> instead of doing the while loop), the output file is being created, but
> the rest of the TOPLEVEL-FUNCTION doesn't appear to be executing.
>
> (defmethod toplevel-function ((app application) init-file)
> (declare (ignore init-file))
> (daemonize)
> (format t "aserver started (~D) at ~A~%" .......
>
> > That said, I'm very skeptical that you can reliably do much in the
> > child (besides #_exit or some flavor of #_exec) after a #_fork in
> > Darwin. This may be true in general, but it's especially true in
> > OpenMCL (which depends on Mach thread-level exception handling and
> > other things that #_fork seems blissfully unaware of.)
>
> Ok, I will dig deeper. Is there a good reference to Mach threads, or
> Mach threads and their interaction with the BSD layer? I searched the
> Apple Documentation, but didn't find a lot of useful stuff. In the
> meantime I will use detachtty. =) Thanks for your help.
I think that Darwin's behavior (where the child process contains a
clone of the thread that calls #_fork and no other threads) is
probably compliant with whatever the pthreads standard says about
#_fork and threads. Some OS's do provide extended versions of #_fork
that cause the child process to inherit all of the parent's threads,
but I don't see any indication that Darwin does so.
For OpenMCL, that means that a child process after a _#fork is running
single-threaded, but no one's bothered to tell it that: it still thinks
that ALL-PROCESSES is basically accurate, might believe that locks are
held by other threads (and might wait forever for them to become available),
and believes that any thread running lisp code has hooked itself into
the Mach exception-handling mechanism. All of these assumptions are false.
A thread conses by doing something like:
(decrement thread-specific-free-pointer-by size-of-object)
(trap-if-less-than thread-specific-free-pointer thread-specific-limit)
and a trap handler tries to arrange that the thread has enough memory
to handle the current allocation and some additional free memory so that
the trap won't be taken for a while. That sort of thing can't work
in a child process after a #_fork: no lisp-aware code will handle
the trap, and that'll probably kill the process.
It -might- be possible to partly fix this, but it'd be necessary to
fix a lot of other things (the lisp kernel's idea of what threads
exist, lisp's idea of what threads exist, what locks are owned by
defunct threads, ...) in order to make the post-fork environment
at all robust. I'm kind of amazed that the child process survives
as long as it does (sometimes) in your examples; I suspect that
the thread-specific memory chunk it inherits is sometimes big enough
to handle the incidental consing that may happen during printing,
but that's probably just blind luck.
If Darwin provided an extended #_fork that cloned all threads - and
was very careful about how it did this - then the post-fork environment
might be more robust. It's probably reasonable to assume that a very
high percentage of all programs that call #_fork do some sort of #_exec
shortly thereafter, so the motivation for offering an extended #_fork
is probably very small.
(I haven't checked, but I doubt if #_vfork is "extended" in this sense.)
>
> --
> eblood
>
>
More information about the Openmcl-devel
mailing list