PThreads: Semi-FAQ Revivison 5
Search
Google
Web
cognitus.net

POSIX Threads: Semi-FAQ Revision 5.2

© 2001-2006 Michael M. Lampkin
email: michael.lampkin<at>ieee.org

5. PTHREAD ATTRIBUTES ( CONT. )

PThread attributes, which are briefly mentioned in section 4 during the discussion on joinable and detached threads, are covered in more detail here. These attributes allow you to modify several aspects of a thread's behavior during the creation of the thread.

5.1 What is the thread Stack (Address and Size) Attribute(s)?

The Stack Attribute(s) allow you to view and specify the start address of a thread’s stack and the number of bytes it will occupy. These attributes and the ability to manipulate then are provided since it is realized that at times the system specific default values may be inadequate.

Prior to attempting a manipulation of the stack address you should verify that _POSIX_THREAD_ATTR_STACKADDR and _POSIX_THREAD_ATTR_STACKSIZE are defined in unistd.h to 200112L. If either is defined to a value less than or equal to zero then the indicated functionality is not provided.

While unusual, there are a few plausible reasons to use these attributes:

a) A thread which creates a very limited number of local variables could reduce the stack size saving system resources.

b) A thread which creates a large number of local variables could increase the stack size to avoid the possibility of over running a stack of the default size.

c) A thread could create a stack with its size set to the default + n bytes during the debugging stage where the additional n bytes could provide for "safe" stack overflowing ( see the topics discussing on the Guard Size Attribute ).

d) A thread could have it’s stack address explicitly set so that it could be passed to other threads for monitoring.

e) A thread could have it’s stack address explicitly set, possibly allowing in some implementations for the re-use of the memory without requiring the system to perform its own allocation calls.

There are many uses for these attribute(s) but their use can in some cases limit the portability of your application across platforms and in many cases similar functionality is provided through other mechanisms. Unless you are writing threads which are very recursive (and require additional stack space) or are in the final optimization stage for your application, its quite often best not to use these unless you know exactly what you are doing.

5.2 How can I determine if a system supports the Stack Attribute(s)?

If the header file unistd.h defines the symbolic constant _POSIX_THREAD_ATTR_STACKADDR to a value greater than 0 then the implementation should support the getting and setting of the Stack Address Attribute. If it defined to a value of 200112L then the current specification is supported.

If the header file unistd.h defines the symbolic constant _POSIX_THREAD_ATTR_STACKSIZE to a value greater than 0, the implementation should support the getting and setting of the Stack Size Attribute. If it defined to a value of 200112L then the current specification is supported.

5.3 How do you get/set the thread Stack Address and Size Attribute(s)?

The Stack Address and Size Attributes of a thread are obtainable by querying the thread attribute object which was used during the creation of the thread. As with most thread attributes, there is no way to directly query an existing thread to obtain a full list of its current attributes.

The function defined to discover the Stack Address and Size of a thread attribute object is:

int pthread_attr_getstack
(
   const pthread_attr_t * attr,
   void ** stackaddr,
   size_t * stacksize
);

One point which should be mentioned concerning the get functionality: if the address attribute of an attribute object has not previously been set, the value placed in the stackaddr location is not defined by the standard. The value which appears to be most often used is a 0 or NULL to indicate that the system is responsible for determining the stack address location during the call to pthread_create( ).

The function defined to set the Stack Address and Size Attributes of a thread attribute object is:

int pthread_attr_setstack
(
   pthread_attr_t *attr,
   void * stackaddr,
   size_t stacksize
);

The stackaddr parameter should contain the start location of the desired stack area and the stack size should indicate the desired size in bytes. The stack address (start location) should be properly allocated and aligned for 8 byte data and the given size must be at least PTHREAD_STACK_MIN bytes. The maximum valid size is system dependent and not currently obtainable through a POSIX specified function call.

On success the set operation will return a zero and on error one of the following error numbers:

EINVAL The value stackaddr does not have proper alignment for use as a stack, the value of stacksize is less than PTHREAD_STACK_MIN or the stacksize exceeds an implementation-defined limit.

EACCES The stack page(s) described by the stackaddr and stacksize parameters are not both readable and writable by the thread.

5.4 How do I query an existing thread for its Stack Attribute(s)?

There is no way to query an existing thread to determine the value of either its Stack Address or Stack Size other than saving the unmodified attribute variable used during the thread's creation.

5.5 How do I modify the Stack Attribute(s) of an existing thread?

Once created, there is no way to modify the value of a thread's Stack Address or Stack Size.

5.6 Why does my program crash / segfault when using Stack Attributes?

There could be many reasons for this but the most common seems to be performing something along the lines of:

pthread_kill( thread_id, 0 )

and seeing the system indicating the thread as terminated and then attempting to re-use the stack space allocated to it. Logic might tell us that this SHOULD work and usually it and other typical strategies will BUT sometimes it doesn't for a variety of reasons.

While the above may be an unusual circumstance, perhaps the safest strategies are:

* fully join any thread who has had stack space explicitly allocated and wait for the join to return completely OR

* for detached threads reallocate a new stack segment etc. on the heap for EVERY new thread who's stack attributes you are going to set on creation.

* for detached threads maintain a pool of previously allocated memory segments and maintain a callback to indicate the thread exit time making certain not to re-use the memory segment unless n ( problematic determination ) amount of time has passed since thread exit.

5.7 Can I use the pthread_attr_setstackaddr() function instead?

Previously the stack address was manipulated via a call to pthread_attr_setstackaddr(...) but the newer pthread_attr_setstack(...) is now the recommended method.

Additionally, the function call did not allow a parameter to indicate and was not required to "automatically" know the direction that the system stack grew in. This means that for systems where the stack grows upward, the application would have to pass the start address of the memory block and for those which grew downward it would have to pass the address + size. In other words, it created the potential for yet more incompatibilities associated with this functionality.

5.8 Can I use the pthread_attr_setstacksize() function instead?

Because I said so. End of discussion.

Seriously, the new function which sets the stack address and size at the same time is much better as it provides better value checking and is more portable. If all you have is the older function then ok... otherwise the new setstack option if better.

5.9 Can I re-use an attribute object after setting the Stack Address?

Yes, you can re-use the attribute object but you should be aware of the fact that you cannot re-use it without changing the Stack Address or you would, on your second call to pthread_create with it, specifying another thread using the same stack space as a thread previously created with the attribute object!

The result is that you must reset that section of the attribute to the default value which indicates the system / implementation is responsible for determining and allocating memory for the stack BUT the IEEE Std. does not define this default value!

So there are two things you can do:

a) Completely re-initialize the attribute object by calling pthread_attr_destroy( ) followed by a call to the function pthread_attr_init( ).

b) Create your attribute object and while it is in the default state (before you do ANY modifications to it), call pthread_attr_getstack( ) and store the supplied default stackaddr and stacksize values returned in a safe location. Then simply use those stored values to reset attribute object’s stack attributes to their default value by calling pthread_attr_setstack(thread, addr, size).

5.10 What is the thread Guard Size Attribute?

The Guard Size Attribute controls the amount of memory in bytes, with the default value being specified as PAGE_SIZE in the limits.h header file, which will be allocated at the overflow end of the created thread's stack.

In the case where the thread's stack pointer overflows into the guard buffer, an error will result. The actual resulting error for such an overflow incident is not explicitly defined by IEEE Std. 1003-2001 though the following casual statement is made:

"If an application overflows into this buffer an error shall result (possibly in a SIGSEGV signal being delivered to the thread)."

While this attribute may not appear useful at first glance it does, if nothing else, allow you to preserve the overflow data so that the culprit can later be identified.

Warnings regarding this attribute:

a) If the Stack Attribute (location or size) is set, the Guard Size Attribute is effectively ignored in all cases.

b) Use of guard spaces, while potentially making a threaded application more robust, is also a drain on system resources. Applications which know their threads never overflow their stacks should save these resources by specifying guard buffers of 0 (zero) size or 1 x {PAGE_SIZE} bytes.

c) Calculations for the guard buffer size should correlate to, at a minimum, the size of the largest data structure which will ever exist on the stack. Any size lower this than will defeat the purpose of the guard buffer. i.e. large data structures need large guard buffers.

d) Conforming implementations may internally round up the Guard Size Attribute's value to a multiple of {PAGE_SIZE}. Even under such circumstances, a get operation performed on the Guard Size Attribute will return the value specified in the most recent set operation, NOT the rounded value.

5.11 How can I determine if a system supports the Guard Size Attribute?

The Guard Size Attribute and associated functions are part of the X/Open specific so you should check the file unistd.h for the symbolic constant _XOPEN_VERSION. If defined to a value greater than 0, preferably 600, the system supports this attribute.

5.12 How do I get/set the thread Guard Size Attribute?

The guard size of a thread is obtainable by querying the thread attribute object which was used during the creation of the thread. There is no way to directly query an existing thread to obtain a full list of its current attributes.

The function defined to get the Guard Size attribute value is:

int pthread_attr_getguardsize
(
   const pthread_attr_t * attr,
   size_t * guardsize
);

Where the current guard size held in the thread attribute object attr is written to the size_t location specified by the argument guardsize. On success (a return value of 0), guardsize will contain a value ranging from 0 to some system dependent value.

The function defined to set the Guard Size attribute value is:

int pthread_attr_setguardsize
(
   pthread_attr_t * attr, size_t guardsize
);

Where the thread attribute object location is specified by the argument attr and on success of the function call, will contain the value specified by guardsize.

The valid values for the argument guardsize are the range 0 (no guard buffer) to some system independent value with the default being equal to PAGESIZE bytes. If supplied with a guardsize larger than allowed by the system, an error number of EINVAL will be returned by the function.

5.13 Why doesn't setting both Guard Size AND Stack Attributes work as expected?

If you set the stack location or size using the stack attribute, the guard size attribute will be ignored in ALL cases. You will not get an error when setting any of these in combination as long as the function parameters are correct but, again, the guard size will be ignored.

The rationale is that if an application is setting the stack location or size, that it is using reasonable values for those attributes and should, if necessary, already have allocated it's own "guard buffer" area in the specified stack.

5.14 What is the thread Scheduling Policy Attribute?

The Scheduling Policy Attribute allows you to specify the method by which a thread and it's associated priority and other modifiers are viewed when comparisons are made to determine which thread will next be allocated execution time on a CPU.

The following are commonly implemented policies and their value names as given in the sched.h header:

  • SCHED_OTHER: Another scheduling policy. (required)
  • SCHED_FIFO: First in-first out (FIFO) scheduling policy. (required)
  • SCHED_RR: Round robin scheduling policy. (required)
  • SCHED_SPORADIC: Sporadic server scheduling policy. (optional - exists only if _POSIX_SPORADIC_SERVER >= 1 in unistd.h)

Each policy is required to be identified by a unique value. Each policy also to specify a valid priority range (see "What are the upper and lower valid priority values?") though the valid priority ranges of differing policies are allowed to overlap.

Additional system specific policies may exist which are not defined by IEEE Std. 1003-2001 and which, by their very nature, should be deemed as non-portable.

5.15 How can I determine if a system supports the Scheduling Policy Attribute?

If the system defines the symbolic constant _POSIX_THREAD_PRIORITY_SCHEDULING in the file unistd.h to a value greater than 0, it supports modifying thread scheduling policies.

5.16 What functions get/set the thread Scheduling Policy Attribute?

The Scheduling Policy Attribute of a thread is obtainable by querying the thread attribute object which was used during the creation of the thread.

The function to get the Scheduling Policy Attribute value, if defined, is given as:

int pthread_attr_getschedpolicy
(
   const pthread_attr_t * attr,
   int * policy
);

Where, on success the function will return zero and the location pointed to by the parameter policy will contain the value of SCHED_OTHER, SCHED_FIFO, SCHED_RR, SCHED_SPORADIC or a system dependent policy value.

To set the scheduling policy in a thread attribute we use:

int pthread_attr_setschedpolicy
(
   pthread_attr_t * attr,
   int policy
);

Where the parameter attr is the attribute object to modify and policy is the value of one of the symbolic constants SCHED_OTHER, SCHED_FIFO, SCHED_RR, SCHED_SPORADIC or a system dependent policy value.

On success the function returns zero.

On error the function will return EINVAL if the given policy value is not valid and ENOTSUP is an attempt was made to set the attribute to an unsupported value. Typically I treat both of these errors as equivalent.

5.17 Scheduling policies in detail

Currently I am directly quoting from IEEE Std. 1003.1-2001 but will, at a later date, attempt to provide "gentler" descriptions for the policies.

See the following sections:

  • What is the SCHED_FIFO scheduling policy?
  • What is the SCHED_OTHER scheduling policy?
  • What is the SCHED_RR scheduling policy?
  • What is the SCHED_SPORADIC scheduling policy?

5.18 What is the SCHED_FIFO scheduling policy?

"Threads scheduled under this policy are chosen from a thread list that is ordered by the time its threads have been on the list without being executed; generally, the head of the list is the thread that has been on the list the longest time, and the tail is the thread that has been on the list the shortest time."

IEEE Std. 1003-2001

5.19 What is the SCHED_OTHER scheduling policy?

"Conforming implementations shall include one scheduling policy identified as SCHED_OTHER (which may execute identically with either the FIFO or round robin scheduling policy). The effect of scheduling threads with the SCHED_OTHER policy in a system in which other threads are executing under SCHED_FIFO, SCHED_RR, or SCHED_SPORADIC is implementation-defined."

"This policy is defined to allow strictly conforming applications to be able to indicate in a portable manner that they no longer need a realtime scheduling policy."

"For threads executing under this policy, the implementation shall use only priorities within the range returned by the sched_get_priority_max( ) and sched_get_priority_min( ) functions when SCHED_OTHER is provided as the parameter."

IEEE Std. 1003-2001

5.20 What is the SCHED_RR scheduling policy?

"This policy shall be identical to the SCHED_FIFO policy with the additional condition that when the implementation detects that a running thread has been executing as a running thread for a time period of the length returned by the sched_rr_get_interval( ) function or longer, the thread shall become the tail of its thread list and the head of that thread list shall be removed and made a running thread."

"The effect of this policy is to ensure that if there are multiple SCHED_RR threads at the same priority, one of them does not monopolize the processor. An application should not rely only on the use of SCHED_RR to ensure application progress among multiple threads if the application includes threads using the SCHED_FIFO policy at the same or higher priority levels or SCHED_RR threads at a higher priority level."

IEEE Std. 1003-2001

5.21 What is the SCHED_SPORADIC scheduling policy?

The sporadic server scheduling policy is supported by the system if the symbolic constant _POSIX_SPORADIC_SERVER or _POSIX_THREAD_SPORADIC_SERVER are defined to be greater or equal to 1 ( though preferably 200112L ) in unistd.h.

Full description to be added later.

In addition to the required member int sched_priority within the structure sched_param, SCHED_SPORADIC requires that the following also be contained:

/* Low scheduling priority for sporadic server. */

int sched_ss_low_priority

/* Replenishment period for sporadic server. */

struct timespec sched_ss_repl_period

/* Initial budget for sporadic server. */

struct timespec sched_ss_init_budget

/* Maximum pending replenishments for sporadic server. */

int sched_ss_max_repl

5.22 How do I query an existing thread for its Scheduling Policy Attribute?

There is also a function which allows us to get the current policy and scheduling parameters of a thread:

int pthread_getschedparam
(
   pthread_t thread,
   int * policy,
   struct sched_param * param
);

Remember that each policy has its own valid priority range and if we modify the policy, we must at a minimum make certain that the value of param.sched_priority is within the new policy's range. Any attempt to set the priority to an out of range value will result in an EINVAL being returned and neither the policy or the scheduling parameters being modified.

Finally, since this is part of the REALTIME THREADS component you need to compile or link with the rt library if your system doesn’t do this for you automatically.

5.23 How do I modify the Scheduling Policy of an existing thread?

To allow for the modification of a thread’s scheduling policy after creation, the following function is provided:

int pthread_setschedparam
(
   pthread_t thread,
   int policy,
   const struct sched_param * param
);

Where thread is the id of the thread you which to modify, policy is the scheduling policy to apply, and param is the scheduling parameter to apply.

As with getting the scheduling parameters, this is part of the REALTIME THREADS components. You will need to compile or link with the rt library if your system doesn$rsquo;t do this for you automatically.

5.24 What is the thread Scheduling Parameter(s) Attribute?

Each thread is controlled by an associated scheduling policy and priority and the Scheduling Parameter(s) Attribute, in its minimal required form, allows you to specify the execution priority of a thread in relation to the thread's scheduling policy. Under some scheduling policies, the priority may consist of multiple values including desired priority, upper and lower bound and so forth. In order to contain these multiple indicators, the Scheduling Parameter Attribute is specified as a structure (see next section) instead of as a simple data type.

This parameter does not allow viewing or manipulation of a thread's actual scheduling policy or its contention scope. The methods by which those attributes may be manipulated are covered later in the FAQ under the appropriately labeled section(s).

5.25 What is the structure format for the Scheduling Parameter(s) Attribute?

The definition for the Scheduling Parameters Attribute is:

struct sched_param
{
   int sched_priority
   ...
};

Where the sched_priority member is required in all cases and the "..." represents additional members defined by the specific scheduling policies. There is no requirement as to how additional members must be defined so they may occur as discrete members or as union.

At this time, only the SCHED_SPORADIC policy specifies any additional fields (scheduling policies and the SCHED_SPORADIC policy in particular are covered in more depth later in the FAQ).

5.26 What functions get/set the thread Scheduling Parameter(s) Attribute?

The Scheduling Parameters of a thread are obtainable by querying the thread attribute object which was used during the creation of the thread. As with most thread attributes, there is no way to directly query an existing thread to obtain a full list of its current attributes.

The function defined to discover the Scheduling Parameter Attribute is:

int pthread_attr_getschedparam
(
   const pthread_attr_t * attr,
   struct sched_param * param
);

The function defined to set the Scheduling Parameter Attribute of a thread attribute object is:

int pthread_attr_setschedparam
(
   pthread_attr_t * attr,
   const struct sched_param * param
);

As previously mentioned, the elements of the structure sched_param depends on what scheduling policies the system provides. In all cases though, it will contain at a minimum, the integer member field sched_priority. The valid value ranges for the sched_priority member are covered in further detail under section 5.19 and the appropriate scheduling policy sections.

The set function will return an value of 0 on success, EINVAL if param is not valid and ENOTSUP if an attempt was made to set the attribute (or a member of it) to an unsupported value.

5.27 How do I query an existing thread for its Scheduling Parameter(s) Attribute?

As the scheduling policies have covered in another section, and making the assumption that you desire only to modify the scheduling attribute parameters, we should point out there is also a get function available that lets us query a thread (as opposed to its creation attribute) with the following prototype:

int pthread_getschedparam
(
   pthread_t thread,
   int * policy,
   struct sched_param * param
);

Where the function returns 0 on success and places the current policy value and schedule parameters into the like named parameters.

The only error that may occur is ESRCH and only under the condition where an unknown thread is passed to the function.

5.28 How do I modify the Scheduling Parameter(s) Attribute of an existing thread?

To allow for the modification of a thread's scheduling parameter(s) after creation, the following function is provided:

int pthread_setschedparam
(
   pthread_t thread,
   int policy,
   const struct sched_param * param
);

Where thread is the id of the thread you which to modify, policy is the scheduling policy to apply, and param is the scheduling parameter to apply.

Calls to this function can return any number of error values including:

ESRCH: The thread parameter refers to an unknown thread.

EINVAL: The policy is invalid or unknown OR one of the scheduling parameters is invalid for the specified policy

EPERM: The caller does not have the appropriate permissions to set either policy or scheduling parameters for the thread.

ENOTSUP: An attempt was made to set the policy or scheduling parameters to an unsupported value.

5.29 What are the valid upper and lower priority values?

Defined within the file sched.h are two function calls which allow you to retrieve the minimum and maximum valid priority values for a particular policy.

These functions are:

int sched_get_priority_min
(
   int policy
);

and...

int sched_get_priority_max
(
   int policy
);

The function "policy" parameter is normally one of the following values:

  • SCHED_OTHER Another scheduling policy.
  • SCHED_FIFO First in-first out (FIFO) scheduling policy.
  • SCHED_RR Round robin scheduling policy.
  • SCHED_SPORADIC Sporadic server scheduling policy.

Notes on the policy parameter:

a) The SCHED_OTHER policy is the most commonly seen as the majority of systems do not adhere 100% to a FIFO or RR mode and are in fact hybrids.

b) The SCHED_SPORADIC policy will only exist if the symbolic constant _POSIX_SPORADIC_SERVER is defined to be >=1 (should be 200112L) in unistd.h.

c) Additional system specific policies may exist which are not defined by IEEE Std. 1003-2001 and which, by their very nature, should be deemed as non-portable.

5.30 How do I set a thread's priority using the Scheduling Parameter(s) Attribute?

Since we are only guaranteed that the sched_priority member will be present in the structure for a Scheduling Parameter Attribute, we will focus on its modification.

To guarantee we set the priority member of the attribute in a valid manner, we must perform some range checking on the member. The sequence would be (similar to):

  1. Create and initialize an attribute variable (if not already done).
  2. Get the value of the Scheduling Parameter(s) attribute. Get the default scheduling policy.
  3. Using the obtained scheduling policy, obtain its max (or min) allowed priority range using calls to sched_get_priority_min and sched_get_priority_max.
  4. Validate the desired Scheduling Parameter's (struct) priority member value against the allowed priority range.
  5. If validated, set the Scheduling Parameter(s) attribute, otherwise skip / perform error handling.

5.31 What is the thread Contention Scope Attribute?

The Scope Attribute controls how a thread has its priority applied when it is contending for processing time on one of the system's CPUs. There are currently two contention scopes, process level and system level, currently defined and represented by the symbolic constants PTHREAD_SCOPE_PROCESS and PTHREAD_SCOPE_SYSTEM respectively.

A thread which has a process level scope will only have its priority viewed in the context of the process to which it belongs. In simplistic terms this means that first the thread's parent process must get a time slice on a CPU based on it's priority and then, and only then, does the parent process look at the priority levels of its threads which have a process level scope. Based on the priority levels of its contained process scope threads the parent process determines which will be alloted at least some execution time if possible.

A thread with system scope contention has its priority level applied directly against the priority level of other processes and system scope threads. For the most part, when it comes to scheduling by the system a thread with this type of scope is treated the same as a process would be.

Comparing the two types of scope we find that threads with a process scope generally take more CPU horsepower to execute since it is a two stage system. That is, first the kernel level scheduler must decide if the parent process should get a time slice then a secondary scheduler must decide which thread(s) in the process get part of that time slice. This can also make it hard to predict the 'true' priority of the threads.

On the other hand, system scope threads require only a kernel level call for their scheduling and since they are scheduled directly against processes etc. on the system, their execution times and CPU allocation is much easier to predict.

5.32 How can I determine if a system supports the Contention Scope Attribute?

If the system defines the symbolic constant _POSIX_THREAD_PRIORITY_SCHEDULING in the header unistd.h to a value greater than 0, it supports the Contention Scope Attribute.

5.33 What functions get/set the thread Contention Scope Attribute?

The contention scope of a thread is obtainable by querying the thread attribute object which was used during the creation of the thread. As with most thread attributes, there is no way to directly query an existing thread to obtain a full list of its current attributes.

The function to get the Contention Scope Attribute value, if defined, is given as:

int pthread_attr_getscope
(
   const pthread_attr_t * attr,
   int * contentionscope
);

On success (a return value of 0), the contentionscope parameter will contain the value PTHREAD_SCOPE_PROCESS or PTHREAD_SCOPE_SYSTEM.

The function to set the Contention Scope Attribute value, if defined, is given as:

int pthread_attr_setscope
(
   pthread_attr_t * attr,
   int contentionscope
);

Where the thread attribute object location is specified by the argument attr and the contentionscope parameter should be either PTHREAD_SCOPE_PROCESS or PTHREAD_SCOPE_SYSTEM.

It should be noted that while both of these scopes should be defined, some implementations may not support the use of both for set operations and you should either explicitly check for their existence prior to first use or be prepared to see an error returned of ENOTSUP.

5.34 When should I set the contention to PTHREAD_PROCESS_SCOPE?

If you do NOT require a high(er) level of predictability in reference to the execution of a thread and / or have concerns about the amount of time consumed by the system due to the kernel level calls required by system scope contention, it is recommended that you set the thread's scope to PTHREAD_PROCESS_SCOPE.

The above is a suggestion and not a definite rule.

5.35 When should I set contention to PTHREAD_SYSTEM_SCOPE?

If you require a high(er) level of predictability in reference to the execution of a thread compared to other processes existing on the system, it is recommended that you set the thread's scope to PTHREAD_SYSTEM_SCOPE.

The above is a suggestion and not a definitive rule.


©2003-2006 Michael M. Lampkin

All rights reserved.

Use of this website signifies your agreement to the Terms of Use.

Untitled Document