Saturday, November 1, 2014

Unix Prog: Synchronization Attributes(1)

1. Mutex Attributes

System Definition:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/pthread.h  
 ......  
 /* Initialize mutex attribute object ATTR with default attributes  
   (kind is PTHREAD_MUTEX_TIMED_NP). */  
 extern int pthread_mutexattr_init (pthread_mutexattr_t *__attr)  
    __THROW __nonnull ((1));  
   
 /* Destroy mutex attribute object ATTR. */  
 extern int pthread_mutexattr_destroy (pthread_mutexattr_t *__attr)  
    __THROW __nonnull ((1));  
   
 /* Get the process-shared flag of the mutex attribute ATTR. */  
 extern int pthread_mutexattr_getpshared (const pthread_mutexattr_t *  
                      __restrict __attr,  
                      int *__restrict __pshared)  
    __THROW __nonnull ((1, 2));  
   
 /* Set the process-shared flag of the mutex attribute ATTR. */  
 extern int pthread_mutexattr_setpshared (pthread_mutexattr_t *__attr,  
                      int __pshared)  
    __THROW __nonnull ((1));  
   
 #if defined __USE_UNIX98 || defined __USE_XOPEN2K8  
 /* Return in *KIND the mutex kind attribute in *ATTR. */  
 extern int pthread_mutexattr_gettype (const pthread_mutexattr_t *__restrict  
                    __attr, int *__restrict __kind)  
    __THROW __nonnull ((1, 2));  
   
 /* Set the mutex kind attribute in *ATTR to KIND (either PTHREAD_MUTEX_NORMAL,  
   PTHREAD_MUTEX_RECURSIVE, PTHREAD_MUTEX_ERRORCHECK, or  
   PTHREAD_MUTEX_DEFAULT). */  
 extern int pthread_mutexattr_settype (pthread_mutexattr_t *__attr, int __kind)  
    __THROW __nonnull ((1));  
 ......
 /* Process shared or private flag.  */
 enum
 {
   PTHREAD_PROCESS_PRIVATE,
 #define PTHREAD_PROCESS_PRIVATE PTHREAD_PROCESS_PRIVATE
   PTHREAD_PROCESS_SHARED
 #define PTHREAD_PROCESS_SHARED  PTHREAD_PROCESS_SHARED
 };
 ......  
 /* Mutex types.  */
 enum
 {
   PTHREAD_MUTEX_TIMED_NP,
   PTHREAD_MUTEX_RECURSIVE_NP,
   PTHREAD_MUTEX_ERRORCHECK_NP,
   PTHREAD_MUTEX_ADAPTIVE_NP
 #if defined __USE_UNIX98 || defined __USE_XOPEN2K8
   ,
   PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_TIMED_NP,
   PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP,
   PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP,
   PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL
 #endif
 #ifdef __USE_GNU
   /* For compatibility.  */
   , PTHREAD_MUTEX_FAST_NP = PTHREAD_MUTEX_TIMED_NP
 #endif
 };


1) Within a process, multiple threads can access the same synchronization object, this is the default behavior, in this case, the process-shared mutex attribute is set to PTHREAD_PROCESS_PRIVATE.
2) Access to shared data by multiple processes usually requires synchronization. If the process-shared mutex attribute is set to PTHREAD_PROCESS_SHARED, a mutex may be used for synchronization by those processes.

With PTHREAD_PROCESS_PRIVATE attribute on, the mutex is implemented with more efficient way which by default serve the multithreaded applications.
With PTHREAD_PROCESS_SHARED attribute on, the mutex is implemented with more expensive way serving the being shared among processes.

3) mutex can be of different type.
PTHREAD_MUTEX_NORMAL indicates that the mutex is standard and doesn't do any special error checking and deadlock detection.
PTHREAD_MUTEX_ERRORCHECK provides error checking(lock the mutex more than once, unlock the mutex which is owned by other thread, unlock the mutex already unlocked)
PTHREAD_MUTEX_RECURSIVE allow the same thread to lock the mutex multiple times without firstly unlocking it. It maintains the lock count and the mutex will not be released until it is unlocked the same number of times it is locked.

4) On conditional variable lock, it is not a good idea to use PTHREAD_MUTEX_RECURSIVE mutex. If it is locked multiple times, when pthread_cond_wait release the lock to wait for the signal coming, signal will never coming, since the mutex is still locked, and no other thread can access the shared data and send out the waking up signal.

2. Recursive mutex example:

mutex.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
 #include<pthread.h>  
   
 int value = 0;  
 pthread_mutex_t mutex;  
 pthread_mutexattr_t m_attr;  
   
 void func_2(void)  
 {  
  // func_2 is called by func_1, if the mutex is not recursive, the program  
  // will fall into deadlock  
  pthread_mutex_lock(&mutex);  
  value++;  
  pthread_mutex_unlock(&mutex);  
 }  
   
 void func_1(void)  
 {  
  pthread_mutex_lock(&mutex);  
  value++;  
  func_2();  
  pthread_mutex_unlock(&mutex);  
 }  
   
 int main(int argc, char* argv[])  
 {  
  //Initialize mutex attribute  
  pthread_mutexattr_init(&m_attr);  
  pthread_mutexattr_settype(&m_attr, PTHREAD_MUTEX_RECURSIVE);  
   
  //Initialize mutex  
  if(pthread_mutex_init(&mutex, &m_attr) != 0) {  
   printf("pthread_mutex_init error!\n");  
   exit(1);  
  }  
   
  func_1();  
   
  printf("value: %d\n", value);  
   
  // Destory the mutex and mutex attribute object  
  pthread_mutexattr_destroy(&m_attr);  
  pthread_mutex_destroy(&mutex);  
 }  

shell:
 ubuntu@ip-172-31-23-227:~$ ./mutex.out  
 value: 2  

If both func_1, and func_2 need to lock/unlock the mutex, and one function need to call the other, then the mutex need to be recursive.

No comments:

Post a Comment