diff --git a/mutex_barriers/ex1/ex1.md b/mutex_barriers/ex1/ex1.md new file mode 100644 index 0000000000000000000000000000000000000000..c09d214da9ea7d7b3e82bfd4cc5a1a29003db0a0 --- /dev/null +++ b/mutex_barriers/ex1/ex1.md @@ -0,0 +1,64 @@ +# Exercice 1 + +Le coeur des 3 threads ci-dessous (appelés en boucle) peuvent avoir grand nombre d’occurrences +chacun. Les mutex sont déjà initialisés correctement. Les appels aux ressources X(), Y() et Z() ne +sont pas thread-safe. + +## Coeur du thread A + +```c +mutex_lock(&mutex_x); +use_X(); +mutex_unlock(&mutex_x); +``` + +## Coeur du thread B + +```c +mutex_lock(&mutex_y); +use_Y(); +mutex_unlock(&mutex_y) +``` + +## Coeur du thread C + +```c +mutex_lock(&mutex_z); +use_Z(); +mutex_unlock(&mutex_z) +``` + +## Exécution + +avec : +```c +use_X() { + X() ; + mutex_lock(&mutex_y); + Y(); + mutex_unlock(&mutex_y); +} + +use_Y() { + Y() ; + mutex_lock(&mutex_z); + Z(); + mutex_unlock(&mutex_z); +} + +use_Z() { + Z() ; + mutex_lock(&mutex_x); + X(); + mutex_unlock(&mutex_x); +} +``` + +## Questions + +1. Mais cette solution peut présenter un dead-lock . Démontrez-le de façon explicite. + +2. Est-ce que la solution proposée ci-dessus fonctionnerait si seulement les version A et B des +threads étaient utilisés (à plusieurs exemplaires) ? + +3. Corrigez la solution complète, **en conservant des sections critiques les plus courtes possibles.** diff --git a/mutex_barriers/ex2/.gitignore b/mutex_barriers/ex2/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b6de771d2fff3a218dbc1b3de5b6b6cc1dc54e08 --- /dev/null +++ b/mutex_barriers/ex2/.gitignore @@ -0,0 +1,2 @@ +*.o +prog diff --git a/mutex_barriers/ex2/Makefile b/mutex_barriers/ex2/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..5dadd7ee2331424c5f77b2a1408f2cfc22f1f613 --- /dev/null +++ b/mutex_barriers/ex2/Makefile @@ -0,0 +1,21 @@ +CC := clang +CFLAGS := -g -pedantic -std=gnu11 -Wall -Wextra +LDFLAGS := -fsanitize=address,leak,undefined -lpthread +TARGET := prog + +all: $(TARGET) + +$(TARGET): prog.o + $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< + +.PHONY: clean + +clean: + rm -f *.o $(TARGET) + +.PHONY: rebuild + +rebuild: clean all diff --git a/mutex_barriers/ex2/prog.c b/mutex_barriers/ex2/prog.c new file mode 100644 index 0000000000000000000000000000000000000000..7cd2218aa35d86fd67e27e720acec3bb1881eb6b --- /dev/null +++ b/mutex_barriers/ex2/prog.c @@ -0,0 +1,84 @@ +#include <pthread.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +typedef struct { + int nb_threads; + pthread_mutex_t counter_lock; + int counter; + pthread_cond_t can_proceed; +} barrier_t; + +barrier_t b; + +void barrier_init(barrier_t *b, int count) { + if (count < 1) { + fprintf(stderr, "Cannot initialize a barrier for less than 1 thread\n"); + return; + } + + b->nb_threads = count; + b->counter = 0; + pthread_cond_init(&b->can_proceed, NULL); + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); + + pthread_mutex_init(&b->counter_lock, &attr); + + fprintf(stdout, "Barrier has been successfully initialized\n"); +} + +void barrier_wait(barrier_t *b) { + pthread_mutex_lock(&b->counter_lock); + + b->counter++; + + if (b->counter == b->nb_threads) { + b->counter = 0; + pthread_cond_broadcast(&b->can_proceed); + } else { + while (pthread_cond_wait(&b->can_proceed, &b->counter_lock) != 0) { + } + } + + pthread_mutex_unlock(&b->counter_lock); +} + +void barrier_destroy(barrier_t *b) { + pthread_cond_destroy(&b->can_proceed); + pthread_mutex_destroy(&b->counter_lock); +} + +void *routine(void __attribute__((unused)) * arg) { + fprintf(stdout, "2nd thread BEFORE barrier\n"); + barrier_wait(&b); + fprintf(stdout, "2nd thread AFTER barrier\n"); + return NULL; +} + +int main(void) { + fprintf(stdout, "main thread started\n"); + barrier_init(&b, 2); + + pthread_t thread; + if (pthread_create(&thread, NULL, routine, NULL) == -1) { + perror("pthread_create"); + exit(EXIT_FAILURE); + } + + fprintf(stdout, "main thread BEFORE barrier\n"); + barrier_wait(&b); + fprintf(stdout, "main thread AFTER barrier\n"); + + if (pthread_join(thread, NULL) == -1) { + perror("pthread_join"); + exit(EXIT_FAILURE); + } + + barrier_destroy(&b); + + return EXIT_SUCCESS; +}