B. Synchronization Primitives
Keywords: perfbook_html

#
B. Synchronization Primitives

All but the simplest parallel programs require synchronization primitives. This appendix gives a quick overview of a set of primitives based loosely on those in the Linux kernel.

Why Linux? Because it is one of the well-known, largest, and easily obtained bodies of parallel code available. We believe that reading code is, if anything, more important to learning than is writing code, so by using examples similar to real code in the Linux kernel, we are enabling you to use Linux to continue your learning as you progress beyond the confines of this book.

Why based loosely rather than following the Linux kernel API exactly? First, the Linux API changes with time, so any attempt to track it exactly would likely end in total frustration for all involved. Second, many of the members of the Linux kernel API are specialized for use in a production-quality operating-system kernel. This specialization introduces complexities that, though absolutely necessary in the Linux kernel itself, are often more trouble than they are worth in the ``toy'' programs that we will be using to demonstrate SMP and realtime design principles and practices. For example, properly checking for error conditions such as memory exhaustion is a ``must'' in the Linux kernel, however, in ``toy'' programs it is perfectly acceptable to simply abort() the program, correct the problem, and rerun.

Finally, it should be possible to implement a trivial mapping layer between this API and most production-level APIs. A pthreads implementation is available (`CodeSamples/api-pthreads/api-pthreads.h`), and a Linux-kernel-module API would not be difficult to create.

Quick Quiz B.1: Give an example of a parallel program that could be written without synchronization primitives. End Quick Quiz

The following sections describe commonly used classes of synchronization primitives. @@@ More esoteric primitives will be introduced in later revision.

Section covers organization/initialization primitives; Section presents thread creation, destruction, and control primitives; Section presents locking primitives; Section presents per-thread and per-CPU variable primitives; and Section gives an overview of the relative performance of the various primitives.

Subsections

- B.1 Organization and Initialization
  - B.1.1 smp_init():
- B.2 Thread Creation, Destruction, and Control
  - B.2.1 create_thread()
  - B.2.2 smp_thread_id()
  - B.2.3 for_each_thread()
  - B.2.4 for_each_running_thread()
  - B.2.5 wait_thread()
  - B.2.6 wait_all_threads()
  - B.2.7 Example Usage
- B.3 Locking
  - B.3.1 spin_lock_init()
  - B.3.2 spin_lock()
  - B.3.3 spin_trylock()
  - B.3.4 spin_unlock()
  - B.3.5 Example Usage
- B.4 Per-Thread Variables
  - B.4.1 DEFINE_PER_THREAD()
  - B.4.2 DECLARE_PER_THREAD()
  - B.4.3 per_thread()
  - B.4.4 __get_thread_var()
  - B.4.5 init_per_thread()
  - B.4.6 Usage Example
- B.5 Performance