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); }
void __pthread_manager_adjust_prio(int thread_prio) { struct sched_param param; if (thread_prio <= manager_thread->p_priority) return; param.sched_priority = thread_prio < __sched_get_priority_max(SCHED_FIFO) ? thread_prio + 1 : thread_prio; __sched_setscheduler(manager_thread->p_pid, SCHED_FIFO, ¶m); manager_thread->p_priority = thread_prio; }
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 (__builtin_expect (invalid_handle(handle, thread), 0)) { __pthread_unlock(&handle->h_lock); return ESRCH; } th = handle->h_descr; if (__builtin_expect (__sched_setscheduler(th->p_pid, policy, param) == -1, 0)) { __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_start_thread(void * arg) { pthread_descr self = (pthread_descr) arg; void * outcome; /* Initialize special thread_self processing, if any. */ #ifdef INIT_THREAD_SELF INIT_THREAD_SELF(self); #endif /* Make sure our pid field is initialized, just in case we get there before our father has initialized it. */ 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 (self->p_start_args.schedpolicy != SCHED_OTHER) __sched_setscheduler(self->p_pid, self->p_start_args.schedpolicy, &self->p_start_args.schedparam); /* Run the thread code */ outcome = self->p_start_args.start_routine(self->p_start_args.arg); /* Exit with the given return value */ pthread_exit(outcome); return 0; }
int __pthread_tpp_change_priority (int previous_prio, int new_prio) { struct pthread *self = THREAD_SELF; struct priority_protection_data *tpp = THREAD_GETMEM (self, tpp); if (tpp == NULL) { if (__sched_fifo_min_prio == -1) __init_sched_fifo_prio (); size_t size = sizeof *tpp; size += (__sched_fifo_max_prio - __sched_fifo_min_prio + 1) * sizeof (tpp->priomap[0]); tpp = calloc (size, 1); if (tpp == NULL) return ENOMEM; tpp->priomax = __sched_fifo_min_prio - 1; THREAD_SETMEM (self, tpp, tpp); } assert (new_prio == -1 || (new_prio >= __sched_fifo_min_prio && new_prio <= __sched_fifo_max_prio)); assert (previous_prio == -1 || (previous_prio >= __sched_fifo_min_prio && previous_prio <= __sched_fifo_max_prio)); int priomax = tpp->priomax; int newpriomax = priomax; if (new_prio != -1) { if (tpp->priomap[new_prio - __sched_fifo_min_prio] + 1 == 0) return EAGAIN; ++tpp->priomap[new_prio - __sched_fifo_min_prio]; if (new_prio > priomax) newpriomax = new_prio; } if (previous_prio != -1) { if (--tpp->priomap[previous_prio - __sched_fifo_min_prio] == 0 && priomax == previous_prio && previous_prio > new_prio) { int i; for (i = previous_prio - 1; i >= __sched_fifo_min_prio; --i) if (tpp->priomap[i - __sched_fifo_min_prio]) break; newpriomax = i; } } if (priomax == newpriomax) return 0; lll_lock (self->lock, LLL_PRIVATE); tpp->priomax = newpriomax; int result = 0; #ifdef TPP_PTHREAD_SCHED int policy; struct sched_param param; #endif if ((self->flags & ATTR_FLAG_SCHED_SET) == 0) { #ifndef TPP_PTHREAD_SCHED if (__sched_getparam (self->tid, &self->schedparam) != 0) #else if (__pthread_getschedparam (self->tid, &policy, &self->schedparam) != 0) #endif result = errno; else self->flags |= ATTR_FLAG_SCHED_SET; } if ((self->flags & ATTR_FLAG_POLICY_SET) == 0) { #ifndef TPP_PTHREAD_SCHED self->schedpolicy = __sched_getscheduler (self->tid); #else if (__pthread_getschedparam (self->tid, &self->schedpolicy, ¶m) != 0) self->schedpolicy = -1; #endif if (self->schedpolicy == -1) result = errno; else self->flags |= ATTR_FLAG_POLICY_SET; } if (result == 0) { struct sched_param sp = self->schedparam; if (sp.sched_priority < newpriomax || sp.sched_priority < priomax) { if (sp.sched_priority < newpriomax) sp.sched_priority = newpriomax; #ifndef TPP_PTHREAD_SCHED if (__sched_setscheduler (self->tid, self->schedpolicy, &sp) < 0) #else if (__pthread_setschedparam (self->tid, self->schedpolicy, &sp) < 0) #endif result = errno; } } lll_unlock (self->lock, LLL_PRIVATE); return result; }
/* Spawn a new process executing PATH with the attributes describes in *ATTRP. Before running the process perform the actions described in FILE-ACTIONS. */ int __spawni (pid_t *pid, const char *file, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], int xflags) { pid_t new_pid; char *path, *p, *name; size_t len; size_t pathlen; /* Do this once. */ short int flags = attrp == NULL ? 0 : attrp->__flags; /* Generate the new process. */ if ((flags & POSIX_SPAWN_USEVFORK) != 0 /* If no major work is done, allow using vfork. Note that we might perform the path searching. But this would be done by a call to execvp(), too, and such a call must be OK according to POSIX. */ || ((flags & (POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_RESETIDS)) == 0 && file_actions == NULL)) new_pid = __vfork (); else new_pid = __fork (); if (new_pid != 0) { if (new_pid < 0) return errno; /* The call was successful. Store the PID if necessary. */ if (pid != NULL) *pid = new_pid; return 0; } /* Set signal mask. */ if ((flags & POSIX_SPAWN_SETSIGMASK) != 0 && __sigprocmask (SIG_SETMASK, &attrp->__ss, NULL) != 0) _exit (SPAWN_ERROR); /* Set signal default action. */ if ((flags & POSIX_SPAWN_SETSIGDEF) != 0) { /* We have to iterate over all signals. This could possibly be done better but it requires system specific solutions since the sigset_t data type can be very different on different architectures. */ int sig; struct sigaction sa; memset (&sa, '\0', sizeof (sa)); sa.sa_handler = SIG_DFL; for (sig = 1; sig <= _NSIG; ++sig) if (__sigismember (&attrp->__sd, sig) != 0 && __sigaction (sig, &sa, NULL) != 0) _exit (SPAWN_ERROR); } #ifdef _POSIX_PRIORITY_SCHEDULING /* Set the scheduling algorithm and parameters. */ if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) == POSIX_SPAWN_SETSCHEDPARAM) { if (__sched_setparam (0, &attrp->__sp) == -1) _exit (SPAWN_ERROR); } else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0) { if (__sched_setscheduler (0, attrp->__policy, &attrp->__sp) == -1) _exit (SPAWN_ERROR); } #endif /* Set the process group ID. */ if ((flags & POSIX_SPAWN_SETPGROUP) != 0 && __setpgid (0, attrp->__pgrp) != 0) _exit (SPAWN_ERROR); /* Set the effective user and group IDs. */ if ((flags & POSIX_SPAWN_RESETIDS) != 0 && (local_seteuid (__getuid ()) != 0 || local_setegid (__getgid ()) != 0)) _exit (SPAWN_ERROR); /* Execute the file actions. */ if (file_actions != NULL) { 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]; switch (action->tag) { case spawn_do_close: if (close_not_cancel (action->action.close_action.fd) != 0) { if (! have_fdlimit) { getrlimit64 (RLIMIT_NOFILE, &fdlimit); have_fdlimit = true; } /* Only signal errors for file descriptors out of range. */ if (action->action.close_action.fd < 0 || action->action.close_action.fd >= fdlimit.rlim_cur) /* Signal the error. */ _exit (SPAWN_ERROR); } break; case spawn_do_open: { int new_fd = open_not_cancel (action->action.open_action.path, action->action.open_action.oflag | O_LARGEFILE, action->action.open_action.mode); if (new_fd == -1) /* The `open' call failed. */ _exit (SPAWN_ERROR); /* Make sure the desired file descriptor is used. */ if (new_fd != action->action.open_action.fd) { if (__dup2 (new_fd, action->action.open_action.fd) != action->action.open_action.fd) /* The `dup2' call failed. */ _exit (SPAWN_ERROR); if (close_not_cancel (new_fd) != 0) /* The `close' call failed. */ _exit (SPAWN_ERROR); } } break; case spawn_do_dup2: if (__dup2 (action->action.dup2_action.fd, action->action.dup2_action.newfd) != action->action.dup2_action.newfd) /* The `dup2' call failed. */ _exit (SPAWN_ERROR); break; } } } if ((xflags & SPAWN_XFLAGS_USE_PATH) == 0 || strchr (file, '/') != NULL) { /* The FILE parameter is actually a path. */ __execve (file, argv, envp); maybe_script_execute (file, argv, envp, xflags); /* Oh, oh. `execve' returns. This is bad. */ _exit (SPAWN_ERROR); } /* We have to search for FILE on the path. */ path = getenv ("PATH"); if (path == NULL) { /* There is no `PATH' in the environment. The default search path is the current directory followed by the path `confstr' returns for `_CS_PATH'. */ len = confstr (_CS_PATH, (char *) NULL, 0); path = (char *) __alloca (1 + len); path[0] = ':'; (void) confstr (_CS_PATH, path + 1, len); } len = strlen (file) + 1; pathlen = strlen (path); name = __alloca (pathlen + len + 1); /* Copy the file name at the top. */ name = (char *) memcpy (name + pathlen + 1, file, len); /* And add the slash. */ *--name = '/'; p = path; do { char *startp; path = p; p = __strchrnul (path, ':'); if (p == path) /* Two adjacent colons, or a colon at the beginning or the end of `PATH' means to search the current directory. */ startp = name + 1; else startp = (char *) memcpy (name - (p - path), path, p - path); /* Try to execute this name. If it works, execv will not return. */ __execve (startp, argv, envp); maybe_script_execute (startp, argv, envp, xflags); switch (errno) { case EACCES: case ENOENT: case ESTALE: case ENOTDIR: /* Those errors indicate the file is missing or not executable by us, in which case we want to just try the next path directory. */ break; default: /* Some other error means we found an executable file, but something went wrong executing it; return the error to our caller. */ _exit (SPAWN_ERROR); } } while (*p++ != '\0'); /* Return with an error. */ _exit (SPAWN_ERROR); }
int __pthread_tpp_change_priority (int previous_prio, int new_prio) { struct pthread *self = THREAD_SELF; struct priority_protection_data *tpp = THREAD_GETMEM (self, tpp); int fifo_min_prio = atomic_load_relaxed (&__sched_fifo_min_prio); int fifo_max_prio = atomic_load_relaxed (&__sched_fifo_max_prio); if (tpp == NULL) { /* See __init_sched_fifo_prio. We need both the min and max prio, so need to check both, and run initialization if either one is not initialized. The memory model's write-read coherence rule makes this work. */ if (fifo_min_prio == -1 || fifo_max_prio == -1) { __init_sched_fifo_prio (); fifo_min_prio = atomic_load_relaxed (&__sched_fifo_min_prio); fifo_max_prio = atomic_load_relaxed (&__sched_fifo_max_prio); } size_t size = sizeof *tpp; size += (fifo_max_prio - fifo_min_prio + 1) * sizeof (tpp->priomap[0]); tpp = calloc (size, 1); if (tpp == NULL) return ENOMEM; tpp->priomax = fifo_min_prio - 1; THREAD_SETMEM (self, tpp, tpp); } assert (new_prio == -1 || (new_prio >= fifo_min_prio && new_prio <= fifo_max_prio)); assert (previous_prio == -1 || (previous_prio >= fifo_min_prio && previous_prio <= fifo_max_prio)); int priomax = tpp->priomax; int newpriomax = priomax; if (new_prio != -1) { if (tpp->priomap[new_prio - fifo_min_prio] + 1 == 0) return EAGAIN; ++tpp->priomap[new_prio - fifo_min_prio]; if (new_prio > priomax) newpriomax = new_prio; } if (previous_prio != -1) { if (--tpp->priomap[previous_prio - fifo_min_prio] == 0 && priomax == previous_prio && previous_prio > new_prio) { int i; for (i = previous_prio - 1; i >= fifo_min_prio; --i) if (tpp->priomap[i - fifo_min_prio]) break; newpriomax = i; } } if (priomax == newpriomax) return 0; /* See CREATE THREAD NOTES in nptl/pthread_create.c. */ lll_lock (self->lock, LLL_PRIVATE); tpp->priomax = newpriomax; int result = 0; if ((self->flags & ATTR_FLAG_SCHED_SET) == 0) { if (__sched_getparam (self->tid, &self->schedparam) != 0) result = errno; else self->flags |= ATTR_FLAG_SCHED_SET; } if ((self->flags & ATTR_FLAG_POLICY_SET) == 0) { self->schedpolicy = __sched_getscheduler (self->tid); if (self->schedpolicy == -1) result = errno; else self->flags |= ATTR_FLAG_POLICY_SET; } if (result == 0) { struct sched_param sp = self->schedparam; if (sp.sched_priority < newpriomax || sp.sched_priority < priomax) { if (sp.sched_priority < newpriomax) sp.sched_priority = newpriomax; if (__sched_setscheduler (self->tid, self->schedpolicy, &sp) < 0) result = errno; } } lll_unlock (self->lock, LLL_PRIVATE); return result; }
/* 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); }