Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 4 Feb 2003 09:39:12 -0500 (EST)
From:      Daniel Eischen <eischen@pcnet1.pcnet.com>
To:        Terry Lambert <tlambert2@mindspring.com>
Cc:        rmkml <rmkml@wanadoo.fr>, freebsd-hackers@FreeBSD.ORG
Subject:   Re: vfork / execve / accept lock
Message-ID:  <Pine.GSO.4.10.10302040918100.11890-100000@pcnet1.pcnet.com>
In-Reply-To: <3E3FB756.AFA5ABFD@mindspring.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On Tue, 4 Feb 2003, Terry Lambert wrote:

> rmkml wrote:
> > Thank for you answer.
> 
> Sorry that it probably was not the answer you wanted.  8-(.
> 
> 
> > It is difficult to find anything concerning the signal model
> > of BSD implementation. In particular, for threaded applications.
> > If you can give me some advise or documentation to read, it will
> > be very helpfull to me .
> 
> I recommend the POSIX standard, the "Go Solo 2" book (I'm afraid
> it's out of print, now), and the O'Reilly book.  The rule of thumb
> is that anything that is "undefined"/"implementation defined" or
> is an extension -- don't use it.
> 
> BSD attempts to implement strict POSIX 1003.1 signals.  It does
> not implement the POSIX "reliable signal delivery" mechanism at
> this time.
> 
> With regard to threads, signals are delivered to the process.  This
> may mean that any thread that happens to be running at the time, or
> the threads schduler, gets the actual signal.
> 
> In general, the user space threads implementation is what's called
> a "call conversion" implementation.  The wayhe this operates is by
> trading a blocking call for a non-blocking call, plus an entry into
> the user space threads scheduler.  If there are other threads that
> are pending execution (not blocked on resources), then the user
> space threads scheduler schedules them to run.
> 
> The scheduler function is _thread_kern_sched(); this is the pthreads
> "kernel", in user space.  It's located in libc_r, in the source file
> /usr/src/lib/libc_r/uthread/uthread_kern.c; if there is only one
> thread, then it sets a timer and retries.  Because everything is
> non-blocking, this is the only way to convert a non-blocking call
> in a single thread to "block" pending completion of the operation
> (really, it polls, and the timer is to keep it from swamping the
> CPU with a buzz-loop in the scheduler).
> 
> The time for this wait is tiny, so it's not going to be the timeout
> you saw.  It's tricky, because handling the signal is different in
> the scheduler from elsewhere, since it's an interval timer signal,
> and you might be using interval timers in your program.
> 
> But basically, this means that on return, when signals are unmasked,
> they are delivered to the unmasking process.  This happens either
> automatically, or as a result of the siglongjmp from the scheduler.
> 
> So signals which are not caught may end up delivered in the context
> of any thread, at random.
> 
> Since signals run on their own stack, and have their own context
> (sort of), you can do everything you'd normally do in a signal
> handler, except assume context other than process context.  So
> if you has a thread allocated (or auto) variable that you set a
> global pointer to, you can't access it from a signal handler,
> and assume that the handler will only fire in the thread that
> originally registered it, etc..
> 
> 
> The best documentation is Chris Provenzo's documentation on
> "MIT pthreads".  There are a couple of papers he wrote on the
> pthreads system he wrote.  It's a distant ancestor of the
> FreeBSD user space pthreads implementation.
> 
> 
> > In the first part of the answer, do you want to say that a threaded
> > application can't use vfork/fork/rfork command because the result
> > will be undefined ?
> 
> No.  vfork() is just a wrapper for fork(), in threads.  THat's
> a library implementation detail.  The source file is in:
> /usr/src/lib/libc_r/uthread/uthread_vfork.c; the rfork()
> call doesn't exist at all.
> 
> If you are expecting the main process execution to syspend from
> the vfork() to the execve(), as documented for vfork(), well, it
> won't.
> 
> 
> > In this case is there a solution to launch external cmd ?
> 
> The fork()/execve() combination and system() work.  I really
> recommend using fork/execve.  The reason for this is that the
> system() is a cancellation point.  Basically, it will suspend
> until the command returns.
> 
> For reapable status, use fork() and system(), rather than
> trying to roll your own.
> 
> 
> > In your opinion, can directly the application create a new thread,
> > this thread used execve, and the parent thread waitpid() ?
> 
> Not without causing a cancellation point, which means that the
> thread you care about isn't waiting on the status, and the SIGCLD
> will interrupt it, and then your SIGCLD will hit the handler and
> not the wait.
> 
> You need to use wait4().  This is probably a bug in the
> implementation, since waitpid() is defined to be identical to
> wait4(), with an rusage value of 0, so the threads waitpid()
> needs to call the threads wait4(), which uses the WNOHANG to
> convert from a blocking call to a non-blocking call, and avoid
> the cancellation point.
> 
> Regular wait() also introduces a cancellation point.  I'm not
> positive that it can be implemented in terms of wait4(), since
> the man page doesn't note an equivalence.  I would *think* it
> could be wait4(-1, status, 0, 0).  If so, this would avoid the
> cancellation point there, too.  This is mostly a "signal thing";
> sigwait() is also suspect, but with nothing to do about it.
> 
> You can grep for _thread_enter_cancellation_point() in all the
> source files in /usr/src/lib/libc_r/uthread/*.c; this will
> basically tell you every place you need to be worried about a
> conversion from a blocking to a non-blocking system call.  I
> don't think there are really any semantic issues, except for
> the signal related calls, and things that could be bad if they
> started and then were restarted (e.g. system() of most programs
> that took any time to run at all would be a terrible idea), for
> what that's worth.
> 
> Cancellation points are problematic; they are permitted by the
> standard, but you really want to avoid them.  There are also
> places you could get bit, that probably should have them, but
> don't, because they're not really supported, like all the
> System V IPC stuff, mmap, end so on.
> 
> 
> 
> My personal recommendation with regard to signals:
> 
> Preestablish signal handlers for all signals, and then trampoline
> them to a signal handling thread using explicit calls to send the
> signal to the thread, using pthread_kill(), in the signal handler
> (but don't do it for SIG_IGN or SIG_DFL, if the default action is
> to ignore).  If you control the signal routing yourself, it will
> not bite you on the butt.

Or, when wanting a specific thread to receive a signal, it is
wise to make sure that signal is masked in all but the one
thread that should receive it.  pthread_kill() generated
signals are an exception since they target a specific thread.
Sigwait() is also your friend since it avoids having the signal
handler called and returns with the received signal.

In general, signals in threaded programs in FreeBSD are delivered
to threads selected as follows:

  1) A thread in sigwait() where the signal is in the waitset.
     The signal handler is not invoked in this case.

  2) Failing 1, a thread in sigsuspend() where the signal is
     not masked in the suspended signal set.

  3) Failing 1 and 2, any thread (chosen at random) which has
     the signal unmasked.

Expect future changes to add:

  0) A synchronous signal is delivered to the thread that generated
     it.  It may be impossible for libc_r to get this correct for
     SIGPIPE since I/O is wrapped.

Installing alternate signal stacks in threaded programs is not
supported and "not defined" by POSIX (at least the '96 POSIX spec,
which is to what libc_r tries to adhere).

"Remember it all.  Write it down.  Tell it.  So that people
will use the chance [] given them" :-)

-- 
Dan Eischen


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.GSO.4.10.10302040918100.11890-100000>