/* Do some minimal initialization which has to be done during the startup of the C library. */ void __pthread_initialize_minimal(void) { #ifdef USE_TLS pthread_descr self; /* First of all init __pthread_handles[0] and [1] if needed. */ # if __LT_SPINLOCK_INIT != 0 __pthread_handles[0].h_lock = __LOCK_INITIALIZER; __pthread_handles[1].h_lock = __LOCK_INITIALIZER; # endif # ifndef SHARED /* Unlike in the dynamically linked case the dynamic linker has not taken care of initializing the TLS data structures. */ __libc_setup_tls (TLS_TCB_SIZE, TLS_TCB_ALIGN); # elif !USE___THREAD if (__builtin_expect (GL(dl_tls_dtv_slotinfo_list) == NULL, 0)) { tcbhead_t *tcbp; /* There is no actual TLS being used, so the thread register was not initialized in the dynamic linker. */ /* We need to install special hooks so that the malloc and memalign calls in _dl_tls_setup and _dl_allocate_tls won't cause full malloc initialization that will try to set up its thread state. */ extern void __libc_malloc_pthread_startup (bool first_time); __libc_malloc_pthread_startup (true); if (__builtin_expect (_dl_tls_setup (), 0) || __builtin_expect ((tcbp = _dl_allocate_tls (NULL)) == NULL, 0)) { static const char msg[] = "\ cannot allocate TLS data structures for initial thread\n"; TEMP_FAILURE_RETRY (write_not_cancel (STDERR_FILENO, msg, sizeof msg - 1)); abort (); } const char *lossage = TLS_INIT_TP (tcbp, 0); if (__builtin_expect (lossage != NULL, 0)) { static const char msg[] = "cannot set up thread-local storage: "; const char nl = '\n'; TEMP_FAILURE_RETRY (write_not_cancel (STDERR_FILENO, msg, sizeof msg - 1)); TEMP_FAILURE_RETRY (write_not_cancel (STDERR_FILENO, lossage, strlen (lossage))); TEMP_FAILURE_RETRY (write_not_cancel (STDERR_FILENO, &nl, 1)); } /* Though it was allocated with libc's malloc, that was done without the user's __malloc_hook installed. A later realloc that uses the hooks might not work with that block from the plain malloc. So we record this block as unfreeable just as the dynamic linker does when it allocates the DTV before the libc malloc exists. */ GL(dl_initial_dtv) = GET_DTV (tcbp); __libc_malloc_pthread_startup (false); }
static void backtrace_and_maps (int do_abort, bool written, int fd) { if (do_abort > 1 && written) { void *addrs[64]; #define naddrs (sizeof (addrs) / sizeof (addrs[0])) int n = __backtrace (addrs, naddrs); if (n > 2) { #define strnsize(str) str, strlen (str) #define writestr(str) write_not_cancel (fd, str) writestr (strnsize ("======= Backtrace: =========\n")); __backtrace_symbols_fd (addrs + 1, n - 1, fd); writestr (strnsize ("======= Memory map: ========\n")); int fd2 = open_not_cancel_2 ("/proc/self/maps", O_RDONLY); char buf[1024]; ssize_t n2; while ((n2 = read_not_cancel (fd2, buf, sizeof (buf))) > 0) if (write_not_cancel (fd, buf, n2) != n2) break; close_not_cancel_no_status (fd2); } } }
pthread_start_thread(void *arg) { pthread_descr self = (pthread_descr) arg; struct pthread_request request; void * outcome; #if HP_TIMING_AVAIL hp_timing_t tmpclock; #endif /* Initialize special thread_self processing, if any. */ #ifdef INIT_THREAD_SELF INIT_THREAD_SELF(self, self->p_nr); #endif #if HP_TIMING_AVAIL HP_TIMING_NOW (tmpclock); THREAD_SETMEM (self, p_cpuclock_offset, tmpclock); #endif /* Make sure our pid field is initialized, just in case we get there before our father has initialized it. */ THREAD_SETMEM(self, p_pid, __getpid()); /* Initial signal mask is that of the creating thread. (Otherwise, we'd just inherit the mask of the thread manager.) */ sigprocmask(SIG_SETMASK, &self->p_start_args.mask, NULL); /* Set the scheduling policy and priority for the new thread, if needed */ if (THREAD_GETMEM(self, p_start_args.schedpolicy) >= 0) /* Explicit scheduling attributes were provided: apply them */ __sched_setscheduler(THREAD_GETMEM(self, p_pid), THREAD_GETMEM(self, p_start_args.schedpolicy), &self->p_start_args.schedparam); else if (manager_thread->p_priority > 0) /* Default scheduling required, but thread manager runs in realtime scheduling: switch new thread to SCHED_OTHER policy */ { struct sched_param default_params; default_params.sched_priority = 0; __sched_setscheduler(THREAD_GETMEM(self, p_pid), SCHED_OTHER, &default_params); } #if !(USE_TLS && HAVE___THREAD) /* Initialize thread-locale current locale to point to the global one. With __thread support, the variable's initializer takes care of this. */ __uselocale (LC_GLOBAL_LOCALE); #else /* Initialize __resp. */ __resp = &self->p_res; #endif /* Make gdb aware of new thread */ if (__pthread_threads_debug && __pthread_sig_debug > 0) { request.req_thread = self; request.req_kind = REQ_DEBUG; TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request, (char *) &request, sizeof(request))); suspend(self); } /* Run the thread code */ outcome = self->p_start_args.start_routine(THREAD_GETMEM(self, p_start_args.arg)); /* Exit with the given return value */ __pthread_do_exit(outcome, CURRENT_STACK_FRAME); }
static void __updwtmp(const char *wtmp_file, const struct utmp *lutmp) { int fd; fd = open_not_cancel_2(wtmp_file, O_APPEND | O_WRONLY); if (fd >= 0) { if (lockf(fd, F_LOCK, 0) == 0) { write_not_cancel(fd, lutmp, sizeof(struct utmp)); lockf(fd, F_ULOCK, 0); close_not_cancel_no_status(fd); } } }
/* Delete a key */ int pthread_key_delete(pthread_key_t key) { pthread_descr self = thread_self(); pthread_mutex_lock(&pthread_keys_mutex); if (key >= PTHREAD_KEYS_MAX || !pthread_keys[key].in_use) { pthread_mutex_unlock(&pthread_keys_mutex); return EINVAL; } pthread_keys[key].in_use = 0; pthread_keys[key].destr = NULL; /* Set the value of the key to NULL in all running threads, so that if the key is reallocated later by pthread_key_create, its associated values will be NULL in all threads. If no threads have been created yet, or if we are exiting, clear it just in the current thread. */ struct pthread_key_delete_helper_args args; args.idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE; args.idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE; if (!l4_is_invalid_cap(__pthread_manager_request) && !(__builtin_expect (__pthread_exit_requested, 0))) { struct pthread_request request; args.self = 0; request.req_thread = self; request.req_kind = REQ_FOR_EACH_THREAD; request.req_args.for_each.arg = &args; request.req_args.for_each.fn = pthread_key_delete_helper; #if 1 __pthread_send_manager_rq(&request, 1); #else TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request, (char *) &request, sizeof(request))); #endif suspend(self); } else { if (self->p_specific[args.idx1st] != NULL) self->p_specific[args.idx1st][args.idx2nd] = NULL; } pthread_mutex_unlock(&pthread_keys_mutex); return 0; }
int sem_post(sem_t * sem) { pthread_descr self = thread_self(); pthread_descr th; #if 0 // AW11 struct pthread_request request; if (THREAD_GETMEM(self, p_in_sighandler) == NULL) #endif { __pthread_lock(&sem->__sem_lock, self); if (sem->__sem_waiting == NULL) { if (sem->__sem_value >= SEM_VALUE_MAX) { /* Overflow */ __set_errno(ERANGE); __pthread_unlock(&sem->__sem_lock); return -1; } sem->__sem_value++; __pthread_unlock(&sem->__sem_lock); } else { th = dequeue(&sem->__sem_waiting); __pthread_unlock(&sem->__sem_lock); th->p_sem_avail = 1; WRITE_MEMORY_BARRIER(); restart(th); } } #if 0 // AW11 else { /* If we're in signal handler, delegate post operation to the thread manager. */ if (__pthread_manager_request < 0) { if (__pthread_initialize_manager() < 0) { __set_errno(EAGAIN); return -1; } } request.req_kind = REQ_POST; request.req_args.post = sem; TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request, (char *) &request, sizeof(request))); } #endif return 0; }
void __pthread_manager_sighandler(int sig) { int kick_manager = terminated_children == 0 && main_thread_exiting; terminated_children = 1; /* If the main thread is terminating, kick the thread manager loop each time some threads terminate. This eliminates a two second shutdown delay caused by the thread manager sleeping in the call to __poll(). Instead, the thread manager is kicked into action, reaps the outstanding threads and resumes the main thread so that it can complete the shutdown. */ if (kick_manager) { struct pthread_request request; request.req_thread = 0; request.req_kind = REQ_KICK; TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request, (char *) &request, sizeof(request))); } }
_IO_ssize_t _IO_new_file_write (_IO_FILE *f, const void *data, _IO_ssize_t n) { _IO_ssize_t to_do = n; while (to_do > 0) { _IO_ssize_t count = (__builtin_expect (f->_flags2 & _IO_FLAGS2_NOTCANCEL, 0) ? write_not_cancel (f->_fileno, data, to_do) : write (f->_fileno, data, to_do)); if (count < 0) { f->_flags |= _IO_ERR_SEEN; break; } to_do -= count; data = (void *) ((char *) data + count); } n -= to_do; if (f->_offset >= 0) f->_offset += n; return n; }
/* Function used in the clone call to setup the signals mask, posix_spawn attributes, and file actions. It run on its own stack (provided by the posix_spawn call). */ static int __spawni_child (void *arguments) { struct posix_spawn_args *args = arguments; const posix_spawnattr_t *restrict attr = args->attr; const posix_spawn_file_actions_t *file_actions = args->fa; int p = args->pipe[1]; int ret; close_not_cancel (args->pipe[0]); /* The child must ensure that no signal handler are enabled because it shared memory with parent, so the signal disposition must be either SIG_DFL or SIG_IGN. It does by iterating over all signals and although it could possibly be more optimized (by tracking which signal potentially have a signal handler), it might requires system specific solutions (since the sigset_t data type can be very different on different architectures). */ struct sigaction sa; memset (&sa, '\0', sizeof (sa)); sigset_t hset; __sigprocmask (SIG_BLOCK, 0, &hset); for (int sig = 1; sig < _NSIG; ++sig) { if ((attr->__flags & POSIX_SPAWN_SETSIGDEF) && sigismember (&attr->__sd, sig)) { sa.sa_handler = SIG_DFL; } else if (sigismember (&hset, sig)) { if (__nptl_is_internal_signal (sig)) sa.sa_handler = SIG_IGN; else { __libc_sigaction (sig, 0, &sa); if (sa.sa_handler == SIG_IGN) continue; sa.sa_handler = SIG_DFL; } } else continue; __libc_sigaction (sig, &sa, 0); } #ifdef _POSIX_PRIORITY_SCHEDULING /* Set the scheduling algorithm and parameters. */ if ((attr->__flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) == POSIX_SPAWN_SETSCHEDPARAM) { if ((ret = __sched_setparam (0, &attr->__sp)) == -1) goto fail; } else if ((attr->__flags & POSIX_SPAWN_SETSCHEDULER) != 0) { if ((ret = __sched_setscheduler (0, attr->__policy, &attr->__sp)) == -1) goto fail; } #endif /* Set the process group ID. */ if ((attr->__flags & POSIX_SPAWN_SETPGROUP) != 0 && (ret = __setpgid (0, attr->__pgrp)) != 0) goto fail; /* Set the effective user and group IDs. */ if ((attr->__flags & POSIX_SPAWN_RESETIDS) != 0 && ((ret = local_seteuid (__getuid ())) != 0 || (ret = local_setegid (__getgid ())) != 0)) goto fail; /* Execute the file actions. */ if (file_actions != 0) { int cnt; struct rlimit64 fdlimit; bool have_fdlimit = false; for (cnt = 0; cnt < file_actions->__used; ++cnt) { struct __spawn_action *action = &file_actions->__actions[cnt]; /* Dup the pipe fd onto an unoccupied one to avoid any file operation to clobber it. */ if ((action->action.close_action.fd == p) || (action->action.open_action.fd == p) || (action->action.dup2_action.fd == p)) { if ((ret = __dup (p)) < 0) goto fail; p = ret; } switch (action->tag) { case spawn_do_close: if ((ret = close_not_cancel (action->action.close_action.fd)) != 0) { if (!have_fdlimit) { __getrlimit64 (RLIMIT_NOFILE, &fdlimit); have_fdlimit = true; } /* Signal errors only for file descriptors out of range. */ if (action->action.close_action.fd < 0 || action->action.close_action.fd >= fdlimit.rlim_cur) goto fail; } break; case spawn_do_open: { ret = open_not_cancel (action->action.open_action.path, action->action. open_action.oflag | O_LARGEFILE, action->action.open_action.mode); if (ret == -1) goto fail; int new_fd = ret; /* Make sure the desired file descriptor is used. */ if (ret != action->action.open_action.fd) { if ((ret = __dup2 (new_fd, action->action.open_action.fd)) != action->action.open_action.fd) goto fail; if ((ret = close_not_cancel (new_fd)) != 0) goto fail; } } break; case spawn_do_dup2: if ((ret = __dup2 (action->action.dup2_action.fd, action->action.dup2_action.newfd)) != action->action.dup2_action.newfd) goto fail; break; } } } /* Set the initial signal mask of the child if POSIX_SPAWN_SETSIGMASK is set, otherwise restore the previous one. */ __sigprocmask (SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK) ? &attr->__ss : &args->oldmask, 0); args->exec (args->file, args->argv, args->envp); /* This is compatibility function required to enable posix_spawn run script without shebang definition for older posix_spawn versions (2.15). */ maybe_script_execute (args); ret = -errno; fail: /* Since sizeof errno < PIPE_BUF, the write is atomic. */ ret = -ret; if (ret) while (write_not_cancel (p, &ret, sizeof ret) < 0) continue; exit (SPAWN_ERROR); }