From a5e76d13d99e2f87a8ce5a71f9ba03fcb8e2a493 Mon Sep 17 00:00:00 2001 From: Matthias Kruk Date: Thu, 21 Nov 2019 17:33:28 +0900 Subject: [PATCH] Add mutex implementation to klibc --- kernel/include/mutex.h | 62 ++++++++++++++++++ kernel/klibc/Makefile | 4 +- kernel/klibc/mutex.c | 139 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 kernel/include/mutex.h create mode 100644 kernel/klibc/mutex.c diff --git a/kernel/include/mutex.h b/kernel/include/mutex.h new file mode 100644 index 0000000..e77b2f4 --- /dev/null +++ b/kernel/include/mutex.h @@ -0,0 +1,62 @@ +#ifndef __MUTEX_H +#define __MUTEX_H + +typedef struct _mutex mutex_t; + +/* + * mutex_lock() - Lock a mutex + * + * SYNOPSIS + * int mutex_lock(mutex_t *mutex); + * + * DESCRIPTION + * The mutex_lock() function locks the mutex pointed to by `mutex'. + * If the mutex cannot be immediately acquired, the calling thread + * will be suspended and added to the mutex's wait queue, and it will + * be resumed when it has arrived at the top of the queue and the + * mutex is unlocked by another thread. + * + * RETURN VALUE + * 0 the mutex was successfully acquired + * -EDEADLK acquiring the mutex would have caused a deadlock + * -ENOMEM the kernel ran out of memory while enqueueing the thread + */ +int mutex_lock(mutex_t*); + +/* + * mutex_trylock() - Attempt to lock a mutex + * + * SYNOPSIS + * int mutex_trylock(mutex_t *mutex); + * + * DESCRIPTION + * The mutex_trylock() function will attempt to lock the mutex + * pointed to by `mutex'. This function will return immediately, + * whether or not the mutex was successfully acquired. + * + * RETURN VALUE + * 0 the mutex was successfully acquired + * -EAGAIN the mutex was not acquired + */ +int mutex_trylock(mutex_t*); + +/* + * mutex_unlock() - Unlock a mutex + * + * SYNOPSIS + * int mutex_unlock(mutex_t *mutex); + * + * DESCRIPTION + * The mutex_unlock() function will attempt to unlock the mutex + * pointed to by `mutex'. If the calling thread is the owner of + * the mutex, the mutex will be unlocked and the thread that is + * waiting at the top of the mutex's wait queue will be awoken. + * + * RETURN VALUE + * 0 the mutex was successfully unlocked + * -EALREADY the mutex was not locked + * -EPERM the mutex is held by a different thread + */ +int mutex_unlock(mutex_t*); + +#endif /* __MUTEX_H */ diff --git a/kernel/klibc/Makefile b/kernel/klibc/Makefile index 6ea855a..515ccae 100644 --- a/kernel/klibc/Makefile +++ b/kernel/klibc/Makefile @@ -1,7 +1,7 @@ OUTPUT = klibc.a -OBJECTS = string.o +OBJECTS = string.o spinlock.o mutex.o PHONY = clean -INCLUDES = -I../include -I../../include +INCLUDES = -I../include -I../../include -I../.. CFLAGS = -m32 -Wall -nostdlib -nodefaultlibs -nostartfiles -ffreestanding $(INCLUDES) ASFLAGS = $(CFLAGS) diff --git a/kernel/klibc/mutex.c b/kernel/klibc/mutex.c new file mode 100644 index 0000000..fc70930 --- /dev/null +++ b/kernel/klibc/mutex.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +struct _waitq { + struct _waitq *next; + task_t *wtask; +}; + +struct _mutex { + spinlock_t mx_lock; + task_t *mx_owner; + struct _waitq *mx_waitq; +}; + +int mutex_lock(mutex_t *mutex) +{ + task_t *ctask; + int ret_val; + + ret_val = -EAGAIN; + ctask = task_get_current(); + + while(ret_val == -EAGAIN) { + task_t *prev; + + prev = NULL; + + /* lock the mutex */ + spinlock_lock(&(mutex->mx_lock)); + + if(!mutex->mx_owner) { + /* mutex is not currently held by anyone */ + mutex->mx_owner = ctask; + ret_val = 0; + + if(mutex->mx_waitq) { + struct _waitq *qitem; + + /* our item is still in the list if we had been waiting before */ + qitem = mutex->mx_waitq; + mutex->mx_waitq = qitem->next; + kfree(qitem); + } + } else { + struct _waitq *qitem; + + /* add the caller to the wait queue */ + + qitem = kmalloc(sizeof(*qitem)); + + if(qitem) { + struct _waitq **cwq; + + qitem->wtask = ctask; + qitem->next = NULL; + + /* + * Add the item to the end of the queue, and get a pointer + * to the task that is in the list in front of us + */ + for(cwq = &(mutex->mx_waitq); *cwq; cwq = &((*cwq)->next)) { + prev = (*cwq)->wtask; + } + *cwq = qitem; + + ret_val = -EAGAIN; + } else { + ret_val = -ENOMEM; + } + } + + /* unlock the mutex */ + spinlock_unlock(&(mutex->mx_lock)); + + if(ret_val == -EAGAIN) { + /* FIXME: Something like task_wait() would be better */ + /* wait for the task in front of us */ + process_wait(prev->t_pid); + } + } + + return(ret_val); +} + +int mutex_trylock(mutex_t *mutex) +{ + int ret_val; + task_t *ctask; + + ctask = task_get_current(); + + spinlock_lock(&(mutex->mx_lock)); + + if(!mutex->mx_owner) { + mutex->mx_owner = ctask; + ret_val = 0; + } else { + ret_val = -EAGAIN; + } + + spinlock_unlock(&(mutex->mx_lock)); + + return(ret_val); +} + +int mutex_unlock(mutex_t *mutex) +{ + int ret_val; + task_t *ctask; + + ctask = task_get_current(); + + spinlock_lock(&(mutex->mx_lock)); + + if(mutex->mx_owner == ctask) { + mutex->mx_owner = NULL; + + if(mutex->mx_waitq) { + /* notify first task on the list */ + process_signal(mutex->mx_waitq->wtask->t_pid); + } + + ret_val = 0; + } else if(!mutex->mx_owner) { + ret_val = -EALREADY; + } else { + ret_val = -EPERM; + } + + spinlock_unlock(&(mutex->mx_lock)); + + return(ret_val); +} -- 2.47.3