Drop main() prototype. Syncs with NetBSD-8
[minix.git] / minix / lib / libmthread / allocate.c
blobf34862aa655e1dacddda7670892dcb5631a954c6
1 #define ALLOCATE
2 #include <errno.h>
3 #include <minix/mthread.h>
4 #include <string.h>
6 #include <machine/param.h>
7 #include <machine/vmparam.h>
9 #include <sys/mman.h>
11 #include <uvm/uvm_param.h>
13 #include "global.h"
14 #include "proto.h"
16 static int mthread_increase_thread_pool(void);
17 static void mthread_thread_init(mthread_thread_t thread, mthread_attr_t
18 *tattr, void *(*proc)(void *), void *arg);
20 static void mthread_thread_stop(mthread_thread_t thread);
21 static void mthread_trampoline(void);
23 static int initialized = 0;
24 #define MTHREAD_GUARDSIZE (1 << PGSHIFT) /* 1 page */
26 static struct __mthread_attr default_attr = { MTHREAD_STACK_MIN,
27 NULL,
28 MTHREAD_CREATE_JOINABLE,
29 NULL, NULL };
31 /*===========================================================================*
32 * mthread_equal *
33 *===========================================================================*/
34 int mthread_equal(l, r)
35 mthread_thread_t l;
36 mthread_thread_t r;
38 /* Compare two thread ids */
40 return(l == r);
44 /*===========================================================================*
45 * mthread_create *
46 *===========================================================================*/
47 int mthread_create(threadid, tattr, proc, arg)
48 mthread_thread_t *threadid;
49 mthread_attr_t *tattr;
50 void *(*proc)(void *);
51 void *arg;
53 /* Register procedure proc for execution in a thread. */
54 mthread_thread_t thread;
56 if (proc == NULL)
57 return(EINVAL);
59 if (!mthread_queue_isempty(&free_threads)) {
60 thread = mthread_queue_remove(&free_threads);
61 mthread_thread_init(thread, tattr, proc, arg);
62 used_threads++;
63 if(threadid != NULL)
64 *threadid = (mthread_thread_t) thread;
65 #ifdef MDEBUG
66 printf("Inited thread %d\n", thread);
67 #endif
68 return(0);
69 } else {
70 if (mthread_increase_thread_pool() == -1)
71 return(EAGAIN);
73 return mthread_create(threadid, tattr, proc, arg);
78 /*===========================================================================*
79 * mthread_detach *
80 *===========================================================================*/
81 int mthread_detach(detach)
82 mthread_thread_t detach;
84 /* Mark a thread as detached. Consequently, upon exit, resources allocated for
85 * this thread are automatically freed.
87 mthread_tcb_t *tcb;
89 if (!isokthreadid(detach))
90 return(ESRCH);
92 tcb = mthread_find_tcb(detach);
93 if (tcb->m_state == MS_DEAD) {
94 return(ESRCH);
95 } else if (tcb->m_attr.ma_detachstate != MTHREAD_CREATE_DETACHED) {
96 if (tcb->m_state == MS_EXITING)
97 mthread_thread_stop(detach);
98 else
99 tcb->m_attr.ma_detachstate = MTHREAD_CREATE_DETACHED;
102 return(0);
106 /*===========================================================================*
107 * mthread_exit *
108 *===========================================================================*/
109 void mthread_exit(value)
110 void *value;
112 /* Make a thread stop running and store the result value. */
113 mthread_tcb_t *tcb;
115 tcb = mthread_find_tcb(current_thread);
117 if (tcb->m_state == MS_EXITING) /* Already stopping, nothing to do. */
118 return;
120 mthread_cleanup_values();
122 tcb->m_result = value;
123 tcb->m_state = MS_EXITING;
125 if (tcb->m_attr.ma_detachstate == MTHREAD_CREATE_DETACHED) {
126 mthread_thread_stop(current_thread);
127 } else {
128 /* Joinable thread; notify possibly waiting thread */
129 if (mthread_cond_signal(&(tcb->m_exited)) != 0)
130 mthread_panic("Couldn't signal exit");
132 /* The thread that's actually doing the join will eventually clean
133 * up this thread (i.e., call mthread_thread_stop).
137 mthread_schedule();
140 /*===========================================================================*
141 * mthread_find_tcb *
142 *===========================================================================*/
143 mthread_tcb_t * mthread_find_tcb(thread)
144 mthread_thread_t thread;
146 mthread_tcb_t *rt = NULL;
148 if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
150 if (thread == MAIN_THREAD)
151 rt = &mainthread;
152 else
153 rt = threads[thread];
155 return(rt);
159 /*===========================================================================*
160 * mthread_increase_thread_pool *
161 *===========================================================================*/
162 static int mthread_increase_thread_pool(void)
164 /* Increase thread pool. No fancy algorithms, just double the size. */
165 mthread_tcb_t **new_tcb;
166 int new_no_threads, old_no_threads, i;
168 old_no_threads = no_threads;
170 if (old_no_threads == 0)
171 new_no_threads = NO_THREADS;
172 else
173 new_no_threads = 2 * old_no_threads;
176 if (new_no_threads >= MAX_THREAD_POOL) {
177 mthread_debug("Reached max number of threads");
178 return(-1);
181 /* Allocate space to store pointers to thread control blocks */
182 if (old_no_threads == 0) /* No data yet: allocate space */
183 new_tcb = calloc(new_no_threads, sizeof(mthread_tcb_t *));
184 else /* Preserve existing data: reallocate space */
185 new_tcb = realloc(threads, new_no_threads * sizeof(mthread_tcb_t *));
187 if (new_tcb == NULL) {
188 mthread_debug("Can't increase thread pool");
189 return(-1);
192 /* Allocate space for thread control blocks itself */
193 for (i = old_no_threads; i < new_no_threads; i++) {
194 new_tcb[i] = malloc(sizeof(mthread_tcb_t));
195 if (new_tcb[i] == NULL) {
196 mthread_debug("Can't allocate space for tcb");
197 /* Undo the allocations made so far. */
198 while (i-- > old_no_threads)
199 free(new_tcb[i]);
200 if (old_no_threads > 0) {
201 new_tcb = realloc(threads, old_no_threads *
202 sizeof(mthread_tcb_t *));
203 if (new_tcb == NULL)
204 mthread_panic("Unable to shrink tcb array");
205 } else
206 free(new_tcb);
207 return(-1);
209 memset(new_tcb[i], '\0', sizeof(mthread_tcb_t)); /* Clear entry */
212 /* We can breathe again, let's tell the others about the good news */
213 threads = new_tcb;
214 no_threads = new_no_threads;
216 /* Add newly available threads to free_threads */
217 for (i = old_no_threads; i < new_no_threads; i++) {
218 mthread_queue_add(&free_threads, i);
219 mthread_thread_reset(i);
222 #ifdef MDEBUG
223 printf("Increased thread pool from %d to %d threads\n", old_no_threads,
224 new_no_threads);
225 #endif
226 return(0);
230 /*===========================================================================*
231 * mthread_init *
232 *===========================================================================*/
233 static void __attribute__((__constructor__, __used__)) mthread_init(void)
235 /* Initialize thread system; allocate thread structures and start creating
236 * threads.
239 if (initialized) return;
241 no_threads = 0;
242 used_threads = 0;
243 need_reset = 0;
244 running_main_thread = 1; /* mthread_init can only be called from the
245 * main thread. Calling it from a thread will
246 * not enter this clause.
249 if (mthread_getcontext(&(mainthread.m_context)) == -1)
250 mthread_panic("Couldn't save state for main thread");
251 current_thread = MAIN_THREAD;
253 mthread_init_valid_mutexes();
254 mthread_init_valid_conditions();
255 mthread_init_valid_attributes();
256 mthread_init_keys();
257 mthread_init_scheduler();
259 initialized = 1;
263 /*===========================================================================*
264 * mthread_join *
265 *===========================================================================*/
266 int mthread_join(join, value)
267 mthread_thread_t join;
268 void **value;
270 /* Wait for a thread to stop running and copy the result. */
272 mthread_tcb_t *tcb;
274 if (!isokthreadid(join))
275 return(ESRCH);
276 else if (join == current_thread)
277 return(EDEADLK);
279 tcb = mthread_find_tcb(join);
280 if (tcb->m_state == MS_DEAD)
281 return(ESRCH);
282 else if (tcb->m_attr.ma_detachstate == MTHREAD_CREATE_DETACHED)
283 return(EINVAL);
285 /* When the thread hasn't exited yet, we have to wait for that to happen */
286 if (tcb->m_state != MS_EXITING) {
287 mthread_cond_t *c;
288 mthread_mutex_t *m;
290 c = &(tcb->m_exited);
291 m = &(tcb->m_exitm);
293 if (mthread_mutex_init(m, NULL) != 0)
294 mthread_panic("Couldn't initialize mutex to join\n");
296 if (mthread_mutex_lock(m) != 0)
297 mthread_panic("Couldn't lock mutex to join\n");
299 if (mthread_cond_wait(c, m) != 0)
300 mthread_panic("Couldn't wait for join condition\n");
302 if (mthread_mutex_unlock(m) != 0)
303 mthread_panic("Couldn't unlock mutex to join\n");
305 if (mthread_mutex_destroy(m) != 0)
306 mthread_panic("Couldn't destroy mutex to join\n");
309 /* Thread has exited; copy results */
310 if(value != NULL)
311 *value = tcb->m_result;
313 /* Deallocate resources */
314 mthread_thread_stop(join);
315 return(0);
319 /*===========================================================================*
320 * mthread_once *
321 *===========================================================================*/
322 int mthread_once(once, proc)
323 mthread_once_t *once;
324 void (*proc)(void);
326 /* Run procedure proc just once */
328 if (once == NULL || proc == NULL)
329 return(EINVAL);
331 if (*once != 1) proc();
332 *once = 1;
333 return(0);
337 /*===========================================================================*
338 * mthread_self *
339 *===========================================================================*/
340 mthread_thread_t mthread_self(void)
342 /* Return the thread id of the thread calling this function. */
344 return(current_thread);
348 /*===========================================================================*
349 * mthread_thread_init *
350 *===========================================================================*/
351 static void mthread_thread_init(thread, tattr, proc, arg)
352 mthread_thread_t thread;
353 mthread_attr_t *tattr;
354 void *(*proc)(void *);
355 void *arg;
357 /* Initialize a thread so that it, when unsuspended, will run the given
358 * procedure with the given parameter. The thread is marked as runnable.
361 #define THIS_CTX (&(threads[thread]->m_context))
362 mthread_tcb_t *tcb;
363 size_t stacksize;
364 char *stackaddr;
366 tcb = mthread_find_tcb(thread);
367 tcb->m_next = NULL;
368 tcb->m_state = MS_DEAD;
369 tcb->m_proc = proc;
370 tcb->m_arg = arg;
371 /* Threads use a copy of the provided attributes. This way, if another
372 * thread modifies the attributes (such as detach state), already running
373 * threads are not affected.
375 if (tattr != NULL)
376 tcb->m_attr = *((struct __mthread_attr *) *tattr);
377 else {
378 tcb->m_attr = default_attr;
381 if (mthread_cond_init(&(tcb->m_exited), NULL) != 0)
382 mthread_panic("Could not initialize thread");
384 tcb->m_context.uc_link = NULL;
386 /* Construct this thread's context to run procedure proc. */
387 if (mthread_getcontext(&(tcb->m_context)) == -1)
388 mthread_panic("Failed to initialize context state");
390 stacksize = tcb->m_attr.ma_stacksize;
391 stackaddr = tcb->m_attr.ma_stackaddr;
393 if (stacksize == (size_t) 0) {
394 /* User provided too small a stack size. Forget about that stack and
395 * allocate a new one ourselves.
397 stacksize = (size_t) MTHREAD_STACK_MIN;
398 tcb->m_attr.ma_stackaddr = stackaddr = NULL;
401 if (stackaddr == NULL) {
402 /* Allocate stack space */
403 size_t guarded_stacksize;
404 char *guard_start, *guard_end;
406 stacksize = round_page(stacksize + MTHREAD_GUARDSIZE);
407 stackaddr = mmap(NULL, stacksize,
408 PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE,
409 -1, 0);
410 if (stackaddr == MAP_FAILED)
411 mthread_panic("Failed to allocate stack to thread");
413 #if defined(__i386__) || defined(__arm__)
414 guard_start = stackaddr;
415 guard_end = stackaddr + MTHREAD_GUARDSIZE;
416 guarded_stacksize = stackaddr + stacksize - guard_end;
418 /* The stack will be used from (stackaddr+stacksize) to stackaddr. That
419 * is, growing downwards. So the "top" of the stack may not grow into
420 * stackaddr+MTHREAD_GUARDSIZE.
422 * +-------+ stackaddr + stacksize
423 * | |
424 * | | |
425 * | \|/ |
426 * | |
427 * +-------+ stackaddr + MTHREAD_GUARDSIZE
428 * | GUARD |
429 * +-------+ stackaddr
431 #else
432 # error "Unsupported platform"
433 #endif
434 stacksize = guarded_stacksize;
435 /* Mere unmapping could allow a later allocation to fill the gap. */
436 if (mmap(guard_start, MTHREAD_GUARDSIZE, PROT_NONE,
437 MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, 0) != guard_start)
438 mthread_panic("unable to overmap stack space for guard");
439 tcb->m_context.uc_stack.ss_sp = guard_end;
440 } else
441 tcb->m_context.uc_stack.ss_sp = stackaddr;
443 tcb->m_context.uc_stack.ss_size = stacksize;
444 makecontext(&(tcb->m_context), mthread_trampoline, 0);
446 mthread_unsuspend(thread); /* Make thread runnable */
450 /*===========================================================================*
451 * mthread_thread_reset *
452 *===========================================================================*/
453 void mthread_thread_reset(thread)
454 mthread_thread_t thread;
456 /* Reset the thread to its default values. Free the allocated stack space. */
458 mthread_tcb_t *rt;
459 size_t stacksize;
460 char *stackaddr;
462 if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
464 rt = mthread_find_tcb(thread);
465 rt->m_tid = thread;
466 rt->m_next = NULL;
467 rt->m_state = MS_DEAD;
468 rt->m_proc = NULL;
469 rt->m_arg = NULL;
470 rt->m_result = NULL;
471 rt->m_cond = NULL;
472 if (rt->m_attr.ma_stackaddr == NULL) { /* We allocated stack space */
473 if (rt->m_context.uc_stack.ss_sp) {
474 stacksize = rt->m_context.uc_stack.ss_size;
475 stackaddr = rt->m_context.uc_stack.ss_sp;
476 #if defined(__i386__) || defined(__arm__)
477 stacksize += MTHREAD_GUARDSIZE;
478 stackaddr -= MTHREAD_GUARDSIZE;
479 #else
480 # error "Unsupported platform"
481 #endif
482 if (munmap(stackaddr, stacksize) != 0) {
483 mthread_panic("unable to unmap memory");
486 rt->m_context.uc_stack.ss_sp = NULL;
488 rt->m_context.uc_stack.ss_size = 0;
489 rt->m_context.uc_link = NULL;
493 /*===========================================================================*
494 * mthread_thread_stop *
495 *===========================================================================*/
496 static void mthread_thread_stop(thread)
497 mthread_thread_t thread;
499 /* Stop thread from running. Deallocate resources. */
500 mthread_tcb_t *stop_thread;
502 if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
504 stop_thread = mthread_find_tcb(thread);
506 if (stop_thread->m_state == MS_DEAD) {
507 /* Already dead, nothing to do */
508 return;
511 if (mthread_cond_destroy(&(stop_thread->m_exited)) != 0)
512 mthread_panic("Could not destroy condition at thread deallocation\n");
514 /* Can't deallocate ourselves (i.e., we're a detached thread) */
515 if (thread == current_thread) {
516 stop_thread->m_state = MS_NEEDRESET;
517 need_reset++;
518 } else {
519 mthread_thread_reset(thread);
520 used_threads--;
521 mthread_queue_add(&free_threads, thread);
526 /*===========================================================================*
527 * mthread_trampoline *
528 *===========================================================================*/
529 static void mthread_trampoline(void)
531 /* Execute the /current_thread's/ procedure. Store its result. */
533 mthread_tcb_t *tcb;
534 void *r;
536 tcb = mthread_find_tcb(current_thread);
538 r = (tcb->m_proc)(tcb->m_arg);
539 mthread_exit(r);
542 /* pthread compatibility layer. */
543 __weak_alias(pthread_create, mthread_create)
544 __weak_alias(pthread_detach, mthread_detach)
545 __weak_alias(pthread_equal, mthread_equal)
546 __weak_alias(pthread_exit, mthread_exit)
547 __weak_alias(pthread_join, mthread_join)
548 __weak_alias(pthread_once, mthread_once)
549 __weak_alias(pthread_self, mthread_self)