1. Answers to frequently asked questions for comp.programming.threads: Part 1 of 1

Copyright © 1996
Bryan O'Sullivan

Last modified: Fri Oct 4 14:42:35 1996
$Revision: 1.7 $

0. Table of contents

1.Answers to frequently asked questions for comp.programming.threads: Part 1 of 1
2.Introduction
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?
4.1.POSIX-style threads
4.2.Microsoft-style threads
4.3.Others
5.Some terminology
5.1.(DCE, POSIX, UI) Async safety
5.2.Asynchronous and blocking system calls
5.3.Context switch
5.4.Critical section
5.5.Lightweight process
5.6.MT safety
5.7.Protection boundary
5.8.Scheduling
6.What are the different kinds of threads?
6.1.Architectural differences
6.2.Performance differences
6.3.Potential problems with functionality
7.Where can I find books on threads?
7.1.POSIX-style threads
7.2.Microsoft-style 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

2. Introduction

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.

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).

2.1. Reader contributions and comments

Your contributions, comments, and corrections are welcomed; mail sent to <threads-faq@serpentine.com> 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.

2.2. How to read this 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 specific vendor.

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.
KeyImplementation
DCEOSF/DCE threads (POSIX draft 4)
OS/2IBM OS/2 threads
POSIXPOSIX 1003.1c-1995 standard threads
UIUnix International threads
UnixOf general relevance to Unix users
WIN32Microsoft Win32 API threads

2.3. Acknowledgments and caveats

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:
Dave Butenhof <butenhof@zko.dec.com>
Bil Lewis <bil@lambdaCS.com>

3. What are threads?

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".

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.

3.1. Why are threads interesting?

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 cooperatively.

3.2. A little history

If you are interested in reading about the history of threads, see the relevant section of the comp.os.research FAQ at <URL: http://www.serpentine.com/~bos/os-faq>.

4. What are the main families of threads?

There are two main families of threads: These families can be further subdivided.

4.1. POSIX-style threads

This family consists of three subgroups: 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.

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.

4.2. Microsoft-style threads

This family consists of two subgroups, both originally developed by Microsoft. 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.

4.3. Others

Mach and its derivatives (such as Digital UNIX) provide a threads package called C threads. This is not very widely used.

5. Some terminology

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 appears.

5.1. (DCE, POSIX, UI) Async safety

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.

5.2. Asynchronous and blocking system calls

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.

Asynchronous system calls are generally much harder for the programmer to deal with than blocking calls.

5.3. Context switch

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.

5.4. Critical section

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 performing poorly.

5.5. Lightweight process

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.

5.6. MT safety

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 MT-safe.

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).

5.7. Protection boundary

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.

5.8. Scheduling

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.

6. What are the different kinds of threads?

There are two main kinds of threads implementations: There are several sets of differences between these different threads implementations.

6.1. Architectural differences

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.

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.

6.2. Performance differences

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.

6.3. Potential problems with functionality

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 process.

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.

7. Where can I find books on threads?

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.

7.1. POSIX-style threads

Bil Lewis and Daniel J. Berg, Threads Primer. SunSoft Press, ISBN 0-13-443698-9. <URL: http://www.sun.com/smi/ssoftpress/books/Lewis/Lewis.html>
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. <URL: http://www.sun.com/smi/ssoftpress/books/Kleiman/Kleiman.html>
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 detailed examples.
Charles J. Northrup, Programming With Unix Threads. John Wiley & Sons, ISBN 0-471-13751-0. <URL: http://www.wiley.com/compbooks/catalog/14/13751-0.html>
This book details the UI threads interface, focusing mostly on the Unixware implementation. This is an introductory book.

7.2. Microsoft-style threads

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, ISBN 0-131-20643-5. <URL: http://www.prenhall.com/013/120642/12064-2.html>
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.

7.3. Books on implementations

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.
Curt Schimmel, Unix Systems for Modern Architectures. Addison-Wesley, ISBN 0-201-63338-8. <URL: http://www.aw.com/cp/schimmel.html>
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 detail.
Uresh Vahalia, Unix Internals: the New Frontiers. Prentice Hall, ISBN 0-13-101908-2. <URL: http://www.prenhall.com/013/101907/10190-7.html>
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. <URL: http://www.sun.com/smi/ssoftpress/books/Catanzaro/Catanzaro.html>
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.

7.4. The POSIX threads standard

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).

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 isn't.

8. Where can I obtain training on using threads?

OrganisationContactDescription
Sun Microsystems<training_seats@Sun.COM>
+1-408-276-3630
Classes at Sun and on-site classes
Lambda Computer Science
(Bil Lewis)
<URL: http://www.lambdaCS.com>
+1-415-328-8952
Seminars and on-site classes
Phoenix Technologies
(Chris Crenshaw)
<phnxtech@attmail.com>
+1-908-286-2118

Marc Staveley<marc@staveley.com>
Init AB
(Mats Löfström)
<mla@init.se>
+46-8650-9700
Regular classes in Stockholm

9. (Unix) Are there any freely-available threads packages?

10. (DCE, POSIX, UI) Why does my threaded program not handle signals sensibly?

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.

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 simplifies things.

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 cancellation.

Finally, using sigwait and installing signals handlers for the signals you are sigwaiting for is a bad idea.

11. (DCE?, POSIX) Why does everyone tell me to avoid asynchronous cancellation?

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.

12. Why are reentrant library and system call interfaces good?

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 time.

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.

12.1. (DCE, POSIX, UI) When should I use thread-safe "_r" library calls?

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 gethostbyname_r().

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.

13. (POSIX) How can I perform a join on any thread?

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 program behaviour.

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.

14. (DCE, UI, POSIX) After I create a certain number of threads, my program crashes

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.

15. Where can I find POSIX thread benchmarks?

I do not know of any benchmarks.

16. Does any DBMS vendor provide a thread-safe interface?

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.

17. Why is my threaded program running into performance problems?

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.

18. What tools will help me to program with threads?

19. What operating systems provide threads?

VendorOS versionThreads modelPOSIX APINotes
Digital Digital UNIX 4.0mixedD4, 1c
Digital UNIX 3.2kernel / boundD4
OpenVMS 7.0 (Alpha)see note 1D4, 1cuser model without patch kit
OpenVMS 7.0 (VAX)userD4, 1c
OpenVMS 6.2userD4
HP HP/UX 10.20??not yet announced
HP/UX 10.10userD4
IBM AIX 4.1 & 4.2kernelD4, D7
AIX 3.5.xuserDCE
OS/2kernelDCE
Linux Linux 1.2.13 and aboveuser / kernel1c, DCEsee free implementations for several packages
Linux 2.xkerneln/aclone() interface
Microsoft Windows NT & 95kernelDCEDCE kits layer on top of WIN32 threads
SGI Irix 6.2mixedsee note 2sproc() is still kernel / bound
Irix 6.1kernel / boundn/asproc() interface only
Sun Solaris 2.5 and abovemixed / system / bound1c
Solaris 2.4mixed / system / boundD8available from Sun only upon request
Solaris 2.xmixed / system / boundn/aUI threads supported across all releases
SunOS 4.xusern/aLWP only
Threads modelMeaning
user
a purely user-level threads system, with threads multiplexed atop a "traditional" Unix-style process
kernel
threads are "kernel entities" with no context switches taking place in user mode

/ bound a thread may be explicitly bound to a particular processor
mixed
a mixed-mode scheduler where user threads are multiplexed across some number of LWPs

/ system threads have "system" contention scope (a user thread may be permanently bound to an LWP)

/ bound an LWP may be permanently bound to a particular processor
API Meaning
n/a no POSIX threads API provided
1c conforms to the POSIX 1003.1c-1995 threads API
DCE POSIX 1003.4a draft 4 API is available as part of the OSF DCE kit for the platform
D4 DCE threads (or something similar) is bundled with the system
D7 POSIX 1003.4a draft 7 API (similar to the final 1003.1c standard, but substantially different in some details)
D8 POSIX 1003.4a draft 8 API (identical in most respects to the 1003.1c standard, but with a few "gotchas")
  1. 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.
  2. SGI ships the POSIX 1003.1c API as a patch for Irix 6.2, but it will be bundled with future revisions of the OS.

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

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).

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 <bos@serpentine.com>. These restrictions are here to protect the contributors, not to restrict you as educators, professionals and learners.