UC3M

Grado en Ing. Telemática/Sist. Audiovisuales/Sist. de Comunicaciones

Arquitectura de Sistemas

Septiembre 2017 - Enero 2018

17.2.2. Ordenamiento de los cerrojos POSIX

Otra de las ventajas ofertadas por Helgrind está en cómo se accede a los cerrojos. El siguiente ejemplo muestra un ejemplo de mala ordenación en locks, donde hay una posibilidad de interbloqueo entre el hilo del main y el otro hilo de la aplicación.

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;
}
 

Al ser ejecutado con Helgrind, el ejemplo produce la siguiente salida:

$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)

Solventar el problema en este caso consiste en invertir el orden en que se cogen los cerrojos. El ejemplo que sigue muestra cómo hacerlo en el segundo hilo creado:

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;
}