Attributes are a way to specify behavior that is different from the default. When a thread is created with pthread_create(3T) or when a synchronization variable is initialized, an attribute object can be specified. The defaults are usually sufficient.
Attributes are specified only at thread creation time; they cannot be altered while the thread is being used.
An attribute object is opaque, and cannot be directly modified by assignments. A set of functions is provided to initialize, configure, and destroy each object type.
Once an attribute is initialized and configured, it has process-wide scope. The suggested method for using attributes is to configure all required state specifications at one time in the early stages of program execution. The appropriate attribute object can then be referred to as needed.
Using attribute objects has two primary advantages.
First, it adds to code portability.
Even though supported attributes might vary between implementations, you need not modify function calls that create thread entities because the attribute object is hidden from the interface.
If the target port supports attributes that are not found in the current port, provision must be made to manage the new attributes. This is an easy porting task though, because attribute objects need only be initialized once in a well-defined location.
Second, state specification in an application is simplified.
As an example, consider that several sets of threads might exist within a process, each providing a separate service, and each with its own state requirements.
At some point in the early stages of the application, a thread attribute object can be initialized for each set. All future thread creations will then refer to the attribute object initialized for that type of thread. The initialization phase is simple and localized, and any future modifications can be made quickly and reliably.
Attribute objects require attention at process exit time. When the object is initialized, memory is allocated for it. This memory must be returned to the system. The pthreads standard provides function calls to destroy attribute objects.
Use pthread_attr_init(3T) to initialize object attributes to their default values. The storage is allocated by the thread system during execution.
Prototype: int pthread_attr_init(pthread_attr_t *tattr);
#include <pthread.h> pthread_attr_t tattr; int ret; /* initialize an attribute to the default value */ ret = pthread_attr_init(&tattr);
The default values for attributes (tattr) are:
Table 3-1 Default Attribute Values for tattr
Attribute |
Value |
Result |
---|---|---|
scope |
PTHREAD_SCOPE_PROCESS |
New thread is unbound - not permanently attached to LWP. |
detachstate |
PTHREAD_CREATE_JOINABLE |
Exit status and thread are preserved after the thread terminates. |
stackaddr |
NULL |
New thread has system-allocated stack address. |
stacksize |
1 megabyte |
New thread has system-defined stack size. |
priority |
|
New thread inherits parent thread priority. |
inheritsched |
PTHREAD_INHERIT_SCHED |
New thread inherits parent thread scheduling priority. |
schedpolicy |
SCHED_OTHER |
New thread uses Solaris-defined fixed priority scheduling; threads run until preempted by a higher-priority thread or until they block or yield. |
Returns zero after completing successfully. Any other returned value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.
ENOMEM
Returned when there is not enough memory to initialize the thread attributes object.
Use pthread_attr_destroy(3T) to remove the storage allocated during initialization. The attribute object becomes invalid.
Prototype: int pthread_attr_destroy(pthread_attr_t *tattr);
#include <pthread.h> pthread_attr_t tattr; int ret; /* destroy an attribute */ ret = pthread_attr_destroy(&tattr);
pthread_attr_destroy() returns zero after completing successfully. Any other returned value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.
When a thread is created detached (PTHREAD_CREATE_DETACHED), its thread ID and other resources can be reused as soon as the thread terminates. Use pthread_attr_setdetachstate(3T) when the calling thread does not want to wait for the thread to terminate.
When a thread is created nondetached (PTHREAD_CREATE_JOINABLE), it is assumed that you will be waiting for it. That is, it is assumed that you will be executing a pthread_join(3T)() on the thread.
Whether a thread is created detached or nondetached, the process does not exit until all threads have exited. See "Finishing Up"for a discussion of process termination caused by premature exit from main().
Prototype: int pthread_attr_setdetachstate(pthread_attr_t *tattr,int detachstate);
#include <pthread.h> pthread_attr_t tattr; int ret; /* set the thread detach state */ ret = pthread_attr_setdetachstate(&tattr,PTHREAD_CREATE_DETACHED);
When there is no explicit synchronization to prevent it, a newly created, detached thread can die and have its thread ID reassigned to another new thread before its creator returns from pthread_create().
For nondetached (PTHREAD_CREATE_JOINABLE) threads, it is very important that some thread join with it after it terminates--otherwise the resources of that thread are not released for use by new threads. This commonly results in a memory leak. So when you do not want a thread to be joined, create it as a detached thread.
#include <pthread.h> pthread_attr_t tattr; pthread_t tid; void *start_routine; void arg int ret; /* initialized with default attributes */ ret = pthread_attr_init()(&tattr); ret = pthread_attr_setdetachstate()(&tattr,PTHREAD_CREATE_DETACHED); ret = pthread_create()(&tid, &tattr, start_routine, arg);
pthread_attr_setdetachstate() returns zero after completing successfully. Any other returned value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.
Use pthread_attr_getdetachstate(3T) to retrieve the thread create state, which can be either detached or joined.
Prototype: int pthread_attr_getdetachstate(const pthread_attr_t *tattr, int *detachstate;
#include <pthread.h> pthread_attr_t tattr; int detachstate; int ret; /* get detachstate of thread */ ret = pthread_attr_getdetachstate (&tattr, &detachstate);
pthread_attr_getdetachstate() returns zero after completing successfully. Any other returned value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.
pthread_attr_setguardsize(3T) sets the guardsize of the attr object.
The guardsize argument provides protection against overflow of the stack pointer. If a thread's stack is created with guard protection, the implementation allocates extra memory at the overflow end of the stack as a buffer against stack overflow of the stack pointer. If an application overflows into this buffer an error results (possibly in a SIGSEGV signal being delivered to the thread).
The guardsize attribute is provided to the application for two reasons:
Overflow protection can potentially result in wasted system resources. An application that creates a large number of threads, and knows its threads will never overflow their stack, can save system resources by turning off guard areas.
When threads allocate large data structures on stack, a large guard area may be needed to detect stack overflow.
If guardsize is zero, a guard area will not be provided for threads created with attr. If guardsize is greater than zero, a guard area of at least size guardsize bytes is provided for each thread created with attr. By default, a thread has an implementation-defined, non-zero guard area.
A conforming implementation is permitted to round up the value contained in guardsize to a multiple of the configurable system variable PAGESIZE (see PAGESIZE in sys/mman.h). If an implementation rounds up the value of guardsize to a multiple of PAGESIZE, a call to pthread_attr_getguardsize() specifying attr will store, in guardsize, the guard size specified in the previous call to pthread_attr_setguardsize().
#include <pthread.h> int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
pthread_attr_setguardsize() fails if:
EINVAL
The argument attr is invalid, the argument guardsize is invalid, or the argument guardsize contains an invalid value,.
pthread_attr_getguardsize(3T) gets the guardsize of the attr object.
A conforming implementation is permitted to round up the value contained in guardsize to a multiple of the configurable system variable PAGESIZE (see PAGESIZE in sys/mman.h). If an implementation rounds up the value of guardsize to a multiple of PAGESIZE, a call to pthread_attr_getguardsize() specifying attr will store, in guardsize, the guard size specified in the previous call to pthread_attr_setguardsize().
#include <pthread.h> int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize);
pthread_attr_getguardsize() fails if:
EINVAL
The argument attr is invalid, the argument guardsize is invalid, or the argument guardsize contains an invalid value,.
Use pthread_attr_setscope(3T) to create a bound thread (PTHREAD_SCOPE_SYSTEM) or an unbound thread (PTHREAD_SCOPE_PROCESS).
Both thread types are accessible only within a given process.
Prototype: int pthread_attr_setscope(pthread_attr_t *tattr,int scope);
#include <pthread.h> pthread_attr_t tattr; int ret; /* bound thread */ ret = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM); /* unbound thread */ ret = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS);
Notice that there are three function calls in this example: one to initialize the attributes, one to set any variations from the default attributes, and one to create the pthreads.
#include <pthread.h> pthread_attr_t attr; pthread_t tid; void start_routine; void arg; int ret; /* initialized with default attributes */ ret = pthread_attr_init (&tattr); /* BOUND behavior */ ret = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM); ret = pthread_create (&tid, &tattr, start_routine, arg);
pthread_attr_setscope() returns zero after completing successfully. Any other returned value indicates that an error occurred. If the following conditions occur, the function fails and returns the corresponding value.
Use pthread_attr_getscope(3T) to retrieve the thread scope, which indicates whether the thread is bound or unbound.
Prototype: int pthread_attr_getscope(pthread_attr_t *tattr, int *scope);
#include <pthread.h> pthread_attr_t tattr; int scope; int ret; /* get scope of thread */ ret = pthread_attr_getscope(&tattr, &scope);
pthread_attr_getscope() returns zero after completing successfully. Any other returned value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.
Unbound threads in a process might or might not be required to be simultaneously active. By default, the threads implementation ensures that a sufficient number of threads are active so that the process can continue to make progress. While this conserves system resources, it might not produce the most effective level of concurrency.
pthread_setconcurrency(3T) allows an application to inform the threads implementation of its desired concurrency level, new_level. The actual level of concurrency provided by the implementation as a result of this function call is unspecified. (For Solaris threads, see "thr_setconcurrency(3T)".)
If new_level is zero, the implementation maintains the concurrency level at its discretion as if pthread_setconcurrency() was never called.
When an application calls pthread_setconcurrency(), it is informing the implementation of its desired concurrency level. The implementation uses this as a hint, not a requirement.
#include <pthread.h> int pthread_setconcurrency(int new_level);
pthread_setconcurrency() fails if:
EINVAL
The value specified by new_level is negative.
EAGAIN
The value specified by new_level would cause a system resource to be exceeded.
pthread_getconcurrency(3T) returns the value set by a previous call to pthread_setconcurrency(). If the pthread_setconcurrency() function was not previously called, pthread_getconcurrency() returns zero to indicate that the implementation is maintaining the concurrency level. (For Solaris threads, see "thr_getconcurrency(3T)".)
#include <pthread.h> int pthread_getconcurrency(void);
pthread_getconcurrency() always returns the concurrency level set by a previous call to pthread_setconcurrency(). If pthread_setconcurrency() has never been called, pthread_getconcurrency() returns zero.
Use pthread_attr_setschedpolicy(3T) to set the scheduling policy. The POSIX draft standard specifies scheduling policy attributes of SCHED_FIFO (first-in-first-out), SCHED_RR (round-robin), or SCHED_OTHER (an implementation-defined method).
SCHED_FIFO
First-In-First-Out; threads scheduled to this policy, if not pre-empted by a higher priority, will proceed until completion. Threads whose contention scope is system (PTHREAD_SCOPE_SYSTEM) are in real-time (RT) scheduling class and the calling process must have a effective user id of 0. SCHED_FIFO for threads whose contention scope is process (PTHREAD_SCOPE_PROCESS) is based on the TS scheduling class.
SCHED_RR
Round-Robin; threads scheduled to this policy, if not pre-empted by a higher priority, will execute for a time period determined by the system. Threads whose contention scope is system (PTHREAD_SCOPE_SYSTEM) are in real-time (RT) scheduling class and the calling process must have a effective user id of 0. SCHED_RR for threads whose contention scope is process (PTHREAD_SCOPE_PROCESS) is based on the TS scheduling class.
SCHED_FIFO and SCHED_RR are optional in POSIX, and are supported for real time bound threads, only.
Currently, only the Solaris SCHED_OTHER, time-sharing, default value is supported in pthreads. For a discussion of scheduling, see the section "Scheduling".
Prototype: int pthread_attr_setschedpolicy(pthread_attr_t *tattr, int policy);
#include <pthread.h> pthread_attr_t tattr; int policy; int ret; /* set the scheduling policy to SCHED_OTHER */ ret = pthread_attr_setschedpolicy(&tattr, SCHED_OTHER);
Returns zero after completing successfully. Any other returned value indicates that an error occurred. When either of the following conditions occurs, the function fails and returns the corresponding value.
Use pthread_attr_getschedpolicy(3T) to retrieve the scheduling policy. Currently, only the Solaris-based SCHED_OTHER default value is supported in pthreads.
Prototype: int pthread_attr_getschedpolicy(pthread_attr_t *tattr, int *policy);
#include <pthread.h> pthread_attr_t tattr; int policy; int ret; /* get scheduling policy of thread */ ret = pthread_attr_getschedpolicy (&tattr, &policy);
Returns zero after completing successfully. Any other returned value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.
Use pthread_attr_setinheritsched(3T) to set the inherited scheduling policy.
An inherit value of PTHREAD_INHERIT_SCHED (the default) means that the scheduling policies defined in the creating thread are to be used, and any scheduling attributes defined in the pthread_create() call are to be ignored. If PTHREAD_EXPLICIT_SCHED is used, the attributes from the pthread_create() call are to be used.
Prototype: int pthread_attr_setinheritsched(pthread_attr_t *tattr, int inherit);
#include <pthread.h> pthread_attr_t tattr; int inherit; int ret; /* use the current scheduling policy */ ret = pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED);
Returns zero after completing successfully. Any other returned value indicates that an error occurred. When either of the following conditions occurs, the function fails and returns the corresponding value.
pthread_attr_getinheritsched(3T) returns the scheduling policy set by pthread_attr_setinheritsched().
Prototype: int pthread_attr_getinheritsched(pthread_attr_t *tattr, int *inherit);
#include <pthread.h> pthread_attr_t tattr; int inherit; int ret; /* get scheduling policy and priority of the creating thread */ ret = pthread_attr_getinheritsched (&tattr, &inherit);
Returns zero after completing successfully. Any other returned value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.
pthread_attr_setschedparam(3T) sets the scheduling parameters.
Scheduling parameters are defined in the param structure; only priority is supported. Newly created threads run with this priority.
SCHED_FIFO
First-In-First-Out; threads scheduled to this policy, if not pre-empted by a higher priority, will proceed until completion. Threads whose contention scope is system (PTHREAD_SCOPE_SYSTEM) are in real-time (RT) scheduling class and the calling process must have a effective user id of 0. SCHED_FIFO for threads whose contention scope is process (PTHREAD_SCOPE_PROCESS) is based on the TS scheduling class.
SCHED_RR
Round-Robin; threads scheduled to this policy, if not pre-empted by a higher priority, will execute for a time period determined by the system. Threads whose contention scope is system (PTHREAD_SCOPE_SYSTEM) are in real-time (RT) scheduling class and the calling process must have a effective user id of 0. SCHED_RR for threads whose contention scope is process (PTHREAD_SCOPE_PROCESS) is based on the TS scheduling class.
Prototype: int pthread_attr_setschedparam(pthread_attr_t *tattr, const struct sched_param *param);
#include <pthread.h> pthread_attr_t tattr; int newprio; sched_param param; newprio = 30; /* set the priority; others are unchanged */ param.sched_priority = newprio; /* set the new scheduling param */ ret = pthread_attr_setschedparam (&tattr, ¶m);
Returns zero after completing successfully. Any other returned value indicates that an error occurred. If the following conditions occur, the function fails and returns the corresponding value.
You can manage pthreads priority two ways. You can set the priority attribute before creating a child thread, or you can change the priority of the parent thread and then change it back.
pthread_attr_getschedparam(3T) returns the scheduling parameters defined by pthread_attr_setschedparam().
Prototype: int pthread_attr_getschedparam(pthread_attr_t *tattr, const struct sched_param *param);
#include <pthread.h> pthread_attr_t attr; struct sched_param param; int ret; /* get the existing scheduling param */ ret = pthread_attr_getschedparam (&tattr, ¶m);
pthread_attr_setschedparam() returns zero after completing successfully. Any other returned value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.
You can set the priority attribute before creating the thread. The child thread is created with the new priority that is specified in the sched_param structure (this structure also contains other scheduling information).
It is always a good idea to get the existing parameters, change the priority, xxx the thread, and then reset the priority.
Example 3-2 shows an example of this.
#include <pthread.h> #include <sched.h> pthread_attr_t tattr; pthread_t tid; int ret; int newprio = 20; sched_param param; /* initialized with default attributes */ ret = pthread_attr_init (&tattr); /* safe to get existing scheduling param */ ret = pthread_attr_getschedparam (&tattr, ¶m); /* set the priority; others are unchanged */ param.sched_priority = newprio; /* setting the new scheduling param */ ret = pthread_attr_setschedparam (&tattr, ¶m); /* with new priority specified */ ret = pthread_create (&tid, &tattr, func, arg);
pthread_attr_setstacksize(3T) sets the thread stack size.
The stacksize attribute defines the size of the stack (in bytes) that the system will allocate. The size should not be less than the system-defined minimum stack size. See "About Stacks"for more information.
Prototype: int pthread_attr_setstacksize(pthread_attr_t *tattr, int size);
#include <pthread.h> pthread_attr_t tattr; int size; int ret; size = (PTHREAD_STACK_MIN + 0x4000); /* setting a new size */ ret = pthread_attr_setstacksize(&tattr, size);
In the example above, size contains the size, in number of bytes, for the stack that the new thread uses. If size is zero, a default size is used. In most cases, a zero value works best.
PTHREAD_STACK_MIN is the amount of stack space required to start a thread. This does not take into consideration the threads routine requirements that are needed to execute application code.
Returns zero after completing successfully. Any other returned value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.
The value returned is less than the value of PTHREAD_STACK_MIN, or exceeds a system-imposed limit, or tattr is not valid.
pthread_attr_getstacksize(3T) returns the stack size set by pthread_attr_setstacksize().
Prototype: int pthread_attr_getstacksize(pthread_attr_t *tattr, size_t *size);
#include <pthread.h> pthread_attr_t tattr; int size; int ret; /* getting the stack size */ ret = pthread_attr_getstacksize(&tattr, &size);
Returns zero after completing successfully. Any other returned value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.
EINVAL
The value returned is less than the value of PTHREAD_STACK_MIN, or exceeds a system-imposed limit.
Typically, thread stacks begin on page boundaries and any specified size is rounded up to the next page boundary. A page with no access permission is appended to the top of the stack so that most stack overflows result in sending a SIGSEGV signal to the offending thread. Thread stacks allocated by the caller are used as is.
When a stack is specified, the thread should also be created PTHREAD_CREATE_JOINABLE. That stack cannot be freed until the pthread_join(3T) call for that thread has returned, because the thread's stack cannot be freed until the thread has terminated. The only reliable way to know if such a thread has terminated is through pthread_join(3T).
Generally, you do not need to allocate stack space for threads. The threads library allocates one megabyte of virtual memory for each thread's stack with no swap space reserved. (The library uses the MAP_NORESERVE option of mmap() to make the allocations.)
Each thread stack created by the threads library has a red zone. The library creates the red zone by appending a page to the top of a stack to catch stack overflows. This page is invalid and causes a memory fault if it is accessed. Red zones are appended to all automatically allocated stacks whether the size is specified by the application or the default size is used.
Because runtime stack requirements vary, you should be absolutely certain that the specified stack will satisfy the runtime requirements needed for library calls and dynamic linking.
There are very few occasions when it is appropriate to specify a stack, its size, or both. It is difficult even for an expert to know if the right size was specified. This is because even a program compliant with ABI standards cannot determine its stack size statically. Its size is dependent on the needs of the particular runtime environment in which it executes.
When you specify the size of a thread stack, be sure to account for the allocations needed by the invoked function and by each function called. The accounting should include calling sequence needs, local variables, and information structures.
Occasionally you want a stack that is a bit different from the default stack. An obvious situation is when the thread needs more than one megabyte of stack space. A less obvious situation is when the default stack is too large. You might be creating thousands of threads and not have enough virtual memory to handle the gigabytes of stack space that this many default stacks require.
The limits on the maximum size of a stack are often obvious, but what about the limits on its minimum size? There must be enough stack space to handle all of the stack frames that are pushed onto the stack, along with their local variables, and so on.
You can get the absolute minimum limit on stack size by calling the macro PTHREAD_STACK_MIN, which returns the amount of stack space required for a thread that executes a NULL procedure. Useful threads need more than this, so be very careful when reducing the stack size.
#include <pthread.h> pthread_attr_t tattr; pthread_t tid; int ret; int size = PTHREAD_STACK_MIN + 0x4000; /* initialized with default attributes */ ret = pthread_attr_init(&tattr); /* setting the size of the stack also */ ret = pthread_attr_setstacksize(&tattr, size); /* only size specified in tattr*/ ret = pthread_create(&tid, &tattr, start_routine, arg);
When you allocate your own stack, be sure to append a red zone to its end by calling mprotect(2).
pthread_attr_setstackaddr(3T) sets the thread stack address.
The stackaddr attribute defines the base of the thread's stack. If this is set to non-null (NULL is the default) the system initializes the stack at that address.
Prototype: int pthread_attr_setstackaddr(pthread_attr_t *tattr,void *stackaddr);
#include <pthread.h> pthread_attr_t tattr; void *base; int ret; base = (void *) malloc(PTHREAD_STACK_MIN + 0x4000); /* setting a new address */ ret = pthread_attr_setstackaddr(&tattr, base);
In the example above, base contains the address for the stack that the new thread uses. If base is NULL, then pthread_create(3T) allocates a stack for the new thread with at least PTHREAD_STACK_MIN bytes.
Returns zero after completing successfully. Any other returned value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.
This example shows how to create a thread with a custom stack address.
#include <pthread.h> pthread_attr_t tattr; pthread_t tid; int ret; void *stackbase; stackbase = (void *) malloc(size); /* initialized with default attributes */ ret = pthread_attr_init(&tattr); /* setting the base address in the attribute */ ret = pthread_attr_setstackaddr(&tattr, stackbase); /* only address specified in attribute tattr */ ret = pthread_create(&tid, &tattr, func, arg);
This example shows how to create a thread with both a custom stack address and a custom stack size.
#include <pthread.h> pthread_attr_t tattr; pthread_t tid; int ret; void *stackbase; int size = PTHREAD_STACK_MIN + 0x4000; stackbase = (void *) malloc(size); /* initialized with default attributes */ ret = pthread_attr_init(&tattr); /* setting the size of the stack also */ ret = pthread_attr_setstacksize(&tattr, size); /* setting the base address in the attribute */ ret = pthread_attr_setstackaddr(&tattr, stackbase); /* address and size specified */ ret = pthread_create(&tid, &tattr, func, arg);
pthread_attr_getstackaddr(3T) returns the thread stack address set by pthread_attr_setstackaddr().
Prototype: int pthread_attr_getstackaddr(pthread_attr_t *tattr,void * *stackaddr);
#include <pthread.h> pthread_attr_t tattr; void *base; int ret; /* getting a new address */ ret = pthread_attr_getstackaddr (&tattr, *base);
Returns zero after completing successfully. Any other returned value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.