#include <mutex.h>
#include <arch.h>
#include <process.h>
-
-struct _waitq {
- struct _waitq *next;
- task_t *wtask;
-};
+#include <sched.h>
struct _mutex {
spinlock_t mx_lock;
task_t *mx_owner;
- struct _waitq *mx_waitq;
+ task_t *mx_waitq[CONFIG_MUTEX_WAITQLEN];
};
int mutex_lock(mutex_t *mutex)
task_t *ctask;
int ret_val;
- ret_val = -EAGAIN;
+ ret_val = EAGAIN;
ctask = task_get_current();
- while(ret_val == -EAGAIN) {
- task_t *prev;
-
- prev = NULL;
+ while(ret_val == EAGAIN) {
+ int i;
/* lock the mutex */
spinlock_lock(&(mutex->mx_lock));
if(!mutex->mx_owner) {
- /* mutex is not currently held by anyone */
+ /* mutex is not currently held by anyone - claim it */
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);
+ /*
+ * Move the waitq up by one item, removing the first one (which should
+ * have been the task that is currently executing this function)
+ */
+ for(i = 0; i < (CONFIG_MUTEX_WAITQLEN - 1) && mutex->mx_waitq[i + 1]; i++) {
+ mutex->mx_waitq[i] = mutex->mx_waitq[i + 1];
}
- } else {
- struct _waitq *qitem;
+ mutex->mx_waitq[CONFIG_MUTEX_WAITQLEN - 1] = NULL;
+ ret_val = 0;
+ } else {
/* 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;
+ for(i = 0; i < CONFIG_MUTEX_WAITQLEN; i++) {
+ if(!mutex->mx_waitq[i]) {
+ mutex->mx_waitq[i] = ctask;
+ break;
}
- *cwq = qitem;
+ }
- ret_val = -EAGAIN;
+ if(i < CONFIG_MUTEX_WAITQLEN) {
+ /* successfully enqueued */
+ ret_val = EAGAIN;
} else {
+ /* the queue was full */
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);
+ /*
+ * At this point, ret_val will have one of the values { -ENOMEM, 0, EAGAIN }.
+ * In case of -ENOMEM and 0, the loop will end and the value is returned to
+ * the caller. In the case of EAGAIN, the task will be suspended since it was
+ * successfully added to the mutex's wait queue. EAGAIN is deliberately chosen
+ * not to be a negative value since it is not an error that is suppposed to be
+ * returned to the caller.
+ */
+ if(ret_val == EAGAIN) {
+ /* suspend this task */
+ sched_task_suspend(NULL);
}
}
spinlock_lock(&(mutex->mx_lock));
if(mutex->mx_owner == ctask) {
+ /* caller currently owns the mutex - unlock it */
mutex->mx_owner = NULL;
- if(mutex->mx_waitq) {
- /* notify first task on the list */
- process_signal(mutex->mx_waitq->wtask->t_pid);
+ /* if there is a task in the waitq, resume it */
+ if(mutex->mx_waitq[0]) {
+ sched_task_resume(mutex->mx_waitq[0]);
}
ret_val = 0;
} else if(!mutex->mx_owner) {
+ /* mutex is not locked */
ret_val = -EALREADY;
} else {
+ /* mutex is locked, but not by the caller */
ret_val = -EPERM;
}