Default event handling infrastructure
[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 __thread struct pthread_tcb *current_thread = 0;
31
32 void _pthread_init()
33 {
34         if (vcore_init())
35                 printf("vcore_init() failed, we're fucked!\n");
36         
37         assert(vcore_id() == 0);
38
39         /* Tell the kernel where and how we want to receive events.  This is just an
40          * example of what to do to have a notification turned on.  We're turning on
41          * USER_IPIs, posting events to vcore 0's vcpd, and telling the kernel to
42          * send to vcore 0.  Note sys_self_notify will ignore the vcoreid pref.
43          * Also note that enable_kevent() is just an example, and you probably want
44          * to use parts of event.c to do what you want. */
45         enable_kevent(EV_USER_IPI, 0, EVENT_IPI);
46
47         /* don't forget to enable notifs on vcore0.  if you don't, the kernel will
48          * restart your _S with notifs disabled, which is a path to confusion. */
49         enable_notifs(0);
50
51         /* Create a pthread_tcb for the main thread */
52         pthread_t t = (pthread_t)calloc(1, sizeof(struct pthread_tcb));
53         t->id = get_next_pid();
54         assert(t->id == 0);
55         /* Put the new pthread on the active queue */
56         mcs_lock_lock(&queue_lock);
57         threads_active++;
58         TAILQ_INSERT_TAIL(&active_queue, t, next);
59         mcs_lock_unlock(&queue_lock);
60         
61         /* Save a pointer to the newly created threads tls region into its tcb */
62         t->tls_desc = get_tls_desc(0);
63         /* Save a pointer to the pthread in its own TLS */
64         current_thread = t;
65
66         /* Change temporarily to vcore0s tls region so we can save the newly created
67          * tcb into its current_thread variable and then restore it.  One minor
68          * issue is that vcore0's transition-TLS isn't TLS_INITed yet.  Until it is
69          * (right before vcore_entry(), don't try and take the address of any of
70          * its TLS vars. */
71         extern void** vcore_thread_control_blocks;
72         set_tls_desc(vcore_thread_control_blocks[0], 0);
73         current_thread = t;
74         set_tls_desc(t->tls_desc, 0);
75
76         // TODO: consider replacing this when we have an interface allowing
77         // requesting absolute num vcores, and moving it to pthread_create and
78         // asking for 2
79         vcore_request(1);
80 }
81
82 void __attribute__((noreturn)) vcore_entry()
83 {
84         uint32_t vcoreid = vcore_id();
85
86         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
87         struct vcore *vc = &__procinfo.vcoremap[vcoreid];
88
89         /* Should always have notifications disabled when coming in here. */
90         assert(vcpd->notif_enabled == FALSE);
91
92         /* Put this in the loop that deals with notifications.  It will return if
93          * there is no preempt pending. */ 
94         handle_events(vcoreid);
95         if (vc->preempt_pending)
96                 sys_yield(TRUE);
97         // TODO: consider making this restart path work for restarting as well as
98         // freshly starting
99         if (current_thread) {
100                 vcpd->notif_pending = 0;
101                 /* Do one last check for notifs after clearing pending */
102                 // TODO: call the handle_notif() here (first)
103
104                 set_tls_desc(current_thread->tls_desc, vcoreid);
105                 /* Pop the user trap frame */
106                 pop_ros_tf(&vcpd->notif_tf, vcoreid);
107                 assert(0);
108         }
109
110         /* no one currently running, so lets get someone from the ready queue */
111         struct pthread_tcb *new_thread = NULL;
112         mcs_lock_lock(&queue_lock);
113         new_thread = TAILQ_FIRST(&ready_queue);
114         if (new_thread) {
115                 TAILQ_REMOVE(&ready_queue, new_thread, next);
116                 TAILQ_INSERT_TAIL(&active_queue, new_thread, next);
117                 threads_active++;
118                 threads_ready--;
119         }
120         mcs_lock_unlock(&queue_lock);
121         if (!new_thread) {
122                 /* TODO: consider doing something more intelligent here */
123                 printd("[P] No threads, vcore %d is yielding\n", vcoreid);
124                 sys_yield(0);
125         }
126         /* Save a ptr to the pthread running in the transition context's TLS */
127         current_thread = new_thread;
128         printd("[P] Vcore %d is starting pthread %d\n", vcoreid, new_thread->id);
129
130         vcpd->notif_pending = 0;
131         /* Do one last check for notifs after clearing pending */
132         // TODO: call the handle_notif() here (first)
133         set_tls_desc(new_thread->tls_desc, vcoreid);
134
135         /* Load silly state (Floating point) too.  For real */
136         // TODO: (HSS)
137
138         /* Pop the user trap frame */
139         pop_ros_tf(&new_thread->utf, vcoreid);
140         assert(0);
141 }
142
143 int pthread_attr_init(pthread_attr_t *a)
144 {
145         a->stacksize = PTHREAD_STACK_SIZE;
146         a->detachstate = PTHREAD_CREATE_JOINABLE;
147         return 0;
148 }
149
150 int pthread_attr_destroy(pthread_attr_t *a)
151 {
152         return 0;
153 }
154
155 /* TODO: probably don't want to dealloc.  Considering caching */
156 static void __pthread_free_tls(struct pthread_tcb *pt)
157 {
158         extern void _dl_deallocate_tls (void *tcb, bool dealloc_tcb) internal_function;
159
160         assert(pt->tls_desc);
161         _dl_deallocate_tls(pt->tls_desc, TRUE);
162         pt->tls_desc = NULL;
163 }
164
165 static int __pthread_allocate_tls(struct pthread_tcb *pt)
166 {
167         assert(!pt->tls_desc);
168         pt->tls_desc = allocate_tls();
169         if (!pt->tls_desc) {
170                 errno = ENOMEM;
171                 return -1;
172         }
173         return 0;
174 }
175
176
177 static void __pthread_free_stack(struct pthread_tcb *pt)
178 {
179         assert(!munmap(pt->stacktop - PTHREAD_STACK_SIZE, PTHREAD_STACK_SIZE));
180 }
181
182 static int __pthread_allocate_stack(struct pthread_tcb *pt)
183 {
184         assert(pt->stacksize);
185         void* stackbot = mmap(0, pt->stacksize,
186                               PROT_READ|PROT_WRITE|PROT_EXEC,
187                               MAP_POPULATE|MAP_ANONYMOUS, -1, 0);
188         if (stackbot == MAP_FAILED)
189                 return -1; // errno set by mmap
190         pt->stacktop = stackbot + pt->stacksize;
191         return 0;
192 }
193
194 void __pthread_run(void)
195 {
196         struct pthread_tcb *me = current_thread;
197         pthread_exit(me->start_routine(me->arg));
198 }
199
200 // Warning, this will reuse numbers eventually
201 static int get_next_pid(void)
202 {
203         static uint32_t next_pid = 0;
204         return next_pid++;
205 }
206
207
208 int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
209 {
210         attr->stacksize = stacksize;
211         return 0;
212 }
213 int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize)
214 {
215         *stacksize = attr->stacksize;
216         return 0;
217 }
218
219 int pthread_create(pthread_t* thread, const pthread_attr_t* attr,
220                    void *(*start_routine)(void *), void* arg)
221 {
222         /* After this init, we are an MCP and the caller is a pthread */
223         pthread_once(&init_once,&_pthread_init);
224
225         struct pthread_tcb *t = pthread_self();
226         assert(t); /* TODO/FYI: doesn't prevent this from being in vcore context */
227         /* Don't migrate this thread to anothe vcore, since it depends on being on
228          * the same vcore throughout. */
229         t->dont_migrate = TRUE;
230         uint32_t vcoreid = vcore_id();
231         *thread = (pthread_t)calloc(1, sizeof(struct pthread_tcb));
232         (*thread)->start_routine = start_routine;
233         (*thread)->arg = arg;
234         (*thread)->stacksize = PTHREAD_STACK_SIZE;      /* default */
235         (*thread)->id = get_next_pid();
236         (*thread)->detached = FALSE;                            /* default */
237         /* Respect the attributes*/
238         if (attr) {
239                 if (attr->stacksize)                                    /* don't set a 0 stacksize */
240                         (*thread)->stacksize = attr->stacksize;
241                 if (attr->detachstate == PTHREAD_CREATE_DETACHED)
242                         (*thread)->detached = TRUE;
243         }
244         if (__pthread_allocate_stack(*thread) ||  __pthread_allocate_tls(*thread))
245                 printf("We're fucked\n");
246         /* Save the ptr to the new pthread in that pthread's TLS */
247         set_tls_desc((*thread)->tls_desc, vcoreid);
248         current_thread = *thread;
249         set_tls_desc(t->tls_desc, vcoreid);
250         /* Set the u_tf to start up in __pthread_run, which will call the real
251          * start_routine and pass it the arg. */
252         init_user_tf(&(*thread)->utf, (uint32_t)__pthread_run, 
253                  (uint32_t)((*thread)->stacktop));
254         /* Insert the newly created thread into the ready queue of threads.
255          * It will be removed from this queue later when vcore_entry() comes up */
256         mcs_lock_lock(&queue_lock);
257         TAILQ_INSERT_TAIL(&ready_queue, *thread, next);
258         threads_ready++;
259         mcs_lock_unlock(&queue_lock);
260         /* Okay to migrate now. */
261         t->dont_migrate = FALSE;
262         /* Attempt to request a new core, may or may not get it... */
263         vcore_request(1);
264         return 0;
265 }
266
267 int pthread_join(pthread_t thread, void** retval)
268 {
269         /* Not sure if this is the right semantics.  There is a race if we deref
270          * thread and he is already freed (which would have happened if he was
271          * detached. */
272         if (thread->detached) {
273                 printf("[pthread] trying to join on a detached pthread");
274                 return -1;
275         }
276         while (!thread->finished)
277                 pthread_yield();
278         if (retval)
279                 *retval = thread->retval;
280         free(thread);
281         return 0;
282 }
283
284 static void __attribute__((noinline, noreturn)) 
285 __pthread_yield(struct pthread_tcb *t)
286 {
287         /* TODO: want to set this to FALSE once we no longer depend on being on this
288          * vcore.  Though if we are using TLS, we are depending on the vcore.  Since
289          * notifs are disabled and we are in a transition context, we probably
290          * shouldn't be moved anyway.  It does mean that a pthread could get jammed.
291          * If we do this after putting it on the active list, we'll have a race on
292          * dont_migrate. */
293         t->dont_migrate = FALSE;
294         /* Take from the active list, and put on the ready list (tail).  Don't do
295          * this until we are done completely with the thread, since it can be
296          * restarted somewhere else. */
297         mcs_lock_lock(&queue_lock);
298         threads_active--;
299         TAILQ_REMOVE(&active_queue, t, next);
300         threads_ready++;
301         TAILQ_INSERT_TAIL(&ready_queue, t, next);
302         mcs_lock_unlock(&queue_lock);
303         /* Leave the current vcore completely */
304         current_thread = NULL; // this might be okay, even with a migration
305         /* Go back to the entry point, where we can handle notifications or
306          * reschedule someone. */
307         vcore_entry();
308 }
309
310 int pthread_yield(void)
311 {
312         struct pthread_tcb *t = pthread_self();
313         volatile bool yielding = TRUE; /* signal to short circuit when restarting */
314
315         /* TODO: (HSS) Save silly state */
316         // save_fp_state(&t->as);
317
318         /* Don't migrate this thread to another vcore, since it depends on being on
319          * the same vcore throughout (once it disables notifs). */
320         t->dont_migrate = TRUE;
321         uint32_t vcoreid = vcore_id();
322         printd("[P] Pthread id %d is yielding on vcore %d\n", t->id, vcoreid);
323         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
324         /* once we do this, we might miss a notif_pending, so we need to enter vcore
325          * entry later.  Need to disable notifs so we don't get in weird loops with
326          * save_ros_tf() and pop_ros_tf(). */
327         disable_notifs(vcoreid);
328         /* take the current state and save it into t->utf when this pthread
329          * restarts, it will continue from right after this, see yielding is false,
330          * and short ciruit the function. */
331         save_ros_tf(&t->utf);
332         if (!yielding)
333                 goto yield_return_path;
334         yielding = FALSE; /* for when it starts back up */
335         /* Change to the transition context (both TLS and stack). */
336         extern void** vcore_thread_control_blocks;
337         set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
338         assert(current_thread == t);    
339         /* After this, make sure you don't use local variables.  Note the warning in
340          * pthread_exit() */
341         set_stack_pointer((void*)vcpd->transition_stack);
342         /* Finish exiting in another function. */
343         __pthread_yield(current_thread);
344         /* Should never get here */
345         assert(0);
346         /* Will jump here when the pthread's trapframe is restarted/popped. */
347 yield_return_path:
348         printd("[P] pthread %d returning from a yield!\n", t->id);
349         return 0;
350 }
351
352 int pthread_mutexattr_init(pthread_mutexattr_t* attr)
353 {
354   attr->type = PTHREAD_MUTEX_DEFAULT;
355   return 0;
356 }
357
358 int pthread_mutexattr_destroy(pthread_mutexattr_t* attr)
359 {
360   return 0;
361 }
362
363
364 int pthread_attr_setdetachstate(pthread_attr_t *__attr, int __detachstate)
365 {
366         __attr->detachstate = __detachstate;
367         return 0;
368 }
369
370 int pthread_mutexattr_gettype(const pthread_mutexattr_t* attr, int* type)
371 {
372   *type = attr ? attr->type : PTHREAD_MUTEX_DEFAULT;
373   return 0;
374 }
375
376 int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type)
377 {
378   if(type != PTHREAD_MUTEX_NORMAL)
379     return EINVAL;
380   attr->type = type;
381   return 0;
382 }
383
384 int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t* attr)
385 {
386   m->attr = attr;
387   m->lock = 0;
388   return 0;
389 }
390
391 /* Set *spun to 0 when calling this the first time.  It will yield after 'spins'
392  * calls.  Use this for adaptive mutexes and such. */
393 static inline void spin_to_sleep(unsigned int spins, unsigned int *spun)
394 {
395         if ((*spun)++ == spins) {
396                 pthread_yield();
397                 *spun = 0;
398         }
399 }
400
401 int pthread_mutex_lock(pthread_mutex_t* m)
402 {
403         unsigned int spinner = 0;
404         while(pthread_mutex_trylock(m))
405                 while(*(volatile size_t*)&m->lock) {
406                         cpu_relax();
407                         spin_to_sleep(PTHREAD_MUTEX_SPINS, &spinner);
408                 }
409         return 0;
410 }
411
412 int pthread_mutex_trylock(pthread_mutex_t* m)
413 {
414   return atomic_swap(&m->lock,1) == 0 ? 0 : EBUSY;
415 }
416
417 int pthread_mutex_unlock(pthread_mutex_t* m)
418 {
419   /* Need to prevent the compiler (and some arches) from reordering older
420    * stores */
421   wmb();
422   m->lock = 0;
423   return 0;
424 }
425
426 int pthread_mutex_destroy(pthread_mutex_t* m)
427 {
428   return 0;
429 }
430
431 int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a)
432 {
433   c->attr = a;
434   memset(c->waiters,0,sizeof(c->waiters));
435   memset(c->in_use,0,sizeof(c->in_use));
436   c->next_waiter = 0;
437   return 0;
438 }
439
440 int pthread_cond_destroy(pthread_cond_t *c)
441 {
442   return 0;
443 }
444
445 int pthread_cond_broadcast(pthread_cond_t *c)
446 {
447   memset(c->waiters,0,sizeof(c->waiters));
448   return 0;
449 }
450
451 int pthread_cond_signal(pthread_cond_t *c)
452 {
453   int i;
454   for(i = 0; i < MAX_PTHREADS; i++)
455   {
456     if(c->waiters[i])
457     {
458       c->waiters[i] = 0;
459       break;
460     }
461   }
462   return 0;
463 }
464
465 int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m)
466 {
467   int old_waiter = c->next_waiter;
468   int my_waiter = c->next_waiter;
469   
470   //allocate a slot
471   while (atomic_swap (& (c->in_use[my_waiter]), SLOT_IN_USE) == SLOT_IN_USE)
472   {
473     my_waiter = (my_waiter + 1) % MAX_PTHREADS;
474     assert (old_waiter != my_waiter);  // do not want to wrap around
475   }
476   c->waiters[my_waiter] = WAITER_WAITING;
477   c->next_waiter = (my_waiter+1) % MAX_PTHREADS;  // race on next_waiter but ok, because it is advisary
478
479   pthread_mutex_unlock(m);
480
481   volatile int* poll = &c->waiters[my_waiter];
482   while(*poll);
483   c->in_use[my_waiter] = SLOT_FREE;
484   pthread_mutex_lock(m);
485
486   return 0;
487 }
488
489 int pthread_condattr_init(pthread_condattr_t *a)
490 {
491   a = PTHREAD_PROCESS_PRIVATE;
492   return 0;
493 }
494
495 int pthread_condattr_destroy(pthread_condattr_t *a)
496 {
497   return 0;
498 }
499
500 int pthread_condattr_setpshared(pthread_condattr_t *a, int s)
501 {
502   a->pshared = s;
503   return 0;
504 }
505
506 int pthread_condattr_getpshared(pthread_condattr_t *a, int *s)
507 {
508   *s = a->pshared;
509   return 0;
510 }
511
512 pthread_t pthread_self()
513 {
514   return current_thread;
515 }
516
517 int pthread_equal(pthread_t t1, pthread_t t2)
518 {
519   return t1 == t2;
520 }
521
522 /* Need to have this as a separate, non-inlined function since we clobber the
523  * stack pointer before calling it, and don't want the compiler to play games
524  * with my hart. */
525 static void __attribute__((noinline, noreturn)) 
526 __pthread_exit(struct pthread_tcb *t)
527 {
528         __pthread_free_tls(t);
529         __pthread_free_stack(t);
530         /* TODO: race on detach state */
531         if (t->detached)
532                 free(t);
533         /* Once we do this, our joiner can free us.  He won't free us if we're
534          * detached, but there is still a potential race there (since he's accessing
535          * someone who is freed. */
536         t->finished = 1;
537         current_thread = NULL;
538         /* Go back to the entry point, where we can handle notifications or
539          * reschedule someone. */
540         vcore_entry();
541 }
542
543 /* This function cannot be migrated to a different vcore by the userspace
544  * scheduler.  Will need to sort that shit out.  */
545 void pthread_exit(void* ret)
546 {
547         struct pthread_tcb *t = pthread_self();
548         /* Don't migrate this thread to anothe vcore, since it depends on being on
549          * the same vcore throughout. */
550         t->dont_migrate = TRUE; // won't set this to false later, since he is dying
551
552         uint32_t vcoreid = vcore_id();
553         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
554
555         t->retval = ret;
556         mcs_lock_lock(&queue_lock);
557         threads_active--;
558         TAILQ_REMOVE(&active_queue, t, next);
559         mcs_lock_unlock(&queue_lock);
560
561         printd("[P] Pthread id %d is exiting on vcore %d\n", t->id, vcoreid);
562         
563         /* once we do this, we might miss a notif_pending, so we need to enter vcore
564          * entry later. */
565         disable_notifs(vcoreid);
566
567         /* Change to the transition context (both TLS and stack). */
568         extern void** vcore_thread_control_blocks;
569         set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
570         assert(current_thread == t);    
571         /* After this, make sure you don't use local variables.  Also, make sure the
572          * compiler doesn't use them without telling you (TODO).
573          *
574          * In each arch's set_stack_pointer, make sure you subtract off as much room
575          * as you need to any local vars that might be pushed before calling the
576          * next function, or for whatever other reason the compiler/hardware might
577          * walk up the stack a bit when calling a noreturn function. */
578         set_stack_pointer((void*)vcpd->transition_stack);
579         /* Finish exiting in another function.  Ugh. */
580         __pthread_exit(current_thread);
581 }
582
583 int pthread_once(pthread_once_t* once_control, void (*init_routine)(void))
584 {
585   if(atomic_swap(once_control,1) == 0)
586     init_routine();
587   return 0;
588 }
589
590 int pthread_barrier_init(pthread_barrier_t* b, const pthread_barrierattr_t* a, int count)
591 {
592   b->nprocs = b->count = count;
593   b->sense = 0;
594   pthread_mutex_init(&b->pmutex, 0);
595   return 0;
596 }
597
598 int pthread_barrier_wait(pthread_barrier_t* b)
599 {
600   unsigned int spinner = 0;
601   int ls = !b->sense;
602
603   pthread_mutex_lock(&b->pmutex);
604   int count = --b->count;
605   pthread_mutex_unlock(&b->pmutex);
606
607   if(count == 0)
608   {
609     printd("Thread %d is last to hit the barrier, resetting...\n", pthread_self()->id);
610     b->count = b->nprocs;
611         wmb();
612     b->sense = ls;
613     return PTHREAD_BARRIER_SERIAL_THREAD;
614   }
615   else
616   {
617     while(b->sense != ls) {
618       cpu_relax();
619       spin_to_sleep(PTHREAD_BARRIER_SPINS, &spinner);
620     }
621     return 0;
622   }
623 }
624
625 int pthread_barrier_destroy(pthread_barrier_t* b)
626 {
627   pthread_mutex_destroy(&b->pmutex);
628   return 0;
629 }
630
631 int pthread_detach(pthread_t thread)
632 {
633         thread->detached = TRUE;
634         return 0;
635 }