Re: Threads
Hi,
[Sorry, I tend to get carried away on threads]
>>"Tom" == Tom Lees <tom@lpsg.demon.co.uk> writes:
Tom> On Mon, Oct 20, 1997 at 05:21:14PM -0500, Manoj Srivastava wrote:
>> Hi, "Bruce" == Bruce Perens <bruce@pixar.com> writes:
>> In any case, I don't think we can code without threads now, and add
>> threads later.
Tom> This all depends on the degree of threaded-ness. Adding threads
Tom> later may be fairly easy, if discarding ~10% of current code to
Tom> do so.
This causes alarums to go off in my head. I have seen a lot of
peoducts being re-engineered to be distributed/multithreaded, and in
the majority of them it was less costly to redesign/rewrite.
Good multi-threaded code is as much of a paradigm shift as OO
methodology is. Would you say that a procedural program can be made
OO later on?
Tom> The underlying engine in this case would not be much
Tom> different. But it would be a good idea to make the UI/widget-set
Tom> side of things thread-safe now, as that would probably need
Tom> larger changes.
Thread safe is simple; just wrap all code at method level into
a common critical region; bingo, thread safe, and inefficient.
Tom> But, there are a few issues with using threads:-
Tom> libc5 doesn't handle threads well. This will limit us to a libc6
Tom> implementation only. This probably won't affect us, though.
Right.
Tom> POSIX threads work nicely, _BUT_ they don't handle signals
Tom> particularly well (LinuxThreads doesn't do POSIX, other
Tom> implementations do). And, the big question is, what about
Tom> libslang, libgpm, etc, etc. Are they thread-safe yet?
Huh? Why do you say that? The programmer has to understand the
difference between type of signals, and one has to set up a signal
handler thread (and *not* muck with the per thread sig mask unless
one bloody well knows what one is doing). Unfortunately, a lot of
programmers start out by writing their code under the mistaken
assumption that they can set a signal handler for each thread; this
is not the way things work. You can _block_ or _unblock_ signals on a
thread-by-thread basis, but this is not the same thing.
As of draft 10, there is one vector of handlers shared by all
threads in the process (i.e., the old behavior for "asynchronous"
signals). Thus, if any thread changes the signal action for a given
signal, all threads are affected.
While we often casually talk of "asynchronous" and
"synchronous" signals, there is really no such concept in
POSIX. There are some differences in behavior based on HOW the signal
was generated, and the terminology results from the fact that some
signals are traditionally generated "synchronously" while others are
traditionally generated "asynchronously". Still, although SIGSEGV is
a "synchronous" signal, if you "kill -SEGV <pid>", the delivery (and
therefore, the BEHAVIOR) of the resulting signal is asynchronous.
Signal ACTIONS (not just handlers, but all the struct
sigaction fields) are shared by all threads in a process. There is no
such thing as a per-thread signal action in POSIX. Each thread has a
unique signal mask, and a unique set of pending signals -- but that's
all. (And the set of signals that a thread might receive are
determined not only by the thread's set of pending signals, but also
by the set of signals pending against the process.)
Finally, in order for ANY thread to reliably utilize sigwait()
(or its realtime cousins sigwaitinfo() and sigtimedwait()), ALL
threads in the process must have the signal(s) masked. Otherwise the
signal may be delivered to another thread rather than the
sigwaiter. (Although most implementations will favor a sigwaiter over
another thread with the signal unmasked, that's not required by the
standard -- and even if it were required, the signal would still go
to another thread if the signal recurs occurs while the sigwaiter is
processing one signal prior to waiting for the next.)
A "synchronous" signal (hardware trap) caused by the current
thread CANNOT be delivered to any other thread, under any
circumstances. That would be a violation of POSIX rules. (Of course,
if the implementation doesn't care to conform to POSIX, it can do
anything it pleases -- but any thread package that does such a thing
by default [without requiring nonportable changes to the application
code] is severely nonconforming and most definitely should not be
pretending to be "Pthreads".)
Asynchronous signals are not NECESSARILY delivered to the
currently running thread. POSIX says only that it will be delivered
to one (and only one) thread in the process, if there are any threads
that do not have the signal masked. While most implementations will
probably choose the currently running thread, if eligible, that is
not required -- and you should therefore never depend upon that
assumption!
Further, if the current thread is not eligible, the
implementation cannot simply hold it pending -- unless NO thread is
eligible to receive the signal. (In which case it will pend against
the process, until some thread unmasks the signal, or sigwaits for
it.)
Tom> SLang could turn out to be a problem with the signals, because it
Tom> will need to handle SIGWINCH. If the signals are delivered to one
Tom> thread only, it will work without necessarily needing to be
Tom> thread-safe. You just need to put the UI into the "main" threads
Tom> (i.e. the one that spawns all the others).
If SIGWINCH is masked in all generated threads, then the
signal shall only be delivered to the one thread that has unmasked
SIGWINCH and is blocked in a sigwait. One should not install signal
handlers.
The problem with signal handlers, of course, is that you are
forbidden to use ANY pthread interfaces from within a signal
handler. That includes not only pthread_cond_wait and
pthread_cond_signal but also pthread_mutex_lock and
pthread_mutex_unlock.
Mutexes and condition variables are specifically NOT required
to be "interrupt reentrant", for a lot of excellent reasons. POSIX
semaphores are, on the other hand, required to have (limited)
interrupt reentrancy; in specific, it is legal to call sem_post() from
a signal handler.
Tom> If you try this under a _TRUE_ POSIX implementation (i.e. signals
Tom> sent to _ALL_ the threads), there may be large reentrancy
Tom> problems which occur, and this hurts portability.
If you think about this a moment, you'll realize that any
programmer coding for POSIX threads would have had to evolve
solutions for this (indeed, the standard would have been pretty
stupid not to have considered this in the design phase).
Tom> I would say, if the libraries we are using can handle it, go for
Tom> using threads, as long as you use them sparingly. Don't optimize
Tom> the whole thing for speed using threads yet, only for ease of
Tom> programming.
You can't just use threads "sparingly". You have to integrate
them into the design of your code. Failing that, the use of threads
is wildly sub-optimal, and one would probably be better off without
it.
manoj
ps. Look at Using signals with POSIX threads, at
<URL:http://developer.austin.ibm.com/sdp/library/aixpert/aug95/aixpert_aug95_signal.html>,
from AIXpert Magazine's August 1995 issue
--
"Bureaucracy is a giant mechanism operated by pygmies." Honore de
Balzac
Manoj Srivastava <srivasta@acm.org> <http://www.datasync.com/%7Esrivasta/>
Key C7261095 fingerprint = CB D9 F4 12 68 07 E4 05 CC 2D 27 12 1D F5 E8 6E
Reply to:
- Follow-Ups:
- Re: Threads
- From: Jason Gunthorpe <jgg@gpu.srv.ualberta.ca>