xref: /minix3/minix/lib/libmthread/allocate.c (revision dd41186aac5f9c05e657f127b7e5d33f375d1686)
1 #define ALLOCATE
2 #include <errno.h>
3 #include <minix/mthread.h>
4 #include <string.h>
5 
6 #include <machine/param.h>
7 #include <machine/vmparam.h>
8 
9 #include <sys/mman.h>
10 
11 #include <uvm/uvm_param.h>
12 
13 #include "global.h"
14 #include "proto.h"
15 
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);
19 
20 static void mthread_thread_stop(mthread_thread_t thread);
21 static void mthread_trampoline(void);
22 
23 static int initialized = 0;
24 #define MTHREAD_GUARDSIZE 	(1 << PGSHIFT) 	/* 1 page */
25 
26 static struct __mthread_attr default_attr = {	MTHREAD_STACK_MIN,
27 						NULL,
28 						MTHREAD_CREATE_JOINABLE,
29 						NULL, NULL };
30 
31 /*===========================================================================*
32  *				mthread_equal				     *
33  *===========================================================================*/
34 int mthread_equal(l, r)
35 mthread_thread_t l;
36 mthread_thread_t r;
37 {
38 /* Compare two thread ids */
39 
40   return(l == r);
41 }
42 
43 
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;
52 {
53 /* Register procedure proc for execution in a thread. */
54   mthread_thread_t thread;
55 
56   if (proc == NULL)
57 	return(EINVAL);
58 
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);
72 
73   	return mthread_create(threadid, tattr, proc, arg);
74   }
75 }
76 
77 
78 /*===========================================================================*
79  *				mthread_detach				     *
80  *===========================================================================*/
81 int mthread_detach(detach)
82 mthread_thread_t detach;
83 {
84 /* Mark a thread as detached. Consequently, upon exit, resources allocated for
85  * this thread are automatically freed.
86  */
87   mthread_tcb_t *tcb;
88 
89   if (!isokthreadid(detach))
90   	return(ESRCH);
91 
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;
100   }
101 
102   return(0);
103 }
104 
105 
106 /*===========================================================================*
107  *				mthread_exit				     *
108  *===========================================================================*/
109 void mthread_exit(value)
110 void *value;
111 {
112 /* Make a thread stop running and store the result value. */
113   mthread_tcb_t *tcb;
114 
115   tcb = mthread_find_tcb(current_thread);
116 
117   if (tcb->m_state == MS_EXITING)	/* Already stopping, nothing to do. */
118   	return;
119 
120   mthread_cleanup_values();
121 
122   tcb->m_result = value;
123   tcb->m_state = MS_EXITING;
124 
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");
131 
132 	/* The thread that's actually doing the join will eventually clean
133 	 * up this thread (i.e., call mthread_thread_stop).
134 	 */
135   }
136 
137   mthread_schedule();
138 }
139 
140 /*===========================================================================*
141  *			mthread_find_tcb				     *
142  *===========================================================================*/
143 mthread_tcb_t * mthread_find_tcb(thread)
144 mthread_thread_t thread;
145 {
146   mthread_tcb_t *rt = NULL;
147 
148   if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
149 
150   if (thread == MAIN_THREAD)
151   	rt = &mainthread;
152   else
153   	rt = threads[thread];
154 
155   return(rt);
156 }
157 
158 
159 /*===========================================================================*
160  *			mthread_increase_thread_pool			     *
161  *===========================================================================*/
162 static int mthread_increase_thread_pool(void)
163 {
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;
167 
168   old_no_threads = no_threads;
169 
170   if (old_no_threads == 0)
171   	new_no_threads = NO_THREADS;
172   else
173 	new_no_threads = 2 * old_no_threads;
174 
175 
176   if (new_no_threads >= MAX_THREAD_POOL) {
177   	mthread_debug("Reached max number of threads");
178   	return(-1);
179   }
180 
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 *));
186 
187   if (new_tcb == NULL) {
188   	mthread_debug("Can't increase thread pool");
189   	return(-1);
190   }
191 
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);
208   	}
209   	memset(new_tcb[i], '\0', sizeof(mthread_tcb_t)); /* Clear entry */
210   }
211 
212   /* We can breathe again, let's tell the others about the good news */
213   threads = new_tcb;
214   no_threads = new_no_threads;
215 
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);
220   }
221 
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);
227 }
228 
229 
230 /*===========================================================================*
231  *				mthread_init				     *
232  *===========================================================================*/
233 static void __attribute__((__constructor__, __used__)) mthread_init(void)
234 {
235 /* Initialize thread system; allocate thread structures and start creating
236  * threads.
237  */
238 
239   if (initialized) return;
240 
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.
247 				 */
248 
249   if (mthread_getcontext(&(mainthread.m_context)) == -1)
250 	mthread_panic("Couldn't save state for main thread");
251   current_thread = MAIN_THREAD;
252 
253   mthread_init_valid_mutexes();
254   mthread_init_valid_conditions();
255   mthread_init_valid_attributes();
256   mthread_init_keys();
257   mthread_init_scheduler();
258 
259   initialized = 1;
260 }
261 
262 
263 /*===========================================================================*
264  *				mthread_join				     *
265  *===========================================================================*/
266 int mthread_join(join, value)
267 mthread_thread_t join;
268 void **value;
269 {
270 /* Wait for a thread to stop running and copy the result. */
271 
272   mthread_tcb_t *tcb;
273 
274   if (!isokthreadid(join))
275   	return(ESRCH);
276   else if (join == current_thread)
277 	return(EDEADLK);
278 
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);
284 
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;
289 
290   	c = &(tcb->m_exited);
291   	m = &(tcb->m_exitm);
292 
293   	if (mthread_mutex_init(m, NULL) != 0)
294 		mthread_panic("Couldn't initialize mutex to join\n");
295 
296 	if (mthread_mutex_lock(m) != 0)
297 		mthread_panic("Couldn't lock mutex to join\n");
298 
299 	if (mthread_cond_wait(c, m) != 0)
300 		mthread_panic("Couldn't wait for join condition\n");
301 
302 	if (mthread_mutex_unlock(m) != 0)
303 		mthread_panic("Couldn't unlock mutex to join\n");
304 
305 	if (mthread_mutex_destroy(m) != 0)
306 		mthread_panic("Couldn't destroy mutex to join\n");
307   }
308 
309   /* Thread has exited; copy results */
310   if(value != NULL)
311 	*value = tcb->m_result;
312 
313   /* Deallocate resources */
314   mthread_thread_stop(join);
315   return(0);
316 }
317 
318 
319 /*===========================================================================*
320  *				mthread_once				     *
321  *===========================================================================*/
322 int mthread_once(once, proc)
323 mthread_once_t *once;
324 void (*proc)(void);
325 {
326 /* Run procedure proc just once */
327 
328   if (once == NULL || proc == NULL)
329   	return(EINVAL);
330 
331   if (*once != 1) proc();
332   *once = 1;
333   return(0);
334 }
335 
336 
337 /*===========================================================================*
338  *				mthread_self				     *
339  *===========================================================================*/
340 mthread_thread_t mthread_self(void)
341 {
342 /* Return the thread id of the thread calling this function. */
343 
344   return(current_thread);
345 }
346 
347 
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;
356 {
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.
359  */
360 
361 #define THIS_CTX (&(threads[thread]->m_context))
362   mthread_tcb_t *tcb;
363   size_t stacksize;
364   char *stackaddr;
365 
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.
374    */
375   if (tattr != NULL)
376   	tcb->m_attr = *((struct __mthread_attr *) *tattr);
377   else {
378   	tcb->m_attr = default_attr;
379   }
380 
381   if (mthread_cond_init(&(tcb->m_exited), NULL) != 0)
382   	mthread_panic("Could not initialize thread");
383 
384   tcb->m_context.uc_link = NULL;
385 
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");
389 
390   stacksize = tcb->m_attr.ma_stacksize;
391   stackaddr = tcb->m_attr.ma_stackaddr;
392 
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.
396 	 */
397 	stacksize = (size_t) MTHREAD_STACK_MIN;
398 	tcb->m_attr.ma_stackaddr = stackaddr = NULL;
399   }
400 
401   if (stackaddr == NULL) {
402 	/* Allocate stack space */
403 	size_t guarded_stacksize;
404 	char *guard_start, *guard_end;
405 
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");
412 
413 #if defined(__i386__) || defined(__arm__)
414 	guard_start = stackaddr;
415 	guard_end = stackaddr + MTHREAD_GUARDSIZE;
416 	guarded_stacksize = stackaddr + stacksize - guard_end;
417 
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.
421 	 *
422 	 * +-------+ stackaddr + stacksize
423 	 * |       |
424 	 * |   |   |
425 	 * |  \|/  |
426 	 * |       |
427 	 * +-------+ stackaddr + MTHREAD_GUARDSIZE
428 	 * | GUARD |
429 	 * +-------+ stackaddr
430 	 */
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;
442 
443   tcb->m_context.uc_stack.ss_size = stacksize;
444   makecontext(&(tcb->m_context), mthread_trampoline, 0);
445 
446   mthread_unsuspend(thread); /* Make thread runnable */
447 }
448 
449 
450 /*===========================================================================*
451  *				mthread_thread_reset			     *
452  *===========================================================================*/
453 void mthread_thread_reset(thread)
454 mthread_thread_t thread;
455 {
456 /* Reset the thread to its default values. Free the allocated stack space. */
457 
458   mthread_tcb_t *rt;
459   size_t stacksize;
460   char *stackaddr;
461 
462   if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
463 
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");
484 		}
485 	}
486 	rt->m_context.uc_stack.ss_sp = NULL;
487   }
488   rt->m_context.uc_stack.ss_size = 0;
489   rt->m_context.uc_link = NULL;
490 }
491 
492 
493 /*===========================================================================*
494  *				mthread_thread_stop			     *
495  *===========================================================================*/
496 static void mthread_thread_stop(thread)
497 mthread_thread_t thread;
498 {
499 /* Stop thread from running. Deallocate resources. */
500   mthread_tcb_t *stop_thread;
501 
502   if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
503 
504   stop_thread = mthread_find_tcb(thread);
505 
506   if (stop_thread->m_state == MS_DEAD) {
507   	/* Already dead, nothing to do */
508   	return;
509   }
510 
511   if (mthread_cond_destroy(&(stop_thread->m_exited)) != 0)
512   	mthread_panic("Could not destroy condition at thread deallocation\n");
513 
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);
522   }
523 }
524 
525 
526 /*===========================================================================*
527  *				mthread_trampoline			     *
528  *===========================================================================*/
529 static void mthread_trampoline(void)
530 {
531 /* Execute the /current_thread's/ procedure. Store its result. */
532 
533   mthread_tcb_t *tcb;
534   void *r;
535 
536   tcb = mthread_find_tcb(current_thread);
537 
538   r = (tcb->m_proc)(tcb->m_arg);
539   mthread_exit(r);
540 }
541 
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)
550 
551