threads(5) DG/UX 5.4R3.00 threads(5)
NAME
threads, pthreads - DG/UX support for Posix Threads
DESCRIPTION
Threads are an emerging model for expressing parallelism within a
process in the UNIX® operating system.
On a system that provides hardware and software support for parallel
execution, the use of threads can increase the speed of execution by
providing an application programmer with the ability to use all
available processors simultaneously. Even on a uniprocessor system,
threads are useful for mapping asynchronous behavior into equivalent
synchronous behavior such as providing I/O parallelism, controlling
asynchronous computations, or structuring applications composed of
many logically distinct tasks (e.g., simulations and windowing
systems).
The thread model is based on a well understood synchronous,
procedural model consistent with the C function model. Threads can
be used to model the parallelism inherent in windowing environments,
realtime event processing, Ada tasking, transaction processing, and
networked/distributed systems.
A thread is a single sequential flow of control within a process.
Each thread has a minimal amount of private state; most of the state
associated with a process is shared among all of the threads in the
process.
This man page has the following sections:
⊕ Posix Threads Under DG/UX
⊕ Compiling and Linking
⊕ Header Files
⊕ Per-Thread Errno
⊕ Portability and Sanity Considerations
⊕ Basic Thread Management
⊕ Thread Attributes Objects
⊕ Thread-Specific Data
⊕ Threads in Familiar Unix Contexts
⊕ Signals
⊕ Thread Cancellation
⊕ Synchronization Primitives
Licensed material--property of copyright holder(s) 1
threads(5) DG/UX 5.4R3.00 threads(5)
⊕ Reentrant Functions
⊕ Debugging Threads Under DG/UX
⊕ Thread Scheduling
⊕ DG/UX Thread Groups
⊕ DG/UX Hierarchical CPU Affinity
Posix Threads Under DG/UX
Posix Threads, or Pthreads, provide an industry-wide standard for
threads. This standard is formally known as the Threads Extension
for Portable Operating Systems, or IEEE 1003.4a. A related standard
is IEEE 1003.4, the realtime extension to Posix.
Because Pthreads are not yet an official standard, DG/UX provides
support for Draft 6 of Pthreads (dated February 26, 1992). In order
to allow easy migration from draft 6 to future drafts and the final
standard, DG/UX plans to support draft 6 for at least one major
revision beyond DG/UX R3.00. This flexibility will give application
writers ample time to migrate their code to these future drafts. In
fact, DG/UX plans to support multiple drafts concurrently, allowing
applications to incrementally migrate their code. As discussed in
the next section, the target draft is selected on the compile line.
DG/UX also provides numerous extensions to Pthreads, which are
described briefly in later sections of this man page. Most notably,
DG/UX allows applications to create groups of computationally related
threads that can be scheduled together onto sets of physically close
CPUs on the underlying machine. Thread Groups and Hierarchical CPU
Affinity are unique features of DG/UX. However, these DG-specific
features can be easily isolated in application code, maintaining
portability to other vendors' thread implementations, while
optimizing performance on Data General systems.
Compiling and Linking
Compiling Pthread programs is only supported under the DG/UX Elf
environment (the default environment). Because Pthreads is not yet a
standard, the desired draft of Pthreads must be specified on the
compile line. For now, draft 6 is the only draft that can be
selected. The Pthread functions and other DG/UX extensions are
implemented in libthread.so, which can only be linked shared.
The following example illustrates how to compile, using the GNU C
compiler, a module named foo.c, which uses Draft 6 of Pthreads:
# gcc -D_POSIX4A_DRAFT6_SOURCE -o foo.o foo.c
The following example illustrates how to link this program named foo:
# gcc -o foo foo.o -lthread -ldgc
At runtime, /usr/lib/libthread.so, /usr/lib/libdgc.so.1 and
Licensed material--property of copyright holder(s) 2
threads(5) DG/UX 5.4R3.00 threads(5)
/usr/dglib/libc.so.1 are linked in dynamically. This allows DG/UX to
supply new revisions of these libraries without requiring that
applications be relinked. This is particularly important as the
implementation of libthread.so is closely related to the
implementation of the DG/UX kernel, and the reentrant libraries are
closely related to the implementation of libthread.so.
Header Files
It is sufficient to #include <pthread.h> in order to gain access to
all threads functionality, including DG/UX extensions for thread
groups. The following list includes other common and internal header
files related to Pthreads and DG/UX extensions for thread groups and
hierarchical CPU affinity:
<pthread.h> Main header file for Pthreads
<sys/pthreads.h> Included by <pthread.h> and contains
draft-specific macros that map from
pthread_create to
__d6_pthread_create, etc.
<sys/_int__d6_pthread.h> Included by <sys/pthread.h> and
contains the actual Draft 6
declarations of __d6_pthread_create,
etc.
<timers.h> Definition of struct timespec
<sched.h> 1004.4 realtime scheduling policies
<sys/dg_lwp.h> DG/UX LWPs, LWP groups, and
dg_lwp_info
<sys/dg_cpu.h> DG/UX dg_cpu_info and hierarchical
CPU affinity
Per-Thread Errno
In a multi-threaded program, each thread must have its own copy of
errno because multiple threads could be making system calls at the
same time. The global definition of errno does not suffice because
multiple threads could stomp on it at the same time. Instead, when a
program is compiled for threads, the definition of errno becomes a
macro that ensures that each thread has its own copy of errno and can
use the name errno as an l-value or r-value.
In order to insure that all DG/UX system calls used by your
application will use the per-thread errno, be sure to include -ldgc
on the linker command as shown above.
In Draft 6 of Pthreads, errno is set upon return from Pthread
functions that fail and return -1. In subsequent drafts, the errno
value is instead returned by Pthread functions that fail. However,
errno will still be set by existing Posix and Unix functions.
Portability and Sanity Considerations
This section suggests the use of icing macros in order to facilitate
error checking and portability to future drafts of Pthreads. It also
discusses portable feature probing for Pthreads functionality.
Failing to check the return value from Pthread functions will cause
Licensed material--property of copyright holder(s) 3
threads(5) DG/UX 5.4R3.00 threads(5)
errors to go undetected. However, error checking can be cumbersome
and can make code more difficult to read. The use of icing macros
can help hide this error checking and avoid extra typing.
Applications should also consider portability to future drafts of
Pthreads. In particular, they must deal with the difference in the
way errno is set in Draft 6 vs. returned in future drafts, and must
handle some gratuitous name changes for functions and literals.
Icing macros can also help to hide these differences.
The following example illustrates the use of an icing macro to
perform error checking on a call to pthread_mutex_lock() and to hide
its interface under draft 6. The macro aborts on error:
#define zthread_mutex_lock(mutex_ptr) \
{ \
if (pthread_mutex_lock(mutex_ptr) != 0) \
{ \
fprintf(stderr \
"pthread_mutex_lock failed in file %s \
at line %d, errno = %d\n", \
__FILE__, __LINE__, errno ); \
abort(); \
} \
}
Posix provides a portable paradigm for inquiring about the features
of Pthreads that are supported by the underlying system. Queries can
be made at compile time by testing to see if certain literals are
defined. This, combined with runtime checks using sysconf(),
provides the best assurance as to whether or not the underlying
system supports particular features. The following example tests to
see if the running system supports Pthreads (in general):
#include <unistd.h>
#ifdef _POSIX_THREADS
if (sysconf(_SC_THREADS)) == 1)
{
/* do threads stuff */
}
#endif
DG/UX supports the following features in draft 6 of Pthreads:
_POSIX_THREADS Pthreads, in general
_POSIX_REENTRANT_FUNCTIONS Reentrant library functions
_POSIX_THREAD_ATTR_STACKSIZE Thread stacksize attribute
_POSIX_THREAD_PRIORITY_SCHEDULING Fixed priority realtime
scheduling
_POSIX_THREADS_PROCESS_SHARED Interprocess mutexes and
conditions
DG/UX does not currently support the following realtime features for
Licensed material--property of copyright holder(s) 4
threads(5) DG/UX 5.4R3.00 threads(5)
mutexes:
_POSIX_THREADS_PRIO_INHERIT Priority inheritance
protocol for mutexes
_POSIX_THREADS_PRIO_PROTECT Priority ceiling protocol
for mutexes
Basic Thread Management
The following Pthread functions create threads, exit threads, wait
for threads to terminate, and manipulate thread IDs:
pthread_create Create a thread
pthread_join Wait for a thread to terminate and clean
it up
pthread_exit Terminate a thread normally
pthread_detach Cause a thread to clean up for itself
when it exits
pthread_self Get the caller's thread ID
pthread_equal Compare two thread IDs
DG/UX provides the following extensions for miscellaneous thread
management:
dg_pthread_sleep Sleep with nanosecond granularity
dg_pthread_get_lwpid Translate a thread ID to an underlying
LWPID
dg_lwp_info Get information about a process's LWPs
Thread Attributes Objects
Thread attributes objects provide a portable and extensible way of
overriding the default creation attributes, such as stack size, for
newly created threads. They also allow an application to change the
scheduling attributes, such as policy and priority, for an existing
thread.
Basic thread attributes manipulation functions are:
pthread_attr_init Initialize a thread attributes
object
pthread_attr_destroy Delete a thread attributes object
pthread_attr_setstacksize Set stack size thread attribute
pthread_attr_getstacksize Retrieve stack size thread
attribute
pthread_attr_setdetachstate Set detach state thread attribute
pthread_attr_getdetachstate Retrieve detach state thread
attribute
Functions for manipulating the scheduling attributes for new and
existing threads are discussed below in the section titled Thread
Scheduling.
Thread-Specific Data
Thread-specific data allows each thread in a process to have
different values for the the same variable name. These variable
Licensed material--property of copyright holder(s) 5
threads(5) DG/UX 5.4R3.00 threads(5)
names are called keys.
Thread-specific data functions are:
pthread_key_create Create a new key (variable name)
pthread_setspecific Set the caller's per-thread value for a
key
pthread_getspecific Get the caller's per-thread value for a
key
Threads in Familiar Unix Contexts
In general, all passive process state is shared by all threads in the
same process, but threads wait on things separately.
For the user address space, all user address space is accessible by
all threads. This implies that a thread can write over any other
thread's stack. All threads share the same malloc pool. If one
thread maps a file, it becomes visible to all threads. However,
threads can page fault separately; the page becomes resident for all
threads in the process.
All file descriptors are shared by all threads. If one thread opens
a file, it will be open for all threads. However, threads block
separately in calls to read(), write(), and select() on the same
file, just as different processes would. If one thread obtains a
file or record lock, it will be obtained for all threads in the
process; this is one of the only exceptions to the "threads wait
separately" rule.
Similarly, all open streams and pipes are accessible by all threads.
If there are multiple threads blocked in a stream, the behavior is
similar to when multiple processes are blocked in the same stream.
All threads share the same controlling TTY. Signals implicitly sent
by streams are sent to the process as a whole.
The process's PID, PPID, PGID, SID, etc. are the same for all
threads. The process's credentials and security attributes are
shared by all threads (with a shared address space there is no point
in having per-thread security attributes). All threads share the
same profiling buffer. By default, threads share the same global
process priority, but each thread can have its own local priority
within the process.
IPC descriptors for Unix semaphores and message queues are shared by
all threads. However, each thread waits separately. Similar
arguments apply to Posix 1003.4 realtime semaphores and message
queues.
When a thread calls exit(), either explicitly or implicitly as a
result of returning from main(), all threads are aborted. No thread-
specific data destructor functions or cancellation handlers are
called. The system pulls the threads out from wherever they are. If
multiple threads are exiting at the same time, one will get there
first and the others will be aborted. When the last thread in a
Licensed material--property of copyright holder(s) 6
threads(5) DG/UX 5.4R3.00 threads(5)
process terminates with a call to pthread_exit(), the whole process
terminates; however, file buffers are not automatically flushed.
When a thread calls exec, the exec'ing thread will change the address
space to that for the new image and all other threads will be
aborted. If multiple threads are exec'ing at the same time, only one
will succeed and the others will be aborted.
When a thread forks a new process, the parent process is not changed.
The child process will contain a copy of the forking thread; no other
threads will exist in the child process. This implies that the child
process cannot safely do much else besides call exec(), as a thread
that didn't make it into the child process could hold a critical
resource whose state in the new process reflects that the missing
thread holds it.
Signals
In general, signals are slow and error-prone, and should be avoided
in multithreaded programs. Signal handlers, in particular, can lead
to deadlocks in multithreaded applications if they are not used
correctly.
The following discussion concentrates on the generation and handling
of signals in the context of multithreaded programs. It then
proposes a safer way, using sigwait(2), to handle signals in a
multithreaded program.
There are only two ways to generate a signal for a specific thread.
Firstly, the signal could have been synchronously generated by the
thread; this results from a SIGSEGV, SIGBUS, SIGFPE, SIGILL,
raise(3C) call, or abort(3C) call. Secondly, the signal could have
been sent with a call to pthread_kill() from a thread in the same
process as the target thread.
All other generated signals are set to the process as a whole. This
includes, but is not limited to, signals sent by kill(), signals
generated asynchronously by any software timer such as setitimer(),
and most signals generated by the operating system.
Signals sent to the process are delivered to exactly one thread. The
signal is delivered to a thread that has expressed "interest" in the
signal. If no thread is interested in the signal, the signal remains
pending for the process as a whole until a thread takes interest in
it.
A thread expresses interest in a signal by unblocking it or waiting
for the signal synchronously by calling sigwait(2). When a signal is
delivered to the process, the signal will be made pending first for a
thread in the middle of sigwait() for the signal, then to a thread
that has the signal unblocked, if any. In the case of ties, an
arbitrary thread is chosen in each case.
A thread will process a pending signal at any time. If the signal is
a terminate signal (e.g., SIGKILL), the entire process and all of its
Licensed material--property of copyright holder(s) 7
threads(5) DG/UX 5.4R3.00 threads(5)
threads are aborted. If the signal is a stop signal (e.g., SIGSTOP),
the entire process is stopped until the process is SIGCONTed.
If a thread invokes a handler, it can occur at any point in the
thread's execution. Hence there isn't much that a signal handler can
safely do. For example, it can't call printf() because the thread
may have been signaled out of printf(), where it may hold a critical
mutex. A function is said to be async-safe if it can be called from
a signal handler. The only Pthread functions that are async-safe
are: pthread_mutex_trylock(), pthread_cond_signal(), and
pthread_cond_broadcast(). If synchronization must be performed in
signal handlers, it should be done using traditional Unix or Posix
semaphores, as mutexes and condition functions are not async-safe.
For these reasons, signal handlers should be avoided in multithreaded
programs. The recommended way to handle signals in a multithreaded
program is to dedicate one or more threads to synchronously wait for
signals using calls to sigwait(). Such calls return with the signal
number caught instead of executing signal handlers; thus upon return,
any Pthread function can be executed. The proper setup for using
sigwait() is as follows. Firstly, have the main thread block all
signals during program initialization. Because the blocked mask is
inherited across pthread_create(), all created threads will inherit
the all-blocked mask. Secondly, have the main thread create one or
more threads dedicated to handling signals. Each of these threads
will call sigwait() to synchronously wait for the signals for which
it is responsible. Any signals directed to the process are
guaranteed to be delivered to these special threads because all
threads in the process have all signals blocked.
Finally, just because signals can be used safely does not imply that
they should be used at all. Thread creation is orders of magnitude
faster than signal handling. Realtime applications should, whenever
possible, use threads instead of signals for asynchronous event
creation.
Thread Cancellation
The Pthread cancellation mechanism allows an application to cleanly
abort another thread in an asynchronous fashion. Each thread can
control whether or not it will accept cancellation and where it will
accept cancellation. By default, threads only accept cancellation at
certain cancellation (interruption) points. In general, a
cancellation point is any standard function call where a thread could
block for an indefinite period of time; this includes, but is not
limited to, all system calls that can return EINTR. In addition,
each thread can push one more cleanup handlers onto a stack of
handlers; when a thread is cancelled, it will implicitly pop these
handlers in reverse order and execute them. Cleanup handlers can
thus be used to release critical resources held by a canceled thread.
Thread cancellation functions are:
pthread_cancel Cancel a thread
pthread_setintr Enable or disable cancellation
Licensed material--property of copyright holder(s) 8
threads(5) DG/UX 5.4R3.00 threads(5)
pthread_setintrtype Allow controlled or asynchronous
cancellation
pthread_testintr Create an explicit cancellation point
pthread_cleanup_push Push a cleanup handler
pthread_cleanup_pop Pop a cleanup handler
Synchronization Primitives
Pthreads provide synchronization primitives in the form of mutexes
and condition variables. Mutexes allow applications to "lock" data
that are write-shared by multiple threads. Conditions allow
applications to arbitrarily suspend and wake up threads. Mutexes and
conditions are typically used together to implement more complex
synchronization constructs. Moreover, mutexes and conditions can be
configured for use among multiple processes that are using shared
memory.
Mutex functions are:
pthread_mutex_init Initialize a mutex
pthread_mutex_destroy Destroy a mutex
pthread_mutex_lock Acquire a mutex lock
pthread_mutex_trylock Acquire a mutex lock without
waiting
pthread_mutex_unlock Release a mutex lock
Condition functions are:
pthread_cond_init Initialize a condition
pthread_cond_destroy Destroy a condition
pthread_cond_wait Wait for a condition
pthread_cond_timedwait Wait for a condition or timeout
pthread_cond_signal Wakeup at least one waiting thread
pthread_cond_broadcast Wakeup all waiting threads
Mutexes and conditions also have attributes objects that can be used
to initialize them with non-default properties. In particular, the
DG/UX system supports the process-shared attribute, which allows
mutexes and conditions to be used by multiple processes.
Mutex attribute functions are:
pthread_mutexattr_init Initialize default attributes
object for a mutex
pthread_mutexattr_destroy Destroy attributes object for a
mutex
pthread_mutexattr_setpshared Set process-shared attribute for a
mutex
pthread_mutexattr_getpshared Get process-shared attribute for a
Licensed material--property of copyright holder(s) 9
threads(5) DG/UX 5.4R3.00 threads(5)
mutex
Condition attribute functions are:
pthread_condattr_init Initialize default attributes
object for a condition
pthread_condattr_destroy Destroy attributes object for a
condition
pthread_condattr_setpshared Set condition's process-shared
attribute
pthread_condattr_getpshared Get condition's process-shared
attribute
The following miscellaneous function can be used to synchronize the
initialization of a dynamically initialized package:
pthread_once Initialize a package dynamically
Reentrant Functions
Applications which call C library functions from threads should only
call those library functions which are known to be reentrant. For a
list of reentrant functions and guidelines for their use, see
reentrant(3).
Debugging Threads Under DG/UX
Threaded programs can be easily debugged using features of Data
General's MXDB debugger. The following MXDB commands are related to
thread debugging:
thread-status View the status of all threads
focus Focus to a particular thread
mutex-status Interrogate a mutex
cond-status Interrogate a condition variable
Note that, breakpoints on Pthread functions must be prefixed by the
draft number. For example, for draft 6, a breakpoint should be set
at __d6_pthread_create instead of pthread_create. These prefixes
allow DG/UX to provide peaceful coexistence of multiple drafts in
future releases.
Thread Scheduling
Thread attributes objects can also be used to set the scheduling
attributes of newly created threads, or to set or retrieve the
scheduling attributes of existing threads.
Scheduling attribute functions are:
pthread_attr_setscope Set the contention scope
attribute
pthread_attr_getscope Get the contention scope
attribute
pthread_attr_setinheritsched Set the inherit-scheduling
attribute
pthread_attr_getinheritsched Get the inherit-scheduling
Licensed material--property of copyright holder(s) 10
threads(5) DG/UX 5.4R3.00 threads(5)
attribute
pthread_attr_setsched Set the scheduling policy
attribute
pthread_attr_getsched Get the scheduling policy
attribute
pthread_attr_setprio Set the scheduling priority
attribute
pthread_attr_getprio Get the scheduling priority
attribute
pthread_setschedattr Change the scheduling
attributes of an existing
thread
pthread_getschedattr Retrieve the scheduling
attributes of an existing
thread
In addition, the pthread_yield() function causes the calling thread
to yield to other threads of equal or better priority.
DG/UX Thread Groups
DG/UX provides extensions for thread groups, which allow sets of
computationally-related threads to be scheduled together for purposes
of CPU affinity. Affinity is particularly important for large
multithreaded processes running on machines with multiple CPUs.
Thread groups can be used alone to give hints to the DG/UX system
about the desired groupings of threads. They can also be used with
the hierarchical CPU affinity calls described in the next section to
manually affine thread groups to sets of CPUs that reside close
together on the underlying machine.
Basic thread group functions are:
dg_pthread_group_create Create an empty thread group
dg_pthread_group_destroy Destroy a thread group
dg_pthread_group_self Get the caller's thread group
ID
dg_pthread_group_equal Compare two thread group IDs
dg_pthread_group_get_lwp_group_id Translate a thread group ID
to an LWP group ID
dg_pthread_group_get_times Get the accumulated CPU times
for a thread group
Thread group attributes objects can be used to establish the
scheduling attributes of newly created thread groups, and to change
or retrieve the scheduling attributes of an existing thread group:
dg_pthread_groupattr_init Initialize a thread group
attributes object
dg_pthread_groupattr_destroy Delete a thread group
attributes object
dg_pthread_groupattr_setinheritsched Set the inherit-scheduling
attribute
dg_pthread_groupattr_getinheritsched Get the inherit-scheduling
attribute
Licensed material--property of copyright holder(s) 11
threads(5) DG/UX 5.4R3.00 threads(5)
dg_pthread_groupattr_setsched Set the scheduling policy
attribute
dg_pthread_groupattr_getsched Get the scheduling policy
attribute
dg_pthread_groupattr_setprio Set the scheduling priority
attribute
dg_pthread_groupattr_getprio Get the scheduling priority
attribute
dg_pthread_group_setschedattr Change the scheduling
attributes of an existing
thread group
dg_pthread_group_getschedattr Retrieve the scheduling
attributes of an existing
thread group
Once a thread group has been created, a new thread can be created in
that thread group by specifying the thread group ID as an attribute
to the thread's creation. This can be done in a well-isolated way
using the following thread attributes object functions:
dg_pthread_attr_setgroup Set the thread group ID
attribute for created threads
dg_pthread_attr_setgroup Get the thread group ID
attribute for created threads
DG/UX Hierarchical CPU Affinity
DG/UX provides a hierarchical CPU affinity feature that can be used
to explicitly affine thread groups (actually their LWP groups) to
sets of CPUs backed by the same secondary cache or local memory.
Hierarchical CPU affinity, when combined with thread groups, can
significantly improve the performance of large multithreaded programs
running on multiprocessor machines. Refer to <sys/dg_cpu.h> and
related functions for more details on hierarchical CPU affinity.
The following functions manipulate opaque sets of CPU IDs, which are
used in subsequent CPU-related calls:
dg_cpu_id_set_init Initialize a CPU ID set to
empty
dg_cpu_id_set_destroy Destroy a CPU ID set
dg_cpu_id_set_add_id Add a CPU ID to a CPU ID set
dg_cpu_id_set_remove_id Remove a CPU ID from a CPU ID
set
dg_cpu_id_set_is_member Test a CPU ID for inclusion
in a CPU ID set
dg_cpu_id_set_assign_set Assign one CPU ID set to
another
dg_cpu_id_set_add_set Compute the union of two CPU
ID sets
dg_cpu_id_set_remove_set Compute the difference
between two CPU ID sets
dg_cpu_id_set_has_members Test a CPU ID set for
inclusion in another
Licensed material--property of copyright holder(s) 12
threads(5) DG/UX 5.4R3.00 threads(5)
The following functions can be used to query the CPU/cache/memory
hierarchy of the underlying machine:
dg_cpu_info_init Initialize a struct
dg_cpu_info
dg_cpu_info_destroy Destroy a struct dg_cpu_info
dg_cpu_info Retrieve the CPU/cache/memory
hierarchy information for one
CPU
The following functions can be used to package up affinity attributes
for affected LWP groups:
dg_cpu_affinity_attr_init Initialize an affinity
attributes object
dg_cpu_affinity_attr_destroy Destroy an affinity
attribtutes object
dg_cpu_affinity_attr_set_cpu_id_set Set the allowed-CPU-ID-set
attribute
dg_cpu_affinity_attr_get_cpu_id_set Get the allowed-CPU-ID-set
attribute
dg_cpu_affinity_attr_set_minimum_level Set the allowed-minimum-
migration-level attribute
dg_cpu_affinity_attr_get_minimum_level Get the allowed-minimum-
migration-level attribute
The following functions can be used to set or retrieve affinity
attributes for LWP groups:
dg_cpu_set_affiniy Set affinity attributes for
one or more LWP groups
dg_cpu_get_affiniy Get affinity attributes for
an LWP group
SEE ALSO
reentrant(3), mxdb(1).
Licensed material--property of copyright holder(s) 13