]> git.corax.cc Git - corax/commitdiff
Add mutex implementation to klibc
authorMatthias Kruk <m@m10k.eu>
Thu, 21 Nov 2019 08:33:28 +0000 (17:33 +0900)
committerMatthias Kruk <m@m10k.eu>
Thu, 21 Nov 2019 08:33:28 +0000 (17:33 +0900)
kernel/include/mutex.h [new file with mode: 0644]
kernel/klibc/Makefile
kernel/klibc/mutex.c [new file with mode: 0644]

diff --git a/kernel/include/mutex.h b/kernel/include/mutex.h
new file mode 100644 (file)
index 0000000..e77b2f4
--- /dev/null
@@ -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 */
index 6ea855aff94fa9b85dc5ee49d9d52204cbddf516..515ccae576ccb8790a62a0952063c53529abc8ff 100644 (file)
@@ -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 (file)
index 0000000..fc70930
--- /dev/null
@@ -0,0 +1,139 @@
+#include <config.h>
+#include <corax/types.h>
+#include <corax/errno.h>
+#include <corax/heap.h>
+#include <spinlock.h>
+#include <mutex.h>
+#include <arch.h>
+#include <process.h>
+
+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);
+}