3 #include <minix/mthread.h>
5 #include <machine/param.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;
19 # define PGSHIFT 12 /* XXX: temporarily for ACK */
21 #define MTHREAD_GUARDSIZE (1 << PGSHIFT) /* 1 page */
23 static struct __mthread_attr default_attr
= { MTHREAD_STACK_MIN
,
25 MTHREAD_CREATE_JOINABLE
,
28 /*===========================================================================*
30 *===========================================================================*/
31 int mthread_equal(l
, r
)
35 /* Compare two thread ids */
36 MTHREAD_CHECK_INIT(); /* Make sure mthreads is initialized */
42 /*===========================================================================*
44 *===========================================================================*/
45 int mthread_create(threadid
, tattr
, proc
, arg
)
46 mthread_thread_t
*threadid
;
47 mthread_attr_t
*tattr
;
48 void *(*proc
)(void *);
51 /* Register procedure proc for execution in a thread. */
52 mthread_thread_t thread
;
54 MTHREAD_CHECK_INIT(); /* Make sure mthreads is initialized */
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.
88 MTHREAD_CHECK_INIT(); /* Make sure libmthread is initialized */
90 if (!isokthreadid(detach
))
93 tcb
= mthread_find_tcb(detach
);
94 if (tcb
->m_state
== MS_DEAD
) {
96 } else if (tcb
->m_attr
.ma_detachstate
!= MTHREAD_CREATE_DETACHED
) {
97 if (tcb
->m_state
== MS_EXITING
)
98 mthread_thread_stop(detach
);
100 tcb
->m_attr
.ma_detachstate
= MTHREAD_CREATE_DETACHED
;
107 /*===========================================================================*
109 *===========================================================================*/
110 void mthread_exit(value
)
113 /* Make a thread stop running and store the result value. */
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. */
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
);
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).
143 /*===========================================================================*
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
)
156 rt
= threads
[thread
];
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
;
176 new_no_threads
= 2 * old_no_threads
;
179 if (new_no_threads
>= MAX_THREAD_POOL
) {
180 mthread_debug("Reached max number of threads");
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");
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");
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 */
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
);
216 printf("Increased thread pool from %d to %d threads\n", old_no_threads
,
223 /*===========================================================================*
225 *===========================================================================*/
226 void mthread_init(void)
228 /* Initialize thread system; allocate thread structures and start creating
232 if (initialized
) return;
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();
250 mthread_init_scheduler();
256 /*===========================================================================*
258 *===========================================================================*/
259 int mthread_join(join
, value
)
260 mthread_thread_t join
;
263 /* Wait for a thread to stop running and copy the result. */
267 MTHREAD_CHECK_INIT(); /* Make sure libmthread is initialized */
269 if (!isokthreadid(join
))
271 else if (join
== current_thread
)
274 tcb
= mthread_find_tcb(join
);
275 if (tcb
->m_state
== MS_DEAD
)
277 else if (tcb
->m_attr
.ma_detachstate
== MTHREAD_CREATE_DETACHED
)
280 /* When the thread hasn't exited yet, we have to wait for that to happen */
281 if (tcb
->m_state
!= MS_EXITING
) {
285 c
= &(tcb
->m_exited
);
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 */
306 *value
= tcb
->m_result
;
308 /* Deallocate resources */
309 mthread_thread_stop(join
);
314 /*===========================================================================*
316 *===========================================================================*/
317 int mthread_once(once
, proc
)
318 mthread_once_t
*once
;
321 /* Run procedure proc just once */
323 MTHREAD_CHECK_INIT(); /* Make sure libmthread is initialized */
325 if (once
== NULL
|| proc
== NULL
)
328 if (*once
!= 1) proc();
334 /*===========================================================================*
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 *);
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))
365 tcb
= mthread_find_tcb(thread
);
367 tcb
->m_state
= MS_DEAD
;
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.
375 tcb
->m_attr
= *((struct __mthread_attr
*) *tattr
);
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
,
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
426 * +-------+ stackaddr + MTHREAD_GUARDSIZE
428 * +-------+ stackaddr
431 # error "Unsupported platform"
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
;
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. */
456 if (!isokthreadid(thread
)) mthread_panic("Invalid thread id");
458 rt
= mthread_find_tcb(thread
);
461 rt
->m_state
= MS_DEAD
;
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 */
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
;
506 mthread_thread_reset(thread
);
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. */
523 tcb
= mthread_find_tcb(current_thread
);
525 r
= (tcb
->m_proc
)(tcb
->m_arg
);