aboutsummaryrefslogtreecommitdiff
path: root/nptl/sysdeps/pthread/createthread.c
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2003-07-20 08:56:05 +0000
committerUlrich Drepper <drepper@redhat.com>2003-07-20 08:56:05 +0000
commit80f536dbf20510cbabdb042c30baa453d0b069c1 (patch)
tree63be8c7d248de20158ef492570d1b18b55a0cf40 /nptl/sysdeps/pthread/createthread.c
parent4e0dc4af0b279a659b94d66beb1397eb473af7d2 (diff)
downloadglibc-80f536dbf20510cbabdb042c30baa453d0b069c1.tar
glibc-80f536dbf20510cbabdb042c30baa453d0b069c1.tar.gz
glibc-80f536dbf20510cbabdb042c30baa453d0b069c1.tar.bz2
glibc-80f536dbf20510cbabdb042c30baa453d0b069c1.zip
Update.
2003-07-20 Ulrich Drepper <drepper@redhat.com> * Makefile (libpthread-routines): Add pthread_attr_getaffinity and pthread_attr_setaffinity. * Versions [libpthread] (GLIBC_2.3.3): Likewise. * sysdeps/unix/sysv/linux/pthread_attr_getaffinity.c: New file. * sysdeps/unix/sysv/linux/pthread_attr_setaffinity.c: New file. * pthread_attr_destroy.c: Free cpuset element if allocated. * pthread_create.c: Pass iattr as additional parameter to create_thread. * sysdeps/pthread/createthread.c: If attribute is provided and a new thread is created with affinity set or scheduling parameters, start thread with CLONE_STOPPED. * sysdeps/pthread/pthread.h: Declare pthread_attr_getaffinity and pthread_attr_setaffinity. * sysdeps/unix/sysv/linux/internaltypes.h (struct pthread_attr): Add cpuset element.
Diffstat (limited to 'nptl/sysdeps/pthread/createthread.c')
-rw-r--r--nptl/sysdeps/pthread/createthread.c196
1 files changed, 131 insertions, 65 deletions
diff --git a/nptl/sysdeps/pthread/createthread.c b/nptl/sysdeps/pthread/createthread.c
index 7563a2b71c..4a02d1c14b 100644
--- a/nptl/sysdeps/pthread/createthread.c
+++ b/nptl/sysdeps/pthread/createthread.c
@@ -27,6 +27,10 @@
#define CLONE_SIGNAL (CLONE_SIGHAND | CLONE_THREAD)
+/* XXX Remove when definition is common place. */
+#ifndef CLONE_STOPPED
+# define CLONE_STOPPED 0x02000000
+#endif
/* Unless otherwise specified, the thread "register" is going to be
initialized with a pointer to the TCB. */
@@ -48,72 +52,84 @@ int *__libc_multiple_threads_ptr attribute_hidden;
static int
-create_thread (struct pthread *pd, STACK_VARIABLES_PARMS)
+do_clone (struct pthread *pd, struct pthread_attr *attr, int clone_flags,
+ int (*fct) (void *), STACK_VARIABLES_PARMS)
{
#ifdef PREPARE_CREATE
PREPARE_CREATE;
#endif
-#ifdef TLS_TCB_AT_TP
- assert (pd->header.tcb != NULL);
-#endif
+ if (ARCH_CLONE (fct, STACK_VARIABLES_ARGS, clone_flags,
+ pd, &pd->tid, TLS_VALUE, &pd->tid) == -1)
+ /* Failed. */
+ return errno;
- if (__builtin_expect (THREAD_GETMEM (THREAD_SELF, report_events), 0))
+ /* Now we have the possibility to set scheduling parameters etc. */
+ if (__builtin_expect ((clone_flags & CLONE_STOPPED) != 0, 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);
+ INTERNAL_SYSCALL_DECL (err);
+ int res = 0;
- if ((_mask & (__nptl_threads_events.event_bits[_idx]
- | pd->eventbuf.eventmask.event_bits[_idx])) != 0)
+ /* Set the affinity mask if necessary. */
+ if (attr->cpuset != NULL)
{
- /* We have to report the new thread. 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);
+ res = INTERNAL_SYSCALL (sched_setaffinity, err, 3, pd->tid,
+ sizeof (cpu_set_t), attr->cpuset);
- /* Create the thread. */
- if (ARCH_CLONE (start_thread_debug, STACK_VARIABLES_ARGS,
- CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL |
- CLONE_SETTLS | CLONE_PARENT_SETTID |
- CLONE_CHILD_CLEARTID | CLONE_DETACHED |
- CLONE_SYSVSEM | 0, pd, &pd->tid, TLS_VALUE,
- &pd->tid) == -1)
- /* Failed. */
- return errno;
-
- /* 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);
-
- /* 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 0;
+ if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
+ goto err_out;
+ }
+
+ /* Set the scheduling parameters. */
+ if ((attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0)
+ {
+ res = INTERNAL_SYSCALL (sched_setparam, err, 2, pd->tid,
+ &pd->schedparam);
+
+ if (__builtin_expect (! INTERNAL_SYSCALL_ERROR_P (res, err), 1))
+ {
+ res = INTERNAL_SYSCALL (sched_setscheduler, err, 2, pd->tid,
+ &pd->schedpolicy);
+
+ if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
+ goto err_out;
+ }
+ }
+
+ /* Now start the thread for real. */
+ res = INTERNAL_SYSCALL (tkill, err, 2, pd->tid, SIGCONT);
+
+ /* If something went wrong, kill the thread. */
+ if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
+ {
+ /* The operation failed. We have to kill the thread. First
+ send it the cancellation signal. */
+ INTERNAL_SYSCALL_DECL (err2);
+ err_out:
+ (void) INTERNAL_SYSCALL (tkill, err2, 2, pd->tid, SIGCANCEL);
+
+ /* Then wake it up so that the signal can be processed. */
+ (void) INTERNAL_SYSCALL (tkill, err, 2, pd->tid, SIGCONT);
+
+ return INTERNAL_SYSCALL_ERRNO (res, err);
}
}
-#ifdef NEED_DL_SYSINFO
- assert (THREAD_GETMEM (THREAD_SELF, header.sysinfo) == pd->header.sysinfo);
+ /* 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
+create_thread (struct pthread *pd, struct pthread_attr *attr,
+ STACK_VARIABLES_PARMS)
+{
+#ifdef TLS_TCB_AT_TP
+ assert (pd->header.tcb != NULL);
#endif
/* We rely heavily on various flags the CLONE function understands:
@@ -147,18 +163,68 @@ create_thread (struct pthread *pd, STACK_VARIABLES_PARMS)
The termination signal is chosen to be zero which means no signal
is sent. */
- if (ARCH_CLONE (start_thread, STACK_VARIABLES_ARGS,
- CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL |
- CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID |
- CLONE_DETACHED | CLONE_SYSVSEM | 0, pd, &pd->tid, TLS_VALUE,
- &pd->tid) == -1)
- /* Failed. */
- return errno;
+ int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL
+ | CLONE_SETTLS | CLONE_PARENT_SETTID
+ | CLONE_CHILD_CLEARTID | CLONE_DETACHED | CLONE_SYSVSEM
+ | 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;
- /* 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);
+ 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);
- return 0;
+ if ((_mask & (__nptl_threads_events.event_bits[_idx]
+ | pd->eventbuf.eventmask.event_bits[_idx])) != 0)
+ {
+ /* We have to report the new thread. 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);
+
+ /* Create the thread. */
+ int res = do_clone (pd, attr, clone_flags, start_thread_debug,
+ STACK_VARIABLES_ARGS);
+ 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
+ assert (THREAD_GETMEM (THREAD_SELF, header.sysinfo) == pd->header.sysinfo);
+#endif
+
+ /* Actually create the thread. */
+ return do_clone (pd, attr, clone_flags, start_thread, STACK_VARIABLES_ARGS);
}