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