Fixed SIGALRM race.
[debugger.git] / testsuite.c
index bcf81d1..76a3d09 100644 (file)
 #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)
 {
@@ -61,6 +46,46 @@ 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;
@@ -138,7 +163,7 @@ static void registry_handler (int signo)
   raise (signo);
 }
 
-static void child_pause (void)
+static __attribute__((__noreturn__)) void child_pause (void)
 {
   for (;;)
     pause ();
@@ -152,16 +177,14 @@ static void child_alrm_handler (int signo)
   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);
@@ -243,7 +266,7 @@ struct child_spawner
     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;
@@ -318,6 +341,7 @@ static void body_spawner (void *(*child) (void *data, void *input), void *data,
   pid_t inferior;
   struct attach_state_struct *attach_state;
   int i;
+  void (*handler_orig) (int signo);
 
   assert (input == NULL);
 
@@ -351,7 +375,12 @@ static void body_spawner (void *(*child) (void *data, void *input), void *data,
   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);
@@ -413,7 +442,7 @@ static void *spawn_singlethreaded (void *data, void *input)
   return pass (data, input);
 }
 
-static void *spawn_threaded_parent_start (void *arg)
+static __attribute__((__noreturn__)) void *spawn_threaded_parent_start (void *arg)
 {
   for (;;)
     pause ();
@@ -421,81 +450,82 @@ static void *spawn_threaded_parent_start (void *arg)
   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),
@@ -519,6 +549,24 @@ static void handler_sigusr1 (int signo)
   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;
@@ -547,6 +595,9 @@ int main (int argc, char **argv)
 
   do
     {
+      pid_t pid_threaded, pid_threaded_got;
+      int status;
+
       while (loops_print > 0)
         {
          printf ("%lu\n", loops);
@@ -556,16 +607,24 @@ int main (int argc, char **argv)
       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;
 }