The support for the three multithreading APIs is complete in Gnulib since
2020-01-19, except for the documentation. Let me now document it.


2020-06-01  Bruno Haible  <br...@clisp.org>

        doc: New chapter 'Multithreading'.
        * doc/multithread.texi: New file.
        * doc/gnulib.texi: Include it.

>From fb5645d8d351d5d89a9bb4b6f3f0158548924514 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 2 Jun 2020 00:37:22 +0200
Subject: [PATCH] doc: New chapter 'Multithreading'.

* doc/multithread.texi: New file.
* doc/gnulib.texi: Include it.
---
 ChangeLog            |   6 ++
 doc/gnulib.texi      |   4 +
 doc/multithread.texi | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 228 insertions(+)
 create mode 100644 doc/multithread.texi

diff --git a/ChangeLog b/ChangeLog
index 94baf96..381ca88 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2020-06-01  Bruno Haible  <br...@clisp.org>
 
+	doc: New chapter 'Multithreading'.
+	* doc/multithread.texi: New file.
+	* doc/gnulib.texi: Include it.
+
+2020-06-01  Bruno Haible  <br...@clisp.org>
+
 	doc: Move 'Running self-tests under valgrind' section.
 	* doc/gnulib.texi (Build Infrastructure Modules): Include
 	valgrind-tests.texi here...
diff --git a/doc/gnulib.texi b/doc/gnulib.texi
index 68a81dc..ec6e633 100644
--- a/doc/gnulib.texi
+++ b/doc/gnulib.texi
@@ -71,6 +71,7 @@ Documentation License''.
 * Glibc Header File Substitutes::   Overriding system headers.
 * Glibc Function Substitutes::      Replacing system functions.
 * Native Windows Support::          Support for the native Windows platforms.
+* Multithreading::                  Multiple threads of execution.
 * Particular Modules::              Documentation of individual modules.
 * Regular expressions::             The regex module.
 * Build Infrastructure Modules::    Modules that extend the GNU Build System.
@@ -6649,6 +6650,9 @@ to POSIX that it can be treated like any other Unix-like platform.
 @include ld-output-def.texi
 
 
+@include multithread.texi
+
+
 @node Particular Modules
 @chapter Particular Modules
 
diff --git a/doc/multithread.texi b/doc/multithread.texi
new file mode 100644
index 0000000..b600517
--- /dev/null
+++ b/doc/multithread.texi
@@ -0,0 +1,218 @@
+@node Multithreading
+@chapter Multithreading
+
+Multithreading is a programming paradigm.  In a multithreaded program,
+multiple threads execute concurrently (or quasi concurrently) at different
+places in the program.
+
+There are three motivations for using multithreading in a program:
+@itemize @bullet
+@item
+Exploiting CPU hardware with multiple execution units.  Nowadays, many CPUs
+have 2 to 8 execution cores in a single chip.  Additionally, often multiple
+CPU chips are combined in a single package.  Thus, some CPU packages support
+64 or 96 simultaneous threads of execution.
+@item
+Simplifying program architecture.  When a program has to read from different
+file descriptors, network sockets, or event channels at the same time, the
+classical single-threaded architecture is to have a main loop which uses
+@code{select} or @code{poll} on all the descriptors and then dispatches
+according to from which descriptor input arrived.  In a multi-threaded
+program, you allocate one thread for each descriptor, and these threads can
+be programmed and managed independently.
+@item
+Offloading work from signal handlers.  A signal handler is not allowed to
+call @code{malloc}; therefore you are very limited in what you can do in
+a signal handler.  But a signal handler can notify a thread, and the thread
+can then do the appropriate processing, as complex as it needs to be.
+@end itemize
+
+A multithreading API offers
+@itemize @bullet
+@item
+Primitives for creating threads, for waiting until threads are terminated,
+and for reaping their results.
+@item
+Primitives through which different threads can operate on the same data or
+use some data structures for communicating between the threads.  These are
+called ``mutexes'' or ``locks''.
+@item
+Primitives for executing a certain (initialization) code at most once.
+@item
+Primitives for notifying one or more other threads.  These are called wait
+queues or ``condition variables''.
+@item
+Primitives for allowing different threads to have different values for a
+variable.  Such a variable is said to reside in ``thread-local storage'' or
+``thread-specific storage''.
+@item
+Primitives for relinquishing control for some time and letting other threads
+go.
+@end itemize
+
+Note: Programs that achieve multithreading through OpenMP (cf. the gnulib
+module @samp{openmp}) don't create and manage their threads themselves.
+Nevertheless, they need to use mutexes/locks in many cases.
+
+@menu
+* Multithreading APIs::
+* Choosing a multithreading API::
+* POSIX multithreading::
+* ISO C multithreading::
+* Gnulib multithreading::
+@end menu
+
+@node Multithreading APIs
+@section The three multithreading APIs
+
+Three multithreading APIs are available to Gnulib users:
+@itemize @bullet
+@item
+POSIX multithreading,
+@item
+ISO C multithreading,
+@item
+Gnulib multithreading.
+@end itemize
+
+They are supported on all platforms that have multithreading in one form or
+the other.  Currently, these are all platforms supported by Gnulib, except
+for Minix.
+
+The main differences are:
+@itemize @bullet
+@item
+The exit code of a thread is a pointer in the POSIX and Gnulib APIs, but
+only an @code{int} in the ISO C API.
+@item
+The POSIX API has additional facilities for detaching threads, setting the
+priority of a thread, assigning a thread to a certain set of processors,
+and much more.
+@item
+In the POSIX and ISO C APIs, most functions have a return code, and you
+are supposed to check the return code; even locking and unlocking a lock
+can fail.  In the Gnulib API, many functions don't have a return code; if
+they cannot complete, the program aborts.  This sounds harsh, but such
+aborts have not been reported in 12 years.
+@item
+In the ISO C API, the initialization of a statically allocated lock is
+clumsy: You have to initialize it through a once-only function.
+@end itemize
+
+@node Choosing a multithreading API
+@section Choosing the right multithreading API
+
+Here are guidelines for determining which multithreading API is best for
+your code.
+
+In programs that use advanced POSIX APIs, such as spin locks,
+detached threads (@code{pthread_detach}),
+signal blocking (@code{pthread_sigmask}),
+priorities (@code{pthread_setschedparam}),
+processor affinity (@code{pthread_setaffinity_np}), it is best to use
+the POSIX API.  This is because you cannot convert an ISO C @code{thrd_t}
+or a Gnulib @code{gl_thread_t} to a POSIX @code{pthread_t}.
+
+In code that is shared with glibc, it is best to use the POSIX API as well.
+
+In libraries, it is best to use the Gnulib API.  This is because it gives
+the person who builds the library an option
+@samp{--enable-threads=@{isoc,posix,windows@}}, that determines on which
+native multithreading API of the platform to rely.  In other words, with
+this choice, you can minimize the amount of glue code that your library
+needs to contain.
+
+In the other cases, the POSIX API and the Gnulib API are equally well suited.
+
+The ISO C API is never the best choice, as of this writing (2020).
+
+@node POSIX multithreading
+@section The POSIX multithreading API
+
+The POSIX multithreading API is documented in POSIX
+@url{https://pubs.opengroup.org/onlinepubs/9699919799/}.
+
+To make use of POSIX multithreading, even on platforms that don't support it
+natively (most prominently, native Windows), use the following Gnulib modules:
+@multitable @columnfractions .75 .25
+@headitem Purpose @tab Module
+@item For thread creation and management:@tie{} @tab @code{pthread-thread}
+@item For simple and recursive locks:@tie{} @tab @code{pthread-mutex}
+@item For read-write locks:@tie{} @tab @code{pthread-rwlock}
+@item For once-only execution:@tie{} @tab @code{pthread-once}
+@item For ``condition variables'' (wait queues):@tie{} @tab @code{pthread-cond}
+@item For thread-local storage:@tie{} @tab @code{pthread-tss}
+@item For relinquishing control:@tie{} @tab @code{sched_yield}
+@item For spin locks:@tie{} @tab @code{pthread-spin}
+@end multitable
+
+There is also a convenience module named @code{pthread} which depends on all
+of these (except @code{sched_yield}); so you don't need to enumerate these
+modules one by one.
+
+@node ISO C multithreading
+@section The ISO C multithreading API
+
+The ISO C multithreading API is documented in ISO C 11
+@url{http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf}.
+
+To make use of ISO C multithreading, even on platforms that don't support it
+or have severe bugs, use the following Gnulib modules:
+@multitable @columnfractions .85 .15
+@headitem Purpose @tab Module
+@item For thread creation and management:@tie{} @tab @code{thrd}
+@item For simple locks, recursive locks, and read-write locks:@tie{}
+      @tab @code{mtx}
+@item For once-only execution:@tie{} @tab @code{mtx}
+@item For ``condition variables'' (wait queues):@tie{} @tab @code{cnd}
+@item For thread-local storage:@tie{} @tab @code{tss}
+@end multitable
+
+There is also a convenience module named @code{threads} which depends on all
+of these; so you don't need to enumerate these modules one by one.
+
+@node Gnulib multithreading
+@section The Gnulib multithreading API
+
+The Gnulib multithreading API is documented in the respective include files:
+@itemize
+@item
+@code{<glthread/thread.h>}
+@item
+@code{<glthread/lock.h>}
+@item
+@code{<glthread/cond.h>}
+@item
+@code{<glthread/tls.h>}
+@item
+@code{<glthread/yield.h>}
+@end itemize
+
+To make use of Gnulib multithreading, use the following Gnulib modules:
+@multitable @columnfractions .85 .15
+@headitem Purpose @tab Module
+@item For thread creation and management:@tie{} @tab @code{thread}
+@item For simple locks, recursive locks, and read-write locks:@tie{}
+      @tab @code{lock}
+@item For once-only execution:@tie{} @tab @code{lock}
+@item For ``condition variables'' (wait queues):@tie{} @tab @code{cond}
+@item For thread-local storage:@tie{} @tab @code{tls}
+@item For relinquishing control:@tie{} @tab @code{yield}
+@end multitable
+
+The Gnulib multithreading supports a configure option
+@samp{--enable-threads=@{isoc,posix,windows@}}, that chooses the underlying
+thread implementation.  Currently (2020):
+@itemize @bullet
+@item
+@code{--enable-threads=posix} is supported and is the best choice on all
+platforms except for native Windows.  It may also work, to a limited extent,
+on mingw with the @code{winpthreads} library, but is not recommended there.
+@item
+@code{--enable-threads=windows} is supported and is the best choice on
+native Windows platforms (mingw and MSVC).
+@item
+@code{--enable-threads=isoc} is supported on all platforms that have the
+ISO C multithreading API.  However, @code{--enable-threads=posix} is always
+a better choice.
+@end itemize
-- 
2.7.4

Title: GNU Gnulib: Multithreading

14 Multithreading

Multithreading is a programming paradigm. In a multithreaded program, multiple threads execute concurrently (or quasi concurrently) at different places in the program.

There are three motivations for using multithreading in a program:

  • Exploiting CPU hardware with multiple execution units. Nowadays, many CPUs have 2 to 8 execution cores in a single chip. Additionally, often multiple CPU chips are combined in a single package. Thus, some CPU packages support 64 or 96 simultaneous threads of execution.
  • Simplifying program architecture. When a program has to read from different file descriptors, network sockets, or event channels at the same time, the classical single-threaded architecture is to have a main loop which uses select or poll on all the descriptors and then dispatches according to from which descriptor input arrived. In a multi-threaded program, you allocate one thread for each descriptor, and these threads can be programmed and managed independently.
  • Offloading work from signal handlers. A signal handler is not allowed to call malloc; therefore you are very limited in what you can do in a signal handler. But a signal handler can notify a thread, and the thread can then do the appropriate processing, as complex as it needs to be.

A multithreading API offers

  • Primitives for creating threads, for waiting until threads are terminated, and for reaping their results.
  • Primitives through which different threads can operate on the same data or use some data structures for communicating between the threads. These are called “mutexes” or “locks”.
  • Primitives for executing a certain (initialization) code at most once.
  • Primitives for notifying one or more other threads. These are called wait queues or “condition variables”.
  • Primitives for allowing different threads to have different values for a variable. Such a variable is said to reside in “thread-local storage” or “thread-specific storage”.
  • Primitives for relinquishing control for some time and letting other threads go.

Note: Programs that achieve multithreading through OpenMP (cf. the gnulib module ‘openmp’) don’t create and manage their threads themselves. Nevertheless, they need to use mutexes/locks in many cases.


14.1 The three multithreading APIs

Three multithreading APIs are available to Gnulib users:

  • POSIX multithreading,
  • ISO C multithreading,
  • Gnulib multithreading.

They are supported on all platforms that have multithreading in one form or the other. Currently, these are all platforms supported by Gnulib, except for Minix.

The main differences are:

  • The exit code of a thread is a pointer in the POSIX and Gnulib APIs, but only an int in the ISO C API.
  • The POSIX API has additional facilities for detaching threads, setting the priority of a thread, assigning a thread to a certain set of processors, and much more.
  • In the POSIX and ISO C APIs, most functions have a return code, and you are supposed to check the return code; even locking and unlocking a lock can fail. In the Gnulib API, many functions don’t have a return code; if they cannot complete, the program aborts. This sounds harsh, but such aborts have not been reported in 12 years.
  • In the ISO C API, the initialization of a statically allocated lock is clumsy: You have to initialize it through a once-only function.

14.2 Choosing the right multithreading API

Here are guidelines for determining which multithreading API is best for your code.

In programs that use advanced POSIX APIs, such as spin locks, detached threads (pthread_detach), signal blocking (pthread_sigmask), priorities (pthread_setschedparam), processor affinity (pthread_setaffinity_np), it is best to use the POSIX API. This is because you cannot convert an ISO C thrd_t or a Gnulib gl_thread_t to a POSIX pthread_t.

In code that is shared with glibc, it is best to use the POSIX API as well.

In libraries, it is best to use the Gnulib API. This is because it gives the person who builds the library an option ‘--enable-threads={isoc,posix,windows}’, that determines on which native multithreading API of the platform to rely. In other words, with this choice, you can minimize the amount of glue code that your library needs to contain.

In the other cases, the POSIX API and the Gnulib API are equally well suited.

The ISO C API is never the best choice, as of this writing (2020).


14.3 The POSIX multithreading API

The POSIX multithreading API is documented in POSIX https://pubs.opengroup.org/onlinepubs/9699919799/.

To make use of POSIX multithreading, even on platforms that don’t support it natively (most prominently, native Windows), use the following Gnulib modules:

PurposeModule
For thread creation and management: pthread-thread
For simple and recursive locks: pthread-mutex
For read-write locks: pthread-rwlock
For once-only execution: pthread-once
For “condition variables” (wait queues): pthread-cond
For thread-local storage: pthread-tss
For relinquishing control: sched_yield
For spin locks: pthread-spin

There is also a convenience module named pthread which depends on all of these (except sched_yield); so you don’t need to enumerate these modules one by one.


14.4 The ISO C multithreading API

The ISO C multithreading API is documented in ISO C 11 http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf.

To make use of ISO C multithreading, even on platforms that don’t support it or have severe bugs, use the following Gnulib modules:

PurposeModule
For thread creation and management: thrd
For simple locks, recursive locks, and read-write locks: mtx
For once-only execution: mtx
For “condition variables” (wait queues): cnd
For thread-local storage: tss

There is also a convenience module named threads which depends on all of these; so you don’t need to enumerate these modules one by one.


14.5 The Gnulib multithreading API

The Gnulib multithreading API is documented in the respective include files:

  • <glthread/thread.h>
  • <glthread/lock.h>
  • <glthread/cond.h>
  • <glthread/tls.h>
  • <glthread/yield.h>

To make use of Gnulib multithreading, use the following Gnulib modules:

PurposeModule
For thread creation and management: thread
For simple locks, recursive locks, and read-write locks: lock
For once-only execution: lock
For “condition variables” (wait queues): cond
For thread-local storage: tls
For relinquishing control: yield

The Gnulib multithreading supports a configure option ‘--enable-threads={isoc,posix,windows}’, that chooses the underlying thread implementation. Currently (2020):

  • --enable-threads=posix is supported and is the best choice on all platforms except for native Windows. It may also work, to a limited extent, on mingw with the winpthreads library, but is not recommended there.
  • --enable-threads=windows is supported and is the best choice on native Windows platforms (mingw and MSVC).
  • --enable-threads=isoc is supported on all platforms that have the ISO C multithreading API. However, --enable-threads=posix is always a better choice.

Reply via email to