[OpenVMS documentation]
[Site home] [Send comments] [Help with this site] [How to order documentation] [OpenVMS site] [Compaq site]
Updated: 11 December 1998

Guide to DECthreads


Previous Contents Index

2.3.2.2.2 Comparing Throughput and Real-Time Policies

The default throughput scheduling policy is intended to be an "adaptive" policy, giving each thread an opportunity to execute based on its behavior. That is, for a thread that doesn't execute often, DECthreads tends to give it high access to the processor because it isn't greatly affecting other threads. On the other hand, DECthreads tends to schedule with less preference any compute-bound threads with throughput scheduling policy.

This yields a responsive system in which all threads with throughput scheduling policy get a chance to run fairly frequently. It also has the effect of automatically resolving priority inversions, because over time any threads that have received less processing time (among those with throughput scheduling policy) will rise in preference while the running thread drops, and eventually the inversion is reversed.

The FIFO and RR scheduling policies are considered "real-time" policies, because they require DECthreads to schedule such threads strictly by the specified priority. Because threads that use real-time scheduling policies require additional DECthreads overhead, incautious use of the FIFO or RR policies can cause the performance of the application to suffer.

If relative priorities of threads are important to your application---that is, if a compute-bound thread really requires consistently predictable execution---then create those threads using either the FIFO or RR scheduling policy. However, use of "real-time" policies can expose the application to unexpected performance problems, such as priority inversions, and therefore their use should be avoided in most applications.

2.3.2.2.3 Portability of Scheduling Policy Settings

Only the SCHED_FIFO and SCHED_RR scheduling policies are portable across POSIX.1c-conformant implementations. The other scheduling policies are DECthreads extensions to the POSIX.1c standard.

Note

The SCHED_OTHER identifier is portable, but the POSIX.1c standard does not specify the behavior that it signifies. For example, on non-DECthreads platforms the SCHED_OTHER scheduling policy could be identical to the SCHED_FIFO or SCHED_RR policy.

2.3.2.3 Setting the Scheduling Parameters Attribute

The scheduling parameters attribute specifies the execution priority of a thread. (Although the terminology and format are designed to allow adding more scheduling parameters in the future, only priority is currently defined.) The priority is an integer value, but each policy can allow only a restricted range of priority values. You can determine the range for any policy by calling the sched_get_priority_min() or sched_get_priority_max() routines. DECthreads also supports a set of nonportable symbols designating the priority range for each policy, as follows:
Low High
PRI_FIFO_MIN PRI_FIFO_MAX
PRI_RR_MIN PRI_RR_MAX
PRI_OTHER_MIN PRI_OTHER_MAX
PRI_FG_MIN_NP PRI_FG_MAX_NP
PRI_BG_MIN_NP PRI_BG_MAX_NP

Section 2.3.6 describes how to specify a priority between the minimum and maximum values, and it also discusses how priority affects thread scheduling.

Use either of two techniques to set a thread attributes object's scheduling parameters attribute:

2.3.2.4 Setting the Stacksize Attribute

The stacksize attribute represents the minimum size (in bytes) of the memory required for a thread's stack. To increase or decrease the size of the stack for a new thread, call the pthread_attr_setstacksize() routine and use this thread attributes object when creating the thread and stack. You must specify at least PTHREAD_STACK_MIN bytes.

After a thread has been created, your program cannot change the size of the thread's stack. See Section 3.4.1 for more information about sizing a stack.

2.3.2.5 Setting the Stack Address Attribute

The stack address attribute represents the location or address of a region of memory that your program allocates to use as a thread's stack. The value of the stack address attribute represents the origin of the thread's stack. However, please be aware that the actual address you specify, relative to the stack memory you have allocated, is inherently nonportable.

To set the address of the stack origin for a new thread, call the
pthread_attr_setstackaddr() routine, specifying an initialized thread attributes object as an argument, and use the thread attributes object when creating the new thread. Use the pthread_attr_getstackaddr() routine to obtain the value of the stack address attribute of an initialized thread attributes object.

After a thread has been created, your program cannot change the address of the thread's stack.

You cannot create two concurrent threads that use the same stack address.

The system uses an unspecified (and varying) amount of the stack to "bootstrap" a newly created thread.

2.3.2.6 Setting the Guardsize Attribute

The guardsize attribute represents the minimum size (in bytes) of the guard area for the stack of a thread. A guard area can help a multithreaded program detect overflow of a thread's stack. A guard area is a region of no-access memory that DECthreads allocates at the overflow end of the thread's stack. When the thread attempts to access a memory location within this region, a memory addressing violation occurs.

A new thread can be created using a thread attributes object with a default guardsize attribute value. This value is platform dependent, but will always be at least one "hardware protection unit" (that is, at least one page; non-zero values are rounded up to the next integral page size). For more information, see this guide's platform-specfic appendixes.

DECthreads allows your program to specify the size of a thread stack guard area for two reasons:

To set the guardsize attribute of a thread attributes object, call the pthread_attr_setguardsize() routine. To obtain the value of the guardsize attribute in a thread attributes object, call the pthread_attr_getguardsize() routine.

The pthread_attr_setguardsize() and pthread_attr_getguardsize() routines replace (and are equivalent to) the pthread_attr_setguardsize_np() and pthread_attr_getguardsize_np() routines, respectively, that were available in previous DECthreads releases. The new routines provide a standardized and portable interface, specified by the Single UNIX Specification, Version 2; however, the older routines remain supported.

2.3.2.7 Setting the Contention Scope Attribute

When creating a thread, you can specify the set of threads with which this thread competes for processing resources. This set of threads is called the thread's contention scope.

A thread attributes object includes a contention scope attribute. The contention scope attribute specifies whether the new thread competes for processing resources only with other threads in its own process, called process contention scope, or with all threads on the system, called system contention scope.

Use the pthread_attr_setscope() routine to set an initialized thread attributes object's contention scope attribute. Use the pthread_attr_getscope() routine to obtain the value of the contention scope attribute of an initialized thread attributes object.

In the thread attributes object, set the contention scope attribute's value to PTHREAD_SCOPE_PROCESS to specify process contention scope, or set the value to PTHREAD_SCOPE_SYSTEM to specify system contention scope.

DECthreads selects at most one thread to execute on each processor at any point in time. DECthreads resolves the contention based on each thread's scheduling attributes (for example, priority) and scheduling policy (for example, round-robin).

A thread created using a thread attributes object whose contention scope attribute is set to PTHREAD_SCOPE_PROCESS contends for processing resources with other threads within its own process that also were created with PTHREAD_SCOPE_PROCESS. It is unspecified how such threads are scheduled relative to threads in other processes or threads in the same process that were created with PTHREAD_SCOPE_SYSTEM contention scope.

A thread created using a thread attributes object whose contention scope attribute is set to PTHREAD_SCOPE_SYSTEM contends for processing resources with other threads in any process that also were created with PTHREAD_SCOPE_SYSTEM.

Whether process contention scope and system contention scope are available for your program's threads depends on the host operating system. The following table summarizes DECthreads support for thread contention scope by operating system:
Operating System Available Thread Contention Scopes Default Thread
Contention Scope
DIGITAL UNIX Process
System
Process
     
OpenVMS Process Process
     
Windows NT System System

Note

On DIGITAL UNIX systems:

When a process contention scope thread creates a system contention scope thread that uses the default inheritance of scheduling attributes, the creation can fail with an [EPERM] error condition. This is because system contention scope threads can exceed "default" priority only if the process is running with root privileges.

2.3.3 Terminating a Thread

Terminating a thread means to cause a thread to end its execution. This can occur for any of the following reasons:

When a thread terminates, DECthreads performs these actions:

  1. DECthreads writes a return value (if one is available) into the terminated thread's thread object:
    Another thread can obtain this return value by joining with the terminated thread (using pthread_join()). See Section 2.3.5 for a description of joining with a thread.

    Note

    If the thread terminated by returning from its start routine normally and the start routine does not provide a return value, the results obtained by joining with that thread are unpredictable.
  2. If the termination results from a cancelation or a call to pthread_exit(), DECthreads calls, in turn, each cleanup handler that this thread declared (using pthread_cleanup_push()) and that is not yet removed (using pthread_cleanup_pop()). (DECthreads also transfers control to any appropriate CATCH, CATCH_ALL, or FINALLY blocks, as described in Chapter 5.)
    DECthreads calls the terminated thread's most recently pushed cleanup handler first. See Section 2.3.3.1 for more information about cleanup handlers.
    For C++ programmers: At normal exit from a thread, your program will call the appropriate destructor functions, just as if an exception had been raised.
  3. To exit the terminated thread due to a call to pthread_exit(), DECthreads raises the pthread_exit_e exception. To exit the terminated thread due to cancelation, DECthreads raises the pthread_cancel_e exception.
    Your program can use the DECthreads exception package to operate on the generated exception. (In particular, note that the practice of using CATCH handlers in place of pthread_cleanup_push() is not portable.) Chapter 5 describes the DECthreads exception package.
  4. For each of the terminated thread's thread-specific data keys that has a non-NULL value:
    DECthreads repeats this step until all thread-specific data values in the thread are NULL, or for up to a number of iterations equal to PTHREAD_DESTRUCTOR_ITERATIONS. This destroys all thread-specific data associated with the terminated thread. See Section 2.6 for more information about thread-specific data.
  5. DECthreads awakens the thread (if there is one) that is currently waiting to join with the terminated thread. That is, DECthreads awakens the thread that is waiting in a call to pthread_join().
  6. If the thread is already detached, DECthreads destroys its thread object. Otherwise, the thread continues to exist until detached or joined with. Section 2.3.4 describes detaching and destroying a thread.

After a thread terminates, its thread object continues to exist. This means that the thread object data structure remains allocated and contains meaningful information---for instance, the thread identifier is still unique and meaningful. This allows another thread to join with the terminated thread (see Section 2.3.5).

When a terminated thread is no longer needed, your program should detach that thread (see Section 2.3.4).

Note

For DIGITAL UNIX systems:

When the initial thread in a multithreaded process returns from the main routine, the entire process terminates, just as it does when a thread calls exit().

For OpenVMS systems:

When the initial thread in a multithreaded image returns from the main routine, the entire image terminates, just as it does when a thread calls SYS$EXIT.

2.3.3.1 Cleanup Handlers

A cleanup handler is a routine you provide that is associated with a particular lexical scope within your program and that can be invoked under certain circumstances when a thread exits that scope. The cleanup handler's purpose is to restore that portion of the program's state that has been changed within the handler's associated lexical scope. In particular, cleanup handlers allow a thread to react to thread-exit and cancelation requests.

Your program declares a cleanup handler for a thread by calling the pthread_cleanup_push() routine. Your program removes (and optionally invokes) a cleanup handler by calling the pthread_cleanup_pop() routine.

A cleanup handler is invoked when the calling thread exits the handler's associated lexical scope, due to:

For each call to pthread_cleanup_push(), your program must contain a corresponding call to pthread_cleanup_pop(). The two calls form a lexical scope within your program. One pair of calls to pthread_cleanup_push() and pthread_cleanup_pop() cannot overlap the scope of another pair. Pairs of calls can be nested.

Because cleanup handlers are specified by the POSIX.1c standard, they are a portable mechanism. An alternative to using cleanup handlers is to define and/or catch DECthreads exceptions with the DECthreads exception package. Chapter 5 describes how to use the DECthreads exception package. DECthreads considers cleanup handler routines, exception handling clauses (that is, CATCH, CATCH_ALL, FINALLY), and C++ object destructors to be functionally equivalent mechanisms.

2.3.4 Detaching and Destroying a Thread

Detaching a thread means to mark a thread for destruction as soon as it terminates. Destroying a thread means to free, or make available for reuse, the resources occupied by the thread object (and by DECthreads internal resources) associated with that thread.

If a thread has terminated, then detaching that thread causes DECthreads to destroy it immediately. If a thread is detached before it terminates, then DECthreads frees the thread's resources immediately after it terminates.

A thread can be detached explicitly or implicitly:

Your program can create a thread that is detached. See Section 2.3.1 for more information about creating a thread.

It is illegal for your program to attempt to join or detach a detached thread. In general, you cannot perform any operation (for example, cancelation) on a detached thread. This is because the thread ID might have become invalid or might have been assigned to a new thread immediately upon termination of the thread. Unless your program is absolutely certain that the detached thread has not terminated (or is not terminating), using the thread ID can have severe consequences.

2.3.5 Joining With a Thread

Joining with a thread means to suspend this thread's execution until another thread (the target thread) terminates. In addition, DECthreads detaches the target thread after it terminates.

For one thread to join with a functionally related thread is one way to synchronize their execution.

A thread joins with another thread by calling the pthread_join() routine and specifying the thread identifier of the thread. If the target thread has already terminated, then this thread does not wait.

The target thread of a join operation must be created with the detachstate attribute of its thread attributes object set to PTHREAD_CREATE_JOINABLE.

Keep in mind these restrictions about joining with a thread:


Previous Next Contents Index

[Site home] [Send comments] [Help with this site] [How to order documentation] [OpenVMS site] [Compaq site]
[OpenVMS documentation]

Copyright © Compaq Computer Corporation 1998. All rights reserved.

Legal
6101PRO_003.HTML