#include "debugger.c"
-#define tkill(tid, sig) syscall (SYS_tkill, (tid), (sig))
+#define TIMEOUT_SECS 4
+#undef LOOPS_MIN
+#define LOOPS_MIN 1000
-static volatile int spawned_threads_count; /* Set for the specific spawner. */
+#define tkill(tid, sig) syscall (SYS_tkill, (tid), (sig))
-static struct attach_state_struct *attach_checked (pid_t pid, int redelivered_expect)
-{
- struct attach_state_struct *attach_state;
-
- STATE (pid, (1 << STATE_SLEEPING) | (1 << STATE_RUNNING) | (1 << STATE_STOPPED));
- attach_state = attach (pid);
- if (redelivered_expect != attach_state_redelivered_get (attach_state))
- {
- fprintf (stderr, "Expecting redelivery of %d but found %d\n",
- redelivered_expect, attach_state_redelivered_get (attach_state));
- abort ();
- }
- assert (spawned_threads_count == attach_state_threads_count_get (attach_state));
- /* FIXME: Why also STATE_STOPPED? */
- STATE (pid, (1 << STATE_PTRACED) | (1 << STATE_STOPPED));
- return attach_state;
-}
static void detach_checked (struct attach_state_struct *attach_state)
{
}
}
+static volatile int spawned_threads_count; /* Set for the specific spawner. */
+
+static struct attach_state_struct *attach_checked (pid_t pid, int redelivered_expect)
+{
+ struct attach_state_struct *attach_state;
+ time_t timeout = time (NULL) + TIMEOUT_SECS;
+ unsigned loops = 0;
+
+ STATE (pid, (1 << STATE_SLEEPING) | (1 << STATE_RUNNING) | (1 << STATE_STOPPED));
+ do
+ {
+ attach_state = attach (pid);
+ if (redelivered_expect != attach_state_redelivered_get (attach_state))
+ {
+ fprintf (stderr, "Expecting redelivery of %d but found %d\n",
+ redelivered_expect,
+ attach_state_redelivered_get (attach_state));
+ abort ();
+ }
+ /* During the inferior's initialization we may catch less threads. */
+ assert (attach_state_threads_count_get (attach_state)
+ <= spawned_threads_count);
+ if (attach_state_threads_count_get (attach_state)
+ == spawned_threads_count
+ /* Inferior may got stopped before it could initialize. */
+ || attach_state_stopped_get (attach_state) == 1)
+ {
+ /* FIXME: Why also STATE_STOPPED? */
+ STATE (pid, (1 << STATE_PTRACED) | (1 << STATE_STOPPED));
+ return attach_state;
+ }
+ /* WARNING: Currently we never use REDELIVERED_EXPECT but we would have to
+ probably reset it back to 0 otherwise. */
+ assert (redelivered_expect == 0);
+ detach_checked (attach_state);
+ }
+ while (loops++ < LOOPS_MIN || time (NULL) < timeout);
+ abort ();
+}
+
struct registry
{
struct registry *next;
raise (signo);
}
-static void child_pause (void)
+static __attribute__((__noreturn__)) void child_pause (void)
{
for (;;)
pause ();
raise (SIGALRM);
}
-static void child_alrm (void)
+static __attribute__((__noreturn__)) void child_alrm (void)
{
- void (*handler_orig) (int signo);
#if 0
int i;
sigset_t oldset;
#endif
- handler_orig = signal (SIGALRM, child_alrm_handler);
- assert (handler_orig == SIG_DFL);
+ /* Assumed already setup SIGALRM for CHILD_ALRM_HANDLER. */
#if 0
i = sigprocmask (SIG_BLOCK, NULL, &oldset);
int fd;
};
-static void *child_spawner (void *param_voidpointer, void *input)
+static __attribute__((__noreturn__)) void *child_spawner (void *param_voidpointer, void *input)
{
struct child_spawner *param = param_voidpointer;
pid_t inferior;
pid_t inferior;
struct attach_state_struct *attach_state;
int i;
+ void (*handler_orig) (int signo);
assert (input == NULL);
murder (inferior);
/* Attach to a stopped process with already pending SIGALRM. */
+ /* Setup the handler already in the parent to avoid the child race. */
+ handler_orig = signal (SIGALRM, child_alrm_handler);
+ assert (handler_orig == SIG_DFL);
inferior = (unsigned long) (*child) (data, child_alrm);
+ handler_orig = signal (SIGALRM, handler_orig);
+ assert (handler_orig == child_alrm_handler);
STATE (inferior, 1 << STATE_SLEEPING);
delay ();
i = tkill (inferior, SIGSTOP);
return pass (data, input);
}
-static void *spawn_threaded_parent_start (void *arg)
+static __attribute__((__noreturn__)) void *spawn_threaded_parent_start (void *arg)
{
for (;;)
pause ();
abort ();
}
+static void *libpthread_handle;
+static int (*libpthread_create_pointer) (pthread_t *thread,
+ const pthread_attr_t *attr,
+ void *(*start_routine) (void *),
+ void *arg);
+static int (*libpthread_join_pointer) (pthread_t thread, void **value_ptr);
+
+static void libpthread_open (void)
+{
+ assert (libpthread_handle == NULL);
+
+ libpthread_handle = dlopen ("libpthread.so.0", RTLD_LAZY);
+ assert (libpthread_handle != NULL);
+
+ libpthread_create_pointer = dlsym (libpthread_handle, "pthread_create");
+ assert (libpthread_create_pointer != NULL);
+ libpthread_join_pointer = dlsym (libpthread_handle, "pthread_join");
+ assert (libpthread_join_pointer != NULL);
+
+}
+
+static void libpthread_close (void)
+{
+ int i;
+
+ assert (libpthread_handle != NULL);
+
+ i = dlclose (libpthread_handle);
+ assert (i == 0);
+ libpthread_handle = NULL;
+}
+
static void *spawn_threaded_parent (void *data, void *input)
{
pthread_t thread;
int i;
- void *handle;
- void *retval;
- int (*pthread_create_pointer) (pthread_t *thread,
- const pthread_attr_t *attr,
- void *(*start_routine) (void *),
- void *arg);
-
- handle = dlopen ("libpthread.so.0", RTLD_LAZY);
- assert (handle != NULL);
- pthread_create_pointer = dlsym (handle, "pthread_create");
- assert (pthread_create_pointer != NULL);
-
- i = (*pthread_create_pointer) (&thread, NULL, spawn_threaded_parent_start,
- NULL);
- assert (i == 0);
- retval = pass (data, input);
- i = dlclose (handle);
+ assert (libpthread_create_pointer != NULL);
+ i = (*libpthread_create_pointer) (&thread, NULL, spawn_threaded_parent_start,
+ NULL);
assert (i == 0);
-
- return retval;
+ return pass (data, input);
}
struct spawn_threaded_child_start
{
void *data;
void *input;
- void *lib_handle;
};
static void *spawn_threaded_child_start (void *arg_voidpointer)
{
struct spawn_threaded_child_start *arg = arg_voidpointer;
- void *retval;
- int i;
-
- retval = pass (arg->data, arg->input);
- i = dlclose (arg->lib_handle);
- assert (i == 0);
-
- return retval;
+ return pass (arg->data, arg->input);
}
-static void *spawn_threaded_child (void *data, void *input)
+static __attribute__((__noreturn__)) void *spawn_threaded_child (void *data, void *input)
{
pthread_t thread;
int i;
struct spawn_threaded_child_start arg_local;
- int (*pthread_create_pointer) (pthread_t *thread,
- const pthread_attr_t *attr,
- void *(*start_routine) (void *),
- void *arg);
- int (*pthread_join_pointer) (pthread_t thread, void **value_ptr);
-
- arg_local.lib_handle = dlopen ("libpthread.so.0", RTLD_LAZY);
- assert (arg_local.lib_handle != NULL);
- pthread_create_pointer = dlsym (arg_local.lib_handle, "pthread_create");
- assert (pthread_create_pointer != NULL);
- pthread_join_pointer = dlsym (arg_local.lib_handle, "pthread_join");
- assert (pthread_join_pointer != NULL);
arg_local.data = data;
arg_local.input = input;
- i = (*pthread_create_pointer) (&thread, NULL, spawn_threaded_child_start,
- &arg_local);
+ assert (libpthread_create_pointer != NULL);
+ i = (*libpthread_create_pointer) (&thread, NULL, spawn_threaded_child_start,
+ &arg_local);
assert (i == 0);
- i = (*pthread_join_pointer) (thread, NULL);
+ assert (libpthread_join_pointer != NULL);
+ i = (*libpthread_join_pointer) (thread, NULL);
assert (i == 0);
_exit (EXIT_SUCCESS);
+ /* NOTREACHED */
+ abort ();
}
static void body_maywaiter (void *(*child) (void *data, void *input),
loops_print++;
}
+static __attribute__((__noreturn__)) void *tests_threaded (void *data, void *input)
+{
+ libpthread_open ();
+
+ spawned_threads_count = 2;
+ body_maywaiter (spawn_threaded_parent, NULL, NULL);
+
+ spawned_threads_count = 2;
+ body_maywaiter (spawn_threaded_child, NULL, NULL);
+
+ libpthread_close ();
+
+ registry_cleanup ();
+ _exit (EXIT_SUCCESS);
+ /* NOTREACHED */
+ abort ();
+}
+
int main (int argc, char **argv)
{
int loop = 0;
do
{
+ pid_t pid_threaded, pid_threaded_got;
+ int status;
+
while (loops_print > 0)
{
printf ("%lu\n", loops);
spawned_threads_count = 0;
body_maywaiter (spawn_singlethreaded, NULL, NULL);
- spawned_threads_count = 2;
- body_maywaiter (spawn_threaded_parent, NULL, NULL);
-
- spawned_threads_count = 2;
- body_maywaiter (spawn_threaded_child, NULL, NULL);
+ /* We cannot rely on LIBPTHREAD_CLOSE as libpthread would be still loaded
+ with ATTACH_STATE_REDELIVERED_GET () reporting 1 for
+ SPAWN_SINGLETHREADED. */
+ pid_threaded = spawn (tests_threaded, NULL, NULL, -1);
+ pid_threaded_got = waitpid (pid_threaded, &status, 0);
+ assert (pid_threaded_got == pid_threaded);
+ assert (!WIFSIGNALED (status)); /* Improve readability in the failed case. */
+ assert (!WIFSTOPPED (status)); /* Improve readability in the failed case. */
+ assert (WIFEXITED (status));
+ assert (WEXITSTATUS (status) == 0);
+ STATE (pid_threaded, 1 << STATE_ENOENT);
+ registry_remove (pid_threaded);
registry_cleanup ();
loops++;
}
- while (loop != 0);
+ /* Run at least twice to test libpthread_close (). */
+ while (loop != 0 || loops == 1);
return EXIT_SUCCESS;
}