int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param) { pthread_handle handle = thread_handle(thread); pthread_descr th; __pthread_lock(&handle->h_lock, NULL); if (invalid_handle(handle, thread)) { __pthread_unlock(&handle->h_lock); return ESRCH; } th = handle->h_descr; if (sched_setscheduler(th->p_pid, policy, param) == -1) { __pthread_unlock(&handle->h_lock); return errno; } th->p_priority = policy == SCHED_OTHER ? 0 : param->sched_priority; __pthread_unlock(&handle->h_lock); if (__pthread_manager_request >= 0) __pthread_manager_adjust_prio(th->p_priority); return 0; }
static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void *), void *arg, sigset_t * mask, int father_pid, int report_events, td_thr_events_t *event_maskp) { size_t sseg; int pid; pthread_descr new_thread; char * new_thread_bottom; char * new_thread_top; pthread_t new_thread_id; char *guardaddr = NULL; size_t guardsize = 0; int pagesize = getpagesize(); int saved_errno = 0; /* First check whether we have to change the policy and if yes, whether we can do this. Normally this should be done by examining the return value of the sched_setscheduler call in pthread_start_thread but this is hard to implement. FIXME */ if (attr != NULL && attr->__schedpolicy != SCHED_OTHER && geteuid () != 0) return EPERM; /* Find a free segment for the thread, and allocate a stack if needed */ for (sseg = 2; ; sseg++) { if (sseg >= PTHREAD_THREADS_MAX) return EAGAIN; if (__pthread_handles[sseg].h_descr != NULL) continue; if (pthread_allocate_stack(attr, thread_segment(sseg), pagesize, &new_thread, &new_thread_bottom, &guardaddr, &guardsize) == 0) break; #ifndef __ARCH_USE_MMU__ else /* When there is MMU, mmap () is used to allocate the stack. If one * segment is already mapped, we should continue to see if we can * use the next one. However, when there is no MMU, malloc () is used. * It's waste of CPU cycles to continue to try if it fails. */ return EAGAIN; #endif } __pthread_handles_num++; /* Allocate new thread identifier */ pthread_threads_counter += PTHREAD_THREADS_MAX; new_thread_id = sseg + pthread_threads_counter; /* Initialize the thread descriptor. Elements which have to be initialized to zero already have this value. */ new_thread->p_tid = new_thread_id; new_thread->p_lock = &(__pthread_handles[sseg].h_lock); new_thread->p_cancelstate = PTHREAD_CANCEL_ENABLE; new_thread->p_canceltype = PTHREAD_CANCEL_DEFERRED; new_thread->p_errnop = &new_thread->p_errno; new_thread->p_h_errnop = &new_thread->p_h_errno; #ifdef __UCLIBC_HAS_XLOCALE__ /* Initialize thread's locale to the global locale. */ new_thread->locale = __global_locale; #endif /* __UCLIBC_HAS_XLOCALE__ */ new_thread->p_guardaddr = guardaddr; new_thread->p_guardsize = guardsize; new_thread->p_self = new_thread; new_thread->p_nr = sseg; /* Initialize the thread handle */ __pthread_init_lock(&__pthread_handles[sseg].h_lock); __pthread_handles[sseg].h_descr = new_thread; __pthread_handles[sseg].h_bottom = new_thread_bottom; /* Determine scheduling parameters for the thread */ new_thread->p_start_args.schedpolicy = -1; if (attr != NULL) { new_thread->p_detached = attr->__detachstate; new_thread->p_userstack = attr->__stackaddr_set; switch(attr->__inheritsched) { case PTHREAD_EXPLICIT_SCHED: new_thread->p_start_args.schedpolicy = attr->__schedpolicy; memcpy (&new_thread->p_start_args.schedparam, &attr->__schedparam, sizeof (struct sched_param)); break; case PTHREAD_INHERIT_SCHED: new_thread->p_start_args.schedpolicy = sched_getscheduler(father_pid); sched_getparam(father_pid, &new_thread->p_start_args.schedparam); break; } new_thread->p_priority = new_thread->p_start_args.schedparam.sched_priority; } /* Finish setting up arguments to pthread_start_thread */ new_thread->p_start_args.start_routine = start_routine; new_thread->p_start_args.arg = arg; new_thread->p_start_args.mask = *mask; /* Raise priority of thread manager if needed */ __pthread_manager_adjust_prio(new_thread->p_priority); /* Do the cloning. We have to use two different functions depending on whether we are debugging or not. */ pid = 0; /* Note that the thread never can have PID zero. */ new_thread_top = ((char *)new_thread - THREAD_STACK_OFFSET); /* ******************************************************** */ /* This code was moved from below to cope with running threads * on uClinux systems. See comment below... * Insert new thread in doubly linked list of active threads */ new_thread->p_prevlive = __pthread_main_thread; new_thread->p_nextlive = __pthread_main_thread->p_nextlive; __pthread_main_thread->p_nextlive->p_prevlive = new_thread; __pthread_main_thread->p_nextlive = new_thread; /* ********************************************************* */ if (report_events) { /* See whether the TD_CREATE event bit is set in any of the masks. */ int idx = __td_eventword (TD_CREATE); uint32_t m = __td_eventmask (TD_CREATE); if ((m & (__pthread_threads_events.event_bits[idx] | event_maskp->event_bits[idx])) != 0) { /* Lock the mutex the child will use now so that it will stop. */ __pthread_lock(new_thread->p_lock, NULL); /* We have to report this event. */ #ifdef __ia64__ pid = __clone2(pthread_start_thread_event, new_thread_top, new_thread_top - new_thread_bottom, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | __pthread_sig_cancel, new_thread); #else pid = clone(pthread_start_thread_event, new_thread_top, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | __pthread_sig_cancel, new_thread); #endif saved_errno = errno; if (pid != -1) { /* Now fill in the information about the new thread in the newly created thread's data structure. We cannot let the new thread do this since we don't know whether it was already scheduled when we send the event. */ new_thread->p_eventbuf.eventdata = new_thread; new_thread->p_eventbuf.eventnum = TD_CREATE; __pthread_last_event = new_thread; /* We have to set the PID here since the callback function in the debug library will need it and we cannot guarantee the child got scheduled before the debugger. */ new_thread->p_pid = pid; /* Now call the function which signals the event. */ __linuxthreads_create_event (); /* Now restart the thread. */ __pthread_unlock(new_thread->p_lock); } } } if (pid == 0) { PDEBUG("cloning new_thread = %p\n", new_thread); #ifdef __ia64__ pid = __clone2(pthread_start_thread, new_thread_top, new_thread_top - new_thread_bottom, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | __pthread_sig_cancel, new_thread); #else pid = clone(pthread_start_thread, new_thread_top, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | __pthread_sig_cancel, new_thread); #endif saved_errno = errno; } /* Check if cloning succeeded */ if (pid == -1) { /******************************************************** * Code inserted to remove the thread from our list of active * threads in case of failure (needed to cope with uClinux), * See comment below. */ new_thread->p_nextlive->p_prevlive = new_thread->p_prevlive; new_thread->p_prevlive->p_nextlive = new_thread->p_nextlive; /********************************************************/ /* Free the stack if we allocated it */ if (attr == NULL || !attr->__stackaddr_set) { #ifdef __ARCH_USE_MMU__ if (new_thread->p_guardsize != 0) munmap(new_thread->p_guardaddr, new_thread->p_guardsize); munmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE), INITIAL_STACK_SIZE); #else free(new_thread_bottom); #endif /* __ARCH_USE_MMU__ */ } __pthread_handles[sseg].h_descr = NULL; __pthread_handles[sseg].h_bottom = NULL; __pthread_handles_num--; return saved_errno; } PDEBUG("new thread pid = %d\n", pid); #if 0 /* *********************************************************** This code has been moved before the call to clone(). In uClinux, the use of wait on a semaphore is dependant upon that the child so the child must be in the active threads list. This list is used in pthread_find_self() to get the pthread_descr of self. So, if the child calls sem_wait before this code is executed , it will hang forever and initial_thread will instead be posted by a sem_post call. */ /* Insert new thread in doubly linked list of active threads */ new_thread->p_prevlive = __pthread_main_thread; new_thread->p_nextlive = __pthread_main_thread->p_nextlive; __pthread_main_thread->p_nextlive->p_prevlive = new_thread; __pthread_main_thread->p_nextlive = new_thread; /************************************************************/ #endif /* Set pid field of the new thread, in case we get there before the child starts. */ new_thread->p_pid = pid; /* We're all set */ *thread = new_thread_id; return 0; }
int attribute_noreturn __pthread_manager(void *arg) { int reqfd = (int) (long int) arg; #ifdef USE_SELECT struct timeval tv; fd_set fd; #else struct pollfd ufd; #endif sigset_t manager_mask; int n; struct pthread_request request; /* If we have special thread_self processing, initialize it. */ #ifdef INIT_THREAD_SELF INIT_THREAD_SELF(&__pthread_manager_thread, 1); #endif /* Set the error variable. */ __pthread_manager_thread.p_errnop = &__pthread_manager_thread.p_errno; __pthread_manager_thread.p_h_errnop = &__pthread_manager_thread.p_h_errno; #ifdef __UCLIBC_HAS_XLOCALE__ /* Initialize thread's locale to the global locale. */ __pthread_manager_thread.locale = __global_locale; #endif /* __UCLIBC_HAS_XLOCALE__ */ /* Block all signals except __pthread_sig_cancel and SIGTRAP */ __sigfillset(&manager_mask); sigdelset(&manager_mask, __pthread_sig_cancel); /* for thread termination */ sigdelset(&manager_mask, SIGTRAP); /* for debugging purposes */ if (__pthread_threads_debug && __pthread_sig_debug > 0) sigdelset(&manager_mask, __pthread_sig_debug); sigprocmask(SIG_SETMASK, &manager_mask, NULL); /* Raise our priority to match that of main thread */ __pthread_manager_adjust_prio(__pthread_main_thread->p_priority); /* Synchronize debugging of the thread manager */ n = TEMP_FAILURE_RETRY(read(reqfd, (char *)&request, sizeof(request))); #ifndef USE_SELECT ufd.fd = reqfd; ufd.events = POLLIN; #endif /* Enter server loop */ while(1) { #ifdef USE_SELECT tv.tv_sec = 2; tv.tv_usec = 0; FD_ZERO (&fd); FD_SET (reqfd, &fd); n = select (reqfd + 1, &fd, NULL, NULL, &tv); #else PDEBUG("before poll\n"); n = poll(&ufd, 1, 2000); PDEBUG("after poll\n"); #endif /* Check for termination of the main thread */ if (getppid() == 1) { pthread_kill_all_threads(SIGKILL, 0); _exit(0); } /* Check for dead children */ if (terminated_children) { terminated_children = 0; pthread_reap_children(); } /* Read and execute request */ #ifdef USE_SELECT if (n == 1) #else if (n == 1 && (ufd.revents & POLLIN)) #endif { PDEBUG("before read\n"); n = read(reqfd, (char *)&request, sizeof(request)); PDEBUG("after read, n=%d\n", n); switch(request.req_kind) { case REQ_CREATE: PDEBUG("got REQ_CREATE\n"); request.req_thread->p_retcode = pthread_handle_create((pthread_t *) &request.req_thread->p_retval, request.req_args.create.attr, request.req_args.create.fn, request.req_args.create.arg, &request.req_args.create.mask, request.req_thread->p_pid, request.req_thread->p_report_events, &request.req_thread->p_eventbuf.eventmask); PDEBUG("restarting %p\n", request.req_thread); restart(request.req_thread); break; case REQ_FREE: PDEBUG("got REQ_FREE\n"); pthread_handle_free(request.req_args.free.thread_id); break; case REQ_PROCESS_EXIT: PDEBUG("got REQ_PROCESS_EXIT from %p, exit code = %d\n", request.req_thread, request.req_args.exit.code); pthread_handle_exit(request.req_thread, request.req_args.exit.code); break; case REQ_MAIN_THREAD_EXIT: PDEBUG("got REQ_MAIN_THREAD_EXIT\n"); main_thread_exiting = 1; /* Reap children in case all other threads died and the signal handler went off before we set main_thread_exiting to 1, and therefore did not do REQ_KICK. */ pthread_reap_children(); if (__pthread_main_thread->p_nextlive == __pthread_main_thread) { restart(__pthread_main_thread); /* The main thread will now call exit() which will trigger an __on_exit handler, which in turn will send REQ_PROCESS_EXIT to the thread manager. In case you are wondering how the manager terminates from its loop here. */ } break; case REQ_POST: PDEBUG("got REQ_POST\n"); sem_post(request.req_args.post); break; case REQ_DEBUG: PDEBUG("got REQ_DEBUG\n"); /* Make gdb aware of new thread and gdb will restart the new thread when it is ready to handle the new thread. */ if (__pthread_threads_debug && __pthread_sig_debug > 0) { PDEBUG("about to call raise(__pthread_sig_debug)\n"); raise(__pthread_sig_debug); } case REQ_KICK: /* This is just a prod to get the manager to reap some threads right away, avoiding a potential delay at shutdown. */ break; } } } }
static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void *), void *arg, sigset_t * mask, int father_pid, int report_events, td_thr_events_t *event_maskp) { size_t sseg; int pid; pthread_descr new_thread; char *stack_addr; char * new_thread_bottom; pthread_t new_thread_id; char *guardaddr = NULL; size_t guardsize = 0, stksize = 0; int pagesize = __getpagesize(); int saved_errno = 0; #ifdef USE_TLS new_thread = _dl_allocate_tls (NULL); if (new_thread == NULL) return EAGAIN; # if TLS_DTV_AT_TP /* pthread_descr is below TP. */ new_thread = (pthread_descr) ((char *) new_thread - TLS_PRE_TCB_SIZE); # endif #else /* Prevent warnings. */ new_thread = NULL; #endif /* First check whether we have to change the policy and if yes, whether we can do this. Normally this should be done by examining the return value of the __sched_setscheduler call in pthread_start_thread but this is hard to implement. FIXME */ if (attr != NULL && attr->__schedpolicy != SCHED_OTHER && geteuid () != 0) return EPERM; /* Find a free segment for the thread, and allocate a stack if needed */ for (sseg = 2; ; sseg++) { if (sseg >= PTHREAD_THREADS_MAX) { #ifdef USE_TLS # if TLS_DTV_AT_TP new_thread = (pthread_descr) ((char *) new_thread + TLS_PRE_TCB_SIZE); # endif _dl_deallocate_tls (new_thread, true); #endif return EAGAIN; } if (__pthread_handles[sseg].h_descr != NULL) continue; if (pthread_allocate_stack(attr, thread_segment(sseg), pagesize, &stack_addr, &new_thread_bottom, &guardaddr, &guardsize, &stksize) == 0) { #ifdef USE_TLS new_thread->p_stackaddr = stack_addr; #else new_thread = (pthread_descr) stack_addr; #endif break; #ifndef __ARCH_USE_MMU__ } else { /* When there is MMU, mmap () is used to allocate the stack. If one * segment is already mapped, we should continue to see if we can * use the next one. However, when there is no MMU, malloc () is used. * It's waste of CPU cycles to continue to try if it fails. */ return EAGAIN; #endif } } __pthread_handles_num++; /* Allocate new thread identifier */ pthread_threads_counter += PTHREAD_THREADS_MAX; new_thread_id = sseg + pthread_threads_counter; /* Initialize the thread descriptor. Elements which have to be initialized to zero already have this value. */ #if !defined USE_TLS || !TLS_DTV_AT_TP new_thread->p_header.data.tcb = new_thread; new_thread->p_header.data.self = new_thread; #endif #if TLS_MULTIPLE_THREADS_IN_TCB || !defined USE_TLS || !TLS_DTV_AT_TP new_thread->p_multiple_threads = 1; #endif new_thread->p_tid = new_thread_id; new_thread->p_lock = &(__pthread_handles[sseg].h_lock); new_thread->p_cancelstate = PTHREAD_CANCEL_ENABLE; new_thread->p_canceltype = PTHREAD_CANCEL_DEFERRED; #if !(USE_TLS && HAVE___THREAD) new_thread->p_errnop = &new_thread->p_errno; new_thread->p_h_errnop = &new_thread->p_h_errno; new_thread->p_resp = &new_thread->p_res; #endif new_thread->p_guardaddr = guardaddr; new_thread->p_guardsize = guardsize; new_thread->p_nr = sseg; new_thread->p_inheritsched = attr ? attr->__inheritsched : 0; new_thread->p_alloca_cutoff = stksize / 4 > __MAX_ALLOCA_CUTOFF ? __MAX_ALLOCA_CUTOFF : stksize / 4; /* Initialize the thread handle */ __pthread_init_lock(&__pthread_handles[sseg].h_lock); __pthread_handles[sseg].h_descr = new_thread; __pthread_handles[sseg].h_bottom = new_thread_bottom; /* Determine scheduling parameters for the thread */ new_thread->p_start_args.schedpolicy = -1; if (attr != NULL) { new_thread->p_detached = attr->__detachstate; new_thread->p_userstack = attr->__stackaddr_set; switch(attr->__inheritsched) { case PTHREAD_EXPLICIT_SCHED: new_thread->p_start_args.schedpolicy = attr->__schedpolicy; memcpy (&new_thread->p_start_args.schedparam, &attr->__schedparam, sizeof (struct sched_param)); break; case PTHREAD_INHERIT_SCHED: new_thread->p_start_args.schedpolicy = __sched_getscheduler(father_pid); __sched_getparam(father_pid, &new_thread->p_start_args.schedparam); break; } new_thread->p_priority = new_thread->p_start_args.schedparam.sched_priority; } /* Finish setting up arguments to pthread_start_thread */ new_thread->p_start_args.start_routine = start_routine; new_thread->p_start_args.arg = arg; new_thread->p_start_args.mask = *mask; /* Make the new thread ID available already now. If any of the later functions fail we return an error value and the caller must not use the stored thread ID. */ *thread = new_thread_id; /* Raise priority of thread manager if needed */ __pthread_manager_adjust_prio(new_thread->p_priority); /* Do the cloning. We have to use two different functions depending on whether we are debugging or not. */ pid = 0; /* Note that the thread never can have PID zero. */ if (report_events) { /* See whether the TD_CREATE event bit is set in any of the masks. */ int idx = __td_eventword (TD_CREATE); uint32_t mask = __td_eventmask (TD_CREATE); if ((mask & (__pthread_threads_events.event_bits[idx] | event_maskp->event_bits[idx])) != 0) { /* Lock the mutex the child will use now so that it will stop. */ __pthread_lock(new_thread->p_lock, NULL); /* We have to report this event. */ #ifdef NEED_SEPARATE_REGISTER_STACK /* Perhaps this version should be used on all platforms. But this requires that __clone2 be uniformly supported everywhere. And there is some argument for changing the __clone2 interface to pass sp and bsp instead, making it more IA64 specific, but allowing stacks to grow outward from each other, to get less paging and fewer mmaps. */ pid = __clone2(pthread_start_thread_event, (void **)new_thread_bottom, (char *)stack_addr - new_thread_bottom, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | __pthread_sig_cancel, new_thread); #elif _STACK_GROWS_UP pid = __clone(pthread_start_thread_event, (void *) new_thread_bottom, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | __pthread_sig_cancel, new_thread); #else pid = __clone(pthread_start_thread_event, stack_addr, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | __pthread_sig_cancel, new_thread); #endif saved_errno = errno; if (pid != -1) { /* Now fill in the information about the new thread in the newly created thread's data structure. We cannot let the new thread do this since we don't know whether it was already scheduled when we send the event. */ new_thread->p_eventbuf.eventdata = new_thread; new_thread->p_eventbuf.eventnum = TD_CREATE; __pthread_last_event = new_thread; /* We have to set the PID here since the callback function in the debug library will need it and we cannot guarantee the child got scheduled before the debugger. */ new_thread->p_pid = pid; /* Now call the function which signals the event. */ __linuxthreads_create_event (); /* Now restart the thread. */ __pthread_unlock(new_thread->p_lock); } } } if (pid == 0) { #ifdef NEED_SEPARATE_REGISTER_STACK pid = __clone2(pthread_start_thread, (void **)new_thread_bottom, (char *)stack_addr - new_thread_bottom, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | __pthread_sig_cancel, new_thread); #elif _STACK_GROWS_UP pid = __clone(pthread_start_thread, (void *) new_thread_bottom, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | __pthread_sig_cancel, new_thread); #else pid = __clone(pthread_start_thread, stack_addr, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | __pthread_sig_cancel, new_thread); #endif /* !NEED_SEPARATE_REGISTER_STACK */ saved_errno = errno; } /* Check if cloning succeeded */ if (pid == -1) { /* Free the stack if we allocated it */ if (attr == NULL || !attr->__stackaddr_set) { #ifdef NEED_SEPARATE_REGISTER_STACK size_t stacksize = ((char *)(new_thread->p_guardaddr) - new_thread_bottom); munmap((caddr_t)new_thread_bottom, 2 * stacksize + new_thread->p_guardsize); #elif _STACK_GROWS_UP # ifdef USE_TLS size_t stacksize = guardaddr - stack_addr; munmap(stack_addr, stacksize + guardsize); # else size_t stacksize = guardaddr - (char *)new_thread; munmap(new_thread, stacksize + guardsize); # endif #else # ifdef USE_TLS size_t stacksize = stack_addr - new_thread_bottom; # else size_t stacksize = (char *)(new_thread+1) - new_thread_bottom; # endif munmap(new_thread_bottom - guardsize, guardsize + stacksize); #endif } #ifdef USE_TLS # if TLS_DTV_AT_TP new_thread = (pthread_descr) ((char *) new_thread + TLS_PRE_TCB_SIZE); # endif _dl_deallocate_tls (new_thread, true); #endif __pthread_handles[sseg].h_descr = NULL; __pthread_handles[sseg].h_bottom = NULL; __pthread_handles_num--; return saved_errno; } /* Insert new thread in doubly linked list of active threads */ new_thread->p_prevlive = __pthread_main_thread; new_thread->p_nextlive = __pthread_main_thread->p_nextlive; __pthread_main_thread->p_nextlive->p_prevlive = new_thread; __pthread_main_thread->p_nextlive = new_thread; /* Set pid field of the new thread, in case we get there before the child starts. */ new_thread->p_pid = pid; return 0; }
__pthread_manager(void *arg) { pthread_descr self = manager_thread = arg; int reqfd = __pthread_manager_reader; struct pollfd ufd; sigset_t manager_mask; int n; struct pthread_request request; /* If we have special thread_self processing, initialize it. */ #ifdef INIT_THREAD_SELF INIT_THREAD_SELF(self, 1); #endif #if !(USE_TLS && HAVE___THREAD) /* Set the error variable. */ self->p_errnop = &self->p_errno; self->p_h_errnop = &self->p_h_errno; #endif /* Block all signals except __pthread_sig_cancel and SIGTRAP */ sigfillset(&manager_mask); sigdelset(&manager_mask, __pthread_sig_cancel); /* for thread termination */ sigdelset(&manager_mask, SIGTRAP); /* for debugging purposes */ if (__pthread_threads_debug && __pthread_sig_debug > 0) sigdelset(&manager_mask, __pthread_sig_debug); sigprocmask(SIG_SETMASK, &manager_mask, NULL); /* Raise our priority to match that of main thread */ __pthread_manager_adjust_prio(__pthread_main_thread->p_priority); /* Synchronize debugging of the thread manager */ n = TEMP_FAILURE_RETRY(read_not_cancel(reqfd, (char *)&request, sizeof(request))); ASSERT(n == sizeof(request) && request.req_kind == REQ_DEBUG); ufd.fd = reqfd; ufd.events = POLLIN; /* Enter server loop */ while(1) { n = __poll(&ufd, 1, 2000); /* Check for termination of the main thread */ if (getppid() == 1) { pthread_kill_all_threads(SIGKILL, 0); _exit(0); } /* Check for dead children */ if (terminated_children) { terminated_children = 0; pthread_reap_children(); } /* Read and execute request */ if (n == 1 && (ufd.revents & POLLIN)) { n = TEMP_FAILURE_RETRY(read_not_cancel(reqfd, (char *)&request, sizeof(request))); #ifdef DEBUG if (n < 0) { char d[64]; write(STDERR_FILENO, d, snprintf(d, sizeof(d), "*** read err %m\n")); } else if (n != sizeof(request)) { write(STDERR_FILENO, "*** short read in manager\n", 26); } #endif switch(request.req_kind) { case REQ_CREATE: request.req_thread->p_retcode = pthread_handle_create((pthread_t *) &request.req_thread->p_retval, request.req_args.create.attr, request.req_args.create.fn, request.req_args.create.arg, &request.req_args.create.mask, request.req_thread->p_pid, request.req_thread->p_report_events, &request.req_thread->p_eventbuf.eventmask); restart(request.req_thread); break; case REQ_FREE: pthread_handle_free(request.req_args.free.thread_id); break; case REQ_PROCESS_EXIT: pthread_handle_exit(request.req_thread, request.req_args.exit.code); /* NOTREACHED */ break; case REQ_MAIN_THREAD_EXIT: main_thread_exiting = 1; /* Reap children in case all other threads died and the signal handler went off before we set main_thread_exiting to 1, and therefore did not do REQ_KICK. */ pthread_reap_children(); if (__pthread_main_thread->p_nextlive == __pthread_main_thread) { restart(__pthread_main_thread); /* The main thread will now call exit() which will trigger an __on_exit handler, which in turn will send REQ_PROCESS_EXIT to the thread manager. In case you are wondering how the manager terminates from its loop here. */ } break; case REQ_POST: sem_post(request.req_args.post); break; case REQ_DEBUG: /* Make gdb aware of new thread and gdb will restart the new thread when it is ready to handle the new thread. */ if (__pthread_threads_debug && __pthread_sig_debug > 0) raise(__pthread_sig_debug); break; case REQ_KICK: /* This is just a prod to get the manager to reap some threads right away, avoiding a potential delay at shutdown. */ break; case REQ_FOR_EACH_THREAD: pthread_for_each_thread(request.req_args.for_each.arg, request.req_args.for_each.fn); restart(request.req_thread); break; } } } }