3.9 Thread Attributes
The threads produced using the mechanisms described in earlier examples can be sufficient for typical applications. These threads use default scheduling and stack management strategies defined by the Threading package and the underlying thread API. However, other situations might require more control over a thread’s scheduling behavior and stack memory utilization.
For this additional control, the Threading package defines a set of thread attributes that allow you to choose various thread scheduling and stack management policies. Instances of the RWThreadAttribute class define the initial attributes for new classes. The RWThread and RWThreadSelf classes have methods for manipulating the scheduling attributes of an active thread.
To define the initial attributes for a thread, you create an RWThreadAttribute instance, set the desired attribute values, and supply this instance to a threaded runnable class instance. The threaded runnable uses the attribute values to configure each new thread created when start() is called. Once a threaded runnable is started, the scheduling attributes of its active thread can be manipulated using member functions included in the RWThread and RWThreadSelf classes.
3.9.1 Thread Attribute Families
The thread attributes supported by the Threading package can be divided into two families: thread scheduling attributes and thread stack management attributes.
3.9.1.1 Scheduling Attributes
The scheduling attributes include:
*Start Policy — Indicates whether or not the newly created thread should be left in the interrupted state upon returning from start(). Threads created by a call to start() are always interrupted immediately after creation and then released before start() returns to its caller. This thread can be released from the interrupt at some later time by calling the runnable’s releaseInterrupt() member.
*Contention Scope — Defines whether a thread is to contend for processing resources relative to other threads within the same process or relative to other threads within the same system.
*Scheduling Inheritance Policy — Defines whether the scheduling attributes for scheduling policy, priority, and time-slice quantum should be taken from the thread attribute object or whether they should be inherited from the creating thread.
*Concurrency Policy — Requests concurrent execution for a new thread. Typically such a request results in the creation of a new kernel-level thread to support parallel (separate processors) or time-sliced execution of a new user-level thread.
*Scheduling Policy — Identifies the scheduling policy to use for deciding which threads should be executed on which processors, when these threads should be run, and how long the threads should be run. Each scheduling policy can define additional attributes that can be used to control specific aspects of that policy, such as priority value, concurrency policy, or time-slice quantum.
*Scheduling Priority — Defines a numerical rank or ordering to use in resolving thread contention for processing resources. In the Threading package, threads with numerically higher priority values receive scheduling preference over threads with lower priorities. The legal range of priority values tends to vary significantly between thread APIs and even between scheduling policies. Some platforms, such as Solaris, require the use of two separate priority values for threads with system contention scope. One value sets the system-level scheduling priority for the thread, and the second value prioritizes access to any thread-level synchronization resources shared with other threads in the same process.
*Time-slice Quantum — Under a time-sliced scheduling policy, this attribute defines the maximum amount of time that a thread is allowed to run before scheduling the next eligible thread to run. Some systems allow a separate quantum value to be defined for each thread, while others can apply the same quantum to all threads within a scheduling class, process, or system.
For more information on these attributes, see Section 3.9.9, “Scheduling Attributes.”
3.9.1.2 Stack Management Attributes
The stack allocation attributes define one of two different stack management policies, either system-managed or user-managed.
If the system allocates and manages a thread’s stack, the two significant attributes are:
*Stack Reserve Size — The amount of virtual address space to reserve for the stack.
*Stack Commit Size — The amount of physical memory and pagefile space to initially commit to the stack.
If the user application allocates and manages a thread’s stack, the relevant attributes include:
*User Stack Address — The lowest address or bottom of the stack address space reserved, allocated, and managed by the user.
*User Stack Size — The memory space size of the user-allocated stack. Locates the top of the stack address space where the bottom of the stack is typically located (for stacks that grow downward in memory).
For more information on these attributes, see Section 3.9.10, “Stack Attributes.”
3.9.2 Thread Attribute Portability
Thread attributes and attribute values are not uniformly supported across all environments. Some of the policies and attribute values described in this guide might be available only under specific operating systems.
Once you begin using thread attributes, you risk compromising the cross-platform portability of your code.
This is largely due to the fact that scheduling and stack allocation policies tend to vary significantly between each of the environments supported by the Threading package. Rogue Wave has made every attempt to alleviate or hide these differences without prohibiting access to the platform-specific controls that some developers require. It is up to you to carefully review and understand the implementation differences between each platform that you plan to support.
To find out which attributes are supported:
*Read the detailed information about each attribute and attribute value in the platform-specific section in the Threads Module Platform Guide.
*Use the feature test macros and functions that allow you to determine, at compile or run-time, which attributes and attribute values are supported in the current environment.
*Use the member functions that manipulate attributes. The member functions are always present, regardless of whether or not the current environment supports the attributes. You can always rely on the existence of the functions and attribute values described in this guide.
If you attempt to access an unsupported attribute or use an illegal or unsupported attribute value:
*An RWTHROperationNotSupported exception is produced if an attribute is not supported.
*An RWTHROperationNotAvailable exception is produced if an attribute value is not supported under current circumstances.
*An RWTHRBoundsError is produced if the value falls outside the legal range for that attribute.
The first two exceptions can always be avoided by using the feature-test functionality included by the library.
3.9.3 Testing For Support
As discussed earlier, the specific level of support for each thread attribute varies from platform to platform. The Threading package has several mechanisms and techniques you can use for testing that level of support.
3.9.3.1 Using Feature Test Macros
Use the following feature-test macros to determine, at compile-time, whether the current environment has any support for the corresponding thread attribute:
*RW_THR_HAS_CONCURRENCY_POLICY
*RW_THR_HAS_CONTENTION_SCOPE
*RW_THR_HAS_INHERITANCE_POLICY
*RW_THR_HAS_PRIORITY
*RW_THR_HAS_PROCESS_SCOPE_PRIORITY
*RW_THR_HAS_SCHEDULING_POLICY
*RW_THR_HAS_STACK_COMMIT_SIZE
*RW_THR_HAS_STACK_RESERVE_SIZE
*RW_THR_HAS_START_POLICY
*RW_THR_HAS_SYSTEM_SCOPE_PRIORITY
*RW_THR_HAS_TIME_SLICE_QUANTUM
*RW_THR_HAS_USER_STACK
*RW_THR_HAS_DUAL_PRIORITY
*RW_THR_HAS_PARTIAL_STACK_COMMITMENT
If a macro is not defined, attempts to query or set the corresponding attribute always produce an exception. If the macro is defined, then the current environment has some level of support or recognition for the attribute and might allow you to get or set the attribute value.
3.9.3.2 “Get” Test Functions
Use the following functions at run-time to determine whether the current environment allows you to query the corresponding thread attributes:
*canGetConcurrencyPolicy()
*canGetContentionScope()
*canGetInheritancePolicy()
*canGetPriority()
*canGetProcessScopePriority()
*canGetSchedulingPolicy()
*canGetStackCommitSize()
*canGetStackReserveSize()
*canGetStartPolicy()
*canGetSystemScopePriority()
*canGetTimeSliceQuantum()
*canGetUserStack()
These functions follow several rules:
*They return false if the corresponding attribute is not supported in the current environment or if the corresponding “get” function cannot return a legal value under current circumstances.
*They return true if the corresponding attribute is supported in the current environment and the corresponding “get” function can return a legal value under current circumstances.
If the corresponding attribute value has not yet been set, then a return value of true indicates that a default value is defined and can be queried.
*They return true if the corresponding attribute still has the value previously set by a call to the matching “set” function. This behavior is similar to that in the “is set” functions.
If the inheritance policy is RW_THR_INHERIT, the scheduling policy, priority, and time-slice quantum values cannot be queried for their default values. If these scheduling attributes are inherited, the “get min” and “get max” functions for these attributes will likely produce RWTHROperationNotAvailable exceptions because not enough information is available to determine the legal range of values.
Even if an attribute is supported in a particular environment, you might still be restricted from getting and setting that attribute as a consequence of other attribute values. As an example, the concurrency policy attribute might only be accessible when the contention scope is set for process-scope. Some attribute settings might also require specific security authorizations or privileges.
Once a thread has been created and is active, you can use the following RWThread or RWThreadSelf functions to test whether or not you can get the corresponding attribute value for an active thread:
*canGetPriority()
*canGetProcessScopePriority()
*canGetSchedulingPolicy()
*canGetSystemScopePriority()
*canGetTimeSliceQuantum()
3.9.3.3 “Set” Test Functions
Use the following functions at run-time to determine whether the current environment allows you to set the corresponding thread attribute value:
*canSetConcurrencyPolicy(RWConcurrencyPolicy)
*canSetContentionScope(RWContentionScope)
*canSetInheritancePolicy(RWInheritancePolicy)
*canSetPriority()
*canSetProcessScopePriority()
*canSetSchedulingPolicy(RWSchedulingPolicy)
*canSetStackCommitSize()
*canSetStackReserveSize()
*canSetStartPolicy(RWStartPolicy)
*canSetSystemScopePriority()
*canSetTimeSliceQuantum()
*canSetUserStack()
These functions return one of the following values:
*false if the corresponding attribute is not supported in the current environment or if the specified attribute value (if any) is not supported under current circumstances.
*true if the corresponding attribute is supported in the current environment and the specified value (if any) is legal the under current circumstances.
Each of these functions that accepts a policy value produces an RWTHRBoundsError exception if the value specified as an argument is not legal policy value for that attribute. This exception can be avoided by making sure that you are using the appropriate enumerated value names for each attribute.
Once a thread has been created and is active, you can use the following RWThread or RWThreadSelf functions to test whether or not you can change the corresponding attribute value for an active thread:
*canSetPriority()
*canSetProcessScopePriority()
*canSetSchedulingPolicy(RWSchedulingPolicy)
*canSetSystemScopePriority()
*canSetTimeSliceQuantum()
3.9.3.4 “Is Set” Test Functions
Use the following functions to determine whether the corresponding attribute still has the value specified by an earlier call to the matching “set” function:
*isConcurrencyPolicySet()
*isContentionScopeSet()
*isInheritancePolicySet()
*isPrioritySet()
*isProcessScopePrioritySet()
*isSchedulingPolicySet()
*isStackCommitSizeSet()
*isStackReserveSizeSet()
*isStartPolicySet()
*isSystemScopePrioritySet()
*isTimeSliceQuantumSet()
*isUserStackSet()
These functions return one of the following values:
*false if the corresponding attribute value is not supported in the current environment, has not yet been set, or if a value specified earlier has been replaced with a some default value in response to a change in some related attribute.
*true if the corresponding attribute is supported, has been set by a call to the matching “set” function, has not been reset with a call to the matching “reset” function, and has not been forced to a new value as the result of changes made in other attributes.
3.9.4 Querying Attribute Values
Use the following functions to query the corresponding attribute value:
*getConcurrencyPolicy()
*getContentionScope()
*getInheritancePolicy()
*getPriority()
*getProcessScopePriority()
*getSchedulingPolicy()
*getStackCommitSize()
*getStackReserveSize()
*getStartPolicy()
*getSystemScopePriority()
*getTimeSliceQuantum()
*getUserStackAddress()
*getUserStackSize()
These functions throw the following exceptions:
*RWTHROperationNotSupported if the attribute is not recognized or supported by the current environment
*RWTHROperationNotAvailable if the specified attribute is supported, but its value has not yet been defined with a call to the matching “set” function, and no default value is available under the current circumstances.
The process-scope and system-scope priority functions can be substituted for getPriority(), and vice-versa, as long as you define the appropriate contention scope.
Once a thread has been created and is active, you can use the following RWThread or RWThreadSelf functions to set the corresponding thread attribute value:
*getPriority()
*getProcessScopePriority()
*getSchedulingPolicy()
*getSystemScopePriority()
*getTimeSliceQuantum()
In addition to the exceptions produced by their RWThreadAttribute counterparts, these functions throw an RWTHRThreadNotActive exception if the threaded runnable instance being manipulated does not have an active thread.
3.9.5 Setting Attribute Values
Use the following functions to set the corresponding attribute value:
*setConcurrencyPolicy(RWConcurrencyPolicy)
*setContentionScope(RWContentionScope)
*setInheritancePolicy(RWInheritancePolicy)
*setPriority(RWPriority)
*setProcessScopePriority(RWPriority)
*setSchedulingPolicy(RWSchedulingPolicy)
*setStackCommitSize(size_t)
*setStackReserveSize(size_t)
*setStartPolicy(RWStartPolicy)
*setSystemScopePriority(RWPriority)
*setTimeSliceQuantum(unsigned long)
*setUserStack(void* address, size_t size)
These functions throw the following exceptions:
*RWTHROperationNotSupported if the attribute is not recognized or supported by the current environment
*RWTHROperationNotAvailable if the specified attribute value is not supported by the current environment or is not allowed under current circumstances.
*RWTHRBoundsError if the specified attribute value is invalid or falls outside the current legal range defined for that attribute.
The process-scope and system-scope priority functions can be substituted for setPriority(RWPriority) and vice-versa, as long as the appropriate contention scope has been defined.
Once a thread has been created and is active, you can use the following RWThread and RWThreadSelf functions to set the corresponding thread attribute value:
*setPriority(RWPriority)
*setProcessScopePriority(RWPriority)
*setSchedulingPolicy(RWSchedulingPolicy)
*setSystemScopePriority(RWPriority)
*setTimeSliceQuantum(unsigned long)
In addition to the exceptions produced by their RWThreadAttribute counterparts, these functions can throw an RWTHRThreadNotActive exception if the threaded runnable instance being manipulated does not have an active thread.
3.9.6 Default Attribute Values
If you do not specify a value for an attribute, then the values are supplied by one of the following:
*The Threading package.
*The underlying API.
*The creating thread or process.
For some attributes, you can query an RWThreadAttribute instance for default attribute values by using the appropriate “get” function prior to setting the corresponding attribute. Before attempting to get the value, though, make sure that a default value is available by calling the appropriate “can get” function, as shown in Example 24.
Example 24 – Querying for default thread attribute values
// Create new attribute instance with default values
RWThreadAttribute attribute;
 
RWSchedulingPolicy policy;
// Is there a default value available?
if (attribute.canGetSchedulingPolicy()) {
// Yes, query for the default value.
policy = attribute.getSchedulingPolicy();
.
.
.
}
The default values for some attributes might change in response to changes in other attribute values. For example, the range of legal priority values often varies according to the scheduling policy attribute value.
The default values for many of the attributes vary from environment to environment. You can find a description of the default values for attributes in the Threads Module Platform Guide.
3.9.7 Restoring Default Attribute Values
Use the following functions to restore the default value, if any, for the corresponding attribute:
*resetConcurrencyPolicy()
*resetContentionScope()
*resetInheritancePolicy()
*resetPriority()
*resetProcessScopePriority()
*resetSchedulingPolicy()
*resetStackCommitSize()
*resetStackReserveSize()
*resetStartPolicy()
*resetSystemScopePriority()
*resetTimeSliceQuantum()
*resetUserStack()
These functions always succeed. If the target attribute is not supported, these functions clear any previous setting, and the appropriate default value is supplied by the underlying API.
3.9.8 Determining the Legal Range For An Attribute
To determine the current limits for the priority, stack size, and time-slice quantum attributes, use the following RWThreadAttribute member functions:
*getMinStackSize() — This static function returns the bare-minimum stack size required by a thread that calls a null function. The Threading package automatically guarantees that the size of any stack allocated by the system is greater than this size.
Use this function when creating a user-defined stack. This function can only be called if the RW_THR_HAS_STACK_RESERVE_SIZE or RW_THR_HAS_USER_STACK macros are defined. If those macros have not been defined, an RWTHROperationNotSupported exception is produced.
*getMinPriority() — Returns the minimum priority value supported by the current contention scope and scheduling policy. This function can only be called if RW_THR_HAS_PRIORITY is defined. If it is not defined, an RWTHROperationNotSupported exception is produced.
*getMaxPriority() — Returns the maximum priority value supported by the current contention scope and scheduling policy. This function can only be called if RW_THR_HAS_PRIORITY is defined. If it is not defined, an RWTHROperationNotSupported exception is produced.
*getMinProcessScopePriority() — Returns the minimum priority value supported by process-scope threads under the current scheduling policy. This function can only be called when the contention scope value is RW_THR_PROCESS_SCOPE, or when the contention scope is RW_THR_SYSTEM_SCOPE and the current environment uses dual-priorities as indicated by the definition of the RW_THR_HAS_DUAL_PRIORITY macro. If those restrictions are not met, an RWTHROperationNotAvailable exception is thrown. In addition, this function can only be called if RW_THR_HAS_PRIORITY is defined. If it is not defined, an RWTHROperationNotSupported exception is produced.
*getMaxProcessScopePriority() — Returns the maximum priority value supported by process-scope threads under the current scheduling policy. This function can only be called when the contention scope value is RW_THR_PROCESS_SCOPE, or when the contention scope is RW_THR_SYSTEM_SCOPE and the current environment uses dual-priorities as indicated by the definition of the RW_THR_HAS_DUAL_PRIORITY macro. If those restrictions are not met, an RWTHROperationNotAvailable exception is thrown. In addition, this function can only be called if RW_THR_HAS_PRIORITY is defined. If it is not defined, an RWTHROperationNotSupported exception is produced.
*getMinSystemScopePriority() — Returns the minimum priority value supported by system-scope threads under the current scheduling policy. This function can only be called when the contention scope value is RW_THR_SYSTEM_SCOPE. If the value is not RW_THR_SYSTEM_SCOPE, an RWTHROperationNotAvailable exception is thrown. In addition, this function can only be called if RW_THR_HAS_PRIORITY is defined. If it is not define, an RWTHROperationNotSupported exception is produced.
*getMaxSystemScopePriority() — Returns the maximum priority value supported by system-scope threads under the current scheduling policy. This function can only be called when the contention scope value is RW_THR_SYSTEM_SCOPE. If the value is not RW_THR_SYSTEM_SCOPE, an RWTHROperationNotAvailable exception is thrown. In addition, this function can only be called if RW_THR_HAS_PRIORITY is defined. If it is not defined, an RWTHROperationNotSupported exception is produced.
*getMinTimeSliceQuantum() — Returns the minimum time-slice quantum value supported under the current scheduling policy. This function can only be called when the environment and scheduling policy support a time-slice quantum attribute. If the current policy does not give access to the time-slice quantum, an RWTHROperationNotAvailable exception is thrown. In addition, this function can only be called if RW_THR_HAS_TIME_SLICE_QUANTUM is defined. If it is not defined, an RWTHROperationNotSupported exception is produced.
*getMaxTimeSliceQuantum() — Returns the maximum time-slice quantum value supported under the current scheduling policy. This function can only be called when the environment and scheduling policy support a time-slice quantum attribute. If the current policy does not give access to the time-slice quantum, an RWTHROperationNotAvailable exception is thrown. In addition, this function can only be called if RW_THR_HAS_TIME_SLICE_QUANTUM is defined. If it is not defined, an RWTHROperationNotSupported exception is produced.
Once a thread has been created and is active, you can use a similar set of functions included in the RWThread and RWThreadSelf classes.
*getMinPriority()
*getMaxPriority()
*getMinProcessScopePriority()
*getMaxProcessScopePriority()
*getMinSystemScopePriority()
*getMaxSystemScopePriority()
*getMinTimeSliceQuantum()
*getMaxTimeSliceQuantum()
In addition to throwing the same exceptions as their RWThreadAttribute counterparts, these functions might also produce an RWTHRThreadNotActive exception if you attempt to query the current limits on a threaded runnable that does not possess an active thread.
3.9.9 Scheduling Attributes
The scheduling attributes are used to control the allocation of processing and synchronization among the various competing threads in an application or system.
The availability and allowable range for a particular scheduling attribute can vary with changes in other attribute values. The dependencies between scheduling attributes are hierarchical in nature and must be considered when getting or setting scheduling attribute values. A change in one attribute can force another previous attribute setting to be discarded. Figure 15 illustrates the structure of these dependency relationships.
Figure 15 – Structure of dependency relationships
To avoid unexpected loss of scheduling attribute settings and to ensure proper availability and validation, use the following sequence when setting these attributes:
1. Contention scope and inheritance policy.
2. Scheduling policy and concurrency policy
3. Priority and time-slice quantum.
The start-policy attribute can be set at any time.
This list is not meant to imply that you must set all or any of these attributes, only that you will probably have better success and suffer less confusion if you adhere to this sequence.
The following sections explain each of the scheduling attributes, their usage, and any interdependencies that might be imposed by the Threading package or the underlying thread API.
3.9.9.1 Start Policy
In the Threading package, new threads of execution are created by calling the start() member on an instance of a threaded runnable class. The start() function creates a new thread and immediately interrupts that thread in order to make any necessary adjustments to the thread’s scheduling attributes before the thread starts executing within its run() member.
The start policy attribute indicates whether or not to leave the newly created thread in the interrupted state upon return from start(). Normally, a new thread is released from this interrupt before start() returns. If requested using this attribute, though, the thread can be left in the interrupted state, where it can be released some time later by calling the releaseInterrupt() member of the threaded runnable instance.
The enumerated type, RWStartPolicy, defines these start policies as:
*RW_THR_START_RUNNING — Specifies that a new thread is to be allowed after successful creation and initialization. This is the default start policy.
*RW_THR_START_INTERRUPTED — Specifies that a new thread should be left interrupted following start-up and initialization. Interrupted threads can be released when convenient by calling the releaseInterrupt() member on the same threaded runnable class instance.
The RWThreadAttribute member functions that are used to manipulate the start policy attribute include:
*canGetStartPolicy() — Indicates whether the start policy attribute is supported in the current environment and whether getStartPolicy() can currently return a legal value. This function always returns true and has been included for consistency.
*isStartPolicySet() — Indicates whether the start policy attribute value is the value that was previously set by a call to setStartPolicy().
*getStartPolicy() — Returns the default start policy value if the attribute value has not yet been set. Otherwise, it returns the value specified in the last call to setStartPolicy(). The default start policy is available under all circumstances, and is defined as RW_THR_START_RUNNING.
*canSetStartPolicy() — Indicates whether the start policy attribute and attribute value passed as an argument are supported in the current environment. This function always return true for either of the start policies and has been included for consistency.
*setStartPolicy() — Sets the start policy attribute to the value passed as an argument. This function only throws an exception if the value passed is not one of the two legal values.
*resetStartPolicy() — Restores the start policy attribute to its default value.
No specific interdependencies exist between the start policy attribute and other scheduling attributes.
The start policy specified by an RWThreadAttribute can only be used at thread creation. To interrupt an active thread, use the RWThread member requestInterrupt() or the RWThreadSelf member interrupt().
3.9.9.2 Scheduling Contention Scope
The contention scope attribute is used to specify whether a thread is to compete for processing resources with other threads in the same process or with other processes in the same system.
A process-scope thread is multiplexed with other process-scope threads on one or more underlying kernel threads in a N:1 or N:M relationship. A system-scope thread is typically bound in a 1:1 relationship with a kernel thread. Figure 16 illustrates these relationships.
Figure 16 – Process-scope and system-scope relationships
Choosing between process-scope and system-scope. When choosing between process-scope and system-scope threads, consider the processing cost associated with scheduling and synchronizing them. Process-scope threads are generally scheduled and synchronized by code executing in the underlying threads library in user-space, while system-level threads are scheduled and synchronized by the kernel. Kernel-level synchronization and scheduling is, in general, significantly more expensive than user-level synchronization and scheduling. Because of this high processing cost, specify system contention scope only when system-wide scheduling issues must be addressed by your code, as when your application must include real-time behavior or operate under real-time constraints.
Using the feature test macro. The feature test macro for contention scope is RW_THR_HAS_CONTENTION_SCOPE. If this macro is not defined, attempts to query or set the contention scope attribute always produce an exception. If it is defined, then the current environment has some level of support or recognition for contention scope and might allow you to get or set the contention scope attribute.
The enumerated type, RWContentionScope, defines these contention scope policies as:
*RW_THR_PROCESS_SCOPE
*RW_THR_SYSTEM_SCOPE
The RWThreadAttribute member functions that manipulate the contention scope attribute value include:
*canGetContentionScope() — Indicates whether the contention scope attribute is supported in the current environment and whether getContentionScope() can currently return a legal value.
*isContentionScopeSet() — Indicates whether the contention scope attribute value is the value that was previously set by a call to setContentionScope(RWContentionScope).
*getContentionScope() — Returns the default contention scope value if the attribute value has not yet been defined. Otherwise, it returns the value specified in the last call to setContentionScope(RWContentionScope). If the current environment does not support or define the contention scope attribute, then attempts to use this function result in exceptions.
*canSetContentionScope() — Indicates whether the contention scope attribute and attribute value passed as an argument are supported in the current environment.
*setContentionScope(RWContentionScope) — Sets the contention scope attribute to the value passed as an argument. If the current environment does not support the contention scope attribute or the specified attribute value, then attempts to use this function result in exceptions.
*resetContentionScope() — Restores the contention scope value to its default value.
Specifying the contention scope. The contention scope of a thread can only be specified when a thread is created. You cannot change the contention scope of an active thread.
Changing the contention scope. Changing the contention scope value might cause a previous setting of the scheduling policy, priority, time-slice quantum, and concurrency attribute to be discarded. When you change the contention scope attribute, the Threading package validates these related attributes to ensure that any previous setting is compatible with the new scope, and if not, resets that attribute. The availability, default value, and supported range for these dependent attributes often vary in response to changes in the contention scope value. The specific dependencies and validation requirements are unique to each environment, and can be found in the appropriate user’s guide supplement.
Determining contention scope. To determine the contention scope of an active thread:
1. Acquire a handle to the thread’s runnable instance.
2. Use the getActiveAttribute() member in the handle class to retrieve a copy of the RWThreadAttribute instance used to create the thread.
3. Use the getContentionScope() member to query the attribute instance for the contention scope used.
3.9.9.3 Scheduling Inheritance Policy
The inheritance policy attribute is used to indicate whether the scheduling policy, priority, and time-slice quantum attributes for a new thread should be inherited from the creating thread or whether they should be based on the values defined within the RWThreadAttribute instance.
The enumerated type, RWInheritancePolicy, defines these inheritance policies as:
*RW_THR_INHERIT — Specifies that the default scheduling policy, priority, and time-slice quantum attributes should be inherited from the creating thread, if each of those attribute values has been left unchanged or has been restored to the default value with a call to the appropriate “reset” function. This is the default inheritance policy.
*RW_THR_EXPLICIT — Specifies that the default scheduling policy, priority, and time-slice quantum attributes should not be inherited, but should be included in the thread attribute instance.
The RWThreadAttribute member functions that manipulate the inheritance policy attribute value include:
*canGetInheritancePolicy() — Indicates whether the inheritance policy attribute is supported in the current environment and whether getInheritancePolicy() can currently return a legal value. This function always returns true and is included for consistency.
*isInheritancePolicySet() — Indicates whether the inheritance policy attribute value is the value that was previously set by a call to setInheritancePolicy(RWInheritancePolicy).
*getInheritancePolicy() — Returns the default inheritance policy value if the attribute value has not yet been set. Otherwise, it returns the value specified in the last call to setInheritancePolicy(RWInheritancePolicy). The default inheritance policy is available under all circumstances and is defined as RW_THR_INHERIT.
*canSetInheritancePolicy() — Indicates whether the inheritance policy attribute and attribute value passed as an argument are supported in the current environment. This function always returns true for either of the inheritance policies and is included for consistency.
*setInheritancePolicy(RWInheritancePolicy) — Sets the inheritance policy attribute to the value passed as an argument. This function only throws an exception if the value passed is not one of the two legal values.
*resetInheritancePolicy() — Restores the inheritance policy attribute to its default value.
Follow these rules when using this attribute:
*If you should choose to set any one of the scheduling policy, priority, or time-slice attributes, the inheritance policy is forced to RW_THR_EXPLICIT.
*Setting the inheritance policy to RW_THR_INHERIT causes the Threading package to discard any previous settings for the scheduling policy, priority, and time-slice attribute.
*The Threading package does not allow you to mix inherited and explicitly-defined scheduling attributes by setting only those attributes that you do not wish inherit.
To determine the inheritance policy used to create an active thread:
1. Acquire a handle to the thread’s runnable instance.
2. Use the getActiveAttribute() member included by the handle class to retrieve a copy of the RWThreadAttribute instance that created the thread.
3. Use the getInheritancePolicy() member to query the attribute instance for the inheritance policy used at thread creation.
3.9.9.4 Concurrency Policy
As described in the previous section, some systems support N-to-M thread scheduling. In these systems, a process-scope thread is multiplexed with other process-scope threads on to one or more underlying kernel threads in N-to-M relationship. The concurrency policy attribute can be used to indicate whether or not the underlying threads system should increase the number of kernel threads when it creates a new process-scope thread, as illustrated in Figure 17.
Figure 17 – Concurrency policy
Issues to consider when requesting the creation of additional kernel threads:
*You can increase the effective concurrency of the threads in your application because most systems can distribute kernel threads on separate processors and often use time-slicing when scheduling kernel threads on a single processor.
*When increasing the effective concurrency level, consider the processing cost associated with creating new kernel threads and the higher cost of scheduling and synchronizing them.
Process-scope threads are generally scheduled and synchronized by code executing in the underlying threads library in user-space, while the underlying kernel threads are scheduled and synchronized by calls to the kernel.
*Even though increasing the effective concurrency level can result in increased processing costs, this method is still preferable to choosing system-scope threads over process-scope threads.
The difference is that kernel threads added when the concurrency level is increased are still shared between process-level threads, while system-scope threads are permanently bound to kernel threads, thereby prohibiting their reuse during blocking waits.
Using the feature test macro. The feature test macro for concurrency policy is RW_THR_HAS_CONCURRENCY_POLICY. If this macro is not defined, attempts to query or set the concurrency policy attribute always produce an exception. If it is defined, then the current environment has some level of support or recognition for concurrency policy and might allow you to get or set the concurrency policy attribute.
The enumerated type, RWConcurrencyPolicy, defines these concurrency policies as:
*RW_THR_NO_CHANGE — The creation of a new process-scope with this attribute value thread does not force the creation of a new underlying kernel thread. This value does not prohibit the threads system from choosing to create new kernel threads if its own policies dictate such a necessity.
*RW_THR_INCREASE — Creation of a new process-scope thread forces the creation of a new underlying kernel thread.
The RWThreadAttribute member functions that manipulate the concurrency policy attribute value include:
*canGetConcurrencyPolicy() — Indicates whether the concurrency policy attribute is supported in the current environment and whether getConcurrencyPolicy() can currently return a legal value.
*isConcurrencyPolicySet() — Indicates whether the concurrency policy attribute value is the value that was previously set by a call to setConcurrencyPolicy(RWConcurrencyPolicy).
*getConcurrencyPolicy() — Returns the default concurrency policy value if the attribute value has not yet been defined. Otherwise, it returns the value specified in the last call to setConcurrencyPolicy(RWConcurrencyPolicy). If the current environment does not support or define the concurrency policy attribute, then attempts to use this function result in exceptions.
*canSetConcurrencyPolicy() — Indicates whether the concurrency policy attribute and attribute value passed as an argument are supported in the current environment.
*setConcurrencyPolicy(RWConcurrencyPolicy) — Sets the concurrency policy attribute to the value passed as an argument. If the current environment does not support the concurrency policy attribute or the specified attribute value, then attempts to use this function result in exceptions.
*resetConcurrencyPolicy() — Restores the concurrency policy value to its default value.
Changing the contention scope attribute value can cause a previous concurrency policy setting to be discarded and can affect the availability of this attribute, as indicated in the following table:
Contention Scope
Concurrency Policy
Process Scope(if available)
No Change or Increase(if available)
System Scope(if available)
Not Available
The concurrency change requested when creating a thread cannot be undone.
To determine the concurrency policy used when creating an active thread:
1. Acquire a handle to the thread’s runnable instance.
2. Use the getActiveAttribute() member in the handle class to retrieve a copy of the RWThreadAttribute instance used to create the thread.
3. Use the getConcurrencyPolicy() member to query the attribute instance for the concurrency policy used when creating the thread.
3.9.9.5 Scheduling Policy
Often in a multithreaded system, more threads exist than processors available to run them. The scheduling policy attribute contains the policy that the system uses in selecting threads to run on available processors.
Threads can exist in several states as explained below and illustrated in Figure 18.
*A thread becomes runnable when created, unless explicitly created in a stopped state.
*Eventually, a runnable thread is selected, or dispatched, for execution on a processor and becomes active.
*While executing, a thread can block waiting on a synchronization resource such as a mutex or condition variable.
*Once the synchronization resource releases the thread, it is awakened and again enters the runnable state.
*If a higher-priority thread become runnable while a lower-priority thread is active, the lower-priority thread is preempted and returned to the runnable state so that the higher-priority thread can execute.
*Threads can also be suspended, leaving them in a stopped stated until continued. A thread can voluntarily preempt itself by yielding execution and explicitly block itself by sleeping.
*A thread usually exits at some point and, depending on the system, might enter an exited state where the thread waits to be joined or simply cease to exist.
Figure 18 – Thread states
A scheduling policy dictates how and when threads are to enter or leave the runnable, active, and blocked states. A scheduling policy defines:
*The criteria for selecting the next runnable thread to dispatch when an active thread yields or blocks.
*The conditions under which an active thread is to be preempted.
*The criteria for selecting the next thread to awaken if more than one thread is waiting on the same synchronization resource. In some cases, this determination is made by the synchronization mechanism and occurs regardless of scheduling policy.
The scheduling policy is system dependent. In some systems, each thread can be assigned a different scheduling policy, allowing the developer to choose the policy that best meets the requirements for that thread. In other systems, the scheduling policies are defined at the process level, or fixed according to a thread’s contention scope.
Possible scheduling policy values. The enumerated type, RWSchedulingPolicy, defines the set of all possible scheduling policy values supported by the Threading package as:
*RW_THR_OTHER — Included as a catch-all for scheduling policies that can’t be categorized as one of the policies defined by the remaining RWSchedulingPolicy values. This policy is usually mapped to the default policy of the underlying thread’s API, but is seldom returned by the getSchedulingPolicy() function, which generally returns the one of the policies (listed below) that most closely describes the default policy.
*RW_THR_PREEMPTIVE — In preemptive scheduling, threads run until preempted by a thread of higher-priority or until blocked. Thread priorities are set by the application; the system does not dynamically change a thread’s priority.
*RW_THR_TIME_SLICED_DYNAMIC — In systems with dynamic time-sliced scheduling, threads still run either until they are preempted by a thread of higher priority, until some time-quantum has elapsed, or until blocked. The difference is that the priority and/or time-slice quantum assigned to a thread can be altered dynamically by the system to give some level of fairness and optimization.
*RW_THR_TIME_SLICED_FIXED — Under a fixed, time-sliced scheduling policy, threads are still time-sliced, but the thread priorities are not altered by the system.
Using the feature test macro. The feature test macro for scheduling policy is RW_THR_HAS_SCHEDULING_POLICY. If this macro is not defined, attempts to query or set the scheduling policy attribute always produce an exception. If it is defined, then the current environment has some level of support or recognition for scheduling policy and might allow you to get or set the scheduling policy attribute.
Member functions. The RWThreadAttribute member functions that manipulate the scheduling policy attribute include:
*canGetSchedulingPolicy() — Indicates whether the scheduling policy attribute is supported in the current environment and whether getSchedulingPolicy() can currently return a legal value.
*isSchedulingPolicySet() — Indicates whether the scheduling policy attribute value is the value that was previously set by a call to setSchedulingPolicy(RWSchedulingPolicy).
*getSchedulingPolicy() — Returns the default scheduling policy value if the attribute value has not yet been defined. Otherwise, it returns the value specified in the last call to setSchedulingPolicy(RWSchedulingPolicy). If the current environment does not support or define the scheduling policy attribute, then attempts to use this function result in exceptions.
*canSetSchedulingPolicy() — Indicates whether the scheduling policy attribute and attribute value passed as an argument are supported in the current environment.
*setSchedulingPolicy(RWSchedulingPolicy) — Sets the scheduling policy attribute to the value passed as an argument. If the current environment does not support the scheduling policy attribute or the specified attribute value, then attempts to use this function result in exceptions.
*resetSchedulingPolicy() — Restores the scheduling policy attribute to its default value. The default value can vary according to the current value of other attributes.
Changing the contention scope. In addition to any environment-specific restrictions, the availability of individual scheduling policies often depends on the current contention scope. Changing the contention scope attribute value can cause a previous scheduling policy setting to be discarded and can affect the availability of specific scheduling policies.
Changing the scheduling policy. Similarly, changing the scheduling policy value can cause a previous setting of the priority and time-slice quantum attributes to be discarded. When you change the scheduling policy attribute, the Threading package validates these related attributes to insure that any previous setting is compatible with the new policy, and if not, resets that attribute. The default value and supported range for these dependent attributes often vary in response to changes in the scheduling policy value. The specific dependencies and validation requirements are unique to each environment.
Inheriting a scheduling policy. Scheduling policy can be inherited from the creating thread if the inheritance policy attribute is defined as RW_THR_INHERIT. If the inheritance attribute is RW_THR_EXPLICIT, the Threading package chooses a default policy appropriate for the current settings and environment.
Setting the scheduling policy. Setting the scheduling policy attribute value when the current inheritance policy is RW_THR_INHERIT forces the inheritance policy attribute to be changed to RW_THR_EXPLICIT.
Using the scheduling policy. The scheduling policy specified by an RWThreadAttribute can only be used when the thread is created. To manipulate the scheduling policy for an active thread, use the following RWThread and RWThreadSelf member functions:
*canGetSchedulingPolicy()
*getSchedulingPolicy()
*canSetSchedulingPolicy()
*setSchedulingPolicy(RWSchedulingPolicy)
Exceptions. In addition to throwing the same exceptions as their RWThreadAttribute counterparts, an RWTHRThreadNotActive exception is thrown if you attempt to query or set the scheduling policy on a threaded runnable that does not possess an active thread.
3.9.9.6 Scheduling Priority
Thread priority is the key factor considered by most scheduling policies when choosing the next thread to execute on a processor. Priority values define a numerical rank or ordering of the threads that a thread scheduler can use to resolve simultaneous contention for processing resources.
In a typical implementation, any threads in the runnable state are represented in an ordered list of queues with one list entry, or queue, per priority level. This is demonstrated in Figure 19.
Figure 19 – Queues and priorities
Range of priority values. In the Threading package, threads with numerically higher priority values receive scheduling preference over threads with lower priorities. The legal range for priority values tends to vary significantly between thread APIs, different contention scopes, and different scheduling policies.
Using the feature test macro. The feature test macro for scheduling priority is RW_THR_HAS_PRIORITY. If this macro is not defined, attempts to query or set a priority attribute always produce an exception. If it is defined, then the current environment has some level of support or recognition for scheduling priorities and might allow you to get or set a scheduling priority attribute.
The macro RW_THR_HAS_DUAL_PRIORITY indicates whether the current environment requires two separate priority values for threads with system contention scope:
*One for resolving contention for thread-level synchronization objects within a process.
*One for scheduling the thread relative to other threads in the system.
RW_THR_HAS_DUAL_PRIORITY can be used to choose between use of the single priority attribute functions and the process-scope and system-scope priority functions. If this macro is defined, then:
*The macro RW_THR_HAS_PROCESS_SCOPE_PRIORITY is defined.
*The macro RW_THR_HAS_SYSTEM_SCOPE_PRIORITY is defined.
Typedef for all priority values. The typedef RWPriority is the type for all priority values in the Threading package. The declaration of this typedef varies from one environment to the next, but is always based on one of the intrinsic integer types.
Member functions. The RWThreadAttribute member functions that manipulate the scheduling priority attribute value include:
*canGetPriority() — Indicates whether the scheduling priority attribute is supported in the current environment and whether getPriority() can currently return a legal value.
*isPrioritySet() — Indicates whether the scheduling policy attribute value is the value that was previously set by a call to setPriority(RWPriority).
*getPriority() — Returns the default scheduling priority value if the attribute value has not yet been defined. Otherwise, it returns the value specified in the last call to setPriority(RWPriority). If the current environment or circumstances do not support priority scheduling, then attempts to use this function result in exceptions.
*canSetPriority() — Indicates whether the scheduling priority attribute is supported and can be set in the current environment.
*setPriority(RWPriority) — Sets the scheduling priority attribute to the value passed as an argument. If the current environment does not support the scheduling priority attribute or the specified attribute value is outside the legal range, then the function produces an exception.
*resetPriority() — Restores the scheduling priority attribute to its default value. The default value can vary according to the current value of other attributes.
*getMinPriority() — Returns the minimum priority value supported by the current contention scope and scheduling policy.
*getMaxPriority() — Returns the maximum priority value supported by the current contention scope and scheduling policy.
Requirement for two priority values. Some platforms, such as Solaris, require the use of two separate priority values for threads with system contention scope;
*One sets the system-level scheduling priority or system-scope priority for the thread.
*The other is a process-scope priority used to control access to any thread-level synchronization resources shared with other threads in the same process.
Requirement for a separate process-scope priority. 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.
*The possibility exists that your system-scope thread can deadlock as a result of priority inversion. Priority inversion occurs because a thread has assumed an inappropriate default process-scope priority value.
Support for two priorities. To support this dual priority scheme, the RWThreadAttribute class includes two sets of functions that are specialized versions of the normal priority function listed above. These functions should only be required when defining attributes for threads that have system contention scope.
*The first set defines a thread priority for use in resolving multithread contention for intra-process synchronization resources. These functions can be used interchangeably with the normal priority functions, as long as the thread attribute instance defines the contention scope as RW_THR_PROCESS_SCOPE or when the environment supports or requires dual-priorities for system-scope threads.
*canGetProcessScopePriority()
*isProcessScopePrioritySet()
*getProcessScopePriority()
*canSetProcessScopePriority()
*setProcessScopePriority()
*resetProcessScopePriority()
*getMinProcessScopePriority()
*getMaxProcessScopePriority()
*The second set of functions defines a thread priority for use in scheduling system-scope threads relative to other system-scope threads in the system. These functions can be used interchangeably with the normal priority functions, as long as the thread attribute instance defines the contention scope as RW_THR_SYSTEM_SCOPE:
*canGetSystemScopePriority()
*isSystemScopePrioritySet()
*getSystemScopePriority()
*canSetSystemScopePriority()
*setSystemScopePriority()
*resetSystemScopePriority()
*getMinSystemScopePriority()
*getMaxSystemScopePriority()
Changing the inheritance or scheduling policy attributes. Changing the inheritance or scheduling policy attributes, either directly or as the result of a change in contention scope, can cause a previous priority setting to be discarded and can affect the range of allowable priority values, in accordance with the following table:
Inheritance Policy
Scheduling Policy
Priority
Inherit
[Inherited]
[Inherited]
Explicit
Preemptive(if available)
Policy-Dependent Range
Fixed Time-Slicing(if available)
Policy-Dependent Range
Dynamic Time-Slicing(if available)
Policy-Dependent Range
Availability of the process-scope priority attributes. The availability of the process-scope priority attributes might be affected by the current setting of the contention scope attribute, as shown in the next table.
Contention Scope
Requires Dual Priorities
Inheritance Policy
Scheduling Policy
Process-Scope Priority
Process Scope(if available)
N/A
Inherit
[Inherited]
[Inherited]
Explicit
Preemptive(if available)
Policy-Dependent Range
Fixed Time-Slicing(if available)
Policy-Dependent Range
Dynamic Time-Slicing(if available)
Policy-Dependent Range
System Scope(if available)
No
N/A
N/A
Not Available
Yes
Inherit
[Inherited]
[Inherited]
Explicit
N/A
System-Dependent Range
Availability of the system-scope priority attributes. The availability of the system-scope priority attributes might be affected by the current setting of the contention scope attribute, as shown in the next table.
Contention Scope
Inheritance Policy
Scheduling Policy
System-Scope Priority
Process Scope(if available)
N/A
N/A
Not Available
System-Scope(if available)
Inherit
[Inherited]
[Inherited]
Explicit
Preemptive(if available)
Policy-Dependent Range
Fixed Time-Slicing(if available)
Policy-Dependent Range
Dynamic Time-Slicing(if available)
Policy-Dependent Range
The specific dependencies and validation requirements for the priority attributes are unique to each environment.
Inheriting a priority value. A priority value can be inherited from the creating thread if the inheritance policy attribute is defined as RW_THR_INHERIT. If the inheritance attribute is RW_THR_EXPLICIT, the Threading package chooses a default value appropriate for the current settings and environment. Setting a priority attribute value when the current inheritance policy is RW_THR_INHERIT forces the inheritance policy attribute to be changed to RW_THR_EXPLICIT.
Using a priority value. The priority specified by an RWThreadAttribute can only be used at thread creation. To manipulate the priority value of an active thread, use the following RWThread and RWThreadSelf functions:
*canGetPriority()
*getPriority()
*canSetPriority()
*setPriority(RWPriority)
*getMinPriority()
*getMaxPriority()
*canGetProcessScopePriority()
*getProcessScopePriority()
*canSetProcessScopePriority()
*setProcessScopePriority()
*getMinProcessScopePriority()
*getMaxProcessScopePriority()
*canGetSystemScopePriority()
*getSystemScopePriority()
*canSetSystemScopePriority()
*setSystemScopePriority()
*getMinSystemScopePriority()
*getMaxSystemScopePriority()
Exceptions. In addition to throwing the same exceptions as their RWThreadAttribute counterparts, an RWTHRThreadNotActive exception is thrown should you attempt to query or set the priority on a threaded runnable that does not possess an active thread.
3.9.9.7 Scheduling Time-Slice Quantum
The time-slice quantum attribute is used to define the maximum amount of time that a thread is allowed to execute before forcibly preempting it to allow another thread to execute. A time-slice quantum is typically associated with a scheduling policy of RW_THR_TIME_SLICED_FIXED.
Using the feature test macro. The feature test macro for scheduling time-slice quantum is RW_THR_HAS_TIME_SLICE_QUANTUM. If this macro is not defined, attempts to query or set the time-slice quantum attribute always produce an exception. If it is defined, then the current environment has some level of support or recognition for time-slice quantum and might allow you to get or set this attribute. A time-slice quantum is defined in terms of milliseconds using an unsigned long value.
Member functions. The RWThreadAttribute member functions that are used to manipulate the scheduling time-slice quantum include:
*canGetTimeSliceQuantum() — Indicates whether the time-slice quantum is supported in the current environment and whether getTimeSliceQuantum() can currently return a legal value.
*isTimeSliceQuantumSet() — Indicates whether the time-slice quantum value is the value that was previously set by a call to setTimeSliceQuantum(unsigned long).
*getTimeSliceQuantum() — Returns the default time-slice quantum value if the attribute value has not yet been defined. Otherwise, it returns the value specified in the last call to setTimeSliceQuantum(unsigned long). If the current environment or circumstances do not allow interrogation of the time-slice quantum, then attempts to use this function result in exceptions.
*canSetTimeSliceQuantum() — Indicates whether the time-slice quantum attribute is supported and can be set in the current environment.
*setTimeSliceQuantum(unsigned long) — Sets the time-slice quantum attribute to the value passed as an argument. If the current environment does not support the adjustments to the time-slice quantum or if the specified attribute value is outside the legal range, then the function produces an exception.
*resetTimeSliceQuantum() — Restores the time-slice quantum attribute to its default value. The default value can vary according to the current value of other attributes, such as priority.
*getMinTimeSliceQuantum() — Returns the minimum priority value supported by the current contention scope and scheduling policy.
*getMaxTimeSliceQuantum() — Returns the maximum priority value supported by the current contention scope and scheduling policy.
Changing the inheritance or scheduling policy attributes. Adjustment of the time-slice quantum is not supported by many platforms and can be safely ignored because this attribute assumes an appropriate default value. Changing the inheritance or scheduling policy attributes, either directly or as the result of a change in contention scope, can cause a previous time-slice quantum setting to be discarded and can affect the availability and allowable range of values for this attribute, as shown in the next table.
Inheritance Policy
Scheduling Policy
Time-Slice Quantum
N/A
N/A
Not Available
Inherit
[Inherited]
[Inherited] (if available)
Explicit
Preemptive(if available)
Not Available
Fixed Time-Slicing(if available)
Policy-Dependent Range(if available)
Dynamic Time-Slicing(if available)
Not Available
The specific dependencies and validation requirements for the time-slice quantum attribute are unique to each environment, and can be found in the appropriate section of the Platform Guide supplement.
Inheriting the time-slice quantum. The time-slice quantum value can be inherited from the creating thread if the inheritance policy attribute is defined as RW_THR_INHERIT. If the inheritance attribute is RW_THR_EXPLICIT, the Threading package chooses a default policy appropriate for the current settings and environment. Setting the time-slice quantum attribute value when the current inheritance policy is RW_THR_INHERIT forces the inheritance policy attribute to be changed to RW_THR_EXPLICIT.
Using the time-slice quantum. The time-slice quantum specified by an RWThreadAttribute can only be used at thread creation. To manipulate the time-slice quantum for an active thread, use the following RWThread and RWThreadSelf member functions:
*canGetTimeSliceQuantum()
*getTimeSliceQuantum()
*canSetTimeSliceQuantum()
*setTimeSliceQuantum(unsigned long)
*getMinTimeSliceQuantum()
*getMaxTimeSliceQuantum()
Exceptions. In addition to throwing the same exceptions as their RWThreadAttribute counterparts, an RWTHRThreadNotActive exception is thrown if you attempt to query or set the time-slice quantum on a threaded runnable that does not possess an active thread.
3.9.10 Stack Attributes
In single-threaded processes, the size of the stack is usually limited only by the amount of free memory space because the stack can continue to grow until it runs into the memory space allocated for the heap.
In a multithreaded application, each thread’s stack must be limited because each thread has its own stack, and every thread stack must be allocated in the same address space. To allocate a stack, each thread must reserve some fixed-sized portion of the address space. The growth of these stacks is now limited by their allocation size and the presence of other thread stacks in the same address space. Stack allocation is demonstrated in Figure 20.
Figure 20 – Stack allocation
3.9.10.1 Allocating and Managing a Thread Stack
The responsibility for allocating and managing a thread stack is usually left to the system, but some environments allow users to create and manage their own stack space.
If the system is given the responsibility for allocating and managing the stack, then you might also have the opportunity to choose whether to commit physical memory and pagefile space to the stack at the time of its creation or to commit memory on-demand as the stack grows.
To support each of these variations, the Threading package defines several attributes that can be used to select and define the desired stack allocation policy:
 
Thread Stack Attributes
Stack Allocation Policy
Stack Reserve Size
Stack Commit Size
User Stack Address and Size
System Allocated
Commit On Demand
X
 
 
Commit At Creation
 
X
 
Mixed
X
X
 
User Allocated
 
 
X
Issues to consider when using stack attributes:
*Unless the user stack attributes are set, the RWThreadAttribute class always assumes that the underlying system has responsibility for allocating and managing the stack.
*Use of the stack attributes is optional. You can safely ignore these attributes in most situations because they usually assume reasonable default values.
*Consider manipulating these attributes if you have threads that make very large stack allocations, perhaps as the result of automatic array allocations.
*If you decide that you want to define the stack size, either for a system-managed or user-managed stack, remember to take into account the additional stack space that might required by code executing in other libraries.
*Some run-time environments place demands on a thread’s stack, and some dynamic linker-loaders require use of a thread’s stack when locating and attaching to library code or data.
*Stack overruns in a multithreaded environment can be quite difficult to detect and debug, so you should use care in limiting your stack sizes. You are generally better off if you can make your stacks bigger instead of smaller.
*Some of the stack attributes mentioned here are mutually exclusive. If you set the stack reserve or commit size, any previous definitions for a user-managed stack are discarded. If the underlying environment does not support partial stack commitment, the Threading package also imposes mutual exclusion between the reserve size and commit size attributes.
3.9.10.2 System-Managed Stack Attributes
The default choice for stack management policy is to let the system allocate and manage the stack, but the final choice between system-managed and user-managed stack allocation is determined by which attributes were most recently set. If the user-stack attributes were the last stack attributes changed prior to thread creation, then user-management of the stack is assumed.
The following attributes control system-managed stack allocation:
*Stack Reserve Size—The stack reserve size attribute defines the amount of virtual address space to reserve for the stack.
On most systems, reserving space for the stack only involves reserving virtual memory locations—memory and pagefile resources are not allocated until the memory is committed. No harm is caused by reserving a large area if it might be needed.
The feature test macro for stack reserve size is RW_THR_HAS_STACK_RESERVE_SIZE. If this macro is not defined, attempts to query or set the reserve size attribute always produce an exception. If it is defined, then the current environment has some level of support or recognition for stack reserve size and might allow you to get or set this attribute.
Stack reserve size is defined in terms of some number of bytes using size_t values. The actual sizes can be rounded to some environment-specific granularity (such as page size).
The RWThreadAttribute member functions that manipulate the stack reserve size include:
*canGetStackReserveSize() — Indicates whether the stack reserve size is supported in the current environment and whether getStackReserveSize() can currently return a legal value.
*isStackReserveSizeSet() — Indicates whether the reserve size value is the value that was previously set by a call to setStackReserveSize(size_t).
*getStackReserveSize() — Returns the default reserve size value if the attribute value has not yet been defined. Otherwise, it returns the value specified in the last call to setStackReserveSize(size_t). If the current environment or circumstances do not allow interrogation of the stack reserve size, then attempts to use this function result in exceptions.
*canSetStackReserveSize() — Indicates whether the stack reserve size attribute is supported and can be set in the current environment.
*setStackReserveSize(size_t) — Sets the reserve size attribute to the value passed as an argument. If the current environment does not support this attribute or if the specified attribute value is outside the legal range, then the function produces an exception.
*resetStackReserveSize() — Restores the reserve size attribute to its default value. The default value and its source vary by environment.
*getMinStackSize() — Returns the minimum amount of stack space required to implement a thread that calls a null routine. The Threading package also ensures that a system-managed stack is of sufficient size by adding this value to any value defined using setStackReserveSize(size_t).
If the current environment or circumstances do not allow interrogation of the minimum stack size, then attempts to use this function result in exceptions. The availability of this function can be inferred by testing the result returned by either canGetStackReserveSize() or canGetUserStack().
An attempt to read the default value for this attribute produces an exception if the user stack attributes have been set, unless the stack reserve or commit size is set or the user stack attributes are reset. In those cases, the default value of system-managed stack attributes are again available.
Systems vary in several ways:
*You might not be allowed to change the amount of space reserved for a stack—sometimes the size is fixed at link time.
*When inheritance defines a thread’s stack reserve size, a method for determining this value might not be available.
*You might be allowed to choose the stack reserve size independently of stack commit size, which allows partial commitment to memory, or you might be allowed to specify only one or the other attribute (the stack is either reserved and can only be committed on demand, or the stack must be entirely committed, if at all).
*The specific attribute availability and validation criteria are unique to each environment and are documented in the appropriate chapters in the Threads Module Platform Guide.
*Stack Commit Size—The stack commit size attribute specifies the amount of physical memory and pagefile space to initially commit to the stack.
The feature test macro for stack commit size is RW_THR_HAS_STACK_COMMIT_SIZE. If this macro is not defined, attempts to query or set the commit size attribute always produce an exception. If it is defined, then the current environment has some level of support or recognition for stack commit size and allows you to get or set this attribute.
Stack commit size is defined in terms of some number of bytes using size_t values. The actual sizes can be rounded to some environment-specific granularity (such as page size).
The RWThreadAttribute member functions that manipulate the stack commit size include:
*canGetStackCommitSize() — Indicates whether the stack commit size is supported in the current environment and whether getStackCommitSize() can currently return a legal value.
*isStackCommitSizeSet() — Indicates whether the commit size value is the value that was previously set by a call to setStackCommitSize(size_t).
*getStackCommitSize() — Returns the default commit size value if the attribute value has not yet been defined. Otherwise, it returns the value specified in the last call to setStackCommitSize(size_t). If the current environment or circumstances do not allow interrogation of the time-slice quantum, then attempts to use this function result in exceptions.
*canSetStackCommitSize() — Indicates whether the stack commit size attribute is supported and can be set in the current environment.
*setStackCommitSize(size_t) — Sets the commit size attribute to the value passed as an argument. If the current environment does not support this attribute or if the specified attribute value is outside the legal range, then the function produces an exception.
*resetStackCommitSize() — Restores the commit size attribute to its default value. The default value and its source vary by environment.
An attempt to read the default value for this attribute produces an exception if the user stack attributes have been set, unless the stack reserve or commit size is set or the user stack attributes are reset. In those cases, the default value of system-managed stack attributes are again available.
Systems vary in several ways:
*You might not be allowed to specify the amount of memory to commit to the stack.
*When inheritance defines a thread’s stack reserve size, a method for determining this value might not be available.
*The specific attribute availability and validation criteria are unique to each environment and are documented in the appropriate section of the Threads Module Platform Guide.
The macro RW_THR_HAS_PARTIAL_STACK_COMMITMENT indicates whether the current environment allows the user to specify that some amount of the memory space allocated for thread stack is to be committed to physical memory at creation.
If RW_THR_HAS_PARTIAL_STACK_COMMITMENT is defined, then the macro RW_THR_HAS_STACK_COMMIT_SIZE is defined. If RW_THR_HAS_STACK_COMMIT_SIZE is defined, but RW_THR_HAS_PARTIAL_STACK_COMMITMENT is not, then the commit size also specifies the amount of memory to reserve for the stack.
3.9.10.3 User-Managed Stack Attributes
If you want to allocate and manage a thread’s stack, the relevant attributes include the user stack address and user stack size.
*The user stack address defines the lowest, or bottom address of the address space reserved, allocated, and managed by the user.
The user stack address is defined as a void* value.
*The user stack size defines the memory space size of the user-allocated stack. It locates the top of the address space where the stack bottom is typically located (in those environments where stacks grow downward in memory).
The user stack size is defined in terms of some number of bytes using size_t values.
Using the feature test macro. The feature test macro for user stack support is RW_THR_HAS_USER_STACK. If this macro is not defined, attempts to query or set the user stack attributes always produce an exception. If it is defined, then the current environment has support for user stack attributes and allows you to get or set these attributes.
Member functions. The RWThreadAttribute member functions that define and manipulate the user stack attributes include:
*canGetUserStack() — Indicates whether the user stack attributes are supported in the current environment and whether getUserStackAddress() or getUserStackSize() can currently return a legal value. Default values for user stack attributes do not exist, so this function returns true only if the attributes have been set.
*isUserStackSet() — Indicates whether the user stack attributes have been previously set by a call to setUserStack(void*, size_t).
*getUserStackAddress() — Returns the address value specified in the last call to setUserStack(void*, size_t). If the current environment or circumstances do not allow interrogation of the user stack address, then attempts to use this function result in exceptions.
*getUserStack() — Returns the stack size value specified in the last call to setUserStack(void*, size_t). If the current environment or circumstances do not allow interrogation of the user stack size, then attempts to use this function result in exceptions.
*canSetUserStack() — Indicates whether the user stack attribute is supported and can be set in the current environment.
*setUserStack(void*, size_t) — Sets the user stack address and size to the values passed as arguments. If the current environment does not support these attributes or if the specified attribute values are outside their legal range, then the function produces an exception.
*resetUserStack() — Restores the system-managed stack as the default stack allocation behavior for this thread attribute instance.
*getMinStackSize() — Returns the minimum amount of stack space required to implement a thread that calls a null routine. The Threading package also insures that a system-managed stack is of sufficient size by adding this value to any value defined using setStackReserveSize(size_t).
If the current environment or circumstances do not allow interrogation of the minimum stack size, then attempts to use this function result in exceptions. The availability of this function can be inferred by testing the result returned by either canGetStackReserveSize() or canGetUserStack().
Exceptions. Any attempt to read the default values for these attributes produces an RWTHRInternalError exception. These attributes have no default values.
For additional information on user stack management support, see the appropriate chapters in the Threads Module Platform Guide.
3.9.11 Initializing Threaded Runnables
Every threaded runnable instance contains a thread attribute instance that defines the thread attributes to use when creating a new thread during a call to start(). The Threading package allows you to change or query the thread attribute instances associated with any threaded runnable.
3.9.11.1 Supplying RWThreadAttribute Instances To Threaded Runnables
RWThreadAttribute instances can be supplied to threaded runnable objects at one of two times:
*When the runnable objects are constructed (see Section 3.9.11.2).
*After construction, using accessor functions (see Section 3.9.11.3).
RWThreadAttribute objects can be supplied to and shared between any number of threaded runnable objects. Changes made to the values of a shared thread attribute instance can be seen by all runnables that have been given a handle to that attribute instance.
The thread attribute instance referenced by each threaded runnable is only used or evaluated at the time the thread is created. A threaded runnable makes a local copy of its current thread attribute instance each time start() is called. This prevents the attribute values that were present when a thread was created from being lost as a result of future changes in a runnable’s thread attribute instance or the attribute’s values.
Changing a runnable’s thread attribute instance or values of that attribute instance does not affect the active thread, if any, possessed by the runnable object. Attribute changes can only affect the next thread created as result of a call to start().
3.9.11.2 Supplying RWThreadAttribute Instances At Construction
RWThreadAttribute instances can be supplied to threaded runnable objects if the runnables are constructed using the appropriate static make() functions. If a thread attribute instance is not specified when a threaded runnable is constructed, then that runnable is initialized with its own RWThreadAttribute instance initialized with default values.
The following list identifies the make() functions for each threaded runnable class that accepts an RWThreadAttribute instance:
*RWRunnableServer
static RWRunnableServer make(const RWThreadAttribute&)— Constructs an RWRunnableServer instance initialized with the specified thread attribute instance.
*RWServerPool
static RWServerPool make(const RWThreadAttribute&,size_t) — Constructs an RWServerPool instance whose main thread is created using the specified thread attribute instance and whose pool threads are created using default thread attributes.
static RWServerPool make(size_t,const RWThreadAttribute&)— Constructs an RWServerPool instance whose main thread is created with default thread attributes and whose pool threads are created using the specified thread attribute instance.
static RWServerPool make(const RWThreadAttribute&
serverThreadAttr, size_t numThreads,
const RWThreadAttribute& poolThreadsAttr,
size_t maxCapacity);
Constructs an RWServerPool instance whose main thread is created using the first thread attribute instance and whose pool threads are created using the second thread attribute instance.
*RWThreadFunction
static RWThreadFunction make(RWTFunctor<void()>&, const RWThreadAttribute&)— Constructs an RWThreadFunction instance initialized with the specified thread attribute instance.
static RWThreadFunction make(const RWThreadAttribute&)— Constructs an RWThreadFunction instance initialized with the specified thread attribute instance.
*RWTThreadIOUFunction
static RWTThreadIOUFunction<R> make(const RWTFunctor<R()>&, const RWThreadAttribute&)— Constructs an RWTThreadIOUFunction instance initialized with the specified thread attribute instance.
static RWTThreadIOUFunction<R> make (const RWTIOUEscrow <R>&, const RWTFunctor<R()>&, const RWThreadAttribute&)— Constructs an RWTThreadIOUFunction instance initialized with the specified thread attribute instance.
3.9.11.3 Supplying RWThreadAttribute Instances After Construction
To set the thread attributes for threaded runnables already constructed, you must use the following accessor functions:
*RWThread and RWThreadSelf
void setAttribute(const RWThreadAttribute&)— Replaces the threaded runnable's current thread attribute object with another. Changing a threaded runnable's attribute object after the runnable has already been started does not affect its current thread—it can only affect the thread created the next time the runnable is started.
*RWServerPool
void setPoolAttribute(const RWThreadAttribute&)— Changes the thread attribute instance used to initialize new runnable server objects as they are created in the thread pool. Changing the pool thread attribute after the server has started does not affect new threads started by the pool server until the server pool instance is restarted.
3.9.12 Querying Threaded Runnables For Thread Creation Attributes
The thread attribute instance associated with a threaded runnable can be queried at any time.
*RWThread and RWThreadSelf
getAttribute(): Retrieves a handle to the thread attribute instance, if any, specified during the threaded runnable’s construction or the instance specified in the last call to setAttribute(). If no attribute has ever been specified, this function returns the default attribute instance created when the runnable was constructed.
Because threaded runnables make a local copy of the thread attribute instance during thread creation, the instance returned by this function might not have the same attribute values as were used to create the runnable’s active thread. Use the getActiveAttribute() function to retrieve a copy of the thread attribute instance that actually created the current active thread.
getActiveAttribute(): Gets a handle to a copy of the actual thread attribute instance used to create the runnable’s current active thread. If the threaded runnable has not yet been started, this function returns a copy of a default attribute instance. Once start() has been called, this function returns a copy of the local attribute instance that was saved when the thread was created.
*RWServerPool
getPoolAttribute(): Retrieves a handle to the pool thread attribute instance, if any, specified during server pool construction or the instance specified in the last call to setPoolAttribute(). If no attribute has ever been specified, this function returns the default attribute instance created when the runnable was constructed.
Because a server pool makes a local copy of the pool thread attribute instance during start-up, this instance might not have the same attribute values as were used to create the threads in the server pool. Use the getActivePoolAttribute() function to retrieve a copy of the thread attribute instance that created the active pool threads.
getActivePoolAttribute(): Gets a handle to a copy of the actual thread attribute instance that created the current server pool threads. If the server pool has not yet been started, this function returns a copy of a default attribute instance. Once start() has been called, this function returns a copy of the attribute instance that was saved when the server was started.