ldivmod, uldivmod: fix qdivrem calls
[minix.git] / lib / libmthread / allocate.c
blobd3f02a3463851ca3e2e8d609425f3a9fc7e8c4ea
1 #define ALLOCATE
2 #include <errno.h>
3 #include <minix/mthread.h>
4 #include <string.h>
5 #include <machine/param.h>
6 #include <sys/mman.h>
7 #include "global.h"
8 #include "proto.h"
10 static int mthread_increase_thread_pool(void);
11 static void mthread_thread_init(mthread_thread_t thread, mthread_attr_t
12 *tattr, void *(*proc)(void *), void *arg);
14 static void mthread_thread_stop(mthread_thread_t thread);
15 static void mthread_trampoline(void);
17 static int initialized = 0;
18 #ifndef PGSHIFT
19 # define PGSHIFT 12 /* XXX: temporarily for ACK */
20 #endif
21 #define MTHREAD_GUARDSIZE (1 << PGSHIFT) /* 1 page */
23 static struct __mthread_attr default_attr = { MTHREAD_STACK_MIN,
24 NULL,
25 MTHREAD_CREATE_JOINABLE,
26 NULL, NULL };
28 /*===========================================================================*
29 * mthread_equal *
30 *===========================================================================*/
31 int mthread_equal(l, r)
32 mthread_thread_t l;
33 mthread_thread_t r;
35 /* Compare two thread ids */
36 MTHREAD_CHECK_INIT(); /* Make sure mthreads is initialized */
38 return(l == r);
42 /*===========================================================================*
43 * mthread_create *
44 *===========================================================================*/
45 int mthread_create(threadid, tattr, proc, arg)
46 mthread_thread_t *threadid;
47 mthread_attr_t *tattr;
48 void *(*proc)(void *);
49 void *arg;
51 /* Register procedure proc for execution in a thread. */
52 mthread_thread_t thread;
54 MTHREAD_CHECK_INIT(); /* Make sure mthreads is initialized */
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;
88 MTHREAD_CHECK_INIT(); /* Make sure libmthread is initialized */
90 if (!isokthreadid(detach))
91 return(ESRCH);
93 tcb = mthread_find_tcb(detach);
94 if (tcb->m_state == MS_DEAD) {
95 return(ESRCH);
96 } else if (tcb->m_attr.ma_detachstate != MTHREAD_CREATE_DETACHED) {
97 if (tcb->m_state == MS_EXITING)
98 mthread_thread_stop(detach);
99 else
100 tcb->m_attr.ma_detachstate = MTHREAD_CREATE_DETACHED;
103 return(0);
107 /*===========================================================================*
108 * mthread_exit *
109 *===========================================================================*/
110 void mthread_exit(value)
111 void *value;
113 /* Make a thread stop running and store the result value. */
114 mthread_tcb_t *tcb;
116 MTHREAD_CHECK_INIT(); /* Make sure libmthread is initialized */
118 tcb = mthread_find_tcb(current_thread);
120 if (tcb->m_state == MS_EXITING) /* Already stopping, nothing to do. */
121 return;
123 mthread_cleanup_values();
125 tcb->m_result = value;
126 tcb->m_state = MS_EXITING;
128 if (tcb->m_attr.ma_detachstate == MTHREAD_CREATE_DETACHED) {
129 mthread_thread_stop(current_thread);
130 } else {
131 /* Joinable thread; notify possibly waiting thread */
132 if (mthread_cond_signal(&(tcb->m_exited)) != 0)
133 mthread_panic("Couldn't signal exit");
135 /* The thread that's actually doing the join will eventually clean
136 * up this thread (i.e., call mthread_thread_stop).
140 mthread_schedule();
143 /*===========================================================================*
144 * mthread_find_tcb *
145 *===========================================================================*/
146 mthread_tcb_t * mthread_find_tcb(thread)
147 mthread_thread_t thread;
149 mthread_tcb_t *rt = NULL;
151 if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
153 if (thread == MAIN_THREAD)
154 rt = &mainthread;
155 else
156 rt = threads[thread];
158 return(rt);
162 /*===========================================================================*
163 * mthread_increase_thread_pool *
164 *===========================================================================*/
165 static int mthread_increase_thread_pool(void)
167 /* Increase thread pool. No fancy algorithms, just double the size. */
168 mthread_tcb_t **new_tcb;
169 int new_no_threads, old_no_threads, i;
171 old_no_threads = no_threads;
173 if (old_no_threads == 0)
174 new_no_threads = NO_THREADS;
175 else
176 new_no_threads = 2 * old_no_threads;
179 if (new_no_threads >= MAX_THREAD_POOL) {
180 mthread_debug("Reached max number of threads");
181 return(-1);
184 /* Allocate space to store pointers to thread control blocks */
185 if (old_no_threads == 0) /* No data yet: allocate space */
186 new_tcb = calloc(new_no_threads, sizeof(mthread_tcb_t *));
187 else /* Preserve existing data: reallocate space */
188 new_tcb = realloc(threads, new_no_threads * sizeof(mthread_tcb_t *));
190 if (new_tcb == NULL) {
191 mthread_debug("Can't increase thread pool");
192 return(-1);
195 /* Allocate space for thread control blocks itself */
196 for (i = old_no_threads; i < new_no_threads; i++) {
197 new_tcb[i] = malloc(sizeof(mthread_tcb_t));
198 if (new_tcb[i] == NULL) {
199 mthread_debug("Can't allocate space for tcb");
200 return(-1);
202 memset(new_tcb[i], '\0', sizeof(mthread_tcb_t)); /* Clear entry */
205 /* We can breath again, let's tell the others about the good news */
206 threads = new_tcb;
207 no_threads = new_no_threads;
209 /* Add newly available threads to free_threads */
210 for (i = old_no_threads; i < new_no_threads; i++) {
211 mthread_queue_add(&free_threads, i);
212 mthread_thread_reset(i);
215 #ifdef MDEBUG
216 printf("Increased thread pool from %d to %d threads\n", old_no_threads,
217 new_no_threads);
218 #endif
219 return(0);
223 /*===========================================================================*
224 * mthread_init *
225 *===========================================================================*/
226 void mthread_init(void)
228 /* Initialize thread system; allocate thread structures and start creating
229 * threads.
232 if (initialized) return;
234 no_threads = 0;
235 used_threads = 0;
236 need_reset = 0;
237 running_main_thread = 1; /* mthread_init can only be called from the
238 * main thread. Calling it from a thread will
239 * not enter this clause.
242 if (mthread_getcontext(&(mainthread.m_context)) == -1)
243 mthread_panic("Couldn't save state for main thread");
244 current_thread = MAIN_THREAD;
246 mthread_init_valid_mutexes();
247 mthread_init_valid_conditions();
248 mthread_init_valid_attributes();
249 mthread_init_keys();
250 mthread_init_scheduler();
252 initialized = 1;
256 /*===========================================================================*
257 * mthread_join *
258 *===========================================================================*/
259 int mthread_join(join, value)
260 mthread_thread_t join;
261 void **value;
263 /* Wait for a thread to stop running and copy the result. */
265 mthread_tcb_t *tcb;
267 MTHREAD_CHECK_INIT(); /* Make sure libmthread is initialized */
269 if (!isokthreadid(join))
270 return(ESRCH);
271 else if (join == current_thread)
272 return(EDEADLK);
274 tcb = mthread_find_tcb(join);
275 if (tcb->m_state == MS_DEAD)
276 return(ESRCH);
277 else if (tcb->m_attr.ma_detachstate == MTHREAD_CREATE_DETACHED)
278 return(EINVAL);
280 /* When the thread hasn't exited yet, we have to wait for that to happen */
281 if (tcb->m_state != MS_EXITING) {
282 mthread_cond_t *c;
283 mthread_mutex_t *m;
285 c = &(tcb->m_exited);
286 m = &(tcb->m_exitm);
288 if (mthread_mutex_init(m, NULL) != 0)
289 mthread_panic("Couldn't initialize mutex to join\n");
291 if (mthread_mutex_lock(m) != 0)
292 mthread_panic("Couldn't lock mutex to join\n");
294 if (mthread_cond_wait(c, m) != 0)
295 mthread_panic("Couldn't wait for join condition\n");
297 if (mthread_mutex_unlock(m) != 0)
298 mthread_panic("Couldn't unlock mutex to join\n");
300 if (mthread_mutex_destroy(m) != 0)
301 mthread_panic("Couldn't destroy mutex to join\n");
304 /* Thread has exited; copy results */
305 if(value != NULL)
306 *value = tcb->m_result;
308 /* Deallocate resources */
309 mthread_thread_stop(join);
310 return(0);
314 /*===========================================================================*
315 * mthread_once *
316 *===========================================================================*/
317 int mthread_once(once, proc)
318 mthread_once_t *once;
319 void (*proc)(void);
321 /* Run procedure proc just once */
323 MTHREAD_CHECK_INIT(); /* Make sure libmthread is initialized */
325 if (once == NULL || proc == NULL)
326 return(EINVAL);
328 if (*once != 1) proc();
329 *once = 1;
330 return(0);
334 /*===========================================================================*
335 * mthread_self *
336 *===========================================================================*/
337 mthread_thread_t mthread_self(void)
339 /* Return the thread id of the thread calling this function. */
341 MTHREAD_CHECK_INIT(); /* Make sure libmthread is initialized */
343 return(current_thread);
347 /*===========================================================================*
348 * mthread_thread_init *
349 *===========================================================================*/
350 static void mthread_thread_init(thread, tattr, proc, arg)
351 mthread_thread_t thread;
352 mthread_attr_t *tattr;
353 void *(*proc)(void *);
354 void *arg;
356 /* Initialize a thread so that it, when unsuspended, will run the given
357 * procedure with the given parameter. The thread is marked as runnable.
360 #define THIS_CTX (&(threads[thread]->m_context))
361 mthread_tcb_t *tcb;
362 size_t stacksize;
363 char *stackaddr;
365 tcb = mthread_find_tcb(thread);
366 tcb->m_next = NULL;
367 tcb->m_state = MS_DEAD;
368 tcb->m_proc = proc;
369 tcb->m_arg = arg;
370 /* Threads use a copy of the provided attributes. This way, if another
371 * thread modifies the attributes (such as detach state), already running
372 * threads are not affected.
374 if (tattr != NULL)
375 tcb->m_attr = *((struct __mthread_attr *) *tattr);
376 else {
377 tcb->m_attr = default_attr;
380 if (mthread_cond_init(&(tcb->m_exited), NULL) != 0)
381 mthread_panic("Could not initialize thread");
383 tcb->m_context.uc_link = NULL;
385 /* Construct this thread's context to run procedure proc. */
386 if (mthread_getcontext(&(tcb->m_context)) == -1)
387 mthread_panic("Failed to initialize context state");
389 stacksize = tcb->m_attr.ma_stacksize;
390 stackaddr = tcb->m_attr.ma_stackaddr;
392 if (stacksize == (size_t) 0) {
393 /* User provided too small a stack size. Forget about that stack and
394 * allocate a new one ourselves.
396 stacksize = (size_t) MTHREAD_STACK_MIN;
397 tcb->m_attr.ma_stackaddr = stackaddr = NULL;
400 if (stackaddr == NULL) {
401 /* Allocate stack space */
402 size_t guarded_stacksize;
403 char *guard_start, *guard_end;
405 stacksize = round_page(stacksize + MTHREAD_GUARDSIZE);
406 stackaddr = minix_mmap(NULL, stacksize,
407 PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE,
408 -1, 0);
409 if (stackaddr == MAP_FAILED)
410 mthread_panic("Failed to allocate stack to thread");
412 #if defined(__i386__) || defined(__arm__)
413 guard_start = stackaddr;
414 guard_end = stackaddr + MTHREAD_GUARDSIZE;
415 guarded_stacksize = stackaddr + stacksize - guard_end;
417 /* The stack will be used from (stackaddr+stacksize) to stackaddr. That
418 * is, growing downwards. So the "top" of the stack may not grow into
419 * stackaddr+MTHREAD_GUARDSIZE.
421 * +-------+ stackaddr + stacksize
422 * | |
423 * | | |
424 * | \|/ |
425 * | |
426 * +-------+ stackaddr + MTHREAD_GUARDSIZE
427 * | GUARD |
428 * +-------+ stackaddr
430 #else
431 # error "Unsupported platform"
432 #endif
433 stacksize = guarded_stacksize;
434 if (minix_munmap(guard_start, MTHREAD_GUARDSIZE) != 0)
435 mthread_panic("unable to unmap stack space for guard");
436 tcb->m_context.uc_stack.ss_sp = guard_end;
437 } else
438 tcb->m_context.uc_stack.ss_sp = stackaddr;
440 tcb->m_context.uc_stack.ss_size = stacksize;
441 makecontext(&(tcb->m_context), mthread_trampoline, 0);
443 mthread_unsuspend(thread); /* Make thread runnable */
447 /*===========================================================================*
448 * mthread_thread_reset *
449 *===========================================================================*/
450 void mthread_thread_reset(thread)
451 mthread_thread_t thread;
453 /* Reset the thread to its default values. Free the allocated stack space. */
455 mthread_tcb_t *rt;
456 if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
458 rt = mthread_find_tcb(thread);
459 rt->m_tid = thread;
460 rt->m_next = NULL;
461 rt->m_state = MS_DEAD;
462 rt->m_proc = NULL;
463 rt->m_arg = NULL;
464 rt->m_result = NULL;
465 rt->m_cond = NULL;
466 if (rt->m_attr.ma_stackaddr == NULL) { /* We allocated stack space */
467 if (rt->m_context.uc_stack.ss_sp) {
468 if (minix_munmap(rt->m_context.uc_stack.ss_sp,
469 rt->m_context.uc_stack.ss_size) != 0) {
470 mthread_panic("unable to unmap memory");
473 rt->m_context.uc_stack.ss_sp = NULL;
475 rt->m_context.uc_stack.ss_size = 0;
476 rt->m_context.uc_link = NULL;
480 /*===========================================================================*
481 * mthread_thread_stop *
482 *===========================================================================*/
483 static void mthread_thread_stop(thread)
484 mthread_thread_t thread;
486 /* Stop thread from running. Deallocate resources. */
487 mthread_tcb_t *stop_thread;
489 if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
491 stop_thread = mthread_find_tcb(thread);
493 if (stop_thread->m_state == MS_DEAD) {
494 /* Already dead, nothing to do */
495 return;
498 if (mthread_cond_destroy(&(stop_thread->m_exited)) != 0)
499 mthread_panic("Could not destroy condition at thread deallocation\n");
501 /* Can't deallocate ourselves (i.e., we're a detached thread) */
502 if (thread == current_thread) {
503 stop_thread->m_state = MS_NEEDRESET;
504 need_reset++;
505 } else {
506 mthread_thread_reset(thread);
507 used_threads--;
508 mthread_queue_add(&free_threads, thread);
513 /*===========================================================================*
514 * mthread_trampoline *
515 *===========================================================================*/
516 static void mthread_trampoline(void)
518 /* Execute the /current_thread's/ procedure. Store its result. */
520 mthread_tcb_t *tcb;
521 void *r;
523 tcb = mthread_find_tcb(current_thread);
525 r = (tcb->m_proc)(tcb->m_arg);
526 mthread_exit(r);