Threads Module User's Guide : PART II Concurrency Packages : Chapter 3 The Threading Package : Thread Attributes : Stack Attributes
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
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.
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.
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.