HP OpenVMS Systems Documentation
Guide to the POSIX Threads Library
A.2.4 Compiling Applications With the tis Interface
Applications that use the Compaq-proprietary thread-independent
services (or tis) interface should include the
header file and link against the shared C run-time library (
Under Tru64 UNIX Version 4.0 and later, the Threads Library implements a two-level scheduling model. The thread library schedules "user threads" onto kernel execution contexts (often known as "kernel threads" or "virtual processors"), just as Tru64 UNIX schedules processes onto the processors of a multiprocessing machine.
A user thread is executed on a kernel thread until it blocks or exhausts its timeslice quantum. Then, the Threads Library schedules a new user thread to run. While the Threads Library is scheduling user threads onto kernel threads, the Tru64 UNIX kernel is independently scheduling those kernel threads to run on physical processors. The term "two-level scheduling" refers to this relationship.
This division allows most thread scheduling to take place completely in user mode, without the intervention of the kernel. Since a thread context switch does not involve any privileged information, it can be done much more efficiently in user mode.
The key to making the two-level scheduling model work is efficient
two-way communication between the Threads Library and the Tru64 UNIX
kernel. When a thread blocks in the kernel, the Threads Library
scheduler is notified so that it can schedule another thread to take
advantage of the idle kernel thread. This mechanism, sometimes referred
to as an upcall, is inspired by original research on
scheduler activations at the University of Washington. (See
Scheduler Activations: Effective Kernel Support for the User-Level
Management of Parallelism by Anderson, Bershad, Lazowska, and
Levy; ACM Operating Systems Review Volume 25, Number 5, Proceedings
of the Thirteenth ACM Symposium on Operating Systems Principles,
October 13-16, 1991).
Tru64 UNIX kernel threads are created as they are needed by the application. The number of kernel threads that the Threads Library creates is limited by normal Tru64 UNIX configuration limits regarding user and system thread creation. Normally, however, the Threads Library creates one kernel thread for each actual processor on the system, plus a "manager thread" for bookkeeping operations.
The Threads Library does not delete these kernel threads or let them terminate. Kernel threads not currently needed are retained in an idle state until they are needed again. (These idled kernel threads are deleted by the kernel if they remain idle for a long time.) When the process terminates, all kernel threads in the process are reclaimed by the kernel.
The Threads Library scheduler can schedule any user thread onto any
kernel thread. Therefore, a user thread can run on different kernel
threads at different times. Normally, this should pose no problem.
However, for example, the kernel thread ID as reported by the dbx or
Ladebug debuggers (in "native" $threadlevel) can change at
The Threads Library supports Tru64 UNIX realtime scheduling. This allows you to set the scheduling policy and priority of threads. By default, threads are created using process contention scope. This means that the full range of POSIX.1 scheduling policy and priority is available. However, threads running in process contention scope do not preempt lower-priority threads in another process. For example, a thread in process contention scope with SCHED_FIFO policy and maximum priority 63 will not preempt a thread in another process running with SCHED_FIFO and lower priority.
In contrast, system contention scope means that each thread created by the program has a direct and unique binding to one kernel execution context. A system contention scope thread competes against all threads in the system and will preempt any thread with lower priority. For this reason, the priority range of threads in system contention scope is restricted unless running with root privilege.
Specifically, a thread with SCHED_FIFO policy cannot run at a priority higher than 18 without privilege, since doing so could lock out all other users on the system until the thread blocked. Threads at any other scheduling policy (including SCHED_RR ) can run at priority 19 because they are subject to periodic timeslicing by the system. For more information, see the Tru64 UNIX Realtime Programming Guide.
If your program lacks necessary privileges, attempting to call the following routines for a thread in system contention scope returns the error value [EPERM]:
Prior to Tru64 UNIX Version 4.0, all threads used only system
contention scope. In Tru64 UNIX Version 4.0, all threads created using
the pthread interface, by default, have process
Tru64 UNIX supports the required system cancelation points specified by the POSIX.1 standard and by the Single UNIX Specification, Version 2 (UNIX98).
For legacy multithreaded applications, note that threads created using the cma or d4 interfaces will not be cancelable at any system call. (Here "system call" means any function without the pthread_ prefix.) If system call cancelation is required, you must write code using the pthread interface.
For more information, see Section 2.3.7.
Note that appropriate non-standard functions that do not appear in the
preceding list might become cancelation points in the future. Tru64
UNIX will also implement new cancelation points, as specified by future
revisions of the relevant formal or consortium standard bodies.
This section discusses signal handling based on the POSIX.1 standard.
Tru64 UNIX Version 4.0 introduced the full POSIX.1 signal model. In previous versions, "synchronous" signals (those resulting from execution errors, such as SIGSEGV and SIGILL) could have different signal actions for each thread. Prior to Tru64 UNIX Version 3.2, all threads shared a common, processwide signal mask, which meant one thread could not receive a signal while another had the signal blocked.
Under Tru64 UNIX Version 4.0 and later, all signal actions are processwide. That is, when any thread uses sigaction or equivalent to either set a signal handler, or to modify the signal action (for example, to ignore a signal), that action will affect all threads. Each thread has a private signal mask so that it can block signals without affecting the behavior of other threads.
Prior to Tru64 UNIX Version 4.0, asynchronous signals were processed only in the main thread. In Tru64 UNIX Version 4.0, any thread that does not have the signal masked can process the signal.
A.5.1 POSIX sigwait Service
The POSIX 1003.1 sigwait() service allows any thread to block until one of a specified set of signals is delivered. A thread can wait for any of the asynchronous signals except for SIGKILL and SIGSTOP.
For example, you can create a thread that blocks on a sigwait() routine for SIGINT, rather than handling a Ctrl/C in the normal way. This thread could then cancel other threads to cause the program to shut down the current activities.
Following are two reasons for avoiding signals:
In a multithreaded program, signal handlers cannot be used in a modular way because there is only one signal handler routine for all of the threads in an application. If two threads install different signal handlers for the signal, all threads will dispatch to the last handler when they receive the signal.
Most applications should avoid using asynchronous programming techniques with threads. For example, techniques that rely on timer and I/O signals are usually more complicated and errorprone than techniques that rely on simply waiting synchronously within a thread. Furthermore, most of the thread services are not supported for use in signal handlers, and most run-time library functions cannot be used reliably inside a signal handler.
Some I/O intensive code may benefit from asynchronous I/O, but these programs will generally be more difficult to write and maintain than "pure" threaded code.
A thread should not wait for a synchronous signal. This is because synchronous signals are the result of an error during the execution of a thread, and if the thread is waiting for a signal, then it is not executing. Therefore, a synchronous signal cannot occur for a particular thread while it is waiting, and the thread will wait forever.
The POSIX.1 standard requires that the thread block the signals for
which it will wait before calling
. For reliable operation, the signals should be blocked in all threads.
Otherwise, the signal might be delivered to another thread before the
, or after it has returned with another signal.
For the signals traditionally representing synchronous errors in the program, the Threads Library catches the signal and converts it into an equivalent exception. This exception is then propagated up the call stack in the current thread and can be caught and handled using the normal exception catching mechanisms.
Table A-3 lists Tru64 UNIX signals that are reported as exceptions by default. If any thread declares an action for one of these signals (using sigaction(2) or equivalent), no thread in the process can receive the exception.
A.6 Thread Stack Guard Areas
When creating a thread based on a thread attributes object, the Threads
Library potentially rounds up the value specified in the object's
guardsize attribute. The Threads Library does so based on the value of
the configurable system variable
. The default value of the guardsize attribute in a thread attributes
object is the number of bytes equal to the setting of
Starting in Version 5.0, for threads that accept the default stack address attribute, the Threads Library allocates a thread's writable stack area from uncommitted virtual memory, then commits predefined increments of the writable stack area to the thread only as it is needed. The stack's corresponding backing store is also reserved incrementally as the stack is committed. In this way, no more backing store is reserved than the stack actually requires.
Because Tru64 UNIX 5.0 does not commit backing store (or physical
pages) for stacks until the pages are used by the program, the default
stack size has been increased. The previous default of about 24Kb (3
pages) has been increased to 5Mb.
Dynamic activation of the Threads Library run-time environment, or of
code that depends on the Threads Library, is currently not supported.
Like normal file I/O operations, pagefaults are "thread synchronous". A thread that incurs a "hard" pagefault (reading the page from backing store) will be blocked while other threads continue to run on the "virtual processor" (or on others). This has implications for realtime scheduling, especially of SCHED_FIFO policy threads, that do not expect to block except for explicit I/O synchronization. To write a SCHED_FIFO thread that cannot block unexpectedly, you must use mlockall to lock the application into memory, preventing pagefaults.