GCC/uthread callbacks on blocking syscalls (XCC)
[akaros.git] / user / pthread / pthread.c
1 #include <ros/arch/trapframe.h>
2 #include <pthread.h>
3 #include <vcore.h>
4 #include <mcs.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <assert.h>
8 #include <rstdio.h>
9 #include <errno.h>
10 #include <parlib.h>
11 #include <ros/event.h>
12 #include <arch/atomic.h>
13 #include <arch/arch.h>
14 #include <sys/queue.h>
15 #include <sys/mman.h>
16 #include <assert.h>
17 #include <event.h>
18
19 struct pthread_queue ready_queue = TAILQ_HEAD_INITIALIZER(ready_queue);
20 struct pthread_queue active_queue = TAILQ_HEAD_INITIALIZER(active_queue);
21 mcs_lock_t queue_lock = MCS_LOCK_INIT;
22 pthread_once_t init_once = PTHREAD_ONCE_INIT;
23 int threads_ready = 0;
24 int threads_active = 0;
25
26 /* Helper / local functions */
27 static int get_next_pid(void);
28 static inline void spin_to_sleep(unsigned int spins, unsigned int *spun);
29
30 /* Pthread 2LS operations */
31 struct uthread *pth_init(void);
32 void pth_sched_entry(void);
33 struct uthread *pth_thread_create(void (*func)(void), void *udata);
34 void pth_thread_runnable(struct uthread *uthread);
35 void pth_thread_yield(struct uthread *uthread);
36 void pth_thread_exit(struct uthread *uthread);
37 unsigned int pth_vcores_wanted(void);
38 void pth_preempt_pending(void);
39 void pth_spawn_thread(uintptr_t pc_start, void *data);
40 void pth_blockon_sysc(struct syscall *sysc);
41
42 struct schedule_ops pthread_sched_ops = {
43         pth_init,
44         pth_sched_entry,
45         pth_thread_create,
46         pth_thread_runnable,
47         pth_thread_yield,
48         pth_thread_exit,
49         pth_blockon_sysc,
50         pth_vcores_wanted,
51         0, /* pth_preempt_pending, */
52         0, /* pth_spawn_thread, */
53 };
54
55 /* Publish our sched_ops, overriding the weak defaults */
56 struct schedule_ops *sched_ops = &pthread_sched_ops;
57
58 /* Static helpers */
59 static void __pthread_free_stack(struct pthread_tcb *pt);
60 static int __pthread_allocate_stack(struct pthread_tcb *pt);
61
62 /* Do whatever init you want.  Return a uthread representing thread0 (int
63  * main()) */
64 struct uthread *pth_init(void)
65 {
66         struct mcs_lock_qnode local_qn = {0};
67         /* Tell the kernel where and how we want to receive events.  This is just an
68          * example of what to do to have a notification turned on.  We're turning on
69          * USER_IPIs, posting events to vcore 0's vcpd, and telling the kernel to
70          * send to vcore 0.  Note sys_self_notify will ignore the vcoreid pref.
71          * Also note that enable_kevent() is just an example, and you probably want
72          * to use parts of event.c to do what you want. */
73         enable_kevent(EV_USER_IPI, 0, EVENT_IPI);
74
75         /* Create a pthread_tcb for the main thread */
76         pthread_t t = (pthread_t)calloc(1, sizeof(struct pthread_tcb));
77         t->id = get_next_pid();
78         assert(t->id == 0);
79
80         /* Put the new pthread on the active queue */
81         mcs_lock_notifsafe(&queue_lock, &local_qn);
82         threads_active++;
83         TAILQ_INSERT_TAIL(&active_queue, t, next);
84         mcs_unlock_notifsafe(&queue_lock, &local_qn);
85         return (struct uthread*)t;
86 }
87
88 /* Called from vcore entry.  Options usually include restarting whoever was
89  * running there before or running a new thread.  Events are handled out of
90  * event.c (table of function pointers, stuff like that). */
91 void __attribute__((noreturn)) pth_sched_entry(void)
92 {
93         if (current_uthread) {
94                 run_current_uthread();
95                 assert(0);
96         }
97         /* no one currently running, so lets get someone from the ready queue */
98         struct pthread_tcb *new_thread = NULL;
99         struct mcs_lock_qnode local_qn = {0};
100         mcs_lock_notifsafe(&queue_lock, &local_qn);
101         new_thread = TAILQ_FIRST(&ready_queue);
102         if (new_thread) {
103                 TAILQ_REMOVE(&ready_queue, new_thread, next);
104                 TAILQ_INSERT_TAIL(&active_queue, new_thread, next);
105                 threads_active++;
106                 threads_ready--;
107         }
108         mcs_unlock_notifsafe(&queue_lock, &local_qn);
109         /* Instead of yielding, you could spin, turn off the core, set an alarm,
110          * whatever.  You want some logic to decide this.  Uthread code wil have
111          * helpers for this (like how we provide run_uthread()) */
112         if (!new_thread) {
113                 /* TODO: consider doing something more intelligent here */
114                 printd("[P] No threads, vcore %d is yielding\n", vcore_id());
115                 sys_yield(0);
116                 assert(0);
117         }
118         run_uthread((struct uthread*)new_thread);
119         assert(0);
120 }
121
122 /* Could move this, along with start_routine and arg, into the 2LSs */
123 static void __pthread_run(void)
124 {
125         struct pthread_tcb *me = pthread_self();
126         pthread_exit(me->start_routine(me->arg));
127 }
128
129 /* Responible for creating the uthread and initializing its user trap frame */
130 struct uthread *pth_thread_create(void (*func)(void), void *udata)
131 {
132         struct pthread_tcb *pthread;
133         pthread_attr_t *attr = (pthread_attr_t*)udata;
134         pthread = (pthread_t)calloc(1, sizeof(struct pthread_tcb));
135         pthread->stacksize = PTHREAD_STACK_SIZE;        /* default */
136         pthread->id = get_next_pid();
137         pthread->detached = FALSE;                              /* default */
138         /* Respect the attributes */
139         if (attr) {
140                 if (attr->stacksize)                                    /* don't set a 0 stacksize */
141                         pthread->stacksize = attr->stacksize;
142                 if (attr->detachstate == PTHREAD_CREATE_DETACHED)
143                         pthread->detached = TRUE;
144         }
145         /* allocate a stack */
146         if (__pthread_allocate_stack(pthread))
147                 printf("We're fucked\n");
148         /* Set the u_tf to start up in __pthread_run, which will call the real
149          * start_routine and pass it the arg.  Note those aren't set until later in
150          * pthread_create(). */
151         init_user_tf(&pthread->uthread.utf, (uint32_t)__pthread_run, 
152                  (uint32_t)(pthread->stacktop));
153         return (struct uthread*)pthread;
154 }
155
156 void pth_thread_runnable(struct uthread *uthread)
157 {
158         struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
159         struct mcs_lock_qnode local_qn = {0};
160         /* Insert the newly created thread into the ready queue of threads.
161          * It will be removed from this queue later when vcore_entry() comes up */
162         mcs_lock_notifsafe(&queue_lock, &local_qn);
163         TAILQ_INSERT_TAIL(&ready_queue, pthread, next);
164         threads_ready++;
165         mcs_unlock_notifsafe(&queue_lock, &local_qn);
166 }
167
168 /* The calling thread is yielding.  Do what you need to do to restart (like put
169  * yourself on a runqueue), or do some accounting.  Eventually, this might be a
170  * little more generic than just yield. */
171 void pth_thread_yield(struct uthread *uthread)
172 {
173         struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
174         struct mcs_lock_qnode local_qn = {0};
175         /* Take from the active list, and put on the ready list (tail).  Don't do
176          * this until we are done completely with the thread, since it can be
177          * restarted somewhere else. */
178         mcs_lock_notifsafe(&queue_lock, &local_qn);
179         threads_active--;
180         TAILQ_REMOVE(&active_queue, pthread, next);
181         threads_ready++;
182         TAILQ_INSERT_TAIL(&ready_queue, pthread, next);
183         mcs_unlock_notifsafe(&queue_lock, &local_qn);
184 }
185
186 /* Thread is exiting, do your 2LS specific stuff.  You're in vcore context.
187  * Don't use the thread's TLS or stack or anything. */
188 void pth_thread_exit(struct uthread *uthread)
189 {
190         struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
191         struct mcs_lock_qnode local_qn = {0};
192         /* Remove from the active runqueue */
193         mcs_lock_notifsafe(&queue_lock, &local_qn);
194         threads_active--;
195         TAILQ_REMOVE(&active_queue, pthread, next);
196         mcs_unlock_notifsafe(&queue_lock, &local_qn);
197         /* Cleanup, mirroring pth_thread_create() */
198         __pthread_free_stack(pthread);
199         /* TODO: race on detach state */
200         if (pthread->detached)
201                 free(pthread);
202         /* Once we do this, our joiner can free us.  He won't free us if we're
203          * detached, but there is still a potential race there (since he's accessing
204          * someone who is freed. */
205         pthread->finished = 1;
206 }
207
208 /* Returns how many *more* vcores we want.  Smarter schedulers should look at
209  * the num_vcores() and how much work is going on to make this decision. */
210 unsigned int pth_vcores_wanted(void)
211 {
212         return 1;
213 }
214
215 void pth_preempt_pending(void)
216 {
217 }
218
219 void pth_spawn_thread(uintptr_t pc_start, void *data)
220 {
221 }
222
223 /* Eventually, this will be called from vcore context, after the current thread
224  * has yielded and is trying to block on sysc. */
225 void pth_blockon_sysc(struct syscall *sysc)
226 {
227         printf("We tried to block on a syscall!\n");    
228         /* for now, just spin.  2LS stuff in later commits */
229         __ros_syscall_blockon(sysc);
230
231         #if 0
232         // testing shit.  should dont_migrate for this
233         uint32_t vcoreid = vcore_id();
234         struct event_queue local_ev_q = {0}, *ev_q = &local_ev_q;
235         ev_q->ev_mbox = &__procdata.vcore_preempt_data[vcoreid].ev_mbox;
236         ev_q->ev_flags = EVENT_IPI;
237         ev_q->ev_vcore = vcoreid;
238         sysc->u_data = current_uthread;
239         wmb();
240         sysc->ev_q = ev_q;
241         if (sysc->flags & (SC_DONE | SC_PROGRESS)) {
242                 // try and atomically swap out our u_data.  if we got it, we restart/
243                 // handle it.  o/w, the receiver of the message restarts.  we can't just
244                 // let them do it, since the kernel might have checked the ev_q before
245                 // we wrote it (but after it wrote the flags).
246         } else {
247                 //we're done
248         }
249         #endif
250 }
251
252 /* Pthread interface stuff and helpers */
253
254 int pthread_attr_init(pthread_attr_t *a)
255 {
256         a->stacksize = PTHREAD_STACK_SIZE;
257         a->detachstate = PTHREAD_CREATE_JOINABLE;
258         return 0;
259 }
260
261 int pthread_attr_destroy(pthread_attr_t *a)
262 {
263         return 0;
264 }
265
266 static void __pthread_free_stack(struct pthread_tcb *pt)
267 {
268         assert(!munmap(pt->stacktop - PTHREAD_STACK_SIZE, PTHREAD_STACK_SIZE));
269 }
270
271 static int __pthread_allocate_stack(struct pthread_tcb *pt)
272 {
273         assert(pt->stacksize);
274         void* stackbot = mmap(0, pt->stacksize,
275                               PROT_READ|PROT_WRITE|PROT_EXEC,
276                               MAP_POPULATE|MAP_ANONYMOUS, -1, 0);
277         if (stackbot == MAP_FAILED)
278                 return -1; // errno set by mmap
279         pt->stacktop = stackbot + pt->stacksize;
280         return 0;
281 }
282
283 // Warning, this will reuse numbers eventually
284 static int get_next_pid(void)
285 {
286         static uint32_t next_pid = 0;
287         return next_pid++;
288 }
289
290 int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
291 {
292         attr->stacksize = stacksize;
293         return 0;
294 }
295 int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize)
296 {
297         *stacksize = attr->stacksize;
298         return 0;
299 }
300
301 int pthread_create(pthread_t* thread, const pthread_attr_t* attr,
302                    void *(*start_routine)(void *), void* arg)
303 {
304         struct pthread_tcb *pthread =
305                (struct pthread_tcb*)uthread_create(__pthread_run, (void*)attr);
306         if (!pthread)
307                 return -1;
308         pthread->start_routine = start_routine;
309         pthread->arg = arg;
310         uthread_runnable((struct uthread*)pthread);
311         *thread = pthread;
312         return 0;
313 }
314
315 int pthread_join(pthread_t thread, void** retval)
316 {
317         /* Not sure if this is the right semantics.  There is a race if we deref
318          * thread and he is already freed (which would have happened if he was
319          * detached. */
320         if (thread->detached) {
321                 printf("[pthread] trying to join on a detached pthread");
322                 return -1;
323         }
324         while (!thread->finished)
325                 pthread_yield();
326         if (retval)
327                 *retval = thread->retval;
328         free(thread);
329         return 0;
330 }
331
332 int pthread_yield(void)
333 {
334         uthread_yield();
335         return 0;
336 }
337
338 int pthread_mutexattr_init(pthread_mutexattr_t* attr)
339 {
340   attr->type = PTHREAD_MUTEX_DEFAULT;
341   return 0;
342 }
343
344 int pthread_mutexattr_destroy(pthread_mutexattr_t* attr)
345 {
346   return 0;
347 }
348
349 int pthread_attr_setdetachstate(pthread_attr_t *__attr, int __detachstate)
350 {
351         __attr->detachstate = __detachstate;
352         return 0;
353 }
354
355 int pthread_mutexattr_gettype(const pthread_mutexattr_t* attr, int* type)
356 {
357   *type = attr ? attr->type : PTHREAD_MUTEX_DEFAULT;
358   return 0;
359 }
360
361 int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type)
362 {
363   if(type != PTHREAD_MUTEX_NORMAL)
364     return EINVAL;
365   attr->type = type;
366   return 0;
367 }
368
369 int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t* attr)
370 {
371   m->attr = attr;
372   m->lock = 0;
373   return 0;
374 }
375
376 /* Set *spun to 0 when calling this the first time.  It will yield after 'spins'
377  * calls.  Use this for adaptive mutexes and such. */
378 static inline void spin_to_sleep(unsigned int spins, unsigned int *spun)
379 {
380         if ((*spun)++ == spins) {
381                 pthread_yield();
382                 *spun = 0;
383         }
384 }
385
386 int pthread_mutex_lock(pthread_mutex_t* m)
387 {
388         unsigned int spinner = 0;
389         while(pthread_mutex_trylock(m))
390                 while(*(volatile size_t*)&m->lock) {
391                         cpu_relax();
392                         spin_to_sleep(PTHREAD_MUTEX_SPINS, &spinner);
393                 }
394         return 0;
395 }
396
397 int pthread_mutex_trylock(pthread_mutex_t* m)
398 {
399   return atomic_swap(&m->lock,1) == 0 ? 0 : EBUSY;
400 }
401
402 int pthread_mutex_unlock(pthread_mutex_t* m)
403 {
404   /* Need to prevent the compiler (and some arches) from reordering older
405    * stores */
406   wmb();
407   m->lock = 0;
408   return 0;
409 }
410
411 int pthread_mutex_destroy(pthread_mutex_t* m)
412 {
413   return 0;
414 }
415
416 int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a)
417 {
418   c->attr = a;
419   memset(c->waiters,0,sizeof(c->waiters));
420   memset(c->in_use,0,sizeof(c->in_use));
421   c->next_waiter = 0;
422   return 0;
423 }
424
425 int pthread_cond_destroy(pthread_cond_t *c)
426 {
427   return 0;
428 }
429
430 int pthread_cond_broadcast(pthread_cond_t *c)
431 {
432   memset(c->waiters,0,sizeof(c->waiters));
433   return 0;
434 }
435
436 int pthread_cond_signal(pthread_cond_t *c)
437 {
438   int i;
439   for(i = 0; i < MAX_PTHREADS; i++)
440   {
441     if(c->waiters[i])
442     {
443       c->waiters[i] = 0;
444       break;
445     }
446   }
447   return 0;
448 }
449
450 int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m)
451 {
452   int old_waiter = c->next_waiter;
453   int my_waiter = c->next_waiter;
454   
455   //allocate a slot
456   while (atomic_swap (& (c->in_use[my_waiter]), SLOT_IN_USE) == SLOT_IN_USE)
457   {
458     my_waiter = (my_waiter + 1) % MAX_PTHREADS;
459     assert (old_waiter != my_waiter);  // do not want to wrap around
460   }
461   c->waiters[my_waiter] = WAITER_WAITING;
462   c->next_waiter = (my_waiter+1) % MAX_PTHREADS;  // race on next_waiter but ok, because it is advisary
463
464   pthread_mutex_unlock(m);
465
466   volatile int* poll = &c->waiters[my_waiter];
467   while(*poll);
468   c->in_use[my_waiter] = SLOT_FREE;
469   pthread_mutex_lock(m);
470
471   return 0;
472 }
473
474 int pthread_condattr_init(pthread_condattr_t *a)
475 {
476   a = PTHREAD_PROCESS_PRIVATE;
477   return 0;
478 }
479
480 int pthread_condattr_destroy(pthread_condattr_t *a)
481 {
482   return 0;
483 }
484
485 int pthread_condattr_setpshared(pthread_condattr_t *a, int s)
486 {
487   a->pshared = s;
488   return 0;
489 }
490
491 int pthread_condattr_getpshared(pthread_condattr_t *a, int *s)
492 {
493   *s = a->pshared;
494   return 0;
495 }
496
497 pthread_t pthread_self()
498 {
499   return (struct pthread_tcb*)current_uthread;
500 }
501
502 int pthread_equal(pthread_t t1, pthread_t t2)
503 {
504   return t1 == t2;
505 }
506
507 /* This function cannot be migrated to a different vcore by the userspace
508  * scheduler.  Will need to sort that shit out. */
509 void pthread_exit(void *ret)
510 {
511         struct pthread_tcb *pthread = pthread_self();
512         pthread->retval = ret;
513         uthread_exit();
514 }
515
516 int pthread_once(pthread_once_t* once_control, void (*init_routine)(void))
517 {
518   if(atomic_swap(once_control,1) == 0)
519     init_routine();
520   return 0;
521 }
522
523 int pthread_barrier_init(pthread_barrier_t* b, const pthread_barrierattr_t* a, int count)
524 {
525   b->nprocs = b->count = count;
526   b->sense = 0;
527   pthread_mutex_init(&b->pmutex, 0);
528   return 0;
529 }
530
531 int pthread_barrier_wait(pthread_barrier_t* b)
532 {
533   unsigned int spinner = 0;
534   int ls = !b->sense;
535
536   pthread_mutex_lock(&b->pmutex);
537   int count = --b->count;
538   pthread_mutex_unlock(&b->pmutex);
539
540   if(count == 0)
541   {
542     printd("Thread %d is last to hit the barrier, resetting...\n", pthread_self()->id);
543     b->count = b->nprocs;
544         wmb();
545     b->sense = ls;
546     return PTHREAD_BARRIER_SERIAL_THREAD;
547   }
548   else
549   {
550     while(b->sense != ls) {
551       cpu_relax();
552       spin_to_sleep(PTHREAD_BARRIER_SPINS, &spinner);
553     }
554     return 0;
555   }
556 }
557
558 int pthread_barrier_destroy(pthread_barrier_t* b)
559 {
560   pthread_mutex_destroy(&b->pmutex);
561   return 0;
562 }
563
564 int pthread_detach(pthread_t thread)
565 {
566         thread->detached = TRUE;
567         return 0;
568 }