UC3M

Telematic/Audiovisual Syst./Communication Syst. Engineering

Systems Architecture

September 2017 - January 2018

11.3.2.  Mutex: Example

The way you can use a pthread_mutex to solve is problem is to enforce all threads that perform the operation unsplitable action: "data read, modify and write" to use the same mutex. The mutex has to be taken before performing this critical section and would be released later, after modifying the data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// compile with $ gcc -Wall -g *.c -pthread -o program
// run with ./program
// check with valgrind --tool=helgrind ./program
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

pthread_mutex_t mutex;


void* thread_run(void* data)
{ int i_data;
  sleep(2); 
  printf("[TH_ID:%ld]: Hello from the thread \n", pthread_self());
  pthread_mutex_lock(&mutex);
  printf("[TH_ID:%ld]: Reading %i \n", pthread_self(),(*(int*)data));  
  i_data=(*(int*)data);
  sleep(1); //It should be removed
  i_data++;
  (*(int*)data)=i_data;  
  printf("[TH_ID:%ld]: Writing %i \n", pthread_self(),(*(int*)data));
  pthread_mutex_unlock(&mutex);
  printf("[TH_ID: %ld]: To exit...............\n",pthread_self());
  return data;
}


int main()
{ int i;
  pthread_t thread[2];
  int data=0;
  int thread_rc;
  
  if(pthread_mutex_init(&mutex,NULL)!=0) 
    return -1;
  for (i=0;i<2; i++)
  { printf("[MAIN:%ld]: Starting............ \n",pthread_self());
    if ((thread_rc=pthread_create(&thread[i],NULL,thread_run,&data))!=0)
      {
       printf("Error creating the thread. Code %i",thread_rc);
       return -1;
      }
  }
  
  sleep(1);
  printf("[MAIN:%ld]: Thread allocated \n",pthread_self());
  int *ptr_output_data;
  for ( i=0;i<2; i++)
  {
    pthread_join(thread[i],(void **)&ptr_output_data);
  }
  pthread_mutex_destroy(&mutex);
  printf("[MAIN:%ld]: Thread returns %d \n",pthread_self(), *ptr_output_data);
  return 0;
} 

In this example you can see that thread execution always returns the same result in the terminal:

[MAIN:1]: Starting............ 
[MAIN:2]: Starting............ 
[MAIN:2]: Thread allocated 
[TH_ID:0]: Hello from the thread 
[TH_ID:0]: Reading 0 
[TH_ID:0]: Writing 1 
[TH_ID:0]: To exit...............
[TH_ID:1]: Hello from the thread 
[TH_ID:1]: Reading 1 
[TH_ID:1]: Writing 2 
[TH_ID:1]: To exit...............
[MAIN:2]: Thread returns 2  

The list of execution that causes the use of lock that makes reading and writing of data from different threads is not performed concurrently includes the following executions:

  • T_0 lock(m), T_1 lock(m), T_0 read_(0), T_0 write_(1), T_0 unlock(m), T_1 read_(1), T_1 write_(2), T_1 unlock(m)

  • T_1 lock(m), T_0 lock(m), T_1 read_(0), T_1 write_(1), T_1 unlock(m), T_0 read_(1), T_0 write_(2), T_0 unlock(m)

And one that should never occur, since the locks avoid this execution, is:

  • T_0 lock(m), T_1 lock(m), T_0 read_(0), T_1 read_(0), T_0 write_(1), H_1 write_(1), T_0 unlock(m), T_1 unlock(m)

This situation never occurs because the mutex prevents two threads take the mutex simultaneously.