diff --git a/mutex_barriers/ex3/.gitignore b/mutex_barriers/ex3/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7ed204fb7126f174687df888937e9c7f61c1b9f7
--- /dev/null
+++ b/mutex_barriers/ex3/.gitignore
@@ -0,0 +1,2 @@
+*.o
+Ex3_etu
diff --git a/mutex_barriers/ex3/Ex3_etu.c b/mutex_barriers/ex3/Ex3_etu.c
new file mode 100644
index 0000000000000000000000000000000000000000..9c12921819f02b3b0111891e6d6bd1de1d9b8f20
--- /dev/null
+++ b/mutex_barriers/ex3/Ex3_etu.c
@@ -0,0 +1,169 @@
+#include "thread_wrapper.h"
+// #include <assert.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define DEBUG true
+
+#define STACK_SIZE 100000
+#define THREAD_COUNT 100
+#define NB_OF_PUSH_POP 1000
+
+// Barrier used to make sure all threads are created and ready before starting
+// to run the stack test
+pthread_barrier_t *b;
+
+typedef struct {
+    int *ptr;
+    int size;
+    int capacity;
+    pthread_mutex_t lock;
+} stack_t;
+
+typedef struct {
+    int id;
+    stack_t *stack;
+} thr_params;
+
+bool stack_create(stack_t *s, int max_size) {
+    if (max_size < 1) {
+        fprintf(stderr, "Stack size has to be equal to or greater than 1");
+        return false;
+    }
+
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+
+    pthread_mutex_init(&s->lock, &attr);
+
+    // pthread_mutexattr_destroy(&attr);
+
+    s->ptr = malloc(max_size * sizeof(int));
+    s->capacity = max_size;
+    s->size = 0;
+
+    return true;
+}
+
+bool stack_is_empty(stack_t *s) { return s->size == 0; }
+
+void stack_push(stack_t *s, int val) {
+    if (s->size < s->capacity) {
+        pthread_mutex_lock(&s->lock);
+        s->ptr[(s->capacity - 1) - s->size] = val;
+        s->size++;
+        pthread_mutex_unlock(&s->lock);
+        // fprintf(stderr, "%d has been inserted\n", val);
+    } else {
+        fprintf(stderr, "Stack is full\n");
+    }
+}
+
+// Returns -1 in case of an error
+int stack_pop(stack_t *s) {
+    if (stack_is_empty(s)) {
+        fprintf(stderr, "Stack is empty\n");
+        return -1;
+    }
+
+    pthread_mutex_lock(&s->lock);
+    int ret = s->ptr[s->capacity - s->size];
+    s->size--;
+    pthread_mutex_unlock(&s->lock);
+
+    return ret;
+}
+
+void stack_destroy(stack_t *s) {
+    if (s->ptr != NULL) {
+        pthread_mutex_destroy(&s->lock);
+        free(s->ptr);
+    }
+}
+
+void *test_stack(void *data) {
+    bar_wait(b);
+
+    thr_params *p = (thr_params *)data;
+
+    struct timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    unsigned int seed = (unsigned int)ts.tv_nsec;
+
+    for (int i = 0; i < NB_OF_PUSH_POP; i++) {
+        // push
+        int val = rand_r(&seed) % 1000;
+        stack_push(p->stack, val);
+
+        // pop
+        if (!stack_is_empty(p->stack)) {
+            stack_pop(p->stack);
+        }
+    }
+    return NULL;
+}
+
+// Testing if stack is correctly implemented in a single-threaded setting
+// int main(void) {
+//     stack_t s;
+//
+//     if (!stack_create(&s, 10)) {
+//         exit(EXIT_FAILURE);
+//     }
+//
+//     for (int i = 0; i < 10; i++) {
+//         stack_push(&s, i);
+//     }
+//
+//     for (int i = 0; i < 20; i++) {
+//         fprintf(stdout, "stack_pop returned: %d\n", stack_pop(&s));
+//     }
+//
+//     stack_destroy(&s);
+//
+//     return EXIT_SUCCESS;
+// }
+
+int main() {
+    b = bar_create(THREAD_COUNT);
+
+    stack_t s;
+    if (!stack_create(&s, STACK_SIZE)) {
+        fprintf(stderr, "Failed creating stack!\n");
+        return EXIT_FAILURE;
+    }
+#ifdef DEBUG
+    printf("Created stack of %d\n", STACK_SIZE);
+#endif
+
+#ifdef DEBUG
+    printf("Launching %d threads\n", THREAD_COUNT);
+#endif
+    pthread_t t[THREAD_COUNT];
+    thr_params p[THREAD_COUNT];
+    for (int i = 0; i < THREAD_COUNT; i++) {
+        p[i].id = i;
+        p[i].stack = &s;
+        thread_create(&t[i], test_stack, &p[i]);
+    }
+
+    for (int i = 0; i < THREAD_COUNT; i++)
+        thread_join(t[i], NULL);
+
+#ifdef DEBUG
+    printf("Stack empty ? %s\n", stack_is_empty(&s) ? "yes" : "NO!");
+#endif
+
+    stack_destroy(&s);
+#ifdef DEBUG
+    printf("Stack destroyed\n");
+#endif
+
+    bar_destroy(b);
+
+    return EXIT_SUCCESS;
+}
diff --git a/mutex_barriers/ex3/Makefile b/mutex_barriers/ex3/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..ffb039b962083cfe1e4f3345ccdde9e374b08482
--- /dev/null
+++ b/mutex_barriers/ex3/Makefile
@@ -0,0 +1,21 @@
+CC=gcc -Wall -Wextra -std=gnu99 -g
+LIBS=-lpthread -lm -lrt
+SRCS=$(wildcard *.c)
+OBJS=$(SRCS:.c=.o)
+BINS=$(SRCS:%.c=%)
+
+all: Ex3_etu
+
+
+Ex3_etu:   Ex3_etu.o thread_wrapper.o
+	$(CC) $^ -o $@ $(LIBS)
+
+Ex3_etu.o: Ex3_etu.c  thread_wrapper.h
+	$(CC) $< -c
+
+
+thread_wrapper.o: thread_wrapper.c thread_wrapper.h
+	$(CC) $< -c
+
+clean:
+	rm -f $(BINS) $(OBJS)
diff --git a/mutex_barriers/ex3/thread_wrapper.c b/mutex_barriers/ex3/thread_wrapper.c
new file mode 100644
index 0000000000000000000000000000000000000000..6ac8f650c3ad7c3e029bf494e5fcde3451dbdf81
--- /dev/null
+++ b/mutex_barriers/ex3/thread_wrapper.c
@@ -0,0 +1,63 @@
+#include <error.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "thread_wrapper.h"
+
+#define ERR_CODE 1
+
+pthread_mutex_t *mutex_create() {
+	pthread_mutex_t *mutex = malloc(sizeof(pthread_mutex_t));
+	if (!mutex) error(ERR_CODE, ENOMEM, "malloc failed!");
+	pthread_mutexattr_t attr;
+	pthread_mutexattr_init(&attr);
+	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+	int status = pthread_mutex_init(mutex, &attr);
+	if (status) error(ERR_CODE, status, "pthread_mutex_init failed!");
+	pthread_mutexattr_destroy(&attr);
+	return mutex;
+}
+
+void mutex_lock(pthread_mutex_t *mutex) {
+	int status = pthread_mutex_lock(mutex);
+	if (status) error(ERR_CODE, status, "pthread_mutex_lock failed!");
+}
+
+void mutex_unlock(pthread_mutex_t *mutex) {
+	int status = pthread_mutex_unlock(mutex);
+	if (status) error(ERR_CODE, status, "pthread_mutex_unlock failed!");
+}
+
+void mutex_destroy(pthread_mutex_t *mutex) {
+	int status = pthread_mutex_destroy(mutex);
+	if (status) error(ERR_CODE, status, "pthread_mutex_destroy failed!");
+	free(mutex);
+}
+
+pthread_barrier_t *bar_create(int count) {
+	pthread_barrier_t *b = malloc(sizeof(pthread_barrier_t));
+	int status = pthread_barrier_init(b, NULL, count);
+	if (status) error(ERR_CODE, status, "pthread_barrier_init failed!");
+	return b;
+}
+
+void bar_destroy(pthread_barrier_t *b) {
+	int status = pthread_barrier_destroy(b);
+	if (status) error(ERR_CODE, status, "pthread_barrier_destroy failed!");
+	free(b);
+}
+
+void bar_wait(pthread_barrier_t *b) {
+	int status = pthread_barrier_wait(b);
+	if (status != PTHREAD_BARRIER_SERIAL_THREAD && status != 0) error(ERR_CODE, status, "pthread_barrier_wait failed!");
+}
+
+void thread_create(pthread_t *thread, void *(*start_routine)(void *), void *arg) {
+	int status = pthread_create(thread, NULL, start_routine, arg);
+	if (status) error(ERR_CODE, status, "pthread_create failed!");
+}
+
+void thread_join(pthread_t thread, void **retval) {
+	int status = pthread_join(thread, retval);
+	if (status) error(ERR_CODE, status, "pthread_join failed!");
+}
+
diff --git a/mutex_barriers/ex3/thread_wrapper.h b/mutex_barriers/ex3/thread_wrapper.h
new file mode 100644
index 0000000000000000000000000000000000000000..40646c80ab9d048c5ff31f38b6244ca7f5c6b03a
--- /dev/null
+++ b/mutex_barriers/ex3/thread_wrapper.h
@@ -0,0 +1,16 @@
+#ifndef _THREAD_WRAPPER_H_
+#define _THREAD_WRAPPER_H_
+
+#include <pthread.h>
+
+extern pthread_mutex_t *mutex_create();
+extern void mutex_lock(pthread_mutex_t *mutex);
+extern void mutex_unlock(pthread_mutex_t *mutex);
+extern void mutex_destroy(pthread_mutex_t *mutex);
+extern pthread_barrier_t *bar_create(int count);
+extern void bar_destroy(pthread_barrier_t *b);
+extern void bar_wait(pthread_barrier_t *b);
+extern void thread_create(pthread_t *thread, void *(*start_routine)(void *), void *arg);
+extern void thread_join(pthread_t thread, void **retval);
+
+#endif