common.h: move common code (non-user) to header
[c11tester.git] / libthreads.c
1 #include <string.h>
2 #include <stdlib.h>
3
4 #include "libthreads.h"
5 #include "common.h"
6
7 #define STACK_SIZE (1024 * 1024)
8
9 static struct thread *current, *main_thread;
10
11 static void *stack_allocate(size_t size)
12 {
13         return malloc(size);
14 }
15
16 static void stack_free(void *stack)
17 {
18         free(stack);
19 }
20
21 static int create_context(struct thread *t)
22 {
23         int ret;
24
25         memset(&t->context, 0, sizeof(t->context));
26         ret = getcontext(&t->context);
27         if (ret)
28                 return ret;
29
30         /* t->start_routine == NULL means this is our initial context */
31         if (!t->start_routine)
32                 return 0;
33
34         /* Initialize new managed context */
35         t->stack = stack_allocate(STACK_SIZE);
36         t->context.uc_stack.ss_sp = t->stack;
37         t->context.uc_stack.ss_size = STACK_SIZE;
38         t->context.uc_stack.ss_flags = 0;
39         t->context.uc_link = &main_thread->context;
40         makecontext(&t->context, t->start_routine, 1, t->arg);
41
42         return 0;
43 }
44
45 static int create_initial_thread(struct thread *t)
46 {
47         memset(t, 0, sizeof(*t));
48         return create_context(t);
49 }
50
51 static int thread_swap(struct thread *old, struct thread *new)
52 {
53         return swapcontext(&old->context, &new->context);
54 }
55
56 static int thread_yield()
57 {
58         struct thread *old, *next;
59
60         DBG();
61         old = current;
62         schedule_add_thread(old);
63         schedule_choose_next(&next);
64         current = next;
65         DEBUG("(%d, %d)\n", old->index, next->index);
66         return thread_swap(old, next);
67 }
68
69 static void thread_dispose(struct thread *t)
70 {
71         DEBUG("completed thread %d\n", thread_current()->index);
72         t->completed = 1;
73         stack_free(t->stack);
74 }
75
76 static void thread_wait_finish()
77 {
78         struct thread *next;
79
80         DBG();
81
82         do {
83                 if (current)
84                         thread_dispose(current);
85                 schedule_choose_next(&next);
86                 current = next;
87         } while (next && !thread_swap(main_thread, next));
88 }
89
90 int thread_create(struct thread *t, void (*start_routine), void *arg)
91 {
92         static int created = 1;
93         int ret = 0;
94
95         DBG();
96
97         memset(t, 0, sizeof(*t));
98         t->index = created++;
99         DEBUG("create thread %d\n", t->index);
100
101         t->start_routine = start_routine;
102         t->arg = arg;
103
104         /* Initialize state */
105         ret = create_context(t);
106         if (ret)
107                 return ret;
108
109         schedule_add_thread(t);
110         return 0;
111 }
112
113 void thread_join(struct thread *t)
114 {
115         while (!t->completed)
116                 thread_yield();
117 }
118
119 struct thread *thread_current(void)
120 {
121         return current;
122 }
123
124 void a(int *parm)
125 {
126         int i;
127
128         for (i = 0; i < 10; i++) {
129                 printf("Thread %d, magic number %d, loop %d\n", thread_current()->index, *parm, i);
130                 if (i % 2)
131                         thread_yield();
132         }
133 }
134
135 void user_main()
136 {
137         struct thread t1, t2;
138         int i = 17, j = 13;
139
140         printf("%s() creating 2 threads\n", __func__);
141         thread_create(&t1, &a, &i);
142         thread_create(&t2, &a, &j);
143
144         thread_join(&t1);
145         thread_join(&t2);
146         printf("%s() is finished\n", __func__);
147 }
148
149 int main()
150 {
151         struct thread user_thread;
152
153         main_thread = malloc(sizeof(struct thread));
154         create_initial_thread(main_thread);
155
156         /* Start user program */
157         thread_create(&user_thread, &user_main, NULL);
158
159         /* Wait for all threads to complete */
160         thread_wait_finish();
161
162         DEBUG("Exiting\n");
163         return 0;
164 }