]> mj.ucw.cz Git - libucw.git/commitdiff
Main: Split off block I/O
authorMartin Mares <mj@ucw.cz>
Wed, 28 Jul 2010 22:30:10 +0000 (00:30 +0200)
committerMartin Mares <mj@ucw.cz>
Wed, 18 Aug 2010 16:12:58 +0000 (18:12 +0200)
Block-based I/O is no longer hard-wired in the mainloop itself,
it became a separate module built on the top of main_file. While the
timer embedded in main_block_io is somewhat illogical, I have decided
to keep it to make porting of old code easier.

I plan to add another I/O layer in the future, which will handle
more complex buffering capable of reading lines and other variable-size
data structures.

ucw/Makefile
ucw/main-block.c [new file with mode: 0644]
ucw/mainloop.c
ucw/mainloop.h

index bfb039a5c316bd5a5f1d044e8bf2acedf073b842..c419f1eccc7ac710883b051eebd6815d70447061 100644 (file)
@@ -1,4 +1,4 @@
-# Makefile for the UCW Library (c) 1997--2009 Martin Mares <mj@ucw.cz>
+# Makefile for the UCW Library (c) 1997--2010 Martin Mares <mj@ucw.cz>
 
 DIRS+=ucw
 LIBUCW=$(o)/ucw/libucw.pc
@@ -23,7 +23,8 @@ LIBUCW_MODS= \
        prime primetable random timer randomkey \
        bit-ffs bit-fls \
        url \
-       mainloop exitstatus runcmd sighandler \
+       mainloop main-block \
+       exitstatus runcmd sighandler \
        lizard lizard-safe adler32 \
        md5 sha1 sha1-hmac \
        base64 base224 \
diff --git a/ucw/main-block.c b/ucw/main-block.c
new file mode 100644 (file)
index 0000000..7363c2c
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ *     UCW Library -- Main Loop: Block I/O
+ *
+ *     (c) 2004--2010 Martin Mares <mj@ucw.cz>
+ *
+ *     This software may be freely distributed and used according to the terms
+ *     of the GNU Lesser General Public License.
+ */
+
+#define LOCAL_DEBUG
+
+#include "ucw/lib.h"
+#include "ucw/mainloop.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+static void
+block_io_timer_expired(struct main_timer *tm)
+{
+  struct main_block_io *bio = tm->data;
+  timer_del(&bio->timer);
+  if (bio->error_handler)
+    bio->error_handler(bio, MFERR_TIMEOUT);
+}
+
+void
+block_io_add(struct main_block_io *bio, int fd)
+{
+  bio->file.fd = fd;
+  file_add(&bio->file);
+  bio->timer.handler = block_io_timer_expired;
+  bio->timer.data = bio;
+}
+
+void
+block_io_del(struct main_block_io *bio)
+{
+  timer_del(&bio->timer);
+  file_del(&bio->file);
+}
+
+static int
+block_io_read_handler(struct main_file *fi)
+{
+  struct main_block_io *bio = (struct main_block_io *) fi;
+
+  while (bio->rpos < bio->rlen)
+    {
+      int l = read(fi->fd, bio->rbuf + bio->rpos, bio->rlen - bio->rpos);
+      DBG("BIO: FD %d: read %d", fi->fd, l);
+      if (l < 0)
+       {
+         if (errno != EINTR && errno != EAGAIN && bio->error_handler)
+           bio->error_handler(bio, MFERR_READ);
+         return 0;
+       }
+      else if (!l)
+       break;
+      bio->rpos += l;
+    }
+  DBG("BIO: FD %d done read %d of %d", fi->fd, bio->rpos, bio->rlen);
+  fi->read_handler = NULL;
+  file_chg(fi);
+  bio->read_done(bio);
+  return 1;
+}
+
+static int
+block_io_write_handler(struct main_file *fi)
+{
+  struct main_block_io *bio = (struct main_block_io *) fi;
+
+  while (bio->wpos < bio->wlen)
+    {
+      int l = write(fi->fd, bio->wbuf + bio->wpos, bio->wlen - bio->wpos);
+      DBG("BIO: FD %d: write %d", fi->fd, l);
+      if (l < 0)
+       {
+         if (errno != EINTR && errno != EAGAIN && bio->error_handler)
+           bio->error_handler(bio, MFERR_WRITE);
+         return 0;
+       }
+      bio->wpos += l;
+    }
+  DBG("BIO: FD %d done write %d", fi->fd, bio->wpos);
+  fi->write_handler = NULL;
+  file_chg(fi);
+  bio->write_done(bio);
+  return 1;
+}
+
+void
+block_io_read(struct main_block_io *bio, void *buf, uns len)
+{
+  ASSERT(bio->file.n.next);
+  if (len)
+    {
+      bio->file.read_handler = block_io_read_handler;
+      bio->rbuf = buf;
+      bio->rpos = 0;
+      bio->rlen = len;
+    }
+  else
+    {
+      bio->file.read_handler = NULL;
+      bio->rbuf = NULL;
+      bio->rpos = bio->rlen = 0;
+    }
+  file_chg(&bio->file);
+}
+
+void
+block_io_write(struct main_block_io *bio, void *buf, uns len)
+{
+  ASSERT(bio->file.n.next);
+  if (len)
+    {
+      bio->file.write_handler = block_io_write_handler;
+      bio->wbuf = buf;
+      bio->wpos = 0;
+      bio->wlen = len;
+    }
+  else
+    {
+      bio->file.write_handler = NULL;
+      bio->wbuf = NULL;
+      bio->wpos = bio->wlen = 0;
+    }
+  file_chg(&bio->file);
+}
index 15e71407e1e35ceba9da6033e273ba76d942ef78..7b5d504506d9c09ee54d07d661cdbf940c63f3af 100644 (file)
@@ -206,15 +206,6 @@ timer_del(struct main_timer *tm)
   timer_add(tm, 0);
 }
 
-static void
-file_timer_expired(struct main_timer *tm)
-{
-  struct main_file *fi = tm->data;
-  timer_del(&fi->timer);
-  if (fi->error_handler)
-    fi->error_handler(fi, MFERR_TIMEOUT);
-}
-
 void
 file_add(struct main_file *fi)
 {
@@ -223,8 +214,6 @@ file_add(struct main_file *fi)
   DBG("MAIN: Adding file %p (fd=%d)", fi, fi->fd);
   ASSERT(!fi->n.next);
   clist_add_tail(&m->file_list, &fi->n);
-  fi->timer.handler = file_timer_expired;
-  fi->timer.data = fi;
   m->file_cnt++;
   m->poll_table_obsolete = 1;
   if (fcntl(fi->fd, F_SETFL, O_NONBLOCK) < 0)
@@ -252,106 +241,12 @@ file_del(struct main_file *fi)
 
   DBG("MAIN: Deleting file %p (fd=%d)", fi, fi->fd);
   ASSERT(fi->n.next);
-  timer_del(&fi->timer);
   clist_remove(&fi->n);
   m->file_cnt--;
   m->poll_table_obsolete = 1;
   fi->n.next = fi->n.prev = NULL;
 }
 
-static int
-file_read_handler(struct main_file *fi)
-{
-  while (fi->rpos < fi->rlen)
-    {
-      int l = read(fi->fd, fi->rbuf + fi->rpos, fi->rlen - fi->rpos);
-      DBG("MAIN: FD %d: read %d", fi->fd, l);
-      if (l < 0)
-       {
-         if (errno != EINTR && errno != EAGAIN && fi->error_handler)
-           fi->error_handler(fi, MFERR_READ);
-         return 0;
-       }
-      else if (!l)
-       break;
-      fi->rpos += l;
-    }
-  DBG("MAIN: FD %d done read %d of %d", fi->fd, fi->rpos, fi->rlen);
-  fi->read_handler = NULL;
-  file_chg(fi);
-  fi->read_done(fi);
-  return 1;
-}
-
-static int
-file_write_handler(struct main_file *fi)
-{
-  while (fi->wpos < fi->wlen)
-    {
-      int l = write(fi->fd, fi->wbuf + fi->wpos, fi->wlen - fi->wpos);
-      DBG("MAIN: FD %d: write %d", fi->fd, l);
-      if (l < 0)
-       {
-         if (errno != EINTR && errno != EAGAIN && fi->error_handler)
-           fi->error_handler(fi, MFERR_WRITE);
-         return 0;
-       }
-      fi->wpos += l;
-    }
-  DBG("MAIN: FD %d done write %d", fi->fd, fi->wpos);
-  fi->write_handler = NULL;
-  file_chg(fi);
-  fi->write_done(fi);
-  return 1;
-}
-
-void
-file_read(struct main_file *fi, void *buf, uns len)
-{
-  ASSERT(fi->n.next);
-  if (len)
-    {
-      fi->read_handler = file_read_handler;
-      fi->rbuf = buf;
-      fi->rpos = 0;
-      fi->rlen = len;
-    }
-  else
-    {
-      fi->read_handler = NULL;
-      fi->rbuf = NULL;
-      fi->rpos = fi->rlen = 0;
-    }
-  file_chg(fi);
-}
-
-void
-file_write(struct main_file *fi, void *buf, uns len)
-{
-  ASSERT(fi->n.next);
-  if (len)
-    {
-      fi->write_handler = file_write_handler;
-      fi->wbuf = buf;
-      fi->wpos = 0;
-      fi->wlen = len;
-    }
-  else
-    {
-      fi->write_handler = NULL;
-      fi->wbuf = NULL;
-      fi->wpos = fi->wlen = 0;
-    }
-  file_chg(fi);
-}
-
-void
-file_set_timeout(struct main_file *fi, timestamp_t expires)
-{
-  ASSERT(fi->n.next);
-  timer_add(&fi->timer, expires);
-}
-
 void
 file_close_all(void)
 {
@@ -584,9 +479,9 @@ main_debug_context(struct main_context *m UNUSED)
     }
   msg(L_DEBUG, "\tActive files:");
   CLIST_FOR_EACH(struct main_file *, fi, m->file_list)
-    msg(L_DEBUG, "\t\t%p (fd %d, rh %p, wh %p, eh %p, expires %lld, data %p)",
-       fi, fi->fd, fi->read_handler, fi->write_handler, fi->error_handler,
-       (long long)(fi->timer.expires ? fi->timer.expires - m->now : 999999), fi->data);
+    msg(L_DEBUG, "\t\t%p (fd %d, rh %p, wh %p, data %p)",
+       fi, fi->fd, fi->read_handler, fi->write_handler, fi->data);
+    // FIXME: Can we display status of block_io requests somehow?
   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);
@@ -706,34 +601,34 @@ main_loop(void)
 #ifdef TEST
 
 static struct main_process mp;
-static struct main_file fin, fout;
+static struct main_block_io fin, fout;
 static struct main_hook hook;
 static struct main_timer tm;
 static struct main_signal sg;
 
 static byte rb[16];
 
-static void dread(struct main_file *fi)
+static void dread(struct main_block_io *bio)
 {
-  if (fi->rpos < fi->rlen)
+  if (bio->rpos < bio->rlen)
     {
       msg(L_INFO, "Read EOF");
-      file_del(fi);
+      block_io_del(bio);
     }
   else
     {
       msg(L_INFO, "Read done");
-      file_read(fi, rb, sizeof(rb));
+      block_io_read(bio, rb, sizeof(rb));
     }
 }
 
-static void derror(struct main_file *fi, int cause)
+static void derror(struct main_block_io *bio, int cause)
 {
   msg(L_INFO, "Error: %m !!! (cause %d)", cause);
-  file_del(fi);
+  block_io_del(bio);
 }
 
-static void dwrite(struct main_file *fi UNUSED)
+static void dwrite(struct main_block_io *bio UNUSED)
 {
   msg(L_INFO, "Write done");
 }
@@ -775,17 +670,15 @@ main(void)
   log_init(NULL);
   main_init();
 
-  fin.fd = 0;
   fin.read_done = dread;
   fin.error_handler = derror;
-  file_add(&fin);
-  file_read(&fin, rb, sizeof(rb));
+  block_io_add(&fin, 0);
+  block_io_read(&fin, rb, sizeof(rb));
 
-  fout.fd = 1;
   fout.write_done = dwrite;
   fout.error_handler = derror;
-  file_add(&fout);
-  file_write(&fout, "Hello, world!\n", 14);
+  block_io_add(&fout, 1);
+  block_io_write(&fout, "Hello, world!\n", 14);
 
   hook.handler = dhook;
   hook_add(&hook);
index 6f67414afa15c0f59193e9257643ea1a6968e9f7..95565d1362dcdce07372c8594d9011245b13b782 100644 (file)
@@ -188,30 +188,10 @@ struct main_file {
   int fd;                                      /* [*] File descriptor */
   int (*read_handler)(struct main_file *fi);   /* [*] To be called when ready for reading/writing; must call file_chg() afterwards */
   int (*write_handler)(struct main_file *fi);
-  void (*error_handler)(struct main_file *fi, int cause);      /* [*] Handler to call on errors */
   void *data;                                  /* [*] Data for use by the handlers */
-  byte *rbuf;                                  /* Read/write pointers for use by file_read/write */
-  uns rpos, rlen;
-  byte *wbuf;
-  uns wpos, wlen;
-  void (*read_done)(struct main_file *fi);     /* [*] Called when file_read is finished; rpos < rlen if EOF */
-  void (*write_done)(struct main_file *fi);    /* [*] Called when file_write is finished */
-  struct main_timer timer;
   struct pollfd *pollfd;
 };
 
-/**
- * Specifies when or why an error happened. This is passed to the error handler.
- * `errno` is still set to the original source of error. The only exception
- * is `MFERR_TIMEOUT`, in which case `errno` is not set and the only possible
- * cause of it is timeout on the file descriptor (see @file_set_timeout).
- **/
-enum main_file_err_cause {
-  MFERR_READ,
-  MFERR_WRITE,
-  MFERR_TIMEOUT
-};
-
 /**
  * Inserts a <<struct_main_file,`main_file`>> structure into the mainloop to be
  * watched for activity. You can call this at any time, even inside a handler
@@ -233,7 +213,41 @@ void file_chg(struct main_file *fi);
  **/
 void file_del(struct main_file *fi);
 /**
- * Asks the mainloop to read @len bytes of data from @fi into @buf.
+ * Closes all file descriptors known to mainloop. Often used between fork()
+ * and exec().
+ **/
+void file_close_all(void);
+
+struct main_block_io {
+  struct main_file file;
+  byte *rbuf;                                  /* Read/write pointers for use by file_read/write */
+  uns rpos, rlen;
+  byte *wbuf;
+  uns wpos, wlen;
+  void (*read_done)(struct main_block_io *bio);        /* [*] Called when file_read is finished; rpos < rlen if EOF */
+  void (*write_done)(struct main_block_io *bio);       /* [*] Called when file_write is finished */
+  void (*error_handler)(struct main_block_io *bio, int cause); /* [*] Handler to call on errors */
+  struct main_timer timer;
+  void *data;                                  /* [*] Data for use by the handlers */
+};
+
+void block_io_add(struct main_block_io *bio, int fd);
+void block_io_del(struct main_block_io *bio);
+
+/**
+ * Specifies when or why an error happened. This is passed to the error handler.
+ * `errno` is still set to the original source of error. The only exception
+ * is `MFERR_TIMEOUT`, in which case `errno` is not set and the only possible
+ * cause of it is timeout on the file descriptor (see @file_set_timeout).
+ **/
+enum block_io_err_cause {
+  MFERR_READ,
+  MFERR_WRITE,
+  MFERR_TIMEOUT
+};
+
+/**
+ * Asks the mainloop to read @len bytes of data from @bio into @buf.
  * It cancels any previous unfinished read requested this way and overwrites
  * `read_handler`.
  *
@@ -245,9 +259,9 @@ void file_del(struct main_file *fi);
  * You can use a call with zero @len to cancel current read, but all read data
  * will be thrown away.
  **/
-void file_read(struct main_file *fi, void *buf, uns len);
+void block_io_read(struct main_block_io *bio, void *buf, uns len);
 /**
- * Requests that the mainloop writes @len bytes of data from @buf to @fi.
+ * Requests that the mainloop writes @len bytes of data from @buf to @bio.
  * Cancels any previous unfinished write and overwrites `write_handler`.
  *
  * When it is written, write_done() handler is called.
@@ -257,11 +271,11 @@ void file_read(struct main_file *fi, void *buf, uns len);
  * If you call it with zero @len, it will cancel the previous write, but note
  * some data may already be written.
  **/
-void file_write(struct main_file *fi, void *buf, uns len);
+void block_io_write(struct main_block_io *bio, void *buf, uns len);
 /**
- * Sets a timer for a file @fi. If the timer is not overwritten or disabled
+ * Sets a timer for a file @bio. If the timer is not overwritten or disabled
  * until @expires, the file timeouts and error_handler() is called with
- * <<enum_main_file_err_cause,`MFERR_TIMEOUT`>>.
+ * <<enum_block_io_err_cause,`MFERR_TIMEOUT`>>.
  *
  * The mainloop does not disable or reset it, when something happens, it just
  * bundles a timer with the file. If you want to watch for inactivity, it is
@@ -269,7 +283,7 @@ void file_write(struct main_file *fi, void *buf, uns len);
  *
  * The @expires parameter is absolute (add <<var_main_now,`main_now`>> if you
  * need relative). The call and overwrites previously set timeout. Value of `0`
- * disables the timeout (the <<enum_main_file_err_cause,`MFERR_TIMEOUT`>> will
+ * disables the timeout (the <<enum_block_io_err_cause,`MFERR_TIMEOUT`>> will
  * not trigger).
  *
  * The use-cases for this are mainly sockets or pipes, when:
@@ -279,19 +293,14 @@ void file_write(struct main_file *fi, void *buf, uns len);
  * - You want to enforce answer in a given time (for example authentication).
  * - You give maximum time for a whole connection.
  **/
-void file_set_timeout(struct main_file *fi, timestamp_t expires);
-/**
- * Closes all file descriptors known to mainloop. Often used between fork()
- * and exec().
- **/
-void file_close_all(void);
+void block_io_set_timeout(struct main_block_io *bio, timestamp_t expires);
 
 /***
  * [[hooks]]
  * Loop hooks
  * ----------
  *
- * The hooks are called whenever the mainloop perform an iteration.
+ * The hooks are called whenever the mainloop performs an iteration.
  * You can shutdown the mainloop from within them or request an iteration
  * to happen without sleeping (just poll, no waiting for events).
  ***/