custom message type for VM_QUERY_EXIT
[minix3.git] / lib / libmthread / allocate.c
blob776525414165776b8ec73cc450db616355260b47
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 return(-1);
199 memset(new_tcb[i], '\0', sizeof(mthread_tcb_t)); /* Clear entry */
202 /* We can breath again, let's tell the others about the good news */
203 threads = new_tcb;
204 no_threads = new_no_threads;
206 /* Add newly available threads to free_threads */
207 for (i = old_no_threads; i < new_no_threads; i++) {
208 mthread_queue_add(&free_threads, i);
209 mthread_thread_reset(i);
212 #ifdef MDEBUG
213 printf("Increased thread pool from %d to %d threads\n", old_no_threads,
214 new_no_threads);
215 #endif
216 return(0);
220 /*===========================================================================*
221 * mthread_init *
222 *===========================================================================*/
223 static void __attribute__((__constructor__, __used__)) mthread_init(void)
225 /* Initialize thread system; allocate thread structures and start creating
226 * threads.
229 if (initialized) return;
231 no_threads = 0;
232 used_threads = 0;
233 need_reset = 0;
234 running_main_thread = 1; /* mthread_init can only be called from the
235 * main thread. Calling it from a thread will
236 * not enter this clause.
239 if (mthread_getcontext(&(mainthread.m_context)) == -1)
240 mthread_panic("Couldn't save state for main thread");
241 current_thread = MAIN_THREAD;
243 mthread_init_valid_mutexes();
244 mthread_init_valid_conditions();
245 mthread_init_valid_attributes();
246 mthread_init_keys();
247 mthread_init_scheduler();
249 initialized = 1;
253 /*===========================================================================*
254 * mthread_join *
255 *===========================================================================*/
256 int mthread_join(join, value)
257 mthread_thread_t join;
258 void **value;
260 /* Wait for a thread to stop running and copy the result. */
262 mthread_tcb_t *tcb;
264 if (!isokthreadid(join))
265 return(ESRCH);
266 else if (join == current_thread)
267 return(EDEADLK);
269 tcb = mthread_find_tcb(join);
270 if (tcb->m_state == MS_DEAD)
271 return(ESRCH);
272 else if (tcb->m_attr.ma_detachstate == MTHREAD_CREATE_DETACHED)
273 return(EINVAL);
275 /* When the thread hasn't exited yet, we have to wait for that to happen */
276 if (tcb->m_state != MS_EXITING) {
277 mthread_cond_t *c;
278 mthread_mutex_t *m;
280 c = &(tcb->m_exited);
281 m = &(tcb->m_exitm);
283 if (mthread_mutex_init(m, NULL) != 0)
284 mthread_panic("Couldn't initialize mutex to join\n");
286 if (mthread_mutex_lock(m) != 0)
287 mthread_panic("Couldn't lock mutex to join\n");
289 if (mthread_cond_wait(c, m) != 0)
290 mthread_panic("Couldn't wait for join condition\n");
292 if (mthread_mutex_unlock(m) != 0)
293 mthread_panic("Couldn't unlock mutex to join\n");
295 if (mthread_mutex_destroy(m) != 0)
296 mthread_panic("Couldn't destroy mutex to join\n");
299 /* Thread has exited; copy results */
300 if(value != NULL)
301 *value = tcb->m_result;
303 /* Deallocate resources */
304 mthread_thread_stop(join);
305 return(0);
309 /*===========================================================================*
310 * mthread_once *
311 *===========================================================================*/
312 int mthread_once(once, proc)
313 mthread_once_t *once;
314 void (*proc)(void);
316 /* Run procedure proc just once */
318 if (once == NULL || proc == NULL)
319 return(EINVAL);
321 if (*once != 1) proc();
322 *once = 1;
323 return(0);
327 /*===========================================================================*
328 * mthread_self *
329 *===========================================================================*/
330 mthread_thread_t mthread_self(void)
332 /* Return the thread id of the thread calling this function. */
334 return(current_thread);
338 /*===========================================================================*
339 * mthread_thread_init *
340 *===========================================================================*/
341 static void mthread_thread_init(thread, tattr, proc, arg)
342 mthread_thread_t thread;
343 mthread_attr_t *tattr;
344 void *(*proc)(void *);
345 void *arg;
347 /* Initialize a thread so that it, when unsuspended, will run the given
348 * procedure with the given parameter. The thread is marked as runnable.
351 #define THIS_CTX (&(threads[thread]->m_context))
352 mthread_tcb_t *tcb;
353 size_t stacksize;
354 char *stackaddr;
356 tcb = mthread_find_tcb(thread);
357 tcb->m_next = NULL;
358 tcb->m_state = MS_DEAD;
359 tcb->m_proc = proc;
360 tcb->m_arg = arg;
361 /* Threads use a copy of the provided attributes. This way, if another
362 * thread modifies the attributes (such as detach state), already running
363 * threads are not affected.
365 if (tattr != NULL)
366 tcb->m_attr = *((struct __mthread_attr *) *tattr);
367 else {
368 tcb->m_attr = default_attr;
371 if (mthread_cond_init(&(tcb->m_exited), NULL) != 0)
372 mthread_panic("Could not initialize thread");
374 tcb->m_context.uc_link = NULL;
376 /* Construct this thread's context to run procedure proc. */
377 if (mthread_getcontext(&(tcb->m_context)) == -1)
378 mthread_panic("Failed to initialize context state");
380 stacksize = tcb->m_attr.ma_stacksize;
381 stackaddr = tcb->m_attr.ma_stackaddr;
383 if (stacksize == (size_t) 0) {
384 /* User provided too small a stack size. Forget about that stack and
385 * allocate a new one ourselves.
387 stacksize = (size_t) MTHREAD_STACK_MIN;
388 tcb->m_attr.ma_stackaddr = stackaddr = NULL;
391 if (stackaddr == NULL) {
392 /* Allocate stack space */
393 size_t guarded_stacksize;
394 char *guard_start, *guard_end;
396 stacksize = round_page(stacksize + MTHREAD_GUARDSIZE);
397 stackaddr = mmap(NULL, stacksize,
398 PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE,
399 -1, 0);
400 if (stackaddr == MAP_FAILED)
401 mthread_panic("Failed to allocate stack to thread");
403 #if defined(__i386__) || defined(__arm__)
404 guard_start = stackaddr;
405 guard_end = stackaddr + MTHREAD_GUARDSIZE;
406 guarded_stacksize = stackaddr + stacksize - guard_end;
408 /* The stack will be used from (stackaddr+stacksize) to stackaddr. That
409 * is, growing downwards. So the "top" of the stack may not grow into
410 * stackaddr+MTHREAD_GUARDSIZE.
412 * +-------+ stackaddr + stacksize
413 * | |
414 * | | |
415 * | \|/ |
416 * | |
417 * +-------+ stackaddr + MTHREAD_GUARDSIZE
418 * | GUARD |
419 * +-------+ stackaddr
421 #else
422 # error "Unsupported platform"
423 #endif
424 stacksize = guarded_stacksize;
425 if (munmap(guard_start, MTHREAD_GUARDSIZE) != 0)
426 mthread_panic("unable to unmap stack space for guard");
427 tcb->m_context.uc_stack.ss_sp = guard_end;
428 } else
429 tcb->m_context.uc_stack.ss_sp = stackaddr;
431 tcb->m_context.uc_stack.ss_size = stacksize;
432 makecontext(&(tcb->m_context), mthread_trampoline, 0);
434 mthread_unsuspend(thread); /* Make thread runnable */
438 /*===========================================================================*
439 * mthread_thread_reset *
440 *===========================================================================*/
441 void mthread_thread_reset(thread)
442 mthread_thread_t thread;
444 /* Reset the thread to its default values. Free the allocated stack space. */
446 mthread_tcb_t *rt;
447 if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
449 rt = mthread_find_tcb(thread);
450 rt->m_tid = thread;
451 rt->m_next = NULL;
452 rt->m_state = MS_DEAD;
453 rt->m_proc = NULL;
454 rt->m_arg = NULL;
455 rt->m_result = NULL;
456 rt->m_cond = NULL;
457 if (rt->m_attr.ma_stackaddr == NULL) { /* We allocated stack space */
458 if (rt->m_context.uc_stack.ss_sp) {
459 if (munmap(rt->m_context.uc_stack.ss_sp,
460 rt->m_context.uc_stack.ss_size) != 0) {
461 mthread_panic("unable to unmap memory");
464 rt->m_context.uc_stack.ss_sp = NULL;
466 rt->m_context.uc_stack.ss_size = 0;
467 rt->m_context.uc_link = NULL;
471 /*===========================================================================*
472 * mthread_thread_stop *
473 *===========================================================================*/
474 static void mthread_thread_stop(thread)
475 mthread_thread_t thread;
477 /* Stop thread from running. Deallocate resources. */
478 mthread_tcb_t *stop_thread;
480 if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
482 stop_thread = mthread_find_tcb(thread);
484 if (stop_thread->m_state == MS_DEAD) {
485 /* Already dead, nothing to do */
486 return;
489 if (mthread_cond_destroy(&(stop_thread->m_exited)) != 0)
490 mthread_panic("Could not destroy condition at thread deallocation\n");
492 /* Can't deallocate ourselves (i.e., we're a detached thread) */
493 if (thread == current_thread) {
494 stop_thread->m_state = MS_NEEDRESET;
495 need_reset++;
496 } else {
497 mthread_thread_reset(thread);
498 used_threads--;
499 mthread_queue_add(&free_threads, thread);
504 /*===========================================================================*
505 * mthread_trampoline *
506 *===========================================================================*/
507 static void mthread_trampoline(void)
509 /* Execute the /current_thread's/ procedure. Store its result. */
511 mthread_tcb_t *tcb;
512 void *r;
514 tcb = mthread_find_tcb(current_thread);
516 r = (tcb->m_proc)(tcb->m_arg);
517 mthread_exit(r);
520 /* pthread compatibility layer. */
521 __weak_alias(pthread_create, mthread_create)
522 __weak_alias(pthread_detach, mthread_detach)
523 __weak_alias(pthread_equal, mthread_equal)
524 __weak_alias(pthread_exit, mthread_exit)
525 __weak_alias(pthread_join, mthread_join)
526 __weak_alias(pthread_once, mthread_once)
527 __weak_alias(pthread_self, mthread_self)