Copyright © 1996
Last modified: Fri Oct 4 14:42:35 1996
$Revision: 1.7 $
0. Table of contents
This posting consists of answers to many of the questions most
frequently asked and summaries of the topics most frequently covered
on comp.programming.threads, the Usenet newsgroup for
discussion of issues in multithreaded programming. The purpose of
this posting is to circulate existing information, and to avoid
rehashing old topics of discussion and questions. Please read all
parts of this document before posting to this newsgroup.
|1.||Answers to frequently asked questions for comp.programming.threads: Part 1 of 1|
|2.1.||Reader contributions and comments|
|2.2.||How to read this FAQ|
|2.3.||Acknowledgments and caveats|
|3.||What are threads?|
|3.1.||Why are threads interesting?|
|3.2.||A little history|
|4.||What are the main families of threads?|
|5.1.||(DCE, POSIX, UI) Async safety|
|5.2.||Asynchronous and blocking system calls|
|6.||What are the different kinds of threads?|
|6.3.||Potential problems with functionality|
|7.||Where can I find books on threads?|
|7.3.||Books on implementations|
|7.4.||The POSIX threads standard|
|8.||Where can I obtain training on using threads?|
|9.||(Unix) Are there any freely-available threads packages?|
|10.||(DCE, POSIX, UI) Why does my threaded program not handle signals sensibly?|
|11.||(DCE?, POSIX) Why does everyone tell me to avoid asynchronous cancellation?|
|12.||Why are reentrant library and system call interfaces good?|
|12.1.||(DCE, POSIX, UI) When should I use thread-safe "_r" library calls?|
|13.||(POSIX) How can I perform a join on any thread?|
|14.||(DCE, UI, POSIX) After I create a certain number of threads, my program crashes|
|15.||Where can I find POSIX thread benchmarks?|
|16.||Does any DBMS vendor provide a thread-safe interface?|
|17.||Why is my threaded program running into performance problems?|
|18.||What tools will help me to program with threads?|
|19.||What operating systems provide threads?|
|20.||What about other threads-related software?|
|21.||Where can I find other information on threads?|
|21.1.||Articles appearing in periodicals|
|22.||Notice of copyright and permissions|
The FAQ is posted monthly to comp.programming.threads, in
multiple parts. It is also available on the World-Wide Web, at
<URL: http://www.serpentine.com/~bos/threads-faq>. You may prefer to
browse the FAQ on the Web rather than on Usenet, as it contains many
useful hyperlinks (and tables are readable, which is unfortunately not
the case for the text version).
Your contributions, comments, and corrections are welcomed; mail sent
to <firstname.lastname@example.org> will be dealt with as
quickly as I can manage. Generally, performing a reply or followup to
this article from within your newsreader should do the Right Thing.
While I am more than happy to include submissions of material for the
FAQ if they seem appropriate, it would make my life a lot easier if
such text were proof-read in advance, and kept concise. I don't have
as much time as I would like to digest 15K text files and summarise
them in three paragraphs for inclusion here. If you are interested in
contributing material, please see the to-do list at the end of part 3
of the FAQ.
Some headers in this FAQ are preceded by words in parentheses, such as
"(POSIX)". This indicates that the sections in question are specific
to a particular threads family, or to the implementation provided by a
Wherever it may not otherwise be obvious that a particular section
refers only to some families or implementations, you will find one or
more of the following key words to help you.
Although this FAQ has been the result of a co-operative effort, any
blame for inaccuracies and/or errors lies entirely with my work. I
would like to thank the following people for their part in
contributing to this FAQ:
A thread is an encapsulation of the flow of control in a program.
Most people are used to writing single-threaded programs - that
is, programs that only execute one path through their code "at a
time". Multithreaded programs may have several threads running
through different code paths "simultaneously".
|DCE||OSF/DCE threads (POSIX draft 4)|
|OS/2||IBM OS/2 threads|
|POSIX||POSIX 1003.1c-1995 standard threads|
|UI||Unix International threads|
|Unix||Of general relevance to Unix users|
|WIN32||Microsoft Win32 API threads|
Why are some phrases above in quotes? In a typical process in which
multiple threads exist, zero or more threads may actually be running
at any one time. This depends on the number of CPUs the computer on
which the process is running, and also on how the threads system is
implemented. A machine with n CPUs can, intuitively enough,
run no more than n threads in parallel, but it may give the
appearance of running many more than n "simultaneously", by
sharing the CPUs among threads.
A context switch between two threads in a single process is
considerably cheaper than a context switch between two
processes. In addition, the fact that all data except for stack and
registers are shared between threads makes them a natural vehicle for
expressing tasks that can be broken down into subtasks that can be run
If you are interested in reading about the history of threads, see the
relevant section of the comp.os.research FAQ at
There are two main families of threads:
These families can be further subdivided.
This family consists of three subgroups:
- POSIX-style threads, which generally run on Unix systems.
- Microsoft-style threads, which generally run on PCs.
Both DCE and UI threads are fairly compatible with the POSIX threads
standard, although converting from either to "real" POSIX threads will
require a moderate amount of work.
- "Real" POSIX threads, based on the IEEE POSIX 1003.1c-1995 (also
known as the ISO/IEC 9945-1:1996) standard, part of the ANSI/IEEE
1003.1, 1996 edition, standard. POSIX implementations
are, not surprisingly, the emerging standard on Unix systems.
- POSIX threads are usually referred to as Pthreads.
- You will
often see POSIX threads referred to as POSIX.1c threads, since 1003.1c is
the section of the POSIX standard that deals with threads.
- You may
also see references to draft 10 of POSIX.1c, which became the
- DCE threads are based on draft 4 (an early draft) of the POSIX
threads standard (which was originally named 1003.4a, and became
1003.1c upon standardisation). You may find these on some Unix
- Unix International (UI) threads, also known as Solaris threads,
are based on the Unix International threads standard (a close relative
of the POSIX standard). The only major Unix variants that support UI
threads are Solaris 2, from Sun, and UnixWare 2, from SCO.
Those few tardy Unix vendors who do not yet ship POSIX threads
implementations are expected to do so "real soon now". If you are
developing multithreaded applications from scratch on Unix, you would
do well to use POSIX threads.
This family consists of two subgroups, both originally developed by
Although both of these were originally implemented by Microsoft, they
have diverged somewhat over the years. Moving from one to the other
will require a moderate amount of work.
Mach and its derivatives (such as Digital UNIX) provide a threads
package called C threads. This is not very widely used.
The terms here refer to each other in a myriad of ways, so the best
way to navigate through this section is to read it, and then read it
again. Don't be afraid to skip forwards or backwards as the need
Some library routines can be safely called from within signal
handlers; these are referred to as async-safe. A thread that is
executing some async-safe code will not deadlock if it is interrupted
by a signal. If you want to make some of your own code async-safe,
you should block signals before you obtain any locks.
Most system calls, whether on Unix or other platforms, block (or
"suspend") the calling thread until they complete, and continue its
execution immediately following the call. Some systems also provide
asynchronous (or non-blocking) forms of these calls; the kernel
notifies the caller through some kind of out-of-band method when such
a system call has completed.
- WIN32 threads are the standard threads on Microsoft
Windows 95 and Windows NT.
- OS/2 threads are the standard threads on OS/2, from IBM.
Asynchronous system calls are generally much harder for the programmer
to deal with than blocking calls.
A context switch is the action of switching a CPU between executing
one thread and another (or transferring control between them). This
may involve crossing one or more protection boundary.
A critical section of code is one in which data that may be accessed
by other threads are inconsistent. At a higher level, a critical
section can be viewed as a section of code in which a guarantee you
make to other threads about the state of some data may not be true.
If other threads can access these data during a critical section, your
program may not behave correctly. This may cause it to crash, lock
up, produce incorrect results, or do just about any other unpleasant
thing you care to imagine.
Other threads are generally denied access to inconsistent data during
a critical section (usually through use of locks). If some of your
critical sections are too long, however, it may result in your code
A lightweight process (also known in some implementations,
confusingly, as a kernel thread) is a schedulable entity that
the kernel is aware of. On most systems, it consists of some
execution context and some accounting information (i.e. much less
than a full-blown process).
Several operating systems allow lightweight processes to be "bound" to
particular CPUs; this guarantees that those threads will only execute
on the specified CPUs.
If some piece of code is described as MT-safe, this indicates that it
can be used safely within a multithreaded program, and that it
supports a "reasonable" level of concurrency. This isn't very
interesting; what you, as a programmer using threads, need to worry
about is code that is not MT-safe. MT-unsafe code may use
global and/or static data. If you need to call MT-unsafe code from
within a multithreaded program, you may need to go to some effort to
ensure that only one thread calls that code at any time.
Wrapping a global lock around MT-unsafe code will generally let you
call it from within a multithreaded program, but since this does not
permit concurrent access to that code, it is not considered to make it
If you are trying to write MT-safe code using POSIX threads, you need
to worry about a few issues such as dealing correctly with locks
across calls to fork(2) (if you are wondering what to do,
read about the pthread_atfork(3) library call).
A protection boundary protects one software subsystem on a computer
from another, in such a way that only data that is explicitly shared
across such a boundary is accessible to the entities on both sides.
In general, all code within a protection boundary will have access to
all data within that boundary.
The canonical example of a protection boundary on most modern systems
is that between processes and the kernel. The kernel is protected
from processes, so that they can only examine or change its internal
state in certain strictly-defined ways.
Protection boundaries also exist between individual processes on most
modern systems. This prevents one buggy or malicious process from
wreaking havoc on others.
Why are protection boundaries interesting? Because transferring
control across them is expensive; it takes a lot of time and work.
Scheduling involves deciding what thread should execute next on a
particular CPU. It is usually also taken as involving the context
switch to that thread.
There are two main kinds of threads implementations:
There are several sets of differences between these different threads
User-space threads live without any support from the kernel; they
maintain all of their state in user space. Since the kernel does not
know about them, they cannot be scheduled to run on multiple
processors in parallel.
- User-space threads, and
- Kernel-supported threads.
Kernel-supported threads fall into two classes.
Because of its performance problems (caused by the need to cross
protection the user/kernel protection boundary twice for every
thread context switch), the former class has fewer members than does
the latter (at least on Unix variants). Both classes allow threads to
be run across multiple processors in parallel.
In terms of context switch time, user-space threads are the fastest,
with two-level threads coming next (all other things being equal).
However, if you have a multiprocessor, user-level threads can only be
run on a single CPU, while both two-level and pure kernel-supported
threads can be run on multiple CPUs simultaneously.
Because the kernel does not know about user threads, there is a danger
that ordinary blocking system calls will block the entire process
(this is bad) rather than just the calling thread. This means
that user-space threads libraries need to jump through hoops in order
to provide "blocking" system calls that don't block the entire
- In a "pure" kernel-supported system, the kernel is responsible for
scheduling all threads.
- Systems in which the kernel cooperates with a user-level library
to do scheduling are known as two-level, or hybrid,
systems. Typically, the kernel schedules LWPs, and the user-level
library schedules threads onto LWPs.
This problem also exists with two-level kernel-supported threads,
though it is not as acute as for user-level threads. What usually
happens here is that system calls block entire LWPs. This means that
if more threads exist than do LWPs and all of the LWPs are blocked in
system calls, then other threads that could potentially make forward
progress are prevented from doing so.
The Solaris threads library provides a reasonable solution to this
problem. If the kernel notices that all LWPs in a process are
blocked, it sends a signal to the process. This signal is caught by
the user-level threads library, which can create another LWP so that
the process will continue to make progress.
There are several books available on programming with threads, with
more due out in the near future. Note also that the programmer's
manuals that come with most systems that provide threads packages will
have sections on using those threads packages.
- Bil Lewis and Daniel J. Berg, Threads Primer. SunSoft
Press, ISBN 0-13-443698-9.
This is a good introduction to programming with threads for
programmers and managers. It concentrates on UI and POSIX threads,
but also covers use of OS/2 and WIN32 threads.
- Steve Kleiman, Devang Shah and Bart Smaalders, Programming With
Threads. SunSoft Press, ISBN 0-13-172389-8.
This book goes into considerably greater depth than the other SunSoft
Press offering, and is recommended for the working programmer who
expects to deal with threads on a day-to-day basis. It includes many
- Charles J. Northrup, Programming With Unix Threads.
John Wiley & Sons, ISBN 0-471-13751-0.
This book details the UI threads interface, focusing mostly on the
Unixware implementation. This is an introductory book.
If you are interested in how modern operating systems support threads
and multiprocessors, there are a few excellent books available that
may be of interest to you.
- Len Dorfman, Marc J. Neuberger, Effective Multithreading
with OS/2. Publisher and ISBN unknown.
This book covers the OS/2 threads API and contains many examples, but
doesn't have much by way of concepts.
- Thuan Q. Pham, Pankaj K. Garg, Multithreaded
Programming with Windows NT. Prentice Hall,
Not surprisingly, this book focuses on WIN32 threads, but it also
mentions other libraries in passing. It also deals with some
relatively advanced topics, and has a thorough bibliography.
To order ISO/IEC standard 9945-1:1996, which is also known as
ANSI/IEEE POSIX 1003.1-1995 (and includes 1003.1c, the part that deals
with threads), you can call +1-908-981-1393. The document reference
number in the IEEE publications catalogue is SH 94352-NYF, and
the price to US customers is $120 (shipping overseas costs extra).
- Curt Schimmel, Unix Systems for Modern Architectures.
Addison-Wesley, ISBN 0-201-63338-8.
- This book gives a lucid account of the work needed to get Unix
(or, for that matter, more or less anything else) working on a modern
system that incorporates multiple processors, each with its own
cache. While it has some overlap with the Vahalia book (see below),
it has a smaller scope, and thus deals with shared topics in more
- Uresh Vahalia, Unix Internals: the New Frontiers. Prentice
Hall, ISBN 0-13-101908-2.
- This is the best kernel internals book currently available. It
deals extensively with building multithreaded kernels, implementing
LWPs, and scheduling on multiprocessors. Given a choice, I would buy
both this and the Schimmel book.
- Ben Catanzaro, Multiprocessor System
Architectures. SunSoft Press, ISBN 0-13-089137-1.
- I don't know much about this book, but it deals with both the
hardware and software (kernel and user) architectures used to put
together modern multiprocessor systems.
Unless you are implementing a POSIX threads package, you should not
ever need to look at the POSIX threads standard. It is the last place
you should look if you wish to learn about threads!
Neither IEEE nor ISO makes standards available for free; please do not
ask whether the POSIX threads standard is available on the Web. It
Signals and threads do not mix well. 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.
- Xavier Leroy <email@example.com> has written a POSIX
threads implementation for Linux 2.x that uses pure kernel-supported
threads. While the package is currently in alpha testing, it is
allegedly very stable. For more information, see
- Michael T. Peterson <firstname.lastname@example.org> has written a
user-space POSIX and DCE threads package for Intel-based Linux
systems; it is called PCthreads. See
<URL: http://www.aa.net/~mtp/PCthreads.html> for more information.
- Christopher Provenzano <email@example.com> has written a
fairly portable implementation of draft 8 of the POSIX threads
<URL: http://www.mit.edu:8001/people/proven/pthreads.html> for further
Note: as far as I can see, development of this library has
halted (at least temporarily), and it still contains many serious
- Georgia Tech's OS group has a fairly portable user-level threads
implementation of the Mach C threads package. It is called Cthreads,
and can be found at
- Frank Müller, of the POSIX / Ada-Runtime Project (PART) has
made available an implementation of draft 6 of the POSIX 1003.4a
Pthreads specification, which runs under SunOS 4,
Solaris 2.x, SCO Unix, FreeBSD and Linux. For more
information, see <URL: file://ftp.cs.fsu.edu/pub/PART/PTHREADS/pthreads_ANNOUNCE>.
- Elan Feingold has written a threads package called ethreads; I
don't know anything about it, other than that it is available from
- QuickThreads is a toolkit for building threads packages, written
by David Keppel <firstname.lastname@example.org>. It is available
from <URL: ftp://ftp.cs.washington.edu/pub/qt-001.tar.Z>, with an
accompanying tech report at
The code as distributed includes ports for the Alpha, x86, 88000,
MIPS, SPARC, VAX, and KSR1.
When it comes to dealing with signals, the best thing you can do is
create a thread whose sole purpose is to handle signals for the entire
process. This thread should loop calling sigwait(2); this
allows it to deal with signals synchronously. You should also make
sure that all threads (including the one that calls
sigwait) have the signals you are interested in handling
blocked. Handling signals synchronously in this way greatly
Note, also, that sending signals to other threads within your own
process is not a friendly thing to do, unless you are careful with
signal masks. For an explanation, see the section on asynchronous
Finally, using sigwait and installing signals handlers for
the signals you are sigwaiting for is a bad idea.
Asynchronous cancellation of threads is, in general, evil. The reason
for this is that it is usually (very) difficult to guarantee that the
recipient of an asynchronous cancellation request will not be in a
critical section. If a thread should die in the middle of a critical
section, this will very likely cause your program to misbehave.
Code that can deal sensibly with asynchronous cancellation requests is
not referred to as async-safe; that means something else (see
the terminology section of the FAQ). You won't see much code around
that handles asynchronous cancellation requests properly, and you
shouldn't try write any of your own unless you have compelling reasons
to do so. Deferred cancellation is your friend.
There are two approaches to providing system calls and library
interfaces that will work with multithreaded programs. One is to
simply wrap all the appropriate code with mutexes, thereby
guaranteeing that only one thread will execute any such routine at a
While this approach mostly works, it provides terrible performance.
For functions that maintain state across multiple invocations
(e.g. strtok() and friends), this approach simply
doesn't work at all, hence the existence of "_r" interfaces
on many Unix systems (see below).
A better solution is to ensure that library calls can safely be
performed by multiple threads at once.
If your system provides threads, it will probably provide a set of
thread-safe variants of standard C library routines. A small number
of these are mandated by the POSIX standard, and many Unix vendors
provide their own useful supersets, including functions such as
Unfortunately, the supersets that different vendors support do not
necessarily overlap, so you can only safely use the standard
POSIX-mandated functions. The thread-safe routines are conceptually
"cleaner" than their stateful counterparts, though, so it is good
practice to use them wherever and whenever you can.
UI threads allow programmers to join on any thread that happens to
terminate by passing the appropriate argument to thr_join().
This is not possible under POSIX and, yes, there is a rationale behind
the absence of this feature.
Unix programmers are used to being able to call wait() in
such a way that it will return when "any" process exits, but expecting
this to work for threads can cause confusion for programmers trying to
use threads. The important thing to note here is that Unix processes
are based around a notion of parent and child; this is a notion that
is not present in most threads systems. Since threads don't
contain this notion, joining on "any" thread could have the
undesirable effect of having the join return once a completely
unrelated thread happened to exit.
In many (perhaps even most) threaded applications, you do not want to
be able to join with any thread in your process. Consider, for
example, a library call that one of your threads might make, which in
its turn might start a few threads and try to join on them. If
another of your threads, joining on "any" thread, happened to join on
one of the library call's threads, that would lead to incorrect
If you want to be able to join on any thread so that, for example, you
can keep track of the number of running threads, you can achieve the
same functionality by starting detached threads and having them
decrememnt a (suitably locked, of course) counter as they exit.
By default, threads are created non-detached. You need to perform a
join on each non-detached thread, or else storage will never be freed
up when they exit. As an alternative, you can create detached
threads, for which storage will be freed as soon as they exit. This
latter approach is generally better; you shouldn't create non-detached
threads unless you explicitly need to know when or if they exit.
I do not know of any benchmarks.
Oracle 7.3 and above provide thread-safe database client
interfaces, as do Sybase System 11.1 and above, and Informix
ESQL 7 and above.
If you are still using an older release of any of these products, it
is possible to hack together a set of intermediate thread-safe
interfaces to your database if you really need it, but this requires a
moderately large amount of work.
There are many possible causes for performance problems in
multithreaded programs. Given the scope for error, all I can do is
detail a few common pitfalls briefly, and point you at the section of
this FAQ on books about multithreaded programming.
- You can probably discount the performance of the threads package
you are using almost straight away, unless it is a user-level
package. If so, you may want to try to find out whether your whole
process is blocking when you execute certain system calls. Otherwise,
you should look at your own code unless you have a very strong reason
for believing that there may be a problem with your threads package.
- Look at the granularity of your locks. If a single lock protects
too much data for which there is contention, you will needlessly
serialise your code. On the other hand, if your locks protect very
small amounts of data, you will spend too much time obtaining and
releasing locks. If your vendor is worth their salt, they will have
a set of tools available that allow you to profile your program's
behaviour and look for areas of high contention for locks. Tools of
this kind are invaluable.
- The TNF Tools are a set of extensible tools that allow users to
gather and analyse trace information from the Solaris kernel and from
user processes and threads. They can be used to correlate user and
kernel thread activity, and also to determine such things as lock hold
times. They are available for free from Sun; for more information,
see <URL: http://opcom.sun.ca/toolpages/tnftools.html>.
- The debugger that comes with the DevPro compiler set from Sun
- GDB nominally understands threads, but only supports them (and in
a flaky way) under some versions of Irix and a few other systems
(mostly embedded machines).
|Vendor||OS version||Threads model||POSIX API||Notes|
||Digital UNIX 4.0||mixed||D4, 1c|
|Digital UNIX 3.2||kernel /
|OpenVMS 7.0 (Alpha)||see note 1||D4, 1c||user model
without patch kit|
|OpenVMS 7.0 (VAX)||user||D4, 1c|
||HP/UX 10.20||?||?||not yet
||AIX 4.1 & 4.2||kernel||D4, D7|
||Linux 1.2.13 and above||user / kernel||1c,
implementations for several packages|
||Windows NT & 95||kernel||DCE||DCE
kits layer on top of WIN32 threads|
||Irix 6.2||mixed||see note 2||sproc() is still
kernel / bound|
|Irix 6.1||kernel /
||Solaris 2.5 and above||mixed / system /
|Solaris 2.4||mixed / system /
bound||D8||available from Sun only upon request|
|Solaris 2.x||mixed / system /
bound||n/a||UI threads supported across all
||a purely user-level threads system, with threads multiplexed atop
a "traditional" Unix-style process
||threads are "kernel entities" with no context switches taking
place in user mode
||a thread may be explicitly bound to a particular processor
||a mixed-mode scheduler where user threads are multiplexed across
some number of LWPs
||threads have "system" contention scope (a user thread may be
permanently bound to an LWP)
||an LWP may be permanently bound to a particular processor
||no POSIX threads API provided
||conforms to the POSIX 1003.1c-1995 threads API
||POSIX 1003.4a draft 4 API is available as part of the
OSF DCE kit for the platform
||DCE threads (or something similar) is bundled with the system
||POSIX 1003.4a draft 7 API (similar to the final 1003.1c
standard, but substantially different in some details)
||POSIX 1003.4a draft 8 API (identical in most respects to the
1003.1c standard, but with a few "gotchas")
- OpenVMS 7.0 for Alpha shipped with
kernel threads support disabled by default. The "mixed" threads model
can be turned on by setting the MULTITHREAD sysgen
parameter. With patch kit, the "mixed" or "user" model is determined
by the main program binary (i.e. via the linker or the
threadcp qualifier) in addition to MULTITHREAD.
- SGI ships the POSIX 1003.1c API as a patch
for Irix 6.2, but it will be bundled with future revisions of the
- Douglas C. Schmidt <email@example.com> has
written a package called ACE (Adaptive Communication
Environment). ACE supports multithreading on Unix and WIN32
platforms, and integrates popular IPC mechanisms (sockets, RPC,
System V IPC) and a host of other features that C++ programmers
will find useful. For details, see
Answers to Frequently Asked Questions for comp.programming.threads
(hereafter referred to as These Articles) are Copyright © 1996 by
Bryan O'Sullivan (hereafter referred to as The Author).
- An introduction to programming with threads, at
from SunWorld Online's
February 1996 issue
- An introduction to programming with threads, at
Magazine's November 1994 issue
- Porting DCE threads to AIX 4.1 (POSIX draft 7), at
Magazine's August 1994 issue
- A less thorough introduction to programming with threads, at
Magazine's August 1995 issue
- Using signals with POSIX threads, at
Magazine's August 1995 issue
These Documents are provided "as is". The information in them is not
warranted to be correct; you use it at your own risk. The Author
makes no representation or warranty as to the suitability of
the information in These Documents, either express or implied,
including but not limited to the implied warranties of fitness for a
particular purpose or non-infringement. The Author shall not be
liable for any damages suffered as a result of using information
distributed in These Documents or any derivatives.
These Documents may be reproduced and distributed in whole or in part,
subject to the following conditions:
Exceptions to these rules may be granted, and I shall be happy to
answer any questions about this copyright notice - write to Bryan
O'Sullivan, PO Box 62215, Sunnyvale, CA 94088-2215, USA or email
<firstname.lastname@example.org>. These restrictions are here to
protect the contributors, not to restrict you as educators,
professionals and learners.
- This copyright and permission notice must be retained on all
complete or partial copies of These Articles.
- These Articles may be copied or distributed in part or in full for
personal or educational use. Any translation, derivative work, or
copies made for other purposes must be approved by the copyright
holder before distribution, unless otherwise stated.
- If you distribute These Articles, instructions for obtaining the
complete current versions of them free or at cost price must be
included. Redistributors must make reasonable efforts to maintain
current copies of These Articles.