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");
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 */
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
);
213 printf("Increased thread pool from %d to %d threads\n", old_no_threads
,
220 /*===========================================================================*
222 *===========================================================================*/
223 static void __attribute__((__constructor__
, __used__
)) mthread_init(void)
225 /* Initialize thread system; allocate thread structures and start creating
229 if (initialized
) return;
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();
247 mthread_init_scheduler();
253 /*===========================================================================*
255 *===========================================================================*/
256 int mthread_join(join
, value
)
257 mthread_thread_t join
;
260 /* Wait for a thread to stop running and copy the result. */
264 if (!isokthreadid(join
))
266 else if (join
== current_thread
)
269 tcb
= mthread_find_tcb(join
);
270 if (tcb
->m_state
== MS_DEAD
)
272 else if (tcb
->m_attr
.ma_detachstate
== MTHREAD_CREATE_DETACHED
)
275 /* When the thread hasn't exited yet, we have to wait for that to happen */
276 if (tcb
->m_state
!= MS_EXITING
) {
280 c
= &(tcb
->m_exited
);
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 */
301 *value
= tcb
->m_result
;
303 /* Deallocate resources */
304 mthread_thread_stop(join
);
309 /*===========================================================================*
311 *===========================================================================*/
312 int mthread_once(once
, proc
)
313 mthread_once_t
*once
;
316 /* Run procedure proc just once */
318 if (once
== NULL
|| proc
== NULL
)
321 if (*once
!= 1) proc();
327 /*===========================================================================*
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 *);
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))
356 tcb
= mthread_find_tcb(thread
);
358 tcb
->m_state
= MS_DEAD
;
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.
366 tcb
->m_attr
= *((struct __mthread_attr
*) *tattr
);
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
,
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
417 * +-------+ stackaddr + MTHREAD_GUARDSIZE
419 * +-------+ stackaddr
422 # error "Unsupported platform"
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
;
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. */
447 if (!isokthreadid(thread
)) mthread_panic("Invalid thread id");
449 rt
= mthread_find_tcb(thread
);
452 rt
->m_state
= MS_DEAD
;
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 */
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
;
497 mthread_thread_reset(thread
);
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. */
514 tcb
= mthread_find_tcb(current_thread
);
516 r
= (tcb
->m_proc
)(tcb
->m_arg
);
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
)