Pthread create, join, and exit
[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
28 __thread struct pthread_tcb *current_thread = 0;
29
30 void _pthread_init()
31 {
32         if (vcore_init())
33                 printf("vcore_init() failed, we're fucked!\n");
34         
35         assert(vcore_id() == 0);
36
37         /* tell the kernel where and how we want to receive notifications */
38         struct notif_method *nm;
39         for (int i = 0; i < MAX_NR_NOTIF; i++) {
40                 nm = &__procdata.notif_methods[i];
41                 nm->flags |= NOTIF_WANTED | NOTIF_MSG | NOTIF_IPI;
42                 nm->vcoreid = i % 2; // vcore0 or 1, keepin' it fresh.
43         }
44
45         /* Create a pthread_tcb for the main thread */
46         pthread_t t = (pthread_t)calloc(sizeof(struct pthread_tcb), 1);
47         t->id = get_next_pid();
48         assert(t->id == 0);
49         
50         /* Save a pointer to the newly created threads tls region into its tcb */
51         t->tls_desc = get_tls_desc(0);
52         /* Save a pointer to the pthread in its own TLS */
53         current_thread = t;
54
55         /* Change temporarily to vcore0s tls region so we can save the newly created
56          * tcb into its current_thread variable and then restore it.  One minor
57          * issue is that vcore0's transition-TLS isn't TLS_INITed yet.  Until it is
58          * (right before vcore_entry(), don't try and take the address of any of
59          * its TLS vars. */
60         extern void** vcore_thread_control_blocks;
61         set_tls_desc(vcore_thread_control_blocks[0], 0);
62         current_thread = t;
63         set_tls_desc(t->tls_desc, 0);
64
65         // TODO: consider replacing this when we have an interface allowing
66         // requesting absolute num vcores, and moving it to pthread_create and
67         // asking for 2
68         vcore_request(1);
69 }
70
71 void vcore_entry()
72 {
73         uint32_t vcoreid = vcore_id();
74
75         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
76         struct vcore *vc = &__procinfo.vcoremap[vcoreid];
77
78         /* Should always have notifications disabled when coming in here. */
79         assert(vcpd->notif_enabled == FALSE);
80
81         /* Put this in the loop that deals with notifications.  It will return if
82          * there is no preempt pending. */ 
83         // TODO: prob make a handle_notif() function
84         if (vc->preempt_pending)
85                 sys_yield(TRUE);
86         // TODO: consider making this restart path work for restarting as well as
87         // freshly starting
88         if (current_thread) {
89                 /* Do one last check for notifs before clearing pending */
90                 // TODO: call the handle_notif() here (first)
91                 vcpd->notif_pending = 0;
92                 set_tls_desc(current_thread->tls_desc, vcoreid);
93                 /* Pop the user trap frame */
94                 pop_ros_tf(&vcpd->notif_tf, vcoreid);
95                 assert(0);
96         }
97
98         /* no one currently running, so lets get someone from the ready queue */
99         mcs_lock_lock(&queue_lock);
100         struct pthread_tcb *new_thread = TAILQ_FIRST(&ready_queue);
101         if (new_thread) {
102                 TAILQ_REMOVE(&ready_queue, new_thread, next);
103                 TAILQ_INSERT_TAIL(&active_queue, new_thread, next);
104                 threads_active++;
105                 threads_ready--;
106         }
107         mcs_lock_unlock(&queue_lock);
108         if (!new_thread) {
109                 printf("[P] No threads, vcore %d is yielding\n", vcoreid);
110                 sys_yield(0);
111         }
112         /* Save a ptr to the pthread running in the transition context's TLS */
113         current_thread = new_thread;
114
115         /* Do one last check for notifs before clearing pending */
116         // TODO: call the handle_notif() here (first)
117         vcpd->notif_pending = 0;
118         set_tls_desc(new_thread->tls_desc, vcoreid);
119
120         /* Load silly state (Floating point) too.  For real */
121         // TODO
122
123         /* Pop the user trap frame */
124         pop_ros_tf(&new_thread->utf, vcoreid);
125         assert(0);
126 }
127
128 int pthread_attr_init(pthread_attr_t *a)
129 {
130   return 0;
131 }
132
133 int pthread_attr_destroy(pthread_attr_t *a)
134 {
135   return 0;
136 }
137
138 static void __pthread_free_tls(struct pthread_tcb *pt)
139 {
140         extern void _dl_deallocate_tls (void *tcb, bool dealloc_tcb) internal_function;
141
142         assert(pt->tls_desc);
143         _dl_deallocate_tls(pt->tls_desc, TRUE);
144         pt->tls_desc = NULL;
145 }
146
147 static int __pthread_allocate_tls(struct pthread_tcb *pt)
148 {
149         extern void *_dl_allocate_tls (void *mem) internal_function;
150
151         assert(!pt->tls_desc);
152         pt->tls_desc = _dl_allocate_tls(NULL);
153         if (!pt->tls_desc) {
154                 errno = ENOMEM;
155                 return -1;
156         }
157         return 0;
158 }
159
160 // TODO: how big do we want these?  ideally, we want to be able to guard and map
161 // more space if we go too far.
162 #define PTHREAD_STACK_PAGES 4
163 #define PTHREAD_STACK_SIZE (PTHREAD_STACK_PAGES*PGSIZE)
164
165 static void __pthread_free_stack(struct pthread_tcb *pt)
166 {
167         assert(!munmap(pt->stacktop - PTHREAD_STACK_SIZE, PTHREAD_STACK_SIZE));
168 }
169
170 static int __pthread_allocate_stack(struct pthread_tcb *pt)
171 {
172         void* stackbot = mmap(0, PTHREAD_STACK_SIZE,
173                               PROT_READ|PROT_WRITE|PROT_EXEC,
174                               MAP_POPULATE|MAP_ANONYMOUS, -1, 0);
175         if (stackbot == MAP_FAILED)
176                 return -1; // errno set by mmap
177         pt->stacktop = stackbot + PTHREAD_STACK_SIZE;
178         return 0;
179 }
180
181 void __pthread_run(void)
182 {
183         struct pthread_tcb *me = current_thread;
184         pthread_exit(me->start_routine(me->arg));
185 }
186
187 // Warning, this will reuse numbers eventually
188 static int get_next_pid(void)
189 {
190         static uint32_t next_pid = 0;
191         return next_pid++;
192 }
193
194 int pthread_create(pthread_t* thread, const pthread_attr_t* attr,
195                    void *(*start_routine)(void *), void* arg)
196 {
197         /* After this init, we are an MCP and the caller is a pthread */
198         pthread_once(&init_once,&_pthread_init);
199
200         struct pthread_tcb *t = pthread_self();
201         assert(t);
202         /* Don't migrate this thread to anothe vcore, since it depends on being on
203          * the same vcore throughout. */
204         t->dont_migrate = TRUE;
205
206         uint32_t vcoreid = vcore_id();
207
208         // Most fields already zeroed out by the calloc below...
209         *thread = (pthread_t)calloc(sizeof(struct pthread_tcb), 1);
210         (*thread)->start_routine = start_routine;
211         (*thread)->arg = arg;
212         (*thread)->id = get_next_pid();
213         if (__pthread_allocate_stack(*thread) ||  __pthread_allocate_tls(*thread))
214                 printf("We're fucked\n");
215         /* Save the ptr to the new pthread in that pthread's TLS */
216         set_tls_desc((*thread)->tls_desc, vcoreid);
217         current_thread = *thread;
218         set_tls_desc(t->tls_desc, vcoreid);
219
220         /* Set the u_tf to start up in __pthread_run, which will call the real
221          * start_routine and pass it the arg. */
222         init_user_tf(&(*thread)->utf, (uint32_t)__pthread_run, 
223                  (uint32_t)((*thread)->stacktop));
224
225         // Insert the newly created thread into the ready queue of threads.
226         // It will be removed from this queue later when vcore_entry() comes up
227         mcs_lock_lock(&queue_lock);
228         TAILQ_INSERT_TAIL(&ready_queue, *thread, next);
229         threads_ready++;
230         mcs_lock_unlock(&queue_lock);
231
232         /* Okay to migrate now. */
233         t->dont_migrate = FALSE;
234
235         // Attempt to request a new core, may or may not get it...
236         vcore_request(1);
237
238         return 0;
239 }
240
241 int pthread_join(pthread_t thread, void** retval)
242 {
243         /* Not sure if this is the right semantics.  There is a race if we deref
244          * thread and he is already freed (which would have happened if he was
245          * detached. */
246         if (thread->detached) {
247                 printf("[pthread] trying to join on a detached pthread");
248                 return -1;
249         }
250         // TODO: something smarter than spinning (deschedule, etc)
251         while(!thread->finished)
252                 cpu_relax(); // has a memory clobber
253         if (retval)
254                 *retval = thread->retval;
255         free(thread);
256         return 0;
257 }
258
259 int pthread_mutexattr_init(pthread_mutexattr_t* attr)
260 {
261   attr->type = PTHREAD_MUTEX_DEFAULT;
262   return 0;
263 }
264
265 int pthread_mutexattr_destroy(pthread_mutexattr_t* attr)
266 {
267   return 0;
268 }
269
270
271 int pthread_attr_setdetachstate(pthread_attr_t *__attr,int __detachstate) {
272         *__attr = __detachstate;
273         // TODO: the right thing
274         return 0;
275 }
276
277 int pthread_mutexattr_gettype(const pthread_mutexattr_t* attr, int* type)
278 {
279   *type = attr ? attr->type : PTHREAD_MUTEX_DEFAULT;
280   return 0;
281 }
282
283 int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type)
284 {
285   if(type != PTHREAD_MUTEX_NORMAL)
286     return EINVAL;
287   attr->type = type;
288   return 0;
289 }
290
291 int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t* attr)
292 {
293   m->attr = attr;
294   m->lock = 0;
295   return 0;
296 }
297
298 int pthread_mutex_lock(pthread_mutex_t* m)
299 {
300   while(pthread_mutex_trylock(m))
301     while(*(volatile size_t*)&m->lock);
302   return 0;
303 }
304
305 int pthread_mutex_trylock(pthread_mutex_t* m)
306 {
307   return atomic_swap(&m->lock,1) == 0 ? 0 : EBUSY;
308 }
309
310 int pthread_mutex_unlock(pthread_mutex_t* m)
311 {
312   m->lock = 0;
313   return 0;
314 }
315
316 int pthread_mutex_destroy(pthread_mutex_t* m)
317 {
318   return 0;
319 }
320
321 int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a)
322 {
323   c->attr = a;
324   memset(c->waiters,0,sizeof(c->waiters));
325   return 0;
326 }
327
328 int pthread_cond_destroy(pthread_cond_t *c)
329 {
330   return 0;
331 }
332
333 int pthread_cond_broadcast(pthread_cond_t *c)
334 {
335   memset(c->waiters,0,sizeof(c->waiters));
336   return 0;
337 }
338
339 int pthread_cond_signal(pthread_cond_t *c)
340 {
341   int i;
342   for(i = 0; i < max_vcores(); i++)
343   {
344     if(c->waiters[i])
345     {
346       c->waiters[i] = 0;
347       break;
348     }
349   }
350   return 0;
351 }
352
353 int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m)
354 {
355   c->waiters[vcore_id()] = 1;
356   pthread_mutex_unlock(m);
357
358   volatile int* poll = &c->waiters[vcore_id()];
359   while(*poll);
360
361   pthread_mutex_lock(m);
362
363   return 0;
364 }
365
366 int pthread_condattr_init(pthread_condattr_t *a)
367 {
368   a = PTHREAD_PROCESS_PRIVATE;
369   return 0;
370 }
371
372 int pthread_condattr_destroy(pthread_condattr_t *a)
373 {
374   return 0;
375 }
376
377 int pthread_condattr_setpshared(pthread_condattr_t *a, int s)
378 {
379   a->pshared = s;
380   return 0;
381 }
382
383 int pthread_condattr_getpshared(pthread_condattr_t *a, int *s)
384 {
385   *s = a->pshared;
386   return 0;
387 }
388
389 pthread_t pthread_self()
390 {
391   return current_thread;
392 }
393
394 int pthread_equal(pthread_t t1, pthread_t t2)
395 {
396   return t1 == t2;
397 }
398
399 /* Need to have this as a separate, non-inlined function since we clobber the
400  * stack pointer before calling it, and don't want the compiler to play games
401  * with my hart. */
402 static void __attribute__((noinline)) internal_function
403 __pthread_exit(struct pthread_tcb *t)
404 {
405         __pthread_free_tls(t);
406         __pthread_free_stack(t);
407         if (t->detached)
408                 free(t);
409         /* Once we do this, our joiner can free us.  He won't free us if we're
410          * detached, but there is still a potential race there (since he's accessing
411          * someone who is freed. */
412         t->finished = 1;
413         current_thread = NULL;
414         /* Go back to the entry point, where we can handle notifications or
415          * reschedule someone. */
416         vcore_entry();
417 }
418
419 /* This function cannot be migrated to a different vcore by the userspace
420  * scheduler.  Will need to sort that shit out.  */
421 void pthread_exit(void* ret)
422 {
423         struct pthread_tcb *t = pthread_self();
424         /* Don't migrate this thread to anothe vcore, since it depends on being on
425          * the same vcore throughout. */
426         assert(t);
427         t->dont_migrate = TRUE; // won't set this to false later, since he is dying
428
429         uint32_t vcoreid = vcore_id();
430         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
431
432         t->retval = ret;
433         mcs_lock_lock(&queue_lock);
434         threads_active--;
435         TAILQ_REMOVE(&active_queue, t, next);
436         mcs_lock_unlock(&queue_lock);
437
438         printf("[P] Pthread id %d is exiting on vcore %d\n", t->id, vcoreid);
439         
440         /* once we do this, we might miss a notif_pending, so we need to enter vcore
441          * entry later. */
442         vcpd->notif_enabled = FALSE;
443
444         /* Change to the transition context (both TLS and stack). */
445         extern void** vcore_thread_control_blocks;
446         set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
447         assert(current_thread == t);    
448         /* After this, make sure you don't use local variables.  Also, make sure the
449          * compiler doesn't use them without telling you (TODO).  We take some space
450          * off the top of the stack since the compiler is going to assume it has a
451          * stack frame setup when it pushes (actually moves) the current_thread onto
452          * the stack before calling __pthread_cleanup(). */
453         set_stack_pointer((void*)vcpd->transition_stack - 32);
454         /* Finish exiting in another function.  Ugh. */
455         __pthread_exit(current_thread);
456 }
457
458 int pthread_once(pthread_once_t* once_control, void (*init_routine)(void))
459 {
460   if(atomic_swap(once_control,1) == 0)
461     init_routine();
462   return 0;
463 }
464
465 int pthread_barrier_init(pthread_barrier_t* b, const pthread_barrierattr_t* a, int count)
466 {
467   memset(b->local_sense,0,sizeof(b->local_sense));
468
469   b->sense = 0;
470   b->nprocs = b->count = count;
471   mcs_lock_init(&b->lock);
472   return 0;
473 }
474
475 int pthread_barrier_wait(pthread_barrier_t* b)
476 {
477   int id = vcore_id();
478   int ls = b->local_sense[32*id] = 1 - b->local_sense[32*id];
479
480   mcs_lock_lock(&b->lock);
481   int count = --b->count;
482   mcs_lock_unlock(&b->lock);
483
484   if(count == 0)
485   {
486     b->count = b->nprocs;
487     b->sense = ls;
488     return PTHREAD_BARRIER_SERIAL_THREAD;
489   }
490   else
491   {
492     while(b->sense != ls);
493     return 0;
494   }
495 }
496
497 int pthread_barrier_destroy(pthread_barrier_t* b)
498 {
499   return 0;
500 }