]> mj.ucw.cz Git - libucw.git/commitdiff
Main: Optimize calls to epoll_ctl()
authorMartin Mares <mj@ucw.cz>
Fri, 30 Jul 2010 18:42:57 +0000 (20:42 +0200)
committerMartin Mares <mj@ucw.cz>
Wed, 18 Aug 2010 16:12:58 +0000 (18:12 +0200)
It often happens that a file callback is removed and then reinstated
again before the next main loop iteration (e.g., on a boundary between
two blocks if block_io is used). This caused lots of unnecessary
calls to epoll_ctl().

We now put all files on which file_chg() was called to a separate list
and just before we call epoll_wait(), we process this list and check
which files have changed their event mask since the last iteration.

ucw/mainloop.c
ucw/mainloop.h

index 5921368330b2ad5c0537553be025324b17fed8da..561ddd3d691a1cdea055f9dc10a85482dd677d1a 100644 (file)
@@ -69,6 +69,7 @@ main_new(void)
   if (m->epoll_fd < 0)
     die("epoll_create() failed: %m");
   m->epoll_events = xmalloc(EPOLL_BUF_SIZE * sizeof(struct epoll_event *));
+  clist_init(&m->file_recalc_list);
 #else
   m->poll_table_obsolete = 1;
 #endif
@@ -99,6 +100,7 @@ main_delete(struct main_context *m)
   ASSERT(clist_empty(&m->signal_list));
   GARY_FREE(m->timer_table);
 #ifdef CONFIG_UCW_EPOLL
+  ASSERT(clist_empty(&m->file_recalc_list));
   xfree(m->epoll_events);
   close(m->epoll_fd);
 #else
@@ -253,6 +255,7 @@ file_add(struct main_file *fi)
   };
   if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, fi->fd, &evt) < 0)
     die("epoll_ctl() failed: %m");
+  fi->last_want_events = evt.events;
 #else
   m->poll_table_obsolete = 1;
 #endif
@@ -264,12 +267,8 @@ void
 file_chg(struct main_file *fi)
 {
 #ifdef CONFIG_UCW_EPOLL
-  struct epoll_event evt = {
-    .events = file_want_events(fi),
-    .data.ptr = fi,
-  };
-  if (epoll_ctl(main_current()->epoll_fd, EPOLL_CTL_MOD, fi->fd, &evt) < 0)
-    die("epoll_ctl() failed: %m");
+  clist_remove(&fi->n);
+  clist_add_tail(&main_current()->file_recalc_list, &fi->n);
 #else
   struct pollfd *p = fi->pollfd;
   if (p)
@@ -537,6 +536,11 @@ main_debug_context(struct main_context *m UNUSED)
     msg(L_DEBUG, "\t\t%p (fd %d, rh %p, wh %p, data %p) [pending events: %x]",
        fi, fi->fd, fi->read_handler, fi->write_handler, fi->data, fi->events);
     // FIXME: Can we display status of block_io requests somehow?
+#ifdef CONFIG_UCW_EPOLL
+  CLIST_FOR_EACH(struct main_file *, fi, m->file_recalc_list)
+    msg(L_DEBUG, "\t\t%p (fd %d, rh %p, wh %p, data %p) [pending recalculation]",
+       fi, fi->fd, fi->read_handler, fi->write_handler, fi->data);
+#endif
   msg(L_DEBUG, "\tActive hooks:");
   CLIST_FOR_EACH(struct main_hook *, ho, m->hook_done_list)
     msg(L_DEBUG, "\t\t%p (func %p, data %p)", ho, ho->handler, ho->data);
@@ -594,10 +598,34 @@ process_hooks(struct main_context *m)
     return HOOK_IDLE;
 }
 
-#ifndef CONFIG_UCW_EPOLL
+#ifdef CONFIG_UCW_EPOLL
+
+static void
+recalc_files(struct main_context *m)
+{
+  struct main_file *fi;
+
+  while (fi = clist_remove_head(&m->file_recalc_list))
+    {
+      struct epoll_event evt = {
+       .events = file_want_events(fi),
+       .data.ptr = fi,
+      };
+      if (evt.events != fi->last_want_events)
+       {
+         DBG("MAIN: Changing requested events for fd %d to %x", fi->fd, evt.events);
+         fi->last_want_events = evt.events;
+         if (epoll_ctl(main_current()->epoll_fd, EPOLL_CTL_MOD, fi->fd, &evt) < 0)
+           die("epoll_ctl() failed: %m");
+       }
+      clist_add_tail(&m->file_list, &fi->n);
+    }
+}
+
+#else
 
 static void
-main_rebuild_poll_table(struct main_context *m)
+rebuild_poll_table(struct main_context *m)
 {
   GARY_INIT_OR_RESIZE(m->poll_table, m->file_cnt);
   GARY_INIT_OR_RESIZE(m->poll_file_table, m->file_cnt);
@@ -608,8 +636,8 @@ main_rebuild_poll_table(struct main_context *m)
   CLIST_FOR_EACH(struct main_file *, fi, m->file_list)
     {
       p->fd = fi->fd;
+      p->events = file_want_events(fi);
       fi->pollfd = p++;
-      file_chg(fi);
       *pf++ = fi;
     }
   m->poll_table_obsolete = 0;
@@ -643,11 +671,12 @@ main_loop(void)
       int timeout = ((wake > m->now) ? wake - m->now : 0);
 
 #ifdef CONFIG_UCW_EPOLL
+      recalc_files(m);
       DBG("MAIN: Epoll for %d fds and timeout %d ms", m->file_cnt, timeout);
       int n = epoll_wait(m->epoll_fd, m->epoll_events, EPOLL_BUF_SIZE, timeout);
 #else
       if (m->poll_table_obsolete)
-       main_rebuild_poll_table(m);
+       rebuild_poll_table(m);
       DBG("MAIN: Poll for %d fds and timeout %d ms", m->file_cnt, timeout);
       int n = poll(m->poll_table, m->file_cnt, timeout);
 #endif
index d2b2d3406ff14b204e669d5447aa3ee75f612e45..930124007df6d544e4fdff078a714a595fabbbf2 100644 (file)
@@ -42,6 +42,7 @@ struct main_context {
 #ifdef CONFIG_UCW_EPOLL
   int epoll_fd;                                /* File descriptor used for epoll */
   struct epoll_event *epoll_events;
+  clist file_recalc_list;
 #else
   uns poll_table_obsolete;
   struct pollfd *poll_table;
@@ -196,7 +197,9 @@ struct main_file {
   int (*write_handler)(struct main_file *fi);
   void *data;                                  /* [*] Data for use by the handlers */
   uns events;
-#ifndef CONFIG_UCW_EPOLL
+#ifdef CONFIG_UCW_EPOLL
+  uns last_want_events;
+#else
   struct pollfd *pollfd;
 #endif
 };