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