schedule: set thread to NULL when there is no next thread
[c11tester.git] / libthreads.c
index 2326f3b1839e965c64b35050e4fb8384d102cc5c..959fb77245e9849f56d4de1a7586a737dbca6246 100644 (file)
@@ -1,13 +1,11 @@
 #include <string.h>
 #include <stdlib.h>
 
-//#define CONFIG_DEBUG
-
 #include "libthreads.h"
 
 #define STACK_SIZE (1024 * 1024)
 
-static struct thread *current;
+static struct thread *current, *main_thread;
 
 static void *stack_allocate(size_t size)
 {
@@ -32,7 +30,7 @@ static int create_context(struct thread *t)
        t->context.uc_stack.ss_sp = t->stack;
        t->context.uc_stack.ss_size = STACK_SIZE;
        t->context.uc_stack.ss_flags = 0;
-       t->context.uc_link = &current->context;
+       t->context.uc_link = &main_thread->context;
        makecontext(&t->context, t->start_routine, 1, t->arg);
 
        return 0;
@@ -44,9 +42,49 @@ static int create_initial_thread(struct thread *t)
        return create_context(t);
 }
 
+static int thread_swap(struct thread *old, struct thread *new)
+{
+       return swapcontext(&old->context, &new->context);
+}
+
+static int thread_yield()
+{
+       struct thread *old, *next;
+
+       DBG();
+       if (current) {
+               old = current;
+               schedule_add_thread(old);
+       } else {
+               old = main_thread;
+       }
+       schedule_choose_next(&next);
+       current = next;
+       return thread_swap(old, next);
+}
+
+static int master_thread_yield()
+{
+       struct thread *next;
+
+       DBG();
+
+       if (current) {
+               DEBUG("completed thread %d\n", current->index);
+               current->completed = 1;
+       }
+       schedule_choose_next(&next);
+       if (next && !next->completed) {
+               current = next;
+               return thread_swap(main_thread, next);
+       }
+       return 1;
+}
+
 int thread_create(struct thread *t, void (*start_routine), void *arg)
 {
        static int created = 1;
+       int ret = 0;
 
        DBG();
 
@@ -58,26 +96,29 @@ int thread_create(struct thread *t, void (*start_routine), void *arg)
        t->arg = arg;
 
        /* Initialize state */
-       return create_context(t);
+       ret = create_context(t);
+       if (ret)
+               return ret;
+
+       schedule_add_thread(t);
+       return 0;
 }
 
-void thread_start(struct thread *t)
+void thread_join(struct thread *t)
 {
-       struct thread *old = current;
-       DBG();
-
-       current = t;
-       swapcontext(&old->context, &current->context);
-
-       DBG();
+       while (!t->completed)
+               thread_yield();
 }
 
 void a(int *idx)
 {
        int i;
 
-       for (i = 0; i < 10; i++)
+       for (i = 0; i < 10; i++) {
                printf("Thread %d, loop %d\n", *idx, i);
+               if (i % 2)
+                       thread_yield();
+       }
 }
 
 void user_main()
@@ -85,26 +126,26 @@ void user_main()
        struct thread t1, t2;
        int i = 2, j = 3;
 
+       printf("%s() creating 2 threads\n", __func__);
        thread_create(&t1, &a, &i);
        thread_create(&t2, &a, &j);
 
-       printf("%s() is going to start 1 thread\n", __func__);
-       thread_start(&t1);
-       thread_start(&t2);
+       thread_join(&t1);
+       thread_join(&t2);
        printf("%s() is finished\n", __func__);
 }
 
 int main()
 {
-       struct thread main_thread, user_thread;
+       struct thread user_thread;
 
-       create_initial_thread(&main_thread);
-       current = &main_thread;
+       main_thread = malloc(sizeof(struct thread));
+       create_initial_thread(main_thread);
 
        thread_create(&user_thread, &user_main, NULL);
-       thread_start(&user_thread);
 
-       DBG();
+       /* Wait for all threads to complete */
+       while (master_thread_yield() == 0);
 
        DEBUG("Exiting\n");
        return 0;