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