static void pthread_reap_children(void) { pid_t pid; int status; while ((pid = waitpid_not_cancel(-1, &status, WNOHANG | __WCLONE)) > 0) { pthread_exited(pid); if (WIFSIGNALED(status)) { /* If a thread died due to a signal, send the same signal to all other threads, including the main thread. */ pthread_kill_all_threads(WTERMSIG(status), 1); _exit(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; } } } }
__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; } } } }
int __pthread_manager(void * arg) { int reqfd = (int) arg; sigset_t mask; fd_set readfds; struct timeval timeout; 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); #endif /* Block all signals except PTHREAD_SIG_RESTART */ sigfillset(&mask); sigdelset(&mask, PTHREAD_SIG_RESTART); sigprocmask(SIG_SETMASK, &mask, NULL); /* Enter server loop */ while(1) { FD_ZERO(&readfds); FD_SET(reqfd, &readfds); timeout.tv_sec = 2; timeout.tv_usec = 0; n = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout); /* 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 && FD_ISSET(reqfd, &readfds)) { n = read(reqfd, (char *)&request, sizeof(request)); ASSERT(n == sizeof(request)); 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); restart(request.req_thread); break; case REQ_FREE: pthread_handle_free(request.req_args.free.thread); break; case REQ_PROCESS_EXIT: pthread_handle_exit(request.req_thread, request.req_args.exit.code); break; case REQ_MAIN_THREAD_EXIT: main_thread_exiting = 1; if (__pthread_main_thread->p_nextlive == __pthread_main_thread) { restart(__pthread_main_thread); return 0; } break; } } } }