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