From f6b199d60159d1a1bc0ab169ae4a81352d22bf11 Mon Sep 17 00:00:00 2001 From: Martin Eyben Date: Tue, 25 Mar 2025 13:52:50 +0100 Subject: [PATCH] feat: add deadlock prevention, still a valgrind issue when returning EDEADLCK --- src/thread/thread.c | 58 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/src/thread/thread.c b/src/thread/thread.c index cc825ac..83f8e28 100644 --- a/src/thread/thread.c +++ b/src/thread/thread.c @@ -18,6 +18,7 @@ #define WAITING 0x4 #define IS_WAITING(entry) (entry->status & WAITING) #define GET_WAITED_THREAD(entry) ((struct context_entry*)entry->waiting) +#define GET_LAST_WAITED_THREAD(entry) (entry->last_waited ? entry->last_waited->last_thread : NULL) #define IS_WAITED_THREAD_FINISHED(entry) (GET_WAITED_THREAD(entry)->status & FINISHED) #define WAITED 0x8 #define IS_WAITED(entry) (entry->status & WAITED) @@ -31,18 +32,27 @@ static char stack_for_freeing[STACK_SIZE] = {0}; static int stack_valgrind_id = 0; static ucontext_t context_for_freeing; +struct last_thread_t; + + struct context_entry { TAILQ_ENTRY(context_entry) link; // Use to navigate inside the list ucontext_t context; thread_t id; void *waiting; // the thread that the entry is waiting for - void *retvalue; // retun value or if the thread is waited, the id of the thread that wait for it + void *retvalue; // return value or if the thread is waited, the id of the thread that wait for it + struct last_thread_t *last_waited; int valgrind_id; char status; char stack[STACK_SIZE]; }; +struct last_thread_t { + struct context_entry * last_thread; + int ref; // number of reference to this struct (for free) +}; + // Use TailQ from queue BSD static TAILQ_HEAD(context_head, context_entry) head = TAILQ_HEAD_INITIALIZER(head); // Current running thread @@ -122,6 +132,7 @@ int thread_create(thread_t* newthread, void* (*func)(void*), void* funcarg) TRACE("ALLOCATED %p", new_entry); new_entry->status = ALLOCATED; new_entry->retvalue = NULL; + new_entry->last_waited = NULL; *newthread = new_entry->id; @@ -145,14 +156,9 @@ int thread_join(thread_t thread, void** retval) return -1; } - // Check if there is a deadlock - struct context_entry* parent = thread; - while (IS_WAITING(parent)) { - if (GET_WAITED_THREAD(parent) == running) { + if(GET_LAST_WAITED_THREAD(entry) == running) { TRACE("Deadlock detected"); return EDEADLK; - } - parent = GET_WAITED_THREAD(parent); } if (!IS_FINISHED(entry)) { @@ -164,6 +170,37 @@ int thread_join(thread_t thread, void** retval) // Use retvalue to share which thread is currently waiting for this thread entry->retvalue = running; + /** Deadlock **/ + // if the running thread is solo (not waited by anyone) + if(!IS_WAITED(running)) { + // if the thread that we want to join is already in a "group" of waiting threads + if (IS_WAITING(entry)) { + // give the last thread waited to the running thread + running->last_waited = entry->last_waited; + } + else { // the thread we want to join is solo + running->last_waited = malloc(sizeof(struct last_thread_t)); + running->last_waited->ref = 0 ; + entry->last_waited = running->last_waited; + entry->last_waited->ref++; + running->last_waited->last_thread = entry; + } + running->last_waited->ref++; + + } else { // the running thread is already part of a groupe of waiting threads + if (IS_WAITING(entry)) { // the thread we want to join is part of a groupe of waiting threads + // release the last_waited of this entry + running->last_waited->last_thread = GET_LAST_WAITED_THREAD(entry); + } + else { // the thread we want to join is solo and has no last_waited allocated + running->last_waited->last_thread = entry; + entry->last_waited = running->last_waited; + entry->last_waited->ref ++; + } + + } + + DBG("%p is waiting for %p", running, entry); do { thread_yield(); @@ -191,6 +228,13 @@ void thread_exit(void* retval) { TRACE("Exit thread %p", running); print_entry(running); + + // free the memory of the last thread struct if no one use it anymore + if(running->last_waited) { + DBG("Last waited ref : %d", running->last_waited->ref); + if(--running->last_waited->ref == 0) + free(running->last_waited); + } if (running == NULL) { exit(0); }