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 WAITING 0x4
|
||||||
#define IS_WAITING(entry) (entry->status & WAITING)
|
#define IS_WAITING(entry) (entry->status & WAITING)
|
||||||
#define GET_WAITED_THREAD(entry) ((struct context_entry*)entry->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 IS_WAITED_THREAD_FINISHED(entry) (GET_WAITED_THREAD(entry)->status & FINISHED)
|
||||||
#define WAITED 0x8
|
#define WAITED 0x8
|
||||||
#define IS_WAITED(entry) (entry->status & WAITED)
|
#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 int stack_valgrind_id = 0;
|
||||||
static ucontext_t context_for_freeing;
|
static ucontext_t context_for_freeing;
|
||||||
|
|
||||||
|
struct last_thread_t;
|
||||||
|
|
||||||
|
|
||||||
struct context_entry {
|
struct context_entry {
|
||||||
TAILQ_ENTRY(context_entry)
|
TAILQ_ENTRY(context_entry)
|
||||||
link; // Use to navigate inside the list
|
link; // Use to navigate inside the list
|
||||||
ucontext_t context;
|
ucontext_t context;
|
||||||
thread_t id;
|
thread_t id;
|
||||||
void *waiting; // the thread that the entry is waiting for
|
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;
|
int valgrind_id;
|
||||||
char status;
|
char status;
|
||||||
char stack[STACK_SIZE];
|
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
|
// Use TailQ from queue BSD
|
||||||
static TAILQ_HEAD(context_head, context_entry) head = TAILQ_HEAD_INITIALIZER(head);
|
static TAILQ_HEAD(context_head, context_entry) head = TAILQ_HEAD_INITIALIZER(head);
|
||||||
// Current running thread
|
// Current running thread
|
||||||
@ -122,6 +132,7 @@ int thread_create(thread_t* newthread, void* (*func)(void*), void* funcarg)
|
|||||||
TRACE("ALLOCATED %p", new_entry);
|
TRACE("ALLOCATED %p", new_entry);
|
||||||
new_entry->status = ALLOCATED;
|
new_entry->status = ALLOCATED;
|
||||||
new_entry->retvalue = NULL;
|
new_entry->retvalue = NULL;
|
||||||
|
new_entry->last_waited = NULL;
|
||||||
|
|
||||||
*newthread = new_entry->id;
|
*newthread = new_entry->id;
|
||||||
|
|
||||||
@ -145,15 +156,10 @@ int thread_join(thread_t thread, void** retval)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if there is a deadlock
|
if(GET_LAST_WAITED_THREAD(entry) == running) {
|
||||||
struct context_entry* parent = thread;
|
|
||||||
while (IS_WAITING(parent)) {
|
|
||||||
if (GET_WAITED_THREAD(parent) == running) {
|
|
||||||
TRACE("Deadlock detected");
|
TRACE("Deadlock detected");
|
||||||
return EDEADLK;
|
return EDEADLK;
|
||||||
}
|
}
|
||||||
parent = GET_WAITED_THREAD(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IS_FINISHED(entry)) {
|
if (!IS_FINISHED(entry)) {
|
||||||
// Use status to be in waiting state
|
// Use status to be in waiting state
|
||||||
@ -164,6 +170,37 @@ int thread_join(thread_t thread, void** retval)
|
|||||||
// Use retvalue to share which thread is currently waiting for this thread
|
// Use retvalue to share which thread is currently waiting for this thread
|
||||||
entry->retvalue = running;
|
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);
|
DBG("%p is waiting for %p", running, entry);
|
||||||
do {
|
do {
|
||||||
thread_yield();
|
thread_yield();
|
||||||
@ -191,6 +228,13 @@ void thread_exit(void* retval)
|
|||||||
{
|
{
|
||||||
TRACE("Exit thread %p", running);
|
TRACE("Exit thread %p", running);
|
||||||
print_entry(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) {
|
if (running == NULL) {
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user