mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-30 21:23:52 +08:00
155 lines
4.9 KiB
C
155 lines
4.9 KiB
C
/* Low-level thread creation for NPTL. Linux version.
|
|
Copyright (C) 2002-2014 Free Software Foundation, Inc.
|
|
This file is part of the GNU C Library.
|
|
Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
|
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with the GNU C Library; if not, see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include <sched.h>
|
|
#include <setjmp.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <atomic.h>
|
|
#include <ldsodefs.h>
|
|
#include <tls.h>
|
|
#include <stdint.h>
|
|
|
|
#include <arch-fork.h>
|
|
|
|
|
|
#ifndef ARCH_CLONE
|
|
# define ARCH_CLONE __clone
|
|
#endif
|
|
|
|
/* See the comments in pthread_create.c for the requirements for these
|
|
two macros and the create_thread function. */
|
|
|
|
#define START_THREAD_DEFN \
|
|
static int __attribute__ ((noreturn)) start_thread (void *arg)
|
|
#define START_THREAD_SELF arg
|
|
|
|
/* pthread_create.c defines this using START_THREAD_DEFN
|
|
We need a forward declaration here so we can take its address. */
|
|
static int start_thread (void *arg) __attribute__ ((noreturn));
|
|
|
|
static int
|
|
create_thread (struct pthread *pd, const struct pthread_attr *attr,
|
|
bool stopped_start, STACK_VARIABLES_PARMS, bool *thread_ran)
|
|
{
|
|
/* Determine whether the newly created threads has to be started
|
|
stopped since we have to set the scheduling parameters or set the
|
|
affinity. */
|
|
if (attr != NULL
|
|
&& (__glibc_unlikely (attr->cpuset != NULL)
|
|
|| __glibc_unlikely ((attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0)))
|
|
stopped_start = true;
|
|
|
|
pd->stopped_start = stopped_start;
|
|
if (__glibc_unlikely (stopped_start))
|
|
/* We make sure the thread does not run far by forcing it to get a
|
|
lock. We lock it here too so that the new thread cannot continue
|
|
until we tell it to. */
|
|
lll_lock (pd->lock, LLL_PRIVATE);
|
|
|
|
/* We rely heavily on various flags the CLONE function understands:
|
|
|
|
CLONE_VM, CLONE_FS, CLONE_FILES
|
|
These flags select semantics with shared address space and
|
|
file descriptors according to what POSIX requires.
|
|
|
|
CLONE_SIGHAND, CLONE_THREAD
|
|
This flag selects the POSIX signal semantics and various
|
|
other kinds of sharing (itimers, POSIX timers, etc.).
|
|
|
|
CLONE_SETTLS
|
|
The sixth parameter to CLONE determines the TLS area for the
|
|
new thread.
|
|
|
|
CLONE_PARENT_SETTID
|
|
The kernels writes the thread ID of the newly created thread
|
|
into the location pointed to by the fifth parameters to CLONE.
|
|
|
|
Note that it would be semantically equivalent to use
|
|
CLONE_CHILD_SETTID but it is be more expensive in the kernel.
|
|
|
|
CLONE_CHILD_CLEARTID
|
|
The kernels clears the thread ID of a thread that has called
|
|
sys_exit() in the location pointed to by the seventh parameter
|
|
to CLONE.
|
|
|
|
The termination signal is chosen to be zero which means no signal
|
|
is sent. */
|
|
const int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SYSVSEM
|
|
| CLONE_SIGHAND | CLONE_THREAD
|
|
| CLONE_SETTLS | CLONE_PARENT_SETTID
|
|
| CLONE_CHILD_CLEARTID
|
|
| 0);
|
|
|
|
TLS_DEFINE_INIT_TP (tp, pd);
|
|
|
|
if (__glibc_unlikely (ARCH_CLONE (&start_thread, STACK_VARIABLES_ARGS,
|
|
clone_flags, pd, &pd->tid, tp, &pd->tid)
|
|
== -1))
|
|
return errno;
|
|
|
|
/* It's started now, so if we fail below, we'll have to cancel it
|
|
and let it clean itself up. */
|
|
*thread_ran = true;
|
|
|
|
/* Now we have the possibility to set scheduling parameters etc. */
|
|
if (attr != NULL)
|
|
{
|
|
INTERNAL_SYSCALL_DECL (err);
|
|
int res;
|
|
|
|
/* Set the affinity mask if necessary. */
|
|
if (attr->cpuset != NULL)
|
|
{
|
|
assert (stopped_start);
|
|
|
|
res = INTERNAL_SYSCALL (sched_setaffinity, err, 3, pd->tid,
|
|
attr->cpusetsize, attr->cpuset);
|
|
|
|
if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (res, err)))
|
|
err_out:
|
|
{
|
|
/* The operation failed. We have to kill the thread.
|
|
We let the normal cancellation mechanism do the work. */
|
|
|
|
INTERNAL_SYSCALL_DECL (err2);
|
|
(void) INTERNAL_SYSCALL (tgkill, err2, 3,
|
|
THREAD_GETMEM (THREAD_SELF, pid),
|
|
pd->tid, SIGCANCEL);
|
|
|
|
return INTERNAL_SYSCALL_ERRNO (res, err);
|
|
}
|
|
}
|
|
|
|
/* Set the scheduling parameters. */
|
|
if ((attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0)
|
|
{
|
|
assert (stopped_start);
|
|
|
|
res = INTERNAL_SYSCALL (sched_setscheduler, err, 3, pd->tid,
|
|
pd->schedpolicy, &pd->schedparam);
|
|
|
|
if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (res, err)))
|
|
goto err_out;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|