--- /dev/null
+#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 */
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)
--- /dev/null
+#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);
+}