| /* This testcase is part of GDB, the GNU debugger. |
| |
| Copyright 2012-2024 Free Software Foundation, Inc. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program 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 General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| #define _GNU_SOURCE |
| #include <pthread.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sched.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <dirent.h> |
| #include <assert.h> |
| #include <unistd.h> |
| |
| /* Count the number of tasks/threads in the PID thread group. */ |
| |
| static int |
| count_tasks (pid_t pid) |
| { |
| char path[100]; |
| int count; |
| DIR *d; |
| |
| snprintf (path, sizeof (path), "/proc/%d/task/", (int) pid); |
| d = opendir (path); |
| if (d == NULL) |
| return -1; |
| |
| for (count = 0; readdir (d) != NULL; count++) |
| ; |
| closedir (d); |
| |
| /* Account for '.' and '..'. */ |
| assert (count > 2); |
| return count - 2; |
| } |
| |
| pthread_attr_t attr[CPU_SETSIZE]; |
| pthread_t thr[CPU_SETSIZE]; |
| |
| static void * |
| mythread (void *_arg) |
| { |
| return NULL; |
| } |
| |
| int |
| main () |
| { |
| int i; |
| |
| for (i = 0; i < CPU_SETSIZE; i++) |
| { |
| cpu_set_t set; |
| int ret; |
| |
| pthread_attr_init (&attr[i]); |
| CPU_ZERO_S (sizeof (set), &set); |
| CPU_SET_S (i, sizeof (set), &set); |
| |
| ret = pthread_attr_setaffinity_np (&attr[i], sizeof (set), &set); |
| if (ret != 0) |
| { |
| fprintf (stderr, "set_affinity: %d: %s\n", ret, strerror (ret)); |
| exit (3); |
| } |
| ret = pthread_create (&thr[i], &attr[i], mythread, NULL); |
| /* Should fail with EINVAL at some point. */ |
| if (ret != 0) |
| { |
| unsigned long t; |
| |
| fprintf (stderr, "pthread_create: %d: %s\n", ret, strerror (ret)); |
| |
| /* Wait for all threads to exit. pthread_create spawns a |
| clone thread even in the failing case, as it can only try |
| to set the affinity after creating the thread. That new |
| thread is immediately canceled (because setting the |
| affinity fails), by killing it with a SIGCANCEL signal, |
| which may end up in pthread_cancel/unwind paths, which |
| may trigger a libgcc_s.so load, making the thread hit the |
| solib-event breakpoint. Now, if we would let the program |
| exit without waiting, sometimes it would happen that the |
| inferior exits just while we're handling the solib-event, |
| resulting in errors being thrown due to failing ptrace |
| call fails (with ESCHR), breaking the test. */ |
| t = 16; |
| while (count_tasks (getpid ()) > 1) |
| { |
| usleep (t); |
| |
| if (t < 256) |
| t *= 2; |
| } |
| |
| /* Normal exit, because this is what we are expecting. */ |
| exit (0); |
| } |
| } |
| |
| /* Should not normally be reached. */ |
| exit (1); |
| } |