]> mj.ucw.cz Git - libucw.git/blobdiff - ucw/mainloop.c
Fix the selfpipe in mainloop
[libucw.git] / ucw / mainloop.c
index f7994748dac991d6a20fd0ae4dfb5a4d6f7b34db..c348ecd1dfda29e79ab36dedc4db430ad55c4b87 100644 (file)
@@ -24,7 +24,8 @@
 #include <sys/time.h>
 
 timestamp_t main_now;
-sh_time_t main_now_seconds;
+ucw_time_t main_now_seconds;
+timestamp_t main_idle_time;
 uns main_shutdown;
 
 clist main_timer_list, main_file_list, main_hook_list, main_process_list;
@@ -32,6 +33,8 @@ static uns main_file_cnt;
 static uns main_poll_table_obsolete, main_poll_table_size;
 static struct pollfd *main_poll_table;
 static uns main_sigchld_set_up;
+static volatile sig_atomic_t chld_received = 0;
+static int sig_pipe_recv, sig_pipe_send;
 
 void
 main_get_time(void)
@@ -250,7 +253,35 @@ hook_del(struct main_hook *ho)
 static void
 main_sigchld_handler(int x UNUSED)
 {
+  int old_errno = errno;
   DBG("SIGCHLD received");
+  chld_received = 1;
+  ssize_t result;
+  while((result = write(sig_pipe_send, "c", 1)) == -1 && errno == EINTR);
+  if(result == -1 && errno != EAGAIN)
+    die("Could not write to selfpipe: %m");
+  errno = old_errno;
+}
+
+static int
+dummy_read_handler(struct main_file *mp)
+{
+  char buffer[1024];
+  ssize_t result = read(mp->fd, buffer, 1024);
+  if(result == -1 && errno != EAGAIN && errno != EINTR)
+    die("Could not read from selfpipe: %m");
+  file_chg(mp);
+  return result == 1024;
+}
+
+static void
+pipe_configure(int fd)
+{
+  int flags;
+  if((flags = fcntl(fd, F_GETFL)) == -1 || fcntl(fd, F_SETFL, flags|O_NONBLOCK))
+    die("Could not set file descriptor %d to non-blocking: %m", fd);
+  if((flags = fcntl(fd, F_GETFD)) == -1 || fcntl(fd, F_SETFD, flags|O_CLOEXEC))
+    die("Could not set file descriptor %d to close-on-exec: %m", fd);
 }
 
 void
@@ -262,12 +293,26 @@ process_add(struct main_process *mp)
   clist_add_tail(&main_process_list, &mp->n);
   if (!main_sigchld_set_up)
     {
+      int pipe_result[2];
+      if(pipe(pipe_result) == -1)
+       die("Could not create selfpipe:%m");
+      pipe_configure(pipe_result[0]);
+      pipe_configure(pipe_result[1]);
+      sig_pipe_recv = pipe_result[0];
+      sig_pipe_send = pipe_result[1];
+      static struct main_file self_pipe;
+      self_pipe = (struct main_file) {
+       .fd = sig_pipe_recv,
+       .read_handler = dummy_read_handler
+      };
+      file_add(&self_pipe);
       struct sigaction sa;
       bzero(&sa, sizeof(sa));
       sa.sa_handler = main_sigchld_handler;
       sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
       sigaction(SIGCHLD, &sa, NULL);
       main_sigchld_set_up = 1;
+      chld_received = 1; // The signal may have come before the handler
     }
 }
 
@@ -366,9 +411,9 @@ main_loop(void)
   struct main_process *pr;
   cnode *tmp;
 
+  main_get_time();
   for (;;)
     {
-      main_get_time();
       timestamp_t wake = main_now + 1000000000;
       while ((tm = clist_head(&main_timer_list)) && tm->expires <= main_now)
        {
@@ -395,11 +440,12 @@ main_loop(void)
        wake = 0;
       if (main_poll_table_obsolete)
        main_rebuild_poll_table();
-      if (!clist_empty(&main_process_list))
+      if (chld_received && !clist_empty(&main_process_list))
        {
          int stat;
          pid_t pid;
          wake = MIN(wake, main_now + 10000);
+         chld_received = 0;
          while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
            {
              DBG("MAIN: Child %d exited with status %x", pid, stat);
@@ -416,15 +462,18 @@ main_loop(void)
              wake = 0;
            }
        }
-      /* FIXME: Here is a small race window where SIGCHLD can come unnoticed. */
       if ((tm = clist_head(&main_timer_list)) && tm->expires < wake)
        wake = tm->expires;
+      main_get_time();
       int timeout = (wake ? wake - main_now : 0);
       DBG("MAIN: Poll for %d fds and timeout %d ms", main_file_cnt, timeout);
-      if (poll(main_poll_table, main_file_cnt, timeout))
+      int p = poll(main_poll_table, main_file_cnt, timeout);
+      timestamp_t old_now = main_now;
+      main_get_time();
+      main_idle_time += main_now - old_now;
+      if (p > 0)
        {
          struct pollfd *p = main_poll_table;
-         main_get_time();
          CLIST_WALK(fi, main_file_list)
            {
              if (p->revents & (POLLIN | POLLHUP | POLLERR))
@@ -462,50 +511,50 @@ static void dread(struct main_file *fi)
 {
   if (fi->rpos < fi->rlen)
     {
-      log(L_INFO, "Read EOF");
+      msg(L_INFO, "Read EOF");
       file_del(fi);
     }
   else
     {
-      log(L_INFO, "Read done");
+      msg(L_INFO, "Read done");
       file_read(fi, rb, sizeof(rb));
     }
 }
 
 static void derror(struct main_file *fi, int cause)
 {
-  log(L_INFO, "Error: %m !!! (cause %d)", cause);
+  msg(L_INFO, "Error: %m !!! (cause %d)", cause);
   file_del(fi);
 }
 
 static void dwrite(struct main_file *fi UNUSED)
 {
-  log(L_INFO, "Write done");
+  msg(L_INFO, "Write done");
 }
 
 static int dhook(struct main_hook *ho UNUSED)
 {
-  log(L_INFO, "Hook called");
+  msg(L_INFO, "Hook called");
   return 0;
 }
 
 static void dtimer(struct main_timer *tm)
 {
-  log(L_INFO, "Timer tick");
+  msg(L_INFO, "Timer tick");
   timer_add(tm, main_now + 10000);
 }
 
 static void dentry(void)
 {
-  log(L_INFO, "*** SUBPROCESS START ***");
+  msg(L_INFO, "*** SUBPROCESS START ***");
   sleep(2);
-  log(L_INFO, "*** SUBPROCESS FINISH ***");
+  msg(L_INFO, "*** SUBPROCESS FINISH ***");
   exit(0);
 }
 
 static void dexit(struct main_process *pr)
 {
-  log(L_INFO, "Subprocess %d exited with status %x", pr->pid, pr->status);
+  msg(L_INFO, "Subprocess %d exited with status %x", pr->pid, pr->status);
 }
 
 int
@@ -539,7 +588,7 @@ main(void)
   main_debug();
 
   main_loop();
-  log(L_INFO, "Finished.");
+  msg(L_INFO, "Finished.");
 }
 
 #endif