Rogue Wave banner
Previous fileTop of DocumentContentsIndexNext file

7.3 Solaris Native Thread Attribute Support

7.3.1 Scheduling Attributes

The following sections describe the Solaris native thread attribute-specific support, behavior, and restrictions for attributes related to thread scheduling.

7.3.1.1 Scheduling Contention Scope

Solaris implicitly supports the concept of contention scope. Solaris allows both process-scope and system-scope threads.

Under Solaris, a process-scope thread is an unbound thread, and a system-scope thread is a bound thread. A bound thread is a thread that is permanently assigned to an underlying light-weight process, or LWP. An LWP can be thought of as the Solaris equivalent of a kernel thread. Unbound threads are scheduled by the threads library and contend for LWPs, while bound threads are scheduled by the kernel and contend directly for processors.

The Threads.h++ implementation maps contention scope policies to the underlying API as follows:

Under Solaris, threads with a contention scope value of RW_THR_PROCESS_SCOPE have a fixed scheduling policy of RW_THR_PREEMPTIVE, while threads with RW_THR_SYSTEM_SCOPE have a fixed scheduling policy of RW_THR_TIME_SLICED_DYNAMIC, unless the process has the super-user privilege, in which case, the thread also has access to RW_THR_TIME_SLICED_FIXED, and RW_THR_PREEMPTIVE policies. Each of these scheduling policies can have a different legal range of priority values.

If you change the contention scope attribute, Threads.h++ will check that the scheduling policy and priority are still legal in the new contention scope, and if not, will force the scheduling policy or priority values to the appropriate default values. (See the sections on Scheduling Policy, Scheduling Priority, and Scheduling Time-Slice Quantum.)

Threads.h++ defines RW_THR_PROCESS_SCOPE as the default contention scope policy under Solaris.

7.3.1.2 Scheduling Inheritance Policy

The scheduling inheritance policy attribute is fully supported by the Solaris implementation of Threads.h++ and defaults to RW_THR_INHERIT.

7.3.1.3 Concurrency Policy

The concurrency policy attribute is supported in the Solaris implementation of Threads.h++, but only while the contention scope is RW_THR_PROCESS_SCOPE.

This attribute is used to tell the underlying threads system that it should create a new LWP when it creates a new unbound thread. By creating new LWPs, the effective concurrency of a multithreaded application can be increased, since LWPs are time-sliced and can scheduled on different processors.

The Threads.h++ implementation maps concurrency policies to the underlying API as follows:

7.3.1.4 Scheduling Policy

If the contention scope is RW_THR_PROCESS_SCOPE, then the scheduling policy is fixed at RW_THR_PREEMPTIVE because, under Solaris, process-scope, or "unbound" threads run until preempted or blocked; the threads are not time-sliced.


NOTE: The scheduling under RW_THR_PROCESS_SCOPE is not truly preemptive, because unbound threads are scheduled for execution on one or more LWPs, and these LWPs are scheduled independently of the unbound threads, usually in a time-sliced manner. This results in a hybrid form of scheduling that is difficult to describe using a single policy.

If the contention scope is RW_THR_PROCESS_SCOPE, you may query the scheduling policy, and may set the scheduling policy to RW_THR_PREEMPTIVE, but any attempt to change to another policy will produce an exception.

If the contention scope is RW_THR_SYSTEM_SCOPE, then the thread will be "bound" to an LWP which, depending on the scheduling attributes of the creating thread, the attributes of the thread's LWP, and the current process privileges, may support any one of RW_THR_TIME_SLICED_DYNAMIC, RW_THR_TIME_SLICED_FIXED, or RW_THR_PREEMPTIVE scheduling policies.

The Threads.h++ implementation for Solaris maps scheduling policies to the underlying API as follows:

If the contention scope is RW_THR_SYSTEM_SCOPE, you may query the scheduling policy, and you may attempt to set the scheduling policy to any of the three policies listed above, but if the process lacks the necessary privileges, or if an unsupported policy is specified, then these attempts will produce an exception.

The Solaris implementation of Threads.h++ requires the use of two separate priority values for threads with system contention scope; one priority value sets the system-level scheduling priority for a thread (a thread's LWP, actually), while the second value is used to prioritize access to any thread-level synchronization resources shared with other threads in the same process. The process-level synchronization priority is called the process scope priority value, and the system-level scheduling priority is called the system-scope priority value.

The RWThreadAttribute class provides separate families of member functions for independently manipulating these two priority values; see the Threads.h++ User's Guide for a listing of these functions.

You should only need to define a separate process-scope priority for a system-scope thread when you have used more than one priority value for your process-scope threads, and it is possible that your system-scope thread may deadlock as a result of priority inversion because it was created with the default process-scope priority value.

In Solaris, the various scheduling attributes that specify scheduling policy, priority, and time-slice duration are always inherited from the creating thread. Threads.h++ makes any necessary adjustments to scheduling attributes for a newly created thread while still inside a threaded runnable's start() member.

A new thread's scheduling policy is inherited from the creating thread by default, unless the scheduling policy attribute has been explicitly set or the inheritance policy has been changed from its default value of RW_THR_INHERIT to RW_THR_EXPLICIT. If the inheritance policy is RW_THR_EXPLICIT, Threads.h++ defines the default scheduling policy according to the current contention scope:

Table 18 -- Solaris native thread support: Relationship of contention scope to default sheduling policy

Contention ScopeDefault Scheduling Policy
RW_THR_PROCESS_SCOPE
RW_THR_PREEMPTIVE
RW_THR_SYSTEM_SCOPE
RW_THR_TIME_SLICED_DYNAMIC

If you change the scheduling policy attribute, Threads.h++ will check that the priority and time-slice quantum values are still legal under the new scheduling policy, and if not, will force those attributes to the appropriate default values. (See the next sections on Scheduling Priority and Scheduling Time-Slice Quantum.)

Similarly, changing the contention scope attribute may force changes in the scheduling policy and related attribute values.

7.3.1.5 Scheduling Priority

Under Solaris, the legal range of thread priority values and the default priorities depend on the current contention scope and scheduling policy.

Scheduling Priority For Process-Scope Threads. For unbound, or process-scope threads, Solaris allows any priority value in the range between 0 and MAX_INT. Process scope threads are scheduled onto LWPs according to this priority value.

Solaris maintains a separate scheduling queue for each thread priority value up to 127. Priority values greater that 127 share a single queue, forcing the Solaris threads scheduler to traverse the entire queue each time another thread must be scheduled.

For this reason, Threads.h++ limits the Solaris process-scope priority range to a minimum of 0 and a maximum of 127. If the inheritance policy is RW_THR_INHERIT, a new thread's priority is inherited from the creating thread, and if the inheritance policy is RW_THR_EXPLICIT, the default priority is 0.

How are process-scope threads scheduled at the system level? That depends on the scheduling attributes of the LWP pool that is used to execute the unbound threads. When a process is created, the initial LWP in the process inherits its scheduling class and priority from the parent process. Each LWP created to execute unbound threads inherits its scheduling class and priority from this initial LWP. This means that all LWPs used for unbound threads will possess the same scheduling parameters.

Scheduling Priority For System-Scope Threads. When a thread is given system contention scope in the Solaris implementation of Threads.h++, that thread is permanently bound to an LWP.

These system-scope threads are scheduled using two separate priority values; one priority value defines the system-level scheduling, or LWP priority of the thread, while the second value is used to resolve any contention for thread-level synchronization resources shared with other threads in the same process (such as a mutex created with the USYNC_THREAD flag).

The process-level, synchronization priority is called the process scope priority value, while the system-level scheduling priority is called the system-scope priority value.

The RWThreadAttribute class provides two additional sets of member functions for use in independently manipulating these two priority values. The regular priority functions may still be used; but it should be understood that these functions are limited to manipulating the priority value corresponding to the current contention scope; when the contention scope is RW_THR_PROCESS_SCOPE, the regular priority functions do the same thing as the process-scope priority functions, and when the contention scope is RW_THR_SYSTEM_SCOPE, the regular priority functions do the same thing as the system-scope priority functions.

You cannot get or set the system-scope priority value when the contention scope is RW_THR_PROCESS_SCOPE; any attempt to do so will result in an exception.

You only need to define a separate process-scope priority for a system-scope thread when you have used more than one priority value for your process-scope threads, and there exists the possibility that your system-scope thread may deadlock as a result of priority inversion because it assumed the default process-scope priority value.

The range of process-scope priority values remains unchanged regardless of contention scope and scheduling policy, while the system-scope priority values can vary with scheduling policy.

To determine the range of priority values for the various system-scope policies, use the getMinPriority() or getMinSystemScopePriority()and getMaxPriority() or getMaxSystemScopePriority() functions to retrieve the minimum and maximum allowable values. The minimum priority value for system-scope threads is always 0. The maximum priority values are defined as part of the operating system configuration on each workstation; the maximum can vary significantly from machine to machine.

If the scheduling policy value of an RWThreadAttribute instance is RW_THR_TIME_SLICED_DYNAMIC, you will not be able to set, get, or query the legal range for the priority, or system-scope priority attribute values, unless the process has the super-user privilege. If the current process does not have this privilege, you will have to wait until the thread has been created and perform operations on the active thread.

In either case, when the desired or current scheduling policy is RW_THR_TIME_SLICED_DYNAMIC, Threads.h++ must consider several factors in determining the maximum allowable priority value:

This information is used to determine whether the user-priority limit can be adjusted up to the maximum priority value, and if not, what maximum priority value can be expected. To make this determination Threads.h++:

When the scheduling policy is RW_THR_TIME_SLICED_FIXED or RW_THR_PREEMPTIVE, the LWP associated a newly-created bound thread is assigned to the real-time (RT) scheduling class. Threads.h++ retrieves the maximum priority for these policies by using the priocntl() function to query the real-time class for the maximum priority value supported by that class. This value is defined as part of the operating system configuration, and can vary from machine to machine. To use either one of these policies requires the super-user privilege.

Scheduling Priority Inheritance. A new thread's priority value is inherited from the creating thread by default, unless the priority attribute has been explicitly set or the inheritance policy has been changed from its default value of RW_THR_INHERIT to RW_THR_EXPLICIT. If the inheritance policy is RW_THR_EXPLICIT, Threads.h++ defines the default priority to be 0, as shown in the following table:

Table 19 -- Solaris native thread support: Setting of scheduling priority values

Contention
Scope
Scheduling PolicyDefault
Process-Scope Priority
Default
System-Scope Priority
Process
RW_THR_PREEMPTIVE
Inherited
or 0
N/A
RW_THR_TIME_SLICED_DYNAMIC
Inherited
or 0
Inherited
or 0
System
RW_THR_TIME_SLICED_FIXED
Inherited
or 0
Inherited
or 0
RW_THR_PREEMPTIVE
Inherited
or 0
Inherited
or 0

In Solaris, the various scheduling attributes that specify scheduling policy, priority, and time-slice duration are always inherited from the creating thread. Threads.h++ makes any necessary adjustments to scheduling attributes for a newly created thread while still inside a threaded runnable's start() member.

If you change the scheduling contention scope or policy attributes, Threads.h++ will check that the priority values are still legal under the new settings, and if not, will force the priority values the appropriate defaults.

7.3.1.6 Scheduling Time-Slice Quantum

Under Solaris, the time-slice quantum attribute is only supported when the contention scope is RW_THR_SYSTEM_SCOPE, and the scheduling policy is RW_THR_TIME_SLICED_FIXED. Attempts to get or set this attribute value under other circumstances will result in exceptions.

Threads.h++ accepts time-slice quantum values as an unsigned long number of milliseconds. The library converts milliseconds into seconds and nanoseconds so the value can be passed to the Solaris priocntl() function using the real-time class parameters structure. Threads.h++ allows you to specify the quantum with an accuracy of 1 millisecond; the system, however, will round the value to the next largest integral multiple of the system clock's resolution.

The minimum time-slice quantum allowed by Threads.h++ under Solaris is 1 millisecond. The maximum is limited by to the largest number of milliseconds that can be represented by an unsigned long. If an infinite time-slice quantum is desired the RW_THR_PREEMPTIVE scheduling policy should be used.

If supported under current circumstances, a new thread's time-slice quantum value is inherited from the creating thread by default, unless the time-slice quantum attribute has been explicitly set or the inheritance policy has been changed from its default value of RW_THR_INHERIT to RW_THR_EXPLICIT. If the inheritance policy is RW_THR_INHERIT, and you query for the default time-slice quantum, Threads.h++ will return the time-slice quantum value of the calling thread. If the calling thread does not have a time-slice quantum to query (because it does not have the proper contention scope or scheduling policy), the query will fail with an exception. If the inheritance policy is RW_THR_EXPLICIT, Threads.h++ will ask the operating system to use the default time-slice quantum for the current priority level. This default value may not be queried, and any attempts to do so will result in an exception.

The following table summarizes the relationship between contention scope, scheduling policy, and the time-slice quantum attribute:

Table 20 -- Solaris native thread support: Summary of the relationship between contention scope, scheduling policy and the time-slice quantum attribute

Contention
Scope
Scheduling PolicyDefault
Time-Slice Quantum
Process
RW_THR_PREEMPTIVE
N/A
RW_THR_TIME_SLICED_DYNAMIC
N/A
System
RW_THR_TIME_SLICED_FIXED
Inherited
or
System Default
RW_THR_PREEMPTIVE
N/A
(Infinite)

7.3.1.7 Start Policy

The start policy attribute is fully supported by the Solaris implementation of Threads.h++ and defaults to RW_THR_START_RUNNING.

7.3.2 Stack Attributes

Solaris supports the use of system-managed and user-managed stacks.

7.3.2.1 System-Managed Stack Attributes

Solaris supports user-specification of the reserve size for a system-managed stack, but provides no support for controlling the commitment of physical memory and page-file space to such a stack.

Stack Reserve Size. Solaris supports the stack reserve size attribute for a system-managed stack.

The minimum stack size necessary to execute a null function is returned by the getMinStackSize() member. Threads.h++ uses the thr_min_stack() function to determine this value. To ensure that each thread's stack size is always greater than the minimum allowable stack size, Threads.h++ adds the minimum stack size value to any reserve size value retrieved from a thread attribute at the time a thread is created.

Threads.h++ imposes no upper limit for stack reserve size; the maximum stack size is effectively limited by available virtual memory space and pagefile size. If the stack reserve size specified is too large for the available resources, an exception will be produced at the time a thread is created using the thread attribute instance with the offending reserve size value. Any such exception is thrown by the start() function invocation that attempted to create the thread.

Solaris uses allocation on demand even when the user specifies the amount of stack size to reserve. When Solaris allocates stack space, it maps the space as anonymous, zeroed memory, but does not allocate real memory until the memory is accessed. Solaris reserves an inaccessible page at the end of each stack area, called a red zone, that is used to produce a segmentation fault should the stack overflow. The stack address space is mapped using the MAP_NORESERVE attribute to delay the cost of reserving swap space until the memory is used. The only resource consumed by reserving space is addresses in the virtual address space of your process; no memory or pagefile space is allocated until the memory is committed. Therefore, there is little harm in reserving a large area if it might be needed.

The default stack reserve size used by Solaris and returned by getStackReserveSize() is 1MB. This stack is allocated on demand, and starts with 1 page committed to memory.

A call to setUserStack() replaces or nullifies the attribute settings produced by any previous call to setStackReserveSize() and vice versa.

Stack Commit Size. The stack commit size attribute is not supported in the Solaris implementation of Threads.h++. Attempts to get or set this attribute value will result in exceptions.

7.3.2.2 User-Managed Stack Attributes

The Solaris implementation of Threads.h++ supports user-managed stacks.

If an attempt is made to set the user stack address to zero, or to set the user stack size to a value less than the minimum stack size returned by the getMinStackSize() function, a RWTHRBoundsError exception is produced.

Threads.h++ imposes no upper limit for user stack size; the maximum stack size is effectively limited by the virtual memory space and pagefile size available to the user.

Attempts to query for a default user-stack address value or user-stack size value will result in an RWTHROperationNotAvailable exception. These values may only be queried after they have been set.

A call to setStackReserveSize() replaces or nullifies the attribute settings produced by any previous call to setUserStack() and vice-versa.


Previous fileTop of DocumentContentsIndexNext file

©Copyright 2000, Rogue Wave Software, Inc.
Contact Rogue Wave about documentation or support issues.