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 return(-1); 198 } 199 memset(new_tcb[i], '\0', sizeof(mthread_tcb_t)); /* Clear entry */ 200 } 201 202 /* We can breath again, let's tell the others about the good news */ 203 threads = new_tcb; 204 no_threads = new_no_threads; 205 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); 210 } 211 212 #ifdef MDEBUG 213 printf("Increased thread pool from %d to %d threads\n", old_no_threads, 214 new_no_threads); 215 #endif 216 return(0); 217 } 218 219 220 /*===========================================================================* 221 * mthread_init * 222 *===========================================================================*/ 223 static void __attribute__((__constructor__, __used__)) mthread_init(void) 224 { 225 /* Initialize thread system; allocate thread structures and start creating 226 * threads. 227 */ 228 229 if (initialized) return; 230 231 no_threads = 0; 232 used_threads = 0; 233 need_reset = 0; 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. 237 */ 238 239 if (mthread_getcontext(&(mainthread.m_context)) == -1) 240 mthread_panic("Couldn't save state for main thread"); 241 current_thread = MAIN_THREAD; 242 243 mthread_init_valid_mutexes(); 244 mthread_init_valid_conditions(); 245 mthread_init_valid_attributes(); 246 mthread_init_keys(); 247 mthread_init_scheduler(); 248 249 initialized = 1; 250 } 251 252 253 /*===========================================================================* 254 * mthread_join * 255 *===========================================================================*/ 256 int mthread_join(join, value) 257 mthread_thread_t join; 258 void **value; 259 { 260 /* Wait for a thread to stop running and copy the result. */ 261 262 mthread_tcb_t *tcb; 263 264 if (!isokthreadid(join)) 265 return(ESRCH); 266 else if (join == current_thread) 267 return(EDEADLK); 268 269 tcb = mthread_find_tcb(join); 270 if (tcb->m_state == MS_DEAD) 271 return(ESRCH); 272 else if (tcb->m_attr.ma_detachstate == MTHREAD_CREATE_DETACHED) 273 return(EINVAL); 274 275 /* When the thread hasn't exited yet, we have to wait for that to happen */ 276 if (tcb->m_state != MS_EXITING) { 277 mthread_cond_t *c; 278 mthread_mutex_t *m; 279 280 c = &(tcb->m_exited); 281 m = &(tcb->m_exitm); 282 283 if (mthread_mutex_init(m, NULL) != 0) 284 mthread_panic("Couldn't initialize mutex to join\n"); 285 286 if (mthread_mutex_lock(m) != 0) 287 mthread_panic("Couldn't lock mutex to join\n"); 288 289 if (mthread_cond_wait(c, m) != 0) 290 mthread_panic("Couldn't wait for join condition\n"); 291 292 if (mthread_mutex_unlock(m) != 0) 293 mthread_panic("Couldn't unlock mutex to join\n"); 294 295 if (mthread_mutex_destroy(m) != 0) 296 mthread_panic("Couldn't destroy mutex to join\n"); 297 } 298 299 /* Thread has exited; copy results */ 300 if(value != NULL) 301 *value = tcb->m_result; 302 303 /* Deallocate resources */ 304 mthread_thread_stop(join); 305 return(0); 306 } 307 308 309 /*===========================================================================* 310 * mthread_once * 311 *===========================================================================*/ 312 int mthread_once(once, proc) 313 mthread_once_t *once; 314 void (*proc)(void); 315 { 316 /* Run procedure proc just once */ 317 318 if (once == NULL || proc == NULL) 319 return(EINVAL); 320 321 if (*once != 1) proc(); 322 *once = 1; 323 return(0); 324 } 325 326 327 /*===========================================================================* 328 * mthread_self * 329 *===========================================================================*/ 330 mthread_thread_t mthread_self(void) 331 { 332 /* Return the thread id of the thread calling this function. */ 333 334 return(current_thread); 335 } 336 337 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 *); 345 void *arg; 346 { 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. 349 */ 350 351 #define THIS_CTX (&(threads[thread]->m_context)) 352 mthread_tcb_t *tcb; 353 size_t stacksize; 354 char *stackaddr; 355 356 tcb = mthread_find_tcb(thread); 357 tcb->m_next = NULL; 358 tcb->m_state = MS_DEAD; 359 tcb->m_proc = proc; 360 tcb->m_arg = arg; 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. 364 */ 365 if (tattr != NULL) 366 tcb->m_attr = *((struct __mthread_attr *) *tattr); 367 else { 368 tcb->m_attr = default_attr; 369 } 370 371 if (mthread_cond_init(&(tcb->m_exited), NULL) != 0) 372 mthread_panic("Could not initialize thread"); 373 374 tcb->m_context.uc_link = NULL; 375 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"); 379 380 stacksize = tcb->m_attr.ma_stacksize; 381 stackaddr = tcb->m_attr.ma_stackaddr; 382 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. 386 */ 387 stacksize = (size_t) MTHREAD_STACK_MIN; 388 tcb->m_attr.ma_stackaddr = stackaddr = NULL; 389 } 390 391 if (stackaddr == NULL) { 392 /* Allocate stack space */ 393 size_t guarded_stacksize; 394 char *guard_start, *guard_end; 395 396 stacksize = round_page(stacksize + MTHREAD_GUARDSIZE); 397 stackaddr = mmap(NULL, stacksize, 398 PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, 399 -1, 0); 400 if (stackaddr == MAP_FAILED) 401 mthread_panic("Failed to allocate stack to thread"); 402 403 #if defined(__i386__) || defined(__arm__) 404 guard_start = stackaddr; 405 guard_end = stackaddr + MTHREAD_GUARDSIZE; 406 guarded_stacksize = stackaddr + stacksize - guard_end; 407 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. 411 * 412 * +-------+ stackaddr + stacksize 413 * | | 414 * | | | 415 * | \|/ | 416 * | | 417 * +-------+ stackaddr + MTHREAD_GUARDSIZE 418 * | GUARD | 419 * +-------+ stackaddr 420 */ 421 #else 422 # error "Unsupported platform" 423 #endif 424 stacksize = guarded_stacksize; 425 /* Mere unmapping could allow a later allocation to fill the gap. */ 426 if (mmap(guard_start, MTHREAD_GUARDSIZE, PROT_NONE, 427 MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, 0) != guard_start) 428 mthread_panic("unable to overmap stack space for guard"); 429 tcb->m_context.uc_stack.ss_sp = guard_end; 430 } else 431 tcb->m_context.uc_stack.ss_sp = stackaddr; 432 433 tcb->m_context.uc_stack.ss_size = stacksize; 434 makecontext(&(tcb->m_context), mthread_trampoline, 0); 435 436 mthread_unsuspend(thread); /* Make thread runnable */ 437 } 438 439 440 /*===========================================================================* 441 * mthread_thread_reset * 442 *===========================================================================*/ 443 void mthread_thread_reset(thread) 444 mthread_thread_t thread; 445 { 446 /* Reset the thread to its default values. Free the allocated stack space. */ 447 448 mthread_tcb_t *rt; 449 size_t stacksize; 450 char *stackaddr; 451 452 if (!isokthreadid(thread)) mthread_panic("Invalid thread id"); 453 454 rt = mthread_find_tcb(thread); 455 rt->m_tid = thread; 456 rt->m_next = NULL; 457 rt->m_state = MS_DEAD; 458 rt->m_proc = NULL; 459 rt->m_arg = NULL; 460 rt->m_result = NULL; 461 rt->m_cond = NULL; 462 if (rt->m_attr.ma_stackaddr == NULL) { /* We allocated stack space */ 463 if (rt->m_context.uc_stack.ss_sp) { 464 stacksize = rt->m_context.uc_stack.ss_size; 465 stackaddr = rt->m_context.uc_stack.ss_sp; 466 #if defined(__i386__) || defined(__arm__) 467 stacksize += MTHREAD_GUARDSIZE; 468 stackaddr -= MTHREAD_GUARDSIZE; 469 #else 470 # error "Unsupported platform" 471 #endif 472 if (munmap(stackaddr, stacksize) != 0) { 473 mthread_panic("unable to unmap memory"); 474 } 475 } 476 rt->m_context.uc_stack.ss_sp = NULL; 477 } 478 rt->m_context.uc_stack.ss_size = 0; 479 rt->m_context.uc_link = NULL; 480 } 481 482 483 /*===========================================================================* 484 * mthread_thread_stop * 485 *===========================================================================*/ 486 static void mthread_thread_stop(thread) 487 mthread_thread_t thread; 488 { 489 /* Stop thread from running. Deallocate resources. */ 490 mthread_tcb_t *stop_thread; 491 492 if (!isokthreadid(thread)) mthread_panic("Invalid thread id"); 493 494 stop_thread = mthread_find_tcb(thread); 495 496 if (stop_thread->m_state == MS_DEAD) { 497 /* Already dead, nothing to do */ 498 return; 499 } 500 501 if (mthread_cond_destroy(&(stop_thread->m_exited)) != 0) 502 mthread_panic("Could not destroy condition at thread deallocation\n"); 503 504 /* Can't deallocate ourselves (i.e., we're a detached thread) */ 505 if (thread == current_thread) { 506 stop_thread->m_state = MS_NEEDRESET; 507 need_reset++; 508 } else { 509 mthread_thread_reset(thread); 510 used_threads--; 511 mthread_queue_add(&free_threads, thread); 512 } 513 } 514 515 516 /*===========================================================================* 517 * mthread_trampoline * 518 *===========================================================================*/ 519 static void mthread_trampoline(void) 520 { 521 /* Execute the /current_thread's/ procedure. Store its result. */ 522 523 mthread_tcb_t *tcb; 524 void *r; 525 526 tcb = mthread_find_tcb(current_thread); 527 528 r = (tcb->m_proc)(tcb->m_arg); 529 mthread_exit(r); 530 } 531 532 /* pthread compatibility layer. */ 533 __weak_alias(pthread_create, mthread_create) 534 __weak_alias(pthread_detach, mthread_detach) 535 __weak_alias(pthread_equal, mthread_equal) 536 __weak_alias(pthread_exit, mthread_exit) 537 __weak_alias(pthread_join, mthread_join) 538 __weak_alias(pthread_once, mthread_once) 539 __weak_alias(pthread_self, mthread_self) 540 541