UC3M

Telematic/Audiovisual Syst./Communication Syst. Engineering

Systems Architecture

September 2017 - January 2018

17.2.2.  Ordering in POSIX locks

Another advantage offered by Helgrind is its access to the locks. The following example shows an example of bad behaviour with a chance of interblocking among the thread of the main thread and second thread of the application.

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
 
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>       
#include <unistd.h>

pthread_mutex_t mutex_1;
pthread_mutex_t mutex_2;
 void *counter_thread(void *ctr)
{  printf("In thread: running...\n");
   sleep(1);
 pthread_mutex_lock(&mutex_2);
  pthread_mutex_lock(&mutex_1);
  printf("In Thread: Locked printf in main");
  pthread_mutex_unlock(&mutex_1);
  pthread_mutex_unlock(&mutex_2);
   
   printf("In thread: exiting .............\n");
   pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
  pthread_t threads[1];
  pthread_mutex_init(&mutex_1, NULL);
  pthread_mutex_init(&mutex_2, NULL);

  int rc=0;
  printf("(log) In main: creating thread %i\n", 1);
  rc = pthread_create(&threads[0], NULL, counter_thread, NULL);
  if (rc){
      printf("ERROR; return code from pthread_create() is %d\n", rc);
      exit(-1);
     }
  sleep(1);
  
  pthread_mutex_lock(&mutex_1);
  pthread_mutex_lock(&mutex_2);
  printf("In main: Locked printf in main");
  pthread_mutex_unlock(&mutex_1);
  pthread_mutex_unlock(&mutex_2);
  
  
  pthread_join(threads[0],NULL);     
  pthread_mutex_destroy(&mutex_1);
  pthread_mutex_destroy(&mutex_2);
  return 0;
}
 

Runing the example with Helgrind produces the following output:

$gcc -Wall -g -pthread helgrind_threads_lock_ordering.c -o helgrind_threads_lock_ordering
$ valgrind -v  --tool=helgrind  ./helgrind_threads_lock_ordering 

==8793== 
==8793== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 36 from 16)
==8793== 
==8793== 1 errors in context 1 of 1:
==8793== ----------------------------------------------------------------
==8793== 
==8793== Thread #2: lock order "0x804A044 before 0x804A05C" violated
==8793== 
==8793== Observed (incorrect) order is: acquisition of lock at 0x804A05C
==8793==    at 0x402E103: pthread_mutex_lock (in /usr/lib/valgrind/vgpreload_helgrind-x86-linux.so)
==8793==    by 0x804869D: counter_thread (helgrind_threads_lock_ordering.c:12)
==8793==    by 0x402DD35: ??? (in /usr/lib/valgrind/vgpreload_helgrind-x86-linux.so)
==8793==    by 0x405AD4B: start_thread (pthread_create.c:308)
==8793==    by 0x415DB8D: clone (clone.S:130)
==8793== 
==8793==  followed by a later acquisition of lock at 0x804A044
==8793==    at 0x402E103: pthread_mutex_lock (in /usr/lib/valgrind/vgpreload_helgrind-x86-linux.so)
==8793==    by 0x80486A9: counter_thread (helgrind_threads_lock_ordering.c:13)
==8793==    by 0x402DD35: ??? (in /usr/lib/valgrind/vgpreload_helgrind-x86-linux.so)
==8793==    by 0x405AD4B: start_thread (pthread_create.c:308)
==8793==    by 0x415DB8D: clone (clone.S:130)
==8793== 
==8793== Required order was established by acquisition of lock at 0x804A044
==8793==    at 0x402E103: pthread_mutex_lock (in /usr/lib/valgrind/vgpreload_helgrind-x86-linux.so)
==8793==    by 0x804879C: main (helgrind_threads_lock_ordering.c:37)
==8793== 
==8793==  followed by a later acquisition of lock at 0x804A05C
==8793==    at 0x402E103: pthread_mutex_lock (in /usr/lib/valgrind/vgpreload_helgrind-x86-linux.so)
==8793==    by 0x80487A8: main (helgrind_threads_lock_ordering.c:38)
==8793== 
--8793-- 
--8793-- used_suppression:      4 helgrind-glibc2X-005
--8793-- used_suppression:      4 helgrind-glibc2X-101
--8793-- used_suppression:     28 helgrind-glibc2X-004
==8793== 
==8793== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 36 from 16)

Solving out the issue in this particular case means changing the order in which the locks are taken. The following listing shows how this change may be carried out in the second thread:

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
 
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>       
#include <unistd.h>

pthread_mutex_t mutex_1;
pthread_mutex_t mutex_2;
 void *counter_thread(void *ctr)
{  printf("In thread: running...\n");
   sleep(1);
 pthread_mutex_lock(&mutex_1);
  pthread_mutex_lock(&mutex_2);
  printf("In Thread: Locked printf in main");
  pthread_mutex_unlock(&mutex_1);
  pthread_mutex_unlock(&mutex_2);
   
   printf("In thread: exiting .............\n");
   pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
  pthread_t threads[1];
  pthread_mutex_init(&mutex_1, NULL);
  pthread_mutex_init(&mutex_2, NULL);

  int rc=0;
  printf("(log) In main: creating thread %i\n", 1);
  rc = pthread_create(&threads[0], NULL, counter_thread, NULL);
  if (rc){
      printf("ERROR; return code from pthread_create() is %d\n", rc);
      exit(-1);
     }
  sleep(1);
  
  pthread_mutex_lock(&mutex_1);
  pthread_mutex_lock(&mutex_2);
  printf("In main: Locked printf in main");
  pthread_mutex_unlock(&mutex_1);
  pthread_mutex_unlock(&mutex_2);
  
  
  pthread_join(threads[0],NULL);     
  pthread_mutex_destroy(&mutex_1);
  pthread_mutex_destroy(&mutex_2);
  return 0;
}