3 #include <minix/mthread.h>
6 #include <machine/param.h>
7 #include <machine/vmparam.h>
11 #include <uvm/uvm_param.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
,
28 MTHREAD_CREATE_JOINABLE
,
31 /*===========================================================================*
33 *===========================================================================*/
34 int mthread_equal(l
, r
)
38 /* Compare two thread ids */
44 /*===========================================================================*
46 *===========================================================================*/
47 int mthread_create(threadid
, tattr
, proc
, arg
)
48 mthread_thread_t
*threadid
;
49 mthread_attr_t
*tattr
;
50 void *(*proc
)(void *);
53 /* Register procedure proc for execution in a thread. */
54 mthread_thread_t thread
;
59 if (!mthread_queue_isempty(&free_threads
)) {
60 thread
= mthread_queue_remove(&free_threads
);
61 mthread_thread_init(thread
, tattr
, proc
, arg
);
64 *threadid
= (mthread_thread_t
) thread
;
66 printf("Inited thread %d\n", thread
);
70 if (mthread_increase_thread_pool() == -1)
73 return mthread_create(threadid
, tattr
, proc
, arg
);
78 /*===========================================================================*
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.
89 if (!isokthreadid(detach
))
92 tcb
= mthread_find_tcb(detach
);
93 if (tcb
->m_state
== MS_DEAD
) {
95 } else if (tcb
->m_attr
.ma_detachstate
!= MTHREAD_CREATE_DETACHED
) {
96 if (tcb
->m_state
== MS_EXITING
)
97 mthread_thread_stop(detach
);
99 tcb
->m_attr
.ma_detachstate
= MTHREAD_CREATE_DETACHED
;
106 /*===========================================================================*
108 *===========================================================================*/
109 void mthread_exit(value
)
112 /* Make a thread stop running and store the result value. */
115 tcb
= mthread_find_tcb(current_thread
);
117 if (tcb
->m_state
== MS_EXITING
) /* Already stopping, nothing to do. */
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
);
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).
140 /*===========================================================================*
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
)
153 rt
= threads
[thread
];
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
;
173 new_no_threads
= 2 * old_no_threads
;
176 if (new_no_threads
>= MAX_THREAD_POOL
) {
177 mthread_debug("Reached max number of threads");
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");
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
)
200 if (old_no_threads
> 0) {
201 new_tcb
= realloc(threads
, old_no_threads
*
202 sizeof(mthread_tcb_t
*));
204 mthread_panic("Unable to shrink tcb array");
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 */
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
);
223 printf("Increased thread pool from %d to %d threads\n", old_no_threads
,
230 /*===========================================================================*
232 *===========================================================================*/
233 static void __attribute__((__constructor__
, __used__
)) mthread_init(void)
235 /* Initialize thread system; allocate thread structures and start creating
239 if (initialized
) return;
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();
257 mthread_init_scheduler();
263 /*===========================================================================*
265 *===========================================================================*/
266 int mthread_join(join
, value
)
267 mthread_thread_t join
;
270 /* Wait for a thread to stop running and copy the result. */
274 if (!isokthreadid(join
))
276 else if (join
== current_thread
)
279 tcb
= mthread_find_tcb(join
);
280 if (tcb
->m_state
== MS_DEAD
)
282 else if (tcb
->m_attr
.ma_detachstate
== MTHREAD_CREATE_DETACHED
)
285 /* When the thread hasn't exited yet, we have to wait for that to happen */
286 if (tcb
->m_state
!= MS_EXITING
) {
290 c
= &(tcb
->m_exited
);
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 */
311 *value
= tcb
->m_result
;
313 /* Deallocate resources */
314 mthread_thread_stop(join
);
319 /*===========================================================================*
321 *===========================================================================*/
322 int mthread_once(once
, proc
)
323 mthread_once_t
*once
;
326 /* Run procedure proc just once */
328 if (once
== NULL
|| proc
== NULL
)
331 if (*once
!= 1) proc();
337 /*===========================================================================*
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 *);
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))
366 tcb
= mthread_find_tcb(thread
);
368 tcb
->m_state
= MS_DEAD
;
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.
376 tcb
->m_attr
= *((struct __mthread_attr
*) *tattr
);
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
,
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
427 * +-------+ stackaddr + MTHREAD_GUARDSIZE
429 * +-------+ stackaddr
432 # error "Unsupported platform"
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
;
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. */
462 if (!isokthreadid(thread
)) mthread_panic("Invalid thread id");
464 rt
= mthread_find_tcb(thread
);
467 rt
->m_state
= MS_DEAD
;
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
;
480 # error "Unsupported platform"
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 */
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
;
519 mthread_thread_reset(thread
);
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. */
536 tcb
= mthread_find_tcb(current_thread
);
538 r
= (tcb
->m_proc
)(tcb
->m_arg
);
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
)