2004-01-21 09:47:11 +08:00
|
|
|
/* Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
|
2003-01-28 18:42:28 +08:00
|
|
|
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, write to the Free
|
|
|
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
|
|
02111-1307 USA. */
|
|
|
|
|
|
|
|
#include <sched.h>
|
|
|
|
#include <setjmp.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <atomic.h>
|
|
|
|
#include <ldsodefs.h>
|
|
|
|
#include <tls.h>
|
|
|
|
|
2003-08-03 14:47:02 +08:00
|
|
|
#include "kernel-features.h"
|
|
|
|
|
2003-01-28 18:42:28 +08:00
|
|
|
|
|
|
|
#define CLONE_SIGNAL (CLONE_SIGHAND | CLONE_THREAD)
|
|
|
|
|
|
|
|
/* Unless otherwise specified, the thread "register" is going to be
|
|
|
|
initialized with a pointer to the TCB. */
|
|
|
|
#ifndef TLS_VALUE
|
|
|
|
# define TLS_VALUE pd
|
|
|
|
#endif
|
|
|
|
|
2003-03-11 17:20:36 +08:00
|
|
|
#ifndef ARCH_CLONE
|
|
|
|
# define ARCH_CLONE __clone
|
|
|
|
#endif
|
|
|
|
|
2003-01-28 18:42:28 +08:00
|
|
|
|
2003-03-12 13:45:00 +08:00
|
|
|
#ifndef TLS_MULTIPLE_THREADS_IN_TCB
|
|
|
|
/* Variable set to a nonzero value if more than one thread runs or ran. */
|
|
|
|
int __pthread_multiple_threads attribute_hidden;
|
|
|
|
/* Pointer to the corresponding variable in libc. */
|
|
|
|
int *__libc_multiple_threads_ptr attribute_hidden;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2003-01-28 18:42:28 +08:00
|
|
|
static int
|
2003-07-23 07:56:53 +08:00
|
|
|
do_clone (struct pthread *pd, const struct pthread_attr *attr,
|
|
|
|
int clone_flags, int (*fct) (void *), STACK_VARIABLES_PARMS)
|
2003-01-28 18:42:28 +08:00
|
|
|
{
|
|
|
|
#ifdef PREPARE_CREATE
|
|
|
|
PREPARE_CREATE;
|
|
|
|
#endif
|
|
|
|
|
2003-08-03 14:47:02 +08:00
|
|
|
/* Lame old kernels do not have CLONE_STOPPED support. For those do
|
|
|
|
not pass the flag, not instead use the futex method. */
|
|
|
|
#ifndef __ASSUME_CLONE_STOPPED
|
|
|
|
# define final_clone_flags clone_flags & ~CLONE_STOPPED
|
|
|
|
if (clone_flags & CLONE_STOPPED)
|
|
|
|
/* 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);
|
|
|
|
#else
|
|
|
|
# define final_clone_flags clone_flags
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (ARCH_CLONE (fct, STACK_VARIABLES_ARGS, final_clone_flags,
|
2003-07-20 16:56:05 +08:00
|
|
|
pd, &pd->tid, TLS_VALUE, &pd->tid) == -1)
|
|
|
|
/* Failed. */
|
|
|
|
return errno;
|
2003-01-28 18:42:28 +08:00
|
|
|
|
2003-07-20 16:56:05 +08:00
|
|
|
/* Now we have the possibility to set scheduling parameters etc. */
|
|
|
|
if (__builtin_expect ((clone_flags & CLONE_STOPPED) != 0, 0))
|
2003-01-28 18:42:28 +08:00
|
|
|
{
|
2003-07-20 16:56:05 +08:00
|
|
|
INTERNAL_SYSCALL_DECL (err);
|
|
|
|
int res = 0;
|
2003-01-28 18:42:28 +08:00
|
|
|
|
2003-07-20 16:56:05 +08:00
|
|
|
/* Set the affinity mask if necessary. */
|
|
|
|
if (attr->cpuset != NULL)
|
2003-01-28 18:42:28 +08:00
|
|
|
{
|
2003-07-20 16:56:05 +08:00
|
|
|
res = INTERNAL_SYSCALL (sched_setaffinity, err, 3, pd->tid,
|
|
|
|
sizeof (cpu_set_t), attr->cpuset);
|
2003-01-28 18:42:28 +08:00
|
|
|
|
2003-07-20 16:56:05 +08:00
|
|
|
if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
|
2003-08-05 05:53:09 +08:00
|
|
|
{
|
|
|
|
/* The operation failed. We have to kill the thread. First
|
|
|
|
send it the cancellation signal. */
|
|
|
|
INTERNAL_SYSCALL_DECL (err2);
|
|
|
|
err_out:
|
2004-02-27 16:09:35 +08:00
|
|
|
#if __ASSUME_TGKILL
|
|
|
|
(void) INTERNAL_SYSCALL (tgkill, err2, 3,
|
|
|
|
THREAD_GETMEM (THREAD_SELF, pid),
|
|
|
|
pd->tid, SIGCANCEL);
|
|
|
|
|
|
|
|
# ifdef __ASSUME_CLONE_STOPPED
|
|
|
|
/* Then wake it up so that the signal can be processed. */
|
|
|
|
(void) INTERNAL_SYSCALL (tgkill, err2, 3,
|
|
|
|
THREAD_GETMEM (THREAD_SELF, pid),
|
|
|
|
pd->tid, SIGCONT);
|
|
|
|
# endif
|
|
|
|
#else
|
2003-08-05 05:53:09 +08:00
|
|
|
(void) INTERNAL_SYSCALL (tkill, err2, 2, pd->tid, SIGCANCEL);
|
|
|
|
|
2004-02-27 16:09:35 +08:00
|
|
|
# ifdef __ASSUME_CLONE_STOPPED
|
2003-08-05 05:53:09 +08:00
|
|
|
/* Then wake it up so that the signal can be processed. */
|
|
|
|
(void) INTERNAL_SYSCALL (tkill, err2, 2, pd->tid, SIGCONT);
|
2004-02-27 16:09:35 +08:00
|
|
|
# endif
|
2003-08-05 05:53:09 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return INTERNAL_SYSCALL_ERRNO (res, err);
|
|
|
|
}
|
2003-07-20 16:56:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the scheduling parameters. */
|
|
|
|
if ((attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0)
|
|
|
|
{
|
2003-08-03 06:31:52 +08:00
|
|
|
res = INTERNAL_SYSCALL (sched_setscheduler, err, 3, pd->tid,
|
|
|
|
pd->schedpolicy, &pd->schedparam);
|
2003-07-20 16:56:05 +08:00
|
|
|
|
2003-08-03 06:31:52 +08:00
|
|
|
if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
|
|
|
|
goto err_out;
|
2003-07-20 16:56:05 +08:00
|
|
|
}
|
|
|
|
|
2003-08-03 14:47:02 +08:00
|
|
|
#ifdef __ASSUME_CLONE_STOPPED
|
2003-07-20 16:56:05 +08:00
|
|
|
/* Now start the thread for real. */
|
2004-02-27 16:09:35 +08:00
|
|
|
# if __ASSUME_TGKILL
|
|
|
|
res = INTERNAL_SYSCALL (tgkill, err, 3, THREAD_GETMEM (THREAD_SELF, pid),
|
|
|
|
pd->tid, SIGCONT);
|
|
|
|
# else
|
2003-07-20 16:56:05 +08:00
|
|
|
res = INTERNAL_SYSCALL (tkill, err, 2, pd->tid, SIGCONT);
|
2004-02-27 16:09:35 +08:00
|
|
|
# endif
|
2003-07-20 16:56:05 +08:00
|
|
|
|
|
|
|
/* If something went wrong, kill the thread. */
|
|
|
|
if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
|
2003-08-05 05:53:09 +08:00
|
|
|
goto err_out;
|
2003-08-03 14:47:02 +08:00
|
|
|
#endif
|
2003-01-28 18:42:28 +08:00
|
|
|
}
|
|
|
|
|
2003-07-20 16:56:05 +08:00
|
|
|
/* We now have for sure more than one thread. The main thread might
|
|
|
|
not yet have the flag set. No need to set the global variable
|
|
|
|
again if this is what we use. */
|
|
|
|
THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2003-07-23 07:56:53 +08:00
|
|
|
create_thread (struct pthread *pd, const struct pthread_attr *attr,
|
2003-07-20 16:56:05 +08:00
|
|
|
STACK_VARIABLES_PARMS)
|
|
|
|
{
|
|
|
|
#ifdef TLS_TCB_AT_TP
|
|
|
|
assert (pd->header.tcb != NULL);
|
2003-01-28 18:42:28 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* 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_SIGNAL
|
|
|
|
This flag selects the POSIX signal semantics.
|
|
|
|
|
|
|
|
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
|
2003-03-05 03:20:26 +08:00
|
|
|
sys_exit() in the location pointed to by the seventh parameter
|
|
|
|
to CLONE.
|
2003-01-28 18:42:28 +08:00
|
|
|
|
|
|
|
CLONE_DETACHED
|
|
|
|
No signal is generated if the thread exists and it is
|
|
|
|
automatically reaped.
|
|
|
|
|
|
|
|
The termination signal is chosen to be zero which means no signal
|
|
|
|
is sent. */
|
2003-07-20 16:56:05 +08:00
|
|
|
int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL
|
|
|
|
| CLONE_SETTLS | CLONE_PARENT_SETTID
|
2004-01-21 09:47:11 +08:00
|
|
|
| CLONE_CHILD_CLEARTID | CLONE_SYSVSEM
|
|
|
|
#if __ASSUME_NO_CLONE_DETACHED == 0
|
|
|
|
| CLONE_DETACHED
|
|
|
|
#endif
|
2003-07-20 16:56:05 +08:00
|
|
|
| 0);
|
|
|
|
|
|
|
|
/* If the newly created threads has to be started stopped since we
|
|
|
|
have to set the scheduling parameters or set the affinity we set
|
|
|
|
the CLONE_STOPPED flag. */
|
|
|
|
if (attr != NULL && (attr->cpuset != NULL
|
|
|
|
|| (attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0))
|
|
|
|
clone_flags |= CLONE_STOPPED;
|
2003-01-28 18:42:28 +08:00
|
|
|
|
2003-07-20 16:56:05 +08:00
|
|
|
if (__builtin_expect (THREAD_GETMEM (THREAD_SELF, report_events), 0))
|
|
|
|
{
|
|
|
|
/* The parent thread is supposed to report events. Check whether
|
|
|
|
the TD_CREATE event is needed, too. */
|
|
|
|
const int _idx = __td_eventword (TD_CREATE);
|
|
|
|
const uint32_t _mask = __td_eventmask (TD_CREATE);
|
2003-01-28 18:42:28 +08:00
|
|
|
|
2003-07-20 16:56:05 +08:00
|
|
|
if ((_mask & (__nptl_threads_events.event_bits[_idx]
|
|
|
|
| pd->eventbuf.eventmask.event_bits[_idx])) != 0)
|
|
|
|
{
|
2003-08-03 14:47:02 +08:00
|
|
|
/* Create the thread. We always create the thread stopped
|
|
|
|
so that it does not get far before we tell the debugger. */
|
|
|
|
int res = do_clone (pd, attr, clone_flags | CLONE_STOPPED,
|
|
|
|
start_thread, STACK_VARIABLES_ARGS);
|
2003-07-20 16:56:05 +08:00
|
|
|
if (res == 0)
|
|
|
|
{
|
|
|
|
/* 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. */
|
|
|
|
pd->eventbuf.eventnum = TD_CREATE;
|
|
|
|
pd->eventbuf.eventdata = pd;
|
|
|
|
|
|
|
|
/* Enqueue the descriptor. */
|
|
|
|
do
|
|
|
|
pd->nextevent = __nptl_last_event;
|
|
|
|
while (atomic_compare_and_exchange_bool_acq (&__nptl_last_event,
|
|
|
|
pd, pd->nextevent)
|
|
|
|
!= 0);
|
|
|
|
|
|
|
|
/* Now call the function which signals the event. */
|
|
|
|
__nptl_create_event ();
|
|
|
|
|
|
|
|
/* And finally restart the new thread. */
|
|
|
|
lll_unlock (pd->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NEED_DL_SYSINFO
|
2003-12-11 07:02:33 +08:00
|
|
|
assert (THREAD_SELF_SYSINFO == THREAD_SYSINFO(pd));
|
2003-07-20 16:56:05 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Actually create the thread. */
|
2003-08-03 14:47:02 +08:00
|
|
|
int res = do_clone (pd, attr, clone_flags, start_thread,
|
|
|
|
STACK_VARIABLES_ARGS);
|
|
|
|
|
|
|
|
#ifndef __ASSUME_CLONE_STOPPED
|
|
|
|
if (res == 0 && (clone_flags & CLONE_STOPPED))
|
|
|
|
{
|
|
|
|
/* And finally restart the new thread. */
|
|
|
|
lll_unlock (pd->lock);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return res;
|
2003-01-28 18:42:28 +08:00
|
|
|
}
|