]> mj.ucw.cz Git - libucw.git/blobdiff - ucw/mainloop.c
UCW::CGI: Bug fixes
[libucw.git] / ucw / mainloop.c
index c68089d616c180a4623c28d923c237dfa8f21446..4138aae1ec237aa42d79d90a56f6833f901b42b4 100644 (file)
@@ -10,6 +10,7 @@
 #undef LOCAL_DEBUG
 
 #include "ucw/lib.h"
+#include "ucw/heap.h"
 #include "ucw/mainloop.h"
 
 #include <stdio.h>
@@ -28,13 +29,27 @@ 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;
+#define GBUF_PREFIX(x) main_timer_table_##x
+#define GBUF_TYPE struct main_timer *
+#include "ucw/gbuf.h"
+static uns main_timer_cnt;
+static main_timer_table_t main_timer_table;
+#define MAIN_TIMER_LESS(x,y) ((x)->expires < (y)->expires)
+#define MAIN_TIMER_SWAP(heap,a,b,t) (t=heap[a], heap[a]=heap[b], heap[b]=t, heap[a]->index=(a), heap[b]->index=(b))
+
+clist main_file_list, main_hook_list, main_hook_done_list, main_process_list;
 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 sig_atomic_t chld_received = 0;
+static volatile sig_atomic_t chld_received = 0;
+
+#ifdef O_CLOEXEC
+// On recent Linux systems, O_CLOEXEC flag is available and we can get around
+// the race condition of poll().
+#define USE_SELF_PIPE
 static int sig_pipe_recv, sig_pipe_send;
+#endif
 
 void
 main_get_time(void)
@@ -50,9 +65,10 @@ void
 main_init(void)
 {
   DBG("MAIN: Initializing");
-  clist_init(&main_timer_list);
+  main_timer_cnt = 0;
   clist_init(&main_file_list);
   clist_init(&main_hook_list);
+  clist_init(&main_hook_done_list);
   clist_init(&main_process_list);
   main_file_cnt = 0;
   main_poll_table_obsolete = 1;
@@ -66,15 +82,36 @@ timer_add(struct main_timer *tm, timestamp_t expires)
     DBG("MAIN: Setting timer %p (expire at now+%lld)", tm, (long long)(expires-main_now));
   else
     DBG("MAIN: Clearing timer %p", tm);
-  if (tm->expires)
-    clist_remove(&tm->n);
-  tm->expires = expires;
-  if (expires)
+  if (tm->expires < expires)
     {
-      cnode *t = main_timer_list.head.next;
-      while (t != &main_timer_list.head && ((struct main_timer *) t)->expires < expires)
-       t = t->next;
-      clist_insert_before(&tm->n, t);
+      if (!tm->expires)
+       {
+         tm->expires = expires;
+         tm->index = ++main_timer_cnt;
+         main_timer_table_grow(&main_timer_table, tm->index + 1);
+         main_timer_table.ptr[tm->index] = tm;
+         HEAP_INSERT(struct main_timer *, main_timer_table.ptr, main_timer_cnt, MAIN_TIMER_LESS, MAIN_TIMER_SWAP);
+       }
+      else
+       {
+         tm->expires = expires;
+         HEAP_INCREASE(struct main_timer *, main_timer_table.ptr, main_timer_cnt, MAIN_TIMER_LESS, MAIN_TIMER_SWAP, tm->index);
+       }
+    }
+  else if (tm->expires > expires)
+    {
+      if (!expires)
+       {
+         ASSERT(tm->index && tm->index <= main_timer_cnt);
+         HEAP_DELETE(struct main_timer *, main_timer_table.ptr, main_timer_cnt, MAIN_TIMER_LESS, MAIN_TIMER_SWAP, tm->index);
+         tm->index = 0;
+         tm->expires = 0;
+       }
+      else
+       {
+         tm->expires = expires;
+         HEAP_DECREASE(struct main_timer *, main_timer_table.ptr, main_timer_cnt, MAIN_TIMER_LESS, MAIN_TIMER_SWAP, tm->index);
+       }
     }
 }
 
@@ -250,26 +287,48 @@ hook_del(struct main_hook *ho)
   ho->n.next = ho->n.prev = NULL;
 }
 
+#ifdef USE_SELF_PIPE
 static void
 main_sigchld_handler(int x UNUSED)
 {
+  int old_errno = errno;
   DBG("SIGCHLD received");
   chld_received = 1;
-  if(write(sig_pipe_send, "c", 1) == -1 && errno != EAGAIN)
-    die("Could not write to selfpipe: %m");
+  ssize_t result;
+  while((result = write(sig_pipe_send, "c", 1)) == -1 && errno == EINTR);
+  if(result == -1 && errno != EAGAIN)
+    msg(L_SIGHANDLER|L_ERROR, "Could not write to self-pipe: %m");
+  errno = old_errno;
 }
 
 static int
 dummy_read_handler(struct main_file *mp)
 {
-  char *buffer[1024];
+  char buffer[1024];
   ssize_t result = read(mp->fd, buffer, 1024);
   if(result == -1 && errno != EAGAIN)
-    die("Could not read from selfpipe: %m");
+    msg(L_ERROR, "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);
+}
+#else
+static void
+main_sigchld_handler(int x UNUSED)
+{
+  DBG("SIGCHLD received");
+}
+#endif
+
 void
 process_add(struct main_process *mp)
 {
@@ -279,9 +338,12 @@ process_add(struct main_process *mp)
   clist_add_tail(&main_process_list, &mp->n);
   if (!main_sigchld_set_up)
     {
+#ifdef USE_SELF_PIPE
       int pipe_result[2];
-      if(pipe2(pipe_result, O_NONBLOCK|O_CLOEXEC) == -1)
+      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;
@@ -290,6 +352,7 @@ process_add(struct main_process *mp)
        .read_handler = dummy_read_handler
       };
       file_add(&self_pipe);
+#endif
       struct sigaction sa;
       bzero(&sa, sizeof(sa));
       sa.sa_handler = main_sigchld_handler;
@@ -338,9 +401,11 @@ main_debug(void)
 #ifdef CONFIG_DEBUG
   msg(L_DEBUG, "### Main loop status on %lld", (long long)main_now);
   msg(L_DEBUG, "\tActive timers:");
-  struct main_timer *tm;
-  CLIST_WALK(tm, main_timer_list)
-    msg(L_DEBUG, "\t\t%p (expires %lld, data %p)", tm, (long long)(tm->expires ? tm->expires-main_now : 999999), tm->data);
+  for (uns i = 1; i <= main_timer_cnt; i++)
+    {
+      struct main_timer *tm = main_timer_table.ptr[i];
+      msg(L_DEBUG, "\t\t%p (expires %lld, data %p)", tm, (long long)(tm->expires ? tm->expires-main_now : 999999), tm->data);
+    }
   struct main_file *fi;
   msg(L_DEBUG, "\tActive files:");
   CLIST_WALK(fi, main_file_list)
@@ -349,6 +414,8 @@ main_debug(void)
        (long long)(fi->timer.expires ? fi->timer.expires-main_now : 999999), fi->data);
   msg(L_DEBUG, "\tActive hooks:");
   struct main_hook *ho;
+  CLIST_WALK(ho, main_hook_done_list)
+    msg(L_DEBUG, "\t\t%p (func %p, data %p)", ho, ho->handler, ho->data);
   CLIST_WALK(ho, main_hook_list)
     msg(L_DEBUG, "\t\t%p (func %p, data %p)", ho, ho->handler, ho->data);
   msg(L_DEBUG, "\tActive processes:");
@@ -387,32 +454,33 @@ void
 main_loop(void)
 {
   DBG("MAIN: Entering main_loop");
-  ASSERT(main_timer_list.head.next);
+  ASSERT(main_hook_list.head.next);
 
   struct main_file *fi;
   struct main_hook *ho;
   struct main_timer *tm;
   struct main_process *pr;
-  cnode *tmp;
 
   main_get_time();
   for (;;)
     {
       timestamp_t wake = main_now + 1000000000;
-      while ((tm = clist_head(&main_timer_list)) && tm->expires <= main_now)
+      while (main_timer_cnt && (tm = main_timer_table.ptr[1])->expires <= main_now)
        {
          DBG("MAIN: Timer %p expired at now-%lld", tm, (long long)(main_now - tm->expires));
          tm->handler(tm);
        }
       int hook_min = HOOK_RETRY;
       int hook_max = HOOK_SHUTDOWN;
-      CLIST_WALK_DELSAFE(ho, main_hook_list, tmp)
+      while (ho = clist_remove_head(&main_hook_list))
        {
+         clist_add_tail(&main_hook_done_list, &ho->n);
          DBG("MAIN: Hook %p", ho);
          int ret = ho->handler(ho);
          hook_min = MIN(hook_min, ret);
          hook_max = MAX(hook_max, ret);
        }
+      clist_move(&main_hook_list, &main_hook_done_list);
       if (hook_min == HOOK_SHUTDOWN ||
          hook_min == HOOK_DONE && hook_max == HOOK_DONE ||
          main_shutdown)
@@ -424,6 +492,10 @@ main_loop(void)
        wake = 0;
       if (main_poll_table_obsolete)
        main_rebuild_poll_table();
+#ifndef USE_SELF_PIPE
+      // We don't have a reliable flag without the self-pipe.
+      chld_received = 1;
+#endif
       if (chld_received && !clist_empty(&main_process_list))
        {
          int stat;
@@ -446,10 +518,10 @@ main_loop(void)
              wake = 0;
            }
        }
-      if ((tm = clist_head(&main_timer_list)) && tm->expires < wake)
+      if (main_timer_cnt && (tm = main_timer_table.ptr[1])->expires < wake)
        wake = tm->expires;
       main_get_time();
-      int timeout = (wake ? wake - main_now : 0);
+      int timeout = ((wake > main_now) ? wake - main_now : 0);
       DBG("MAIN: Poll for %d fds and timeout %d ms", main_file_cnt, timeout);
       int p = poll(main_poll_table, main_file_cnt, timeout);
       timestamp_t old_now = main_now;