Table of Contents Previous Section Next Section

7.6 Controlling Thread and Synchronization Attributes

In our discussion thus far, we have noted that entities such as threads and synchronization variables can have several attributes associated with them. For example, different threads may be scheduled differently (round-robin, prioritized, etc.), they may have different stack sizes, and so on. Similarly, a synchronization variable such as a mutex-lock may be of different types. The Pthreads API allows a programmer to change the default attributes of entities using attributes objects.

An attributes object is a data-structure that describes entity (thread, mutex, condition variable) properties. When creating a thread or a synchronization variable, we can specify the attributes object that determines the properties of the entity. Once created, the thread or synchronization variable's properties are largely fixed (Pthreads allows the user to change the priority of the thread). Subsequent changes to attributes objects do not change the properties of entities created using the attributes object prior to the change. There are several advantages of using attributes objects. First, it separates the issues of program semantics and implementation. Thread properties are specified by the user. How these are implemented at the system level is transparent to the user. This allows for greater portability across operating systems. Second, using attributes objects improves modularity and readability of the programs. Third, it allows the user to modify the program easily. For instance, if the user wanted to change the scheduling from round robin to time-sliced for all threads, they would only need to change the specific attribute in the attributes object.

To create an attributes object with the desired properties, we must first create an object with default properties and then modify the object as required. We look at Pthreads functions for accomplishing this for threads and synchronization variables.

7.6.1 Attributes Objects for Threads

The function pthread_attr_init lets us create an attributes object for threads. The prototype of this function is

1   int 
2   pthread_attr_init ( 
3       pthread_attr_t *attr); 

This function initializes the attributes object attr to the default values. Upon successful completion, the function returns a 0, otherwise it returns an error code. The attributes object may be destroyed using the function pthread_attr_destroy. The prototype of this function is:

1   int 
2   pthread_attr_destroy ( 
3       pthread_attr_t *attr); 

The call returns a 0 on successful removal of the attributes object attr. Individual properties associated with the attributes object can be changed using the following functions: pthread_attr_setdetachstate, pthread_attr_setguardsize_np, pthread_attr_setstacksize, pthread_attr_setinheritsched, pthread_attr_setschedpolicy, and pthread_attr_setschedparam. These functions can be used to set the detach state in a thread attributes object, the stack guard size, the stack size, whether scheduling policy is inherited from the creating thread, the scheduling policy (in case it is not inherited), and scheduling parameters, respectively. We refer the reader to the Pthreads manuals for a detailed description of these functions. For most parallel programs, default thread properties are generally adequate.

7.6.2 Attributes Objects for Mutexes

The Pthreads API supports three different kinds of locks. All of these locks use the same functions for locking and unlocking; however, the type of lock is determined by the lock attribute. The mutex lock used in examples thus far is called a normal mutex. This is the default type of lock. Only a single thread is allowed to lock a normal mutex once at any point in time. If a thread with a lock attempts to lock it again, the second locking call results in a deadlock.

Consider the following example of a thread searching for an element in a binary tree. To ensure that other threads are not changing the tree during the search process, the thread locks the tree with a single mutex tree_lock. The search function is as follows:

1   search_tree(void *tree_ptr) 
2   { 
3       struct node *node_pointer; 
4       node_pointer = (struct node *) tree_ptr; 
5       pthread_mutex_lock(&tree_lock); 
6       if (is_search_node(node_pointer) == 1) { 
7           /* solution is found here */ 
8           print_node(node_pointer); 
9           pthread_mutex_unlock(&tree_lock); 
10          return(1); 
11      } 
12      else { 
13          if (tree_ptr -> left != NULL) 
14              search_tree((void *) tree_ptr -> left); 
15          if (tree_ptr -> right != NULL) 
16              search_tree((void *) tree_ptr -> right); 
17      } 
18      printf("Search unsuccessful\n"); 
19      pthread_mutex_unlock(&tree_lock); 
20  } 

If tree_lock is a normal mutex, the first recursive call to the function search_tree ends in a deadlock since a thread attempts to lock a mutex that it holds a lock on. For addressing such situations, the Pthreads API supports a recursive mutex. A recursive mutex allows a single thread to lock a mutex multiple times. Each time a thread locks the mutex, a lock counter is incremented. Each unlock decrements the counter. For any other thread to be able to successfully lock a recursive mutex, the lock counter must be zero (i.e., each lock by another thread must have a corresponding unlock). A recursive mutex is useful when a thread function needs to call itself recursively.

In addition to normal and recursive mutexes, a third kind of mutex called an errorcheck mutex is also supported. The operation of an errorcheck mutex is similar to a normal mutex in that a thread can lock a mutex only once. However, unlike a normal mutex, when a thread attempts a lock on a mutex it has already locked, instead of deadlocking it returns an error. Therefore, an errorcheck mutex is more useful for debugging purposes.

The type of mutex can be specified using a mutex attribute object. To create and initialize a mutex attribute object to default values, Pthreads provides the function pthread_mutexattr_init. The prototype of the function is:

1   int 
2   pthread_mutexattr_init ( 
3       pthread_mutexattr_t   *attr); 

This creates and initializes a mutex attributes object attr. The default type of mutex is a normal mutex. Pthreads provides the function pthread_mutexattr_settype_np for setting the type of mutex specified by the mutex attributes object. The prototype for this function is:

1   int 
2   pthread_mutexattr_settype_np ( 
3       pthread_mutexattr_t   *attr, 
4       int type); 

Here, type specifies the type of the mutex and can take one of the following values corresponding to the three mutex types - normal, recursive, or errorcheck:

  • PTHREAD_MUTEX_NORMAL_ NP

  • PTHREAD_MUTEX_RECURSIVE_NP

  • PTHREAD_MUTEX_ERRORCHECK_NP

A mutex-attributes object can be destroyed using the pthread_attr_destroy that takes the mutex attributes object attr as its only argument.

    Table of Contents Previous Section Next Section