feat: add deadlock prevention, still a valgrind issue when returning EDEADLCK
This commit is contained in:
parent
f6a78b3516
commit
f6b199d601
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user