1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-07-29 11:41:21 +03:00
1998-07-29  Mark Kettenis  <kettenis@phys.uva.nl>

	* sysdeps/mach/hurd/dl-sysdep.c (__lseek): New function.
	(__getpid): New function.
	(abort): New function.

	* nis/nis_cache2_xdr.c: Removed.
This commit is contained in:
Ulrich Drepper
1998-07-29 18:41:02 +00:00
parent ef5742267c
commit ddbf7fef45
13 changed files with 353 additions and 263 deletions

View File

@ -1,8 +1,14 @@
1998-07-29 Mark Kettenis <kettenis@phys.uva.nl>
* sysdeps/mach/hurd/dl-sysdep.c (__lseek): New function.
(__getpid): New function.
(abort): New function.
1998-07-29 15:07 Ulrich Drepper <drepper@cygnus.com> 1998-07-29 15:07 Ulrich Drepper <drepper@cygnus.com>
* sysdeps/arm/fpu/Dist: New file. Add ieee754.h. * sysdeps/arm/fpu/Dist: New file. Add ieee754.h.
* nis/nis_chache2_xdr.c: Removed. * nis/nis_cache2_xdr.c: Removed.
* nis/nis_cache.c: Removed. * nis/nis_cache.c: Removed.
* nis/nis_cache2.h: Removed. * nis/nis_cache2.h: Removed.

View File

@ -1 +1 @@
linuxthreads-0.7 by Xavier Leroy linuxthreads-0.8 by Xavier Leroy

View File

@ -1,3 +1,20 @@
1998-07-29 Xavier Leroy <Xavier.Leroy@inria.fr>
* Banner: Bump version number to 0.8
* FAQ.html: Many updates, in particular w.r.t. debugging.
* manager.c: Support for non-default stacksize for
LinuxThreads-allocated stacks;
don't use guard pages for stacks with default size, rely on
rlimit(RLIMIT_STACK) instead (it's cheaper).
* attr.c: Likewise.
* cancel.c: Use __pthread_sig_cancel and __pthread_sig_restart
everywhere instead of PTHREAD_SIG_CANCEL and PTHREAD_SIG_RESTART.
* condvar.c: Likewise.
* internals.h: Likewise.
* restart.h: Likewise.
* signals.c: Likewise.
* pthread.c: Likewise; set rlimit(RLIMIT_STACK) as we need it.
1998-07-23 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de> 1998-07-23 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* weaks.c: Define pthread_mutexattr_[sg]ettype instead of * weaks.c: Define pthread_mutexattr_[sg]ettype instead of

View File

@ -5,6 +5,7 @@
<BODY> <BODY>
<H1 ALIGN=center>LinuxThreads Frequently Asked Questions <BR> <H1 ALIGN=center>LinuxThreads Frequently Asked Questions <BR>
(with answers)</H1> (with answers)</H1>
<H2 ALIGN=center>[For LinuxThreads version 0.8]</H2>
<HR><P> <HR><P>
@ -30,7 +31,7 @@
LinuxThreads is a Linux library for multi-threaded programming. LinuxThreads is a Linux library for multi-threaded programming.
It implements the Posix 1003.1c API (Application Programming It implements the Posix 1003.1c API (Application Programming
Interface) for threads. It runs on any Linux system with kernel 2.0.0 Interface) for threads. It runs on any Linux system with kernel 2.0.0
or more recent, and a suitable C library (see section <A HREF="B">B</A>). or more recent, and a suitable C library (see section <A HREF="C">C</A>).
<P> <P>
<H4><A NAME="A.2">A.2: What are threads?</A></H4> <H4><A NAME="A.2">A.2: What are threads?</A></H4>
@ -176,11 +177,27 @@ including 1003.1c, with some extensions and clarifications.<P>
<H4><A NAME="C.1">C.1: Which version of the C library should I use <H4><A NAME="C.1">C.1: Which version of the C library should I use
with LinuxThreads?</A></H4> with LinuxThreads?</A></H4>
Most current Linux distributions come with libc version 5, maintained The best choice by far is glibc 2, a.k.a. libc 6. It offers very good
by H.J.Lu. For LinuxThreads to work properly, you must use either support for multi-threading, and LinuxThreads has been closely
libc 5.2.18 or libc 5.4.12 or later. Avoid 5.3.12 and 5.4.7: these integrated with glibc 2. The glibc 2 distribution contains the
have problems with the per-thread errno variable. sources of a specially adapted version of LinuxThreads.<P>
<P>
glibc 2 comes preinstalled as the default C library on several Linux
distributions, such as RedHat 5.0 and 5.1, and recent beta versions of
Debian. Those distributions include the version of LinuxThreads matching
glibc 2.<P>
<H4><A NAME="C.2">C.2: My system has libc 5 preinstalled, not glibc
2. Can I still use LinuxThreads?</H4>
Yes, but you're likely to run into some problems, as libc 5 only
offers minimal support for threads and contains some bugs that affect
multithreaded programs. <P>
The versions of libc 5 that work best with LinuxThreads are
libc 5.2.18 on the one hand, and libc 5.4.12 or later on the other hand.
Avoid 5.3.12 and 5.4.7: these have problems with the per-thread errno
variable. <P>
Unfortunately, many popular Linux distributions (e.g. RedHat 4.2) come Unfortunately, many popular Linux distributions (e.g. RedHat 4.2) come
with libc 5.3.12 preinstalled -- the one that does not work with with libc 5.3.12 preinstalled -- the one that does not work with
@ -190,28 +207,19 @@ of RedHat 4, there is a RPM package for libc-5.4 in the "contrib"
area of RedHat FTP sites. area of RedHat FTP sites.
<P> <P>
<H4><A NAME="C.2">C.2: What about glibc 2, a.k.a. libc 6?</A></H4>
It's the next generation libc for Linux, developed by Ulrich
Drepper and other FSF collaborators. glibc 2 offers much better
support for threads than libc 5. Indeed, thread support was
planned from the very early stages of glibc 2, while it's a
last-minute addition to libc 5. glibc 2 actually comes with a
specially adapted version of LinuxThreads, which you can drop in the
glibc 2 sources as an add-on package.
<H4><A NAME="C.3">C.3: So, should I switch to glibc 2, or stay with a <H4><A NAME="C.3">C.3: So, should I switch to glibc 2, or stay with a
recent libc 5?</A></H4> recent libc 5?</A></H4>
Depends how you plan to do it. Switching an already installed I'd recommend you switch to glibc 2. Even for single-threaded
programs, glibc 2 is more solid and more standard-conformant than libc
5. And the shortcomings of libc 5 almost preclude any serious
multi-threaded programming.<P>
Switching an already installed
system from libc 5 to glibc 2 is not completely straightforward. system from libc 5 to glibc 2 is not completely straightforward.
See the <A HREF="http://sunsite.unc.edu/LDP/HOWTO/Glibc2-HOWTO.html">Glibc2 See the <A HREF="http://sunsite.unc.edu/LDP/HOWTO/Glibc2-HOWTO.html">Glibc2
HOWTO</A> for more information. HOWTO</A> for more information. Much easier is (re-)installing a
But (re-)installing a Linux distribution based on glibc 2 is easy. Linux distribution based on glibc 2, such as RedHat 5.1.<P>
One such distribution available now is RedHat 5.0. Debian and other
Linux distributors will also provide glibc 2-based distributions in the
near future.
<P>
<H4><A NAME="C.4">C.4: Where can I find glibc 2 and the version of <H4><A NAME="C.4">C.4: Where can I find glibc 2 and the version of
LinuxThreads that goes with it?</A></H4> LinuxThreads that goes with it?</A></H4>
@ -221,6 +229,14 @@ See <A
HREF="http://www.gnu.org/order/ftp.html">http://www.gnu.org/order/ftp.html</A> HREF="http://www.gnu.org/order/ftp.html">http://www.gnu.org/order/ftp.html</A>
for a list of mirrors.<P> for a list of mirrors.<P>
<H4><A NAME="C.5">C.5: Where can I find libc 5 and the version of
LinuxThreads that goes with it?</A></H4>
For libc 5, see <A HREF="ftp://sunsite.unc.edu/pub/Linux/devel/GCC/"><code>ftp://sunsite.unc.edu/pub/Linux/devel/GCC/</code></A>.<P>
For the libc 5 version of LinuxThreads, see
<A HREF="ftp://ftp.inria.fr/INRIA/Projects/cristal/Xavier.Leroy/linuxthreads/">ftp://ftp.inria.fr/INRIA/Projects/cristal/Xavier.Leroy/linuxthreads/</A>.<P>
<HR> <HR>
<P> <P>
@ -236,8 +252,8 @@ You probably mean:
I haven't actually seen this problem, but several users reported it. I haven't actually seen this problem, but several users reported it.
My understanding is that something is wrong in the include files of My understanding is that something is wrong in the include files of
your Linux installation (<code>/usr/include/*</code>). Make sure your Linux installation (<code>/usr/include/*</code>). Make sure
you're using a supported version of the C library. (See section <A you're using a supported version of the libc 5 library. (See question <A
HREF="#B">B</A>).<P> HREF="#C.2">C.2</A>).<P>
<H4><A NAME="D.2">D.2: When I compile LinuxThreads, I run into problems with <H4><A NAME="D.2">D.2: When I compile LinuxThreads, I run into problems with
<CODE>/usr/include/sched.h</CODE>: there are several occurrences of <CODE>/usr/include/sched.h</CODE>: there are several occurrences of
@ -246,7 +262,7 @@ HREF="#B">B</A>).<P>
Yes, <CODE>/usr/include/sched.h</CODE> that comes with libc 5.3.12 is broken. Yes, <CODE>/usr/include/sched.h</CODE> that comes with libc 5.3.12 is broken.
Replace it with the <code>sched.h</code> file contained in the Replace it with the <code>sched.h</code> file contained in the
LinuxThreads distribution. But really you should not be using libc LinuxThreads distribution. But really you should not be using libc
5.3.12 with LinuxThreads! (See question <A HREF="#C.1">C.1</A>.)<P> 5.3.12 with LinuxThreads! (See question <A HREF="#C.2">C.1</A>.)<P>
<H4><A NAME="D.3">D.3: My program does <CODE>fdopen()</CODE> on a file <H4><A NAME="D.3">D.3: My program does <CODE>fdopen()</CODE> on a file
descriptor opened on a pipe. When I link it with LinuxThreads, descriptor opened on a pipe. When I link it with LinuxThreads,
@ -279,39 +295,53 @@ is strong contention on a mutex: instead of giving the mutex to each
thread in turn, it seems that it's almost always the same thread that thread in turn, it seems that it's almost always the same thread that
gets the mutex. Isn't this completely broken behavior?</A></H4> gets the mutex. Isn't this completely broken behavior?</A></H4>
What happens is the following: when a thread unlocks a mutex, all That behavior has mostly disappeared in recent releases of
other threads that were waiting on the mutex are sent a signal which LinuxThreads (version 0.8 and up). It was fairly common in older
makes them runnable. However, the kernel scheduler may or may not releases, though.
restart them immediately. If the thread that unlocked the mutex
tries to lock it again immediately afterwards, it is likely that it
will succeed, because the threads haven't yet restarted. This results
in an apparently very unfair behavior, when the same thread repeatedly
locks and unlocks the mutex, while other threads can't lock the mutex.<P>
This is perfectly acceptable behavior with respect to the POSIX What happens in LinuxThreads 0.7 and before is the following: when a
standard: for the default scheduling policy, POSIX makes no guarantees thread unlocks a mutex, all other threads that were waiting on the
of fairness, such as "the thread waiting for the mutex for the longest mutex are sent a signal which makes them runnable. However, the
time always acquires it first". This allows implementations of kernel scheduler may or may not restart them immediately. If the
mutexes to remain simple and efficient. Properly written thread that unlocked the mutex tries to lock it again immediately
multithreaded code avoids that kind of heavy contention on mutexes, afterwards, it is likely that it will succeed, because the threads
and does not run into fairness problems. If you need scheduling haven't yet restarted. This results in an apparently very unfair
guarantees, you should consider using the real-time scheduling behavior, when the same thread repeatedly locks and unlocks the mutex,
policies <code>SCHED_RR</code> and <code>SCHED_FIFO</code>, which have while other threads can't lock the mutex.<P>
precisely defined scheduling behaviors. <P>
In LinuxThreads 0.8 and up, <code>pthread_unlock</code> restarts only
one waiting thread, and pre-assign the mutex to that thread. Hence,
if the thread that unlocked the mutex tries to lock it again
immediately, it will block until other waiting threads have had a
chance to lock and unlock the mutex. This results in much fairer
scheduling.<P>
Notice however that even the old "unfair" behavior is perfectly
acceptable with respect to the POSIX standard: for the default
scheduling policy, POSIX makes no guarantees of fairness, such as "the
thread waiting for the mutex for the longest time always acquires it
first". Properly written multithreaded code avoids that kind of heavy
contention on mutexes, and does not run into fairness problems. If
you need scheduling guarantees, you should consider using the
real-time scheduling policies <code>SCHED_RR</code> and
<code>SCHED_FIFO</code>, which have precisely defined scheduling
behaviors. <P>
<H4><A NAME="D.7">D.7: I have a simple test program with two threads <H4><A NAME="D.7">D.7: I have a simple test program with two threads
that do nothing but <CODE>printf()</CODE> in tight loops, and from the that do nothing but <CODE>printf()</CODE> in tight loops, and from the
printout it seems that only one thread is running, the other doesn't printout it seems that only one thread is running, the other doesn't
print anything!</A></H4> print anything!</A></H4>
If you wait long enough, you should see the second thread kick in. Again, this behavior is characteristic of old releases of LinuxThreads
But still, you're right, one thread prevents the other one from (0.7 and before); more recent versions (0.8 and up) should not exhibit
running for long periods of time. The reason is explained in this behavior.<P>
The reason for this behavior is explained in
question <A HREF="#D.6">D.6</A> above: <CODE>printf()</CODE> performs question <A HREF="#D.6">D.6</A> above: <CODE>printf()</CODE> performs
locking on <CODE>stdout</CODE>, and thus your two threads contend very locking on <CODE>stdout</CODE>, and thus your two threads contend very
heavily for the mutex associated with <CODE>stdout</CODE>. But if you heavily for the mutex associated with <CODE>stdout</CODE>. But if you
do some real work between two calls to <CODE>printf()</CODE>, you'll do some real work between two calls to <CODE>printf()</CODE>, you'll
see that scheduling becomes much smoother. <P> see that scheduling becomes much smoother.<P>
<H4><A NAME="D.8">D.8: I've looked at <code>&lt;pthread.h&gt;</code> <H4><A NAME="D.8">D.8: I've looked at <code>&lt;pthread.h&gt;</code>
and there seems to be a gross error in the <code>pthread_cleanup_push</code> and there seems to be a gross error in the <code>pthread_cleanup_push</code>
@ -402,29 +432,23 @@ threads when one thread receives the <CODE>SIGSTOP</CODE> signal!
One day, LinuxThreads will implement that behavior, and the One day, LinuxThreads will implement that behavior, and the
non-portable hack with <CODE>SIGSTOP</CODE> won't work anymore.<P> non-portable hack with <CODE>SIGSTOP</CODE> won't work anymore.<P>
<H4><A NAME="E.5">E.5: LinuxThreads does not implement <H4><A NAME="E.5">E.5: Does LinuxThreads implement
<CODE>pthread_attr_setstacksize()</CODE> nor <CODE>pthread_attr_setstacksize()</CODE> and
<CODE>pthread_attr_setstackaddr()</CODE>. Why? </A></H4> <CODE>pthread_attr_setstackaddr()</CODE>?</A></H4>
These two functions are part of optional components of the POSIX These optional functions are provided in recent versions of
standard, meaning that portable applications should test for the LinuxThreads (0.8 and up). Earlier releases did not provide these
"feature test" macros <CODE>_POSIX_THREAD_ATTR_STACKSIZE</CODE> and optional components of the POSIX standard.<P>
<CODE>_POSIX_THREAD_ATTR_STACKADDR</CODE> (respectively) before using these
functions.<P>
<CODE>pthread_attr_setstacksize()</CODE> lets the programmer specify Even if <CODE>pthread_attr_setstacksize()</CODE> and
the maximum stack size for a thread. In LinuxThreads, stacks start <CODE>pthread_attr_setstackaddr()</CODE> are now provided, we still
small (4k) and grow on demand to a fairly large limit (2M), which recommend that you do not use them unless you really have strong
cannot be modified on a per-thread basis for architectural reasons. reasons for doing so. The default stack allocation strategy for
Hence there is really no need to specify any stack size yourself: the LinuxThreads is nearly optimal: stacks start small (4k) and
system does the right thing all by itself. Besides, there is no automatically grow on demand to a fairly large limit (2M).
portable way to estimate the stack requirements of a thread, so Moreover, there is no portable way to estimate the stack requirements
setting the stack size is pretty useless anyway.<P> of a thread, so setting the stack size yourself makes your program
less reliable and non-portable.<P>
<CODE>pthread_attr_setstackaddr()</CODE> is even more questionable: it
lets users specify the stack location for a thread. Again,
LinuxThreads takes care of that for you. Why you would ever need to
set the stack address escapes me.<P>
<H4><A NAME="E.6">E.6: LinuxThreads does not support the <H4><A NAME="E.6">E.6: LinuxThreads does not support the
<CODE>PTHREAD_SCOPE_PROCESS</CODE> value of the "contentionscope" <CODE>PTHREAD_SCOPE_PROCESS</CODE> value of the "contentionscope"
@ -511,50 +535,17 @@ want to use the libg++, I have a libg++ add-on for egcs.
<H4><A NAME="G.1">G.1: Can I debug LinuxThreads program using gdb?</A></H4> <H4><A NAME="G.1">G.1: Can I debug LinuxThreads program using gdb?</A></H4>
Essentially, no. gdb is basically not aware of the threads. It Yes, but not with the stock gdb 4.17. You need a specially patched
will let you debug the main thread, and also inspect the global state, version of gdb 4.17 developed by Eric Paire and colleages at The Open
but you won't have any control over the other threads. Worse, you Group, Grenoble. The patches against gdb 4.17 are available at
can't put any breakpoint anywhere in the code: if a thread other than <A HREF="http://www.gr.opengroup.org/java/jdk/linux/debug.htm"><code>http://www.gr.opengroup.org/java/jdk/linux/debug.htm</code></A>.
the main thread hits the breakpoint, it will just crash!<P> Precompiled binaries of the patched gdb are available in RedHat's RPM
format at <A HREF="http://odin.appliedtheory.com/"><code>http://odin.appliedtheory.com/</code></A>.
For running gdb on the main thread, you need to instruct gdb to ignore <H4><A NAME="G.2">G.2: Does it work with post-mortem debugging?</A></H4>
the signals used by LinuxThreads. Just do:
<PRE>
handle SIGUSR1 nostop pass noprint
handle SIGUSR2 nostop pass noprint
</PRE> Not very well. Generally, the core file does not correspond to the
thread that crashed. The reason is that the kernel will not dump core
<H4><A NAME="G.2">G.2: What about attaching to a running thread using
the <code>attach</code> command of gdb?</A></H4>
For reasons I don't fully understand, this does not work.<P>
<H4><A NAME="G.3">G.3: But I know gdb supports threads on some
platforms! Why not on Linux?</A></H4>
You're correct that gdb has some built-in support for threads, in
particular the IRIX "sprocs" model, which is a "one thread = one
process" model fairly close to LinuxThreads. But gdb under IRIX uses
ioctls on <code>/proc</code> to control debugged processes, while
under Linux it uses the traditional <CODE>ptrace()</CODE>. The support
for threads is built in the <code>/proc</code> interface, but some
work remains to be done to have it in the <CODE>ptrace()</CODE>
interface. In summary, it should not be impossible to get gdb to work
with LinuxThreads, but it's definitely not trivial.
<H4><A NAME="G.4">G.4: OK, I'll do post-mortem debugging, then. But
gdb cannot read core files generated by a multithreaded program! Or,
the core file is readable from gcc, but does not correspond to the
thread that crashed! What happens?</A></H4>
Some versions of gdb do indeed have problems with post-mortem
debugging in general, but this is not specific to LinuxThreads.
Recent Linux distributions seem to have corrected this problem,
though.<P>
Regarding the fact that the core file does not correspond to the
thread that crashed, the reason is that the kernel will not dump core
for a process that shares its memory with other processes, such as the for a process that shares its memory with other processes, such as the
other threads of your program. So, the thread that crashes silently other threads of your program. So, the thread that crashes silently
disappears without generating a core file. Then, all other threads of disappears without generating a core file. Then, all other threads of
@ -564,7 +555,7 @@ one that dies is no longer sharing its memory with anyone else, so the
kernel generates a core file for that thread. Unfortunately, that's kernel generates a core file for that thread. Unfortunately, that's
not the thread you are interested in. not the thread you are interested in.
<H4><A NAME="G.5">G.5: How can I debug multithreaded programs, then?</A></H4> <H4><A NAME="G.3">G.3: Any other ways to debug multithreaded programs, then?</A></H4>
Assertions and <CODE>printf()</CODE> are your best friends. Try to debug Assertions and <CODE>printf()</CODE> are your best friends. Try to debug
sequential parts in a single-threaded program first. Then, put sequential parts in a single-threaded program first. Then, put
@ -572,8 +563,8 @@ sequential parts in a single-threaded program first. Then, put
Also, check invariants often with the <CODE>assert()</CODE> macro. In truth, Also, check invariants often with the <CODE>assert()</CODE> macro. In truth,
there is no other effective way (save for a full formal proof of your there is no other effective way (save for a full formal proof of your
program) to track down concurrency bugs. Debuggers are not really program) to track down concurrency bugs. Debuggers are not really
effective for concurrency problems, because they disrupt program effective for subtle concurrency problems, because they disrupt
execution too much.<P> program execution too much.<P>
<HR> <HR>
<P> <P>
@ -664,32 +655,24 @@ code concludes that it cannot handle the error and stops.<P>
<H4><A NAME="H.4">H.4: With LinuxThreads, I can no longer use the signals <H4><A NAME="H.4">H.4: With LinuxThreads, I can no longer use the signals
<code>SIGUSR1</code> and <code>SIGUSR2</code> in my programs! Why? </A></H4> <code>SIGUSR1</code> and <code>SIGUSR2</code> in my programs! Why? </A></H4>
The short answer is: because the Linux kernel you're using does not
support realtime signals. <P>
LinuxThreads needs two signals for its internal operation. LinuxThreads needs two signals for its internal operation.
One is used to suspend and restart threads blocked on mutex, condition One is used to suspend and restart threads blocked on mutex, condition
or semaphore operations. The other is used for thread cancellation. or semaphore operations. The other is used for thread
Since the only two signals not reserved for the Linux kernel are cancellation.<P>
<code>SIGUSR1</code> and <code>SIGUSR2</code>, LinuxThreads has no
other choice than using them. I know this is unfortunate, and hope
this problem will be addressed in future Linux kernels, either by
freeing some of the regular signals (unlikely), or by providing more
than 32 signals (as per the POSIX 1003.1b realtime extensions).<P>
In the meantime, you can try to use kernel-reserved signals either in On ``old'' kernels (2.0 and early 2.1 kernels), there are only 32
your program or in LinuxThreads. For instance, signals available and the kernel reserves all of them but two:
<code>SIGSTKFLT</code> and <code>SIGUNUSED</code> appear to be <code>SIGUSR1</code> and <code>SIGUSR2</code>. So, LinuxThreads has
unused in the current Linux kernels for the Intel x86 architecture. no choice but use those two signals.<P>
To use these in LinuxThreads, the only file you need to change
is <code>internals.h</code>, more specifically the two lines: On recent kernels (late 2.1 kernels and the forthcoming 2.2 kernels),
<PRE> more than 32 signals are provided in the form of realtime signals.
#define PTHREAD_SIG_RESTART SIGUSR1 When run on one of those kernels, LinuxThreads uses two reserved
#define PTHREAD_SIG_CANCEL SIGUSR2 realtime signals for its internal operation, thus leaving
</PRE> <code>SIGUSR1</code> and <code>SIGUSR2</code> free for user code.<P>
Replace them by e.g.
<PRE>
#define PTHREAD_SIG_RESTART SIGSTKFLT
#define PTHREAD_SIG_CANCEL SIGUNUSED
</PRE>
Warning: you're doing this at your own risks.<P>
<H4><A NAME="H.5">H.5: Is the stack of one thread visible from the <H4><A NAME="H.5">H.5: Is the stack of one thread visible from the
other threads? Can I pass a pointer into my stack to other threads? other threads? Can I pass a pointer into my stack to other threads?
@ -731,16 +714,21 @@ occurred.<P>
<H4><A NAME="I.2">I.2: So, what can I do to build a multithreaded X <H4><A NAME="I.2">I.2: So, what can I do to build a multithreaded X
Windows client? </A></H4> Windows client? </A></H4>
The best solution is to recompile the X libraries with multithreading The best solution is to use X libraries that have been compiled with
multithreading options set. Linux distributions that come with glibc
2 as the main C library generally provide thread-safe X libraries.
At least, that seems to be the case for RedHat 5.<P>
You can try to recompile yourself the X libraries with multithreading
options set. They contain optional support for multithreading; it's options set. They contain optional support for multithreading; it's
just that all binary distributions for Linux were built without this just that the binaries provided by your Linux distribution were built
support. See the file <code>README.Xfree3.3</code> in the LinuxThreads without this support. See the file <code>README.Xfree3.3</code> in
distribution for patches and info on how to compile thread-safe X the LinuxThreads distribution for patches and info on how to compile
libraries from the Xfree3.3 distribution. The Xfree3.3 sources are thread-safe X libraries from the Xfree3.3 distribution. The Xfree3.3
readily available in most Linux distributions, e.g. as a source RPM sources are readily available in most Linux distributions, e.g. as a
for RedHat. Be warned, however, that X Windows is a huge system, and source RPM for RedHat. Be warned, however, that X Windows is a huge
recompiling even just the libraries takes a lot of time and disk system, and recompiling even just the libraries takes a lot of time
space.<P> and disk space.<P>
Another, less involving solution is to call X functions only from the Another, less involving solution is to call X functions only from the
main thread of your program. Even if all threads have their own errno main thread of your program. Even if all threads have their own errno
@ -752,9 +740,8 @@ only. <P>
<H4><A NAME="I.2">This is a lot of work. Don't you have precompiled <H4><A NAME="I.2">This is a lot of work. Don't you have precompiled
thread-safe X libraries that you could distribute?</A></H4> thread-safe X libraries that you could distribute?</A></H4>
No, I don't. Sorry. But you could approach the maintainers of No, I don't. Sorry. But consider installing a Linux distribution
your Linux distribution to see if they would be willing to provide that comes with thread-safe X libraries, such as RedHat 5.<P>
thread-safe X libraries.<P>
<H4><A NAME="I.3">I.3: Can I use library FOO in a multithreaded <H4><A NAME="I.3">I.3: Can I use library FOO in a multithreaded
program?</A></H4> program?</A></H4>
@ -786,8 +773,7 @@ execute code not compiled with <CODE>-D_REENTRANT</CODE>.<P>
</A></H4> </A></H4>
Because both LinuxThreads and SVGAlib use the signals Because both LinuxThreads and SVGAlib use the signals
<code>SIGUSR1</code> and <code>SIGUSR2</code>. One of the two should <code>SIGUSR1</code> and <code>SIGUSR2</code>. See question <A
be recompiled to use different signals. See question <A
HREF="#H.4">H.4</A>. HREF="#H.4">H.4</A>.
<P> <P>
@ -966,20 +952,6 @@ pretty good job of making kernel-level context switches between
threads efficient. LinuxThreads is just following the general threads efficient. LinuxThreads is just following the general
direction they set.<P> direction they set.<P>
<H4><A NAME="K.3">K.3: I looked at the LinuxThreads sources, and I saw
quite a lot of spinlocks and busy-waiting loops to acquire these
spinlocks. Isn't this a big waste of CPU time?</A></H4>
Look more carefully. Spinlocks are used internally to protect
LinuxThreads's data structures, but these locks are held for very
short periods of time: 10 instructions or so. The probability that a
thread has to loop busy-waiting on a taken spinlock for more than,
say, 100 cycles is very, very low. When a thread needs to wait on a
mutex, condition, or semaphore, it actually puts itself on a waiting
queue, then suspends on a signal, consuming no CPU time at all. The
thread will later be restarted by sending it a signal when the state
of the mutex, condition, or semaphore changes.<P>
<HR> <HR>
<ADDRESS>Xavier.Leroy@inria.fr</ADDRESS> <ADDRESS>Xavier.Leroy@inria.fr</ADDRESS>
</BODY> </BODY>

View File

@ -146,10 +146,8 @@ int __pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize)
/* First round up the guard size. */ /* First round up the guard size. */
guardsize = roundup (guardsize, ps); guardsize = roundup (guardsize, ps);
/* The current implementation of LinuxThreads allocates 2MB stack space /* The guard size must not be larger than the stack itself */
for each thread. So the maximum guardsize is 2MB - pagesize. */ if (guardsize >= attr->stacksize) return EINVAL;
if (guardsize >= STACK_SIZE - ps)
return EINVAL;
attr->guardsize = guardsize; attr->guardsize = guardsize;

View File

@ -61,7 +61,7 @@ int pthread_cancel(pthread_t thread)
handle->h_descr->p_canceled = 1; handle->h_descr->p_canceled = 1;
pid = handle->h_descr->p_pid; pid = handle->h_descr->p_pid;
__pthread_unlock(&handle->h_lock); __pthread_unlock(&handle->h_lock);
kill(pid, PTHREAD_SIG_CANCEL); kill(pid, __pthread_sig_cancel);
return 0; return 0;
} }

View File

@ -86,7 +86,7 @@ pthread_cond_timedwait_relative(pthread_cond_t *cond,
} else { } else {
/* Unblock the restart signal */ /* Unblock the restart signal */
sigemptyset(&unblock); sigemptyset(&unblock);
sigaddset(&unblock, PTHREAD_SIG_RESTART); sigaddset(&unblock, __pthread_sig_restart);
sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask); sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
/* Sleep for the required duration */ /* Sleep for the required duration */
retsleep = __libc_nanosleep(reltime, NULL); retsleep = __libc_nanosleep(reltime, NULL);

View File

@ -130,16 +130,13 @@ struct pthread_request {
/* Signals used for suspend/restart and for cancellation notification. */ /* Signals used for suspend/restart and for cancellation notification. */
#ifdef SIGRTMIN
/* The have real-time signals. */
extern int __pthread_sig_restart; extern int __pthread_sig_restart;
extern int __pthread_sig_cancel; extern int __pthread_sig_cancel;
# define PTHREAD_SIG_RESTART __pthread_sig_restart
# define PTHREAD_SIG_CANCEL __pthread_sig_cancel /* Default signals used if we don't have realtime signals */
#else
# define PTHREAD_SIG_RESTART SIGUSR1 #define DEFAULT_SIG_RESTART SIGUSR1
# define PTHREAD_SIG_CANCEL SIGUSR2 #define DEFAULT_SIG_CANCEL SIGUSR2
#endif
/* Global array of thread handles, used for validating a thread id /* Global array of thread handles, used for validating a thread id
and retrieving the corresponding thread descriptor. Also used for and retrieving the corresponding thread descriptor. Also used for

View File

@ -23,6 +23,7 @@
#include <unistd.h> #include <unistd.h>
#include <sys/poll.h> /* for poll */ #include <sys/poll.h> /* for poll */
#include <sys/mman.h> /* for mmap */ #include <sys/mman.h> /* for mmap */
#include <sys/param.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/wait.h> /* for waitpid macros */ #include <sys/wait.h> /* for waitpid macros */
#include <linux/tasks.h> #include <linux/tasks.h>
@ -99,11 +100,11 @@ int __pthread_manager(void *arg)
/* Set the error variable. */ /* Set the error variable. */
__pthread_manager_thread.p_errnop = &__pthread_manager_thread.p_errno; __pthread_manager_thread.p_errnop = &__pthread_manager_thread.p_errno;
__pthread_manager_thread.p_h_errnop = &__pthread_manager_thread.p_h_errno; __pthread_manager_thread.p_h_errnop = &__pthread_manager_thread.p_h_errno;
/* Block all signals except PTHREAD_SIG_RESTART, PTHREAD_SIG_CANCEL /* Block all signals except __pthread_sig_restart, __pthread_sig_cancel
and SIGTRAP */ and SIGTRAP */
sigfillset(&mask); sigfillset(&mask);
sigdelset(&mask, PTHREAD_SIG_RESTART); sigdelset(&mask, __pthread_sig_restart);
sigdelset(&mask, PTHREAD_SIG_CANCEL); /* for debugging new threads */ sigdelset(&mask, __pthread_sig_cancel); /* for debugging new threads */
sigdelset(&mask, SIGTRAP); /* for debugging purposes */ sigdelset(&mask, SIGTRAP); /* for debugging purposes */
sigprocmask(SIG_SETMASK, &mask, NULL); sigprocmask(SIG_SETMASK, &mask, NULL);
/* Raise our priority to match that of main thread */ /* Raise our priority to match that of main thread */
@ -161,7 +162,7 @@ int __pthread_manager(void *arg)
break; break;
case REQ_DEBUG: case REQ_DEBUG:
/* Make gdb aware of new thread */ /* Make gdb aware of new thread */
if (__pthread_threads_debug) raise(PTHREAD_SIG_CANCEL); if (__pthread_threads_debug) raise(__pthread_sig_cancel);
restart(request.req_thread); restart(request.req_thread);
break; break;
} }
@ -205,6 +206,78 @@ static int pthread_start_thread(void *arg)
return 0; return 0;
} }
static int pthread_allocate_stack(const pthread_attr_t *attr,
pthread_descr default_new_thread,
int pagesize,
pthread_descr * out_new_thread,
char ** out_new_thread_bottom,
char ** out_guardaddr,
size_t * out_guardsize)
{
pthread_descr new_thread;
char * new_thread_bottom;
char * guardaddr;
size_t stacksize, guardsize;
if (attr != NULL && attr->stackaddr_set)
{
/* The user provided a stack. */
new_thread =
(pthread_descr) ((long)(attr->stackaddr) & -sizeof(void *)) - 1;
new_thread_bottom = (char *) attr->stackaddr - attr->stacksize;
guardaddr = NULL;
guardsize = 0;
}
else
{
/* Allocate space for stack and thread descriptor at default address */
new_thread = default_new_thread;
new_thread_bottom = (char *) new_thread - STACK_SIZE;
if (mmap((caddr_t)((char *)(new_thread + 1) - INITIAL_STACK_SIZE),
INITIAL_STACK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_GROWSDOWN,
-1, 0) == MAP_FAILED)
/* Bad luck, this segment is already mapped. */
return -1;
/* We manage to get a stack. Now see whether we need a guard
and allocate it if necessary. Notice that the default
attributes (stack_size = STACK_SIZE - pagesize and
guardsize = pagesize) do not need a guard page, since
the RLIMIT_STACK soft limit prevents stacks from
running into one another. */
if (attr == NULL ||
attr->guardsize == 0 ||
(attr->guardsize == pagesize &&
attr->stacksize == STACK_SIZE - pagesize))
{
/* We don't need a guard page. */
guardaddr = NULL;
guardsize = 0;
}
else
{
/* Put a bad page at the bottom of the stack */
stacksize = roundup(attr->stacksize, pagesize);
if (stacksize >= STACK_SIZE - pagesize)
stacksize = STACK_SIZE - pagesize;
guardaddr = (void *)new_thread - stacksize;
guardsize = attr->guardsize;
if (mmap ((caddr_t) guardaddr, guardsize, 0, MAP_FIXED, -1, 0)
== MAP_FAILED)
{
/* We don't make this an error. */
guardaddr = NULL;
guardsize = 0;
}
}
}
*out_new_thread = new_thread;
*out_new_thread_bottom = new_thread_bottom;
*out_guardaddr = guardaddr;
*out_guardsize = guardsize;
return 0;
}
static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
void * (*start_routine)(void *), void *arg, void * (*start_routine)(void *), void *arg,
sigset_t * mask, int father_pid) sigset_t * mask, int father_pid)
@ -214,8 +287,9 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
pthread_descr new_thread; pthread_descr new_thread;
char * new_thread_bottom; char * new_thread_bottom;
pthread_t new_thread_id; pthread_t new_thread_id;
void *guardaddr = NULL; char *guardaddr = NULL;
size_t guardsize = 0; size_t guardsize = 0;
int pagesize = __getpagesize();
/* First check whether we have to change the policy and if yes, whether /* First check whether we have to change the policy and if yes, whether
we can do this. Normally this should be done by examining the we can do this. Normally this should be done by examining the
@ -223,51 +297,18 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
but this is hard to implement. FIXME */ but this is hard to implement. FIXME */
if (attr != NULL && attr->schedpolicy != SCHED_OTHER && geteuid () != 0) if (attr != NULL && attr->schedpolicy != SCHED_OTHER && geteuid () != 0)
return EPERM; return EPERM;
/* Find a free stack segment for the current stack */ /* Find a free segment for the thread, and allocate a stack if needed */
for (sseg = 1; ; sseg++) for (sseg = 1; ; sseg++)
{ {
if (sseg >= PTHREAD_THREADS_MAX) if (sseg >= PTHREAD_THREADS_MAX)
return EAGAIN; return EAGAIN;
if (__pthread_handles[sseg].h_descr != NULL) if (__pthread_handles[sseg].h_descr != NULL)
continue; continue;
if (pthread_allocate_stack(attr, thread_segment(sseg), pagesize,
if (attr == NULL || !attr->stackaddr_set) &new_thread, &new_thread_bottom,
{ &guardaddr, &guardsize) == 0)
new_thread = thread_segment(sseg);
new_thread_bottom = (char *) new_thread - STACK_SIZE;
/* Allocate space for stack and thread descriptor. */
if (mmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),
INITIAL_STACK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_GROWSDOWN,
-1, 0) != MAP_FAILED)
{
/* We manage to get a stack. Now see whether we need a guard
and allocate it if necessary. */
if (attr == NULL || attr->guardsize != 0)
{
guardsize = attr ? attr->guardsize : __getpagesize ();
guardaddr = mmap ((caddr_t)((char *)(new_thread+1)
- STACK_SIZE),
guardsize, 0, MAP_FIXED, -1, 0);
if (guardaddr == MAP_FAILED)
{
/* We don't make this an error. */
guardaddr = NULL;
guardsize = 0;
}
}
break; break;
} }
/* It seems part of this segment is already mapped. Try the next. */
}
else
{
new_thread = (pthread_descr) ((long) attr->stackaddr
& -sizeof(void *)) - 1;
new_thread_bottom = (char *) attr->stackaddr - attr->stacksize;
break;
}
}
/* Allocate new thread identifier */ /* Allocate new thread identifier */
pthread_threads_counter += PTHREAD_THREADS_MAX; pthread_threads_counter += PTHREAD_THREADS_MAX;
new_thread_id = sseg + pthread_threads_counter; new_thread_id = sseg + pthread_threads_counter;
@ -329,7 +370,7 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
/* Do the cloning */ /* Do the cloning */
pid = __clone(pthread_start_thread, (void **) new_thread, pid = __clone(pthread_start_thread, (void **) new_thread,
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
PTHREAD_SIG_RESTART, __pthread_sig_restart,
new_thread); new_thread);
/* Check if cloning succeeded */ /* Check if cloning succeeded */
if (pid == -1) { if (pid == -1) {
@ -492,7 +533,7 @@ static void pthread_handle_exit(pthread_descr issuing_thread, int exitcode)
for (th = issuing_thread->p_nextlive; for (th = issuing_thread->p_nextlive;
th != issuing_thread; th != issuing_thread;
th = th->p_nextlive) { th = th->p_nextlive) {
kill(th->p_pid, PTHREAD_SIG_CANCEL); kill(th->p_pid, __pthread_sig_cancel);
} }
/* Now, wait for all these threads, so that they don't become zombies /* Now, wait for all these threads, so that they don't become zombies
and their times are properly added to the thread manager's times. */ and their times are properly added to the thread manager's times. */
@ -505,7 +546,7 @@ static void pthread_handle_exit(pthread_descr issuing_thread, int exitcode)
_exit(0); _exit(0);
} }
/* Handler for PTHREAD_SIG_RESTART in thread manager thread */ /* Handler for __pthread_sig_restart in thread manager thread */
void __pthread_manager_sighandler(int sig) void __pthread_manager_sighandler(int sig)
{ {

View File

@ -22,6 +22,7 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/resource.h>
#include "pthread.h" #include "pthread.h"
#include "internals.h" #include "internals.h"
#include "spinlock.h" #include "spinlock.h"
@ -134,8 +135,8 @@ const int __pthread_offsetof_pid = offsetof(struct _pthread_descr_struct,
p_pid); p_pid);
/* Signal numbers used for the communication. */ /* Signal numbers used for the communication. */
int __pthread_sig_restart; int __pthread_sig_restart = DEFAULT_SIG_RESTART;
int __pthread_sig_cancel; int __pthread_sig_cancel = DEFAULT_SIG_CANCEL;
/* These variables are used by the setup code. */ /* These variables are used by the setup code. */
extern int _errno; extern int _errno;
@ -149,7 +150,7 @@ static void pthread_handle_sigrestart(int sig);
/* Initialize the pthread library. /* Initialize the pthread library.
Initialization is split in two functions: Initialization is split in two functions:
- a constructor function that blocks the PTHREAD_SIG_RESTART signal - a constructor function that blocks the __pthread_sig_restart signal
(must do this very early, since the program could capture the signal (must do this very early, since the program could capture the signal
mask with e.g. sigsetjmp before creating the first thread); mask with e.g. sigsetjmp before creating the first thread);
- a regular function called from pthread_create when needed. */ - a regular function called from pthread_create when needed. */
@ -160,6 +161,8 @@ static void pthread_initialize(void)
{ {
struct sigaction sa; struct sigaction sa;
sigset_t mask; sigset_t mask;
struct rlimit limit;
int max_stack;
/* If already done (e.g. by a constructor called earlier!), bail out */ /* If already done (e.g. by a constructor called earlier!), bail out */
if (__pthread_initial_thread_bos != NULL) return; if (__pthread_initial_thread_bos != NULL) return;
@ -172,6 +175,15 @@ static void pthread_initialize(void)
STACK_SIZE boundary. */ STACK_SIZE boundary. */
__pthread_initial_thread_bos = __pthread_initial_thread_bos =
(char *)(((long)CURRENT_STACK_FRAME - 2 * STACK_SIZE) & ~(STACK_SIZE - 1)); (char *)(((long)CURRENT_STACK_FRAME - 2 * STACK_SIZE) & ~(STACK_SIZE - 1));
/* Play with the stack size limit to make sure that no stack ever grows
beyond STACK_SIZE minus two pages (one page for the thread descriptor
immediately beyond, and one page to act as a guard page). */
getrlimit(RLIMIT_STACK, &limit);
max_stack = STACK_SIZE - 2 * __getpagesize();
if (limit.rlim_cur > max_stack) {
limit.rlim_cur = max_stack;
setrlimit(RLIMIT_STACK, &limit);
}
/* Update the descriptor for the initial thread. */ /* Update the descriptor for the initial thread. */
__pthread_initial_thread.p_pid = __getpid(); __pthread_initial_thread.p_pid = __getpid();
/* If we have special thread_self processing, initialize that for the /* If we have special thread_self processing, initialize that for the
@ -190,8 +202,8 @@ static void pthread_initialize(void)
{ {
/* The kernel does not support real-time signals. Use as before /* The kernel does not support real-time signals. Use as before
the available signals in the fixed set. */ the available signals in the fixed set. */
__pthread_sig_restart = SIGUSR1; __pthread_sig_restart = DEFAULT_SIG_RESTART;
__pthread_sig_cancel = SIGUSR2; __pthread_sig_cancel = DEFAULT_SIG_CANCEL;
} }
#endif #endif
/* Setup signal handlers for the initial thread. /* Setup signal handlers for the initial thread.
@ -201,14 +213,14 @@ static void pthread_initialize(void)
sigemptyset(&sa.sa_mask); sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; /* does not matter for regular threads, but sa.sa_flags = SA_RESTART; /* does not matter for regular threads, but
better for the thread manager */ better for the thread manager */
__sigaction(PTHREAD_SIG_RESTART, &sa, NULL); __sigaction(__pthread_sig_restart, &sa, NULL);
sa.sa_handler = pthread_handle_sigcancel; sa.sa_handler = pthread_handle_sigcancel;
sa.sa_flags = 0; sa.sa_flags = 0;
__sigaction(PTHREAD_SIG_CANCEL, &sa, NULL); __sigaction(__pthread_sig_cancel, &sa, NULL);
/* Initially, block PTHREAD_SIG_RESTART. Will be unblocked on demand. */ /* Initially, block __pthread_sig_restart. Will be unblocked on demand. */
sigemptyset(&mask); sigemptyset(&mask);
sigaddset(&mask, PTHREAD_SIG_RESTART); sigaddset(&mask, __pthread_sig_restart);
sigprocmask(SIG_BLOCK, &mask, NULL); sigprocmask(SIG_BLOCK, &mask, NULL);
/* Register an exit function to kill all other threads. */ /* Register an exit function to kill all other threads. */
/* Do it early so that user-registered atexit functions are called /* Do it early so that user-registered atexit functions are called
@ -254,7 +266,7 @@ int __pthread_initialize_manager(void)
__pthread_manager_reader = manager_pipe[0]; /* reading end */ __pthread_manager_reader = manager_pipe[0]; /* reading end */
__pthread_manager_thread.p_pid = pid; __pthread_manager_thread.p_pid = pid;
/* Make gdb aware of new thread manager */ /* Make gdb aware of new thread manager */
if (__pthread_threads_debug) raise(PTHREAD_SIG_CANCEL); if (__pthread_threads_debug) raise(__pthread_sig_cancel);
/* Synchronize debugging of the thread manager */ /* Synchronize debugging of the thread manager */
request.req_kind = REQ_DEBUG; request.req_kind = REQ_DEBUG;
__libc_write(__pthread_manager_request, (char *) &request, sizeof(request)); __libc_write(__pthread_manager_request, (char *) &request, sizeof(request));
@ -433,7 +445,7 @@ static void pthread_handle_sigrestart(int sig)
The debugging strategy is as follows: The debugging strategy is as follows:
On reception of a REQ_DEBUG request (sent by new threads created to On reception of a REQ_DEBUG request (sent by new threads created to
the thread manager under debugging mode), the thread manager throws the thread manager under debugging mode), the thread manager throws
PTHREAD_SIG_CANCEL to itself. The debugger (if active) intercepts __pthread_sig_cancel to itself. The debugger (if active) intercepts
this signal, takes into account new threads and continue execution this signal, takes into account new threads and continue execution
of the thread manager by propagating the signal because it doesn't of the thread manager by propagating the signal because it doesn't
know what it is specifically done for. In the current implementation, know what it is specifically done for. In the current implementation,

View File

@ -18,7 +18,7 @@
static inline void restart(pthread_descr th) static inline void restart(pthread_descr th)
{ {
kill(th->p_pid, PTHREAD_SIG_RESTART); kill(th->p_pid, __pthread_sig_restart);
} }
static inline void suspend(pthread_descr self) static inline void suspend(pthread_descr self)
@ -26,11 +26,11 @@ static inline void suspend(pthread_descr self)
sigset_t mask; sigset_t mask;
sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */ sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */
sigdelset(&mask, PTHREAD_SIG_RESTART); /* Unblock the restart signal */ sigdelset(&mask, __pthread_sig_restart); /* Unblock the restart signal */
do { do {
self->p_signal = 0; self->p_signal = 0;
sigsuspend(&mask); /* Wait for signal */ sigsuspend(&mask); /* Wait for signal */
} while (self->p_signal != PTHREAD_SIG_RESTART); } while (self->p_signal !=__pthread_sig_restart );
} }
static inline void suspend_with_cancellation(pthread_descr self) static inline void suspend_with_cancellation(pthread_descr self)
@ -39,7 +39,7 @@ static inline void suspend_with_cancellation(pthread_descr self)
sigjmp_buf jmpbuf; sigjmp_buf jmpbuf;
sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */ sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */
sigdelset(&mask, PTHREAD_SIG_RESTART); /* Unblock the restart signal */ sigdelset(&mask, __pthread_sig_restart); /* Unblock the restart signal */
/* No need to save the signal mask, we'll restore it ourselves */ /* No need to save the signal mask, we'll restore it ourselves */
if (sigsetjmp(jmpbuf, 0) == 0) { if (sigsetjmp(jmpbuf, 0) == 0) {
self->p_cancel_jmp = &jmpbuf; self->p_cancel_jmp = &jmpbuf;
@ -47,11 +47,11 @@ static inline void suspend_with_cancellation(pthread_descr self)
do { do {
self->p_signal = 0; self->p_signal = 0;
sigsuspend(&mask); /* Wait for a signal */ sigsuspend(&mask); /* Wait for a signal */
} while (self->p_signal != PTHREAD_SIG_RESTART); } while (self->p_signal != __pthread_sig_restart);
} }
self->p_cancel_jmp = NULL; self->p_cancel_jmp = NULL;
} else { } else {
sigaddset(&mask, PTHREAD_SIG_RESTART); /* Reblock the restart signal */ sigaddset(&mask, __pthread_sig_restart); /* Reblock the restart signal */
sigprocmask(SIG_SETMASK, &mask, NULL); sigprocmask(SIG_SETMASK, &mask, NULL);
} }
} }

View File

@ -26,18 +26,18 @@ int pthread_sigmask(int how, const sigset_t * newmask, sigset_t * oldmask)
if (newmask != NULL) { if (newmask != NULL) {
mask = *newmask; mask = *newmask;
/* Don't allow PTHREAD_SIG_RESTART to be unmasked. /* Don't allow __pthread_sig_restart to be unmasked.
Don't allow PTHREAD_SIG_CANCEL to be masked. */ Don't allow __pthread_sig_cancel to be masked. */
switch(how) { switch(how) {
case SIG_SETMASK: case SIG_SETMASK:
sigaddset(&mask, PTHREAD_SIG_RESTART); sigaddset(&mask, __pthread_sig_restart);
sigdelset(&mask, PTHREAD_SIG_CANCEL); sigdelset(&mask, __pthread_sig_cancel);
break; break;
case SIG_BLOCK: case SIG_BLOCK:
sigdelset(&mask, PTHREAD_SIG_CANCEL); sigdelset(&mask, __pthread_sig_cancel);
break; break;
case SIG_UNBLOCK: case SIG_UNBLOCK:
sigdelset(&mask, PTHREAD_SIG_RESTART); sigdelset(&mask, __pthread_sig_restart);
break; break;
} }
newmask = &mask; newmask = &mask;
@ -94,7 +94,7 @@ int sigaction(int sig, const struct sigaction * act,
{ {
struct sigaction newact; struct sigaction newact;
if (sig == PTHREAD_SIG_RESTART || sig == PTHREAD_SIG_CANCEL) if (sig == __pthread_sig_restart || sig == __pthread_sig_cancel)
return EINVAL; return EINVAL;
newact = *act; newact = *act;
if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL) if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL)
@ -116,9 +116,9 @@ int sigwait(const sigset_t * set, int * sig)
/* Get ready to block all signals except those in set /* Get ready to block all signals except those in set
and the cancellation signal */ and the cancellation signal */
sigfillset(&mask); sigfillset(&mask);
sigdelset(&mask, PTHREAD_SIG_CANCEL); sigdelset(&mask, __pthread_sig_cancel);
for (s = 1; s <= NSIG; s++) { for (s = 1; s <= NSIG; s++) {
if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL) if (sigismember(set, s) && s != __pthread_sig_cancel)
sigdelset(&mask, s); sigdelset(&mask, s);
} }
/* Test for cancellation */ /* Test for cancellation */

View File

@ -500,6 +500,18 @@ __libc_read (int fd, void *buf, size_t nbytes)
return nread; return nread;
} }
off_t weak_function
__lseek (int fd, off_t offset, int whence)
{
error_t err;
err = __io_seek ((mach_port_t) fd, offset, whence, &offset);
if (err)
return __hurd_fail (err);
return offset;
}
__ptr_t weak_function __ptr_t weak_function
__mmap (__ptr_t addr, size_t len, int prot, int flags, int fd, off_t offset) __mmap (__ptr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
{ {
@ -570,7 +582,6 @@ __fxstat (int vers, int fd, struct stat *buf)
return __hurd_fail (err); return __hurd_fail (err);
return 0; return 0;
} }
int weak_function int weak_function
@ -590,6 +601,19 @@ __xstat (int vers, const char *file, struct stat *buf)
return 0; return 0;
} }
pid_t weak_function
__getpid ()
{
pid_t pid, ppid;
int orphaned;
if (__proc_getpids (_dl_hurd_data->portarray[INIT_PORT_PROC],
&pid, &ppid, &orphaned))
return -1;
return pid;
}
void weak_function void weak_function
_exit (int status) _exit (int status)
{ {
@ -598,6 +622,29 @@ _exit (int status)
while (__task_terminate (__mach_task_self ())) while (__task_terminate (__mach_task_self ()))
__mach_task_self_ = (__mach_task_self) (); __mach_task_self_ = (__mach_task_self) ();
} }
/* Try to get a machine dependent instruction which will make the
program crash. This is used in case everything else fails. */
#include <abort-instr.h>
#ifndef ABORT_INSTRUCTION
/* No such instruction is available. */
# define ABORT_INSTRUCTION
#endif
void weak_function
abort (void)
{
/* Try to abort using the system specific command. */
ABORT_INSTRUCTION;
/* If the abort instruction failed, exit. */
_exit (127);
/* If even this fails, make sure we never return. */
while (1)
/* Try for ever and ever. */
ABORT_INSTRUCTION;
}
/* This function is called by interruptible RPC stubs. For initial /* This function is called by interruptible RPC stubs. For initial
dynamic linking, just use the normal mach_msg. Since this defn is dynamic linking, just use the normal mach_msg. Since this defn is