]> git.corax.cc Git - corax/commitdiff
Add a fixed-length wait queue to mutexes, to speed up locking and unlocking operation...
authorMatthias Kruk <m@m10k.eu>
Mon, 25 Nov 2019 07:23:15 +0000 (16:23 +0900)
committerMatthias Kruk <m@m10k.eu>
Mon, 25 Nov 2019 07:23:15 +0000 (16:23 +0900)
config.h
kernel/klibc/mutex.c

index d5d63e141093b02ec4fc98e503cc8945e6f64bf6..830468816dc92023bdfd5f2db9a5991e437ad7ff 100644 (file)
--- a/config.h
+++ b/config.h
@@ -30,6 +30,7 @@
 #endif /* !__ASSEMBLY_SOURCE */
 #define CONFIG_USER_STACK_SIZE      4096
 #define CONFIG_PROC_MAXTASKS        8
+#define CONFIG_MUTEX_WAITQLEN       8
 
 #define CONFIG_APIC     0
 
index fc709302656bbe250c0f9e89131d8135f42d194b..9422b890f85abe8221acc3c85d4c9ccced48af1c 100644 (file)
@@ -6,16 +6,12 @@
 #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)
@@ -23,54 +19,44 @@ 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;
                        }
                }
@@ -78,10 +64,17 @@ int mutex_lock(mutex_t *mutex)
                /* 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);
                }
        }
 
@@ -119,17 +112,20 @@ int mutex_unlock(mutex_t *mutex)
        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;
        }