guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Unbuffered socket I/O


From: Ludovic Courtès
Subject: Re: Unbuffered socket I/O
Date: Tue, 07 Aug 2007 18:01:32 +0200
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.1 (gnu/linux)

Hi,

Here is an updated patch that adds `setvbuf-input' and `setvbuf-output'.

As suggested by Kevin, `setvbuf-{input,output}' now take a MODE
argument.  Unlike previous versions of the patch,
`setvbuf-{input,output}' simply discard the input/output buffer.  In
addition, `setvbuf' and `fport_buffer_add ()' are now implemented in
terms of the two other.

I'd be grateful if someone (Kevin?) could review this.

Thanks in advance,
Ludovic.

--- orig/libguile/fports.c
+++ mod/libguile/fports.c
@@ -86,71 +86,78 @@
 
 scm_t_bits scm_tc16_fport;
 
+static void fport_flush (SCM port);
+
+/* Hints passed to the GC allocation functions for port buffers and file
+   ports.  */
+static const char gc_port_buffer_hint[] = "port buffer";
+static const char gc_file_port_hint[]   = "file port";
 
 /* default buffer size, used if the O/S won't supply a value.  */
 static const size_t default_buffer_size = 1024;
 
-/* create FPORT buffer with specified sizes (or -1 to use default size or
-   0 for no buffer.  */
+/* Create FPORT buffer with specified sizes (or -1 to use default size or
+   0 for no buffer).  */
 static void
-scm_fport_buffer_add (SCM port, long read_size, int write_size)
+fport_buffer_add (SCM port, long c_buffer_size)
 #define FUNC_NAME "scm_fport_buffer_add"
 {
+  SCM buffer_size, mode;
+  int c_mode = _IOFBF;
   scm_t_port *pt = SCM_PTAB_ENTRY (port);
 
-  if (read_size == -1 || write_size == -1)
+  if (c_buffer_size == -1)
     {
-      size_t default_size;
 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
       struct stat st;
       scm_t_fport *fp = SCM_FSTREAM (port);
-      
-      default_size = (fstat (fp->fdes, &st) == -1) ? default_buffer_size
+
+      c_buffer_size = (fstat (fp->fdes, &st) == -1) ? default_buffer_size
        : st.st_blksize;
 #else
-      default_size = default_buffer_size;
+      c_buffer_size = default_buffer_size;
 #endif
-      if (read_size == -1)
-       read_size = default_size;
-      if (write_size == -1)
-       write_size = default_size;
     }
 
-  if (SCM_INPUT_PORT_P (port) && read_size > 0)
-    {
-      pt->read_buf = scm_gc_malloc (read_size, "port buffer");
-      pt->read_pos = pt->read_end = pt->read_buf;
-      pt->read_buf_size = read_size;
-    }
+  buffer_size = scm_from_long (c_buffer_size);
+  c_mode = (SCM_CELL_WORD_0 (port) & SCM_BUFLINE) ? _IOLBF : _IOFBF;
+  mode = scm_from_int (c_mode);
+
+  if (SCM_INPUT_PORT_P (port) && (buffer_size > 0))
+    scm_setvbuf_input (port, mode, buffer_size);
   else
     {
       pt->read_pos = pt->read_buf = pt->read_end = &pt->shortbuf;
       pt->read_buf_size = 1;
     }
 
-  if (SCM_OUTPUT_PORT_P (port) && write_size > 0)
-    {
-      pt->write_buf = scm_gc_malloc (write_size, "port buffer");
-      pt->write_pos = pt->write_buf;
-      pt->write_buf_size = write_size;
-    }
+  if (SCM_OUTPUT_PORT_P (port) && (buffer_size > 0))
+    scm_setvbuf_output (port, mode, buffer_size);
   else
     {
       pt->write_buf = pt->write_pos = &pt->shortbuf;
       pt->write_buf_size = 1;
     }
-
-  pt->write_end = pt->write_buf + pt->write_buf_size;
-  if (read_size > 0 || write_size > 0)
-    SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) & ~SCM_BUF0);
-  else
-    SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) | SCM_BUF0);
 }
 #undef FUNC_NAME
 
+/* Convert MODE to a C buffering mode.  */
+static inline int
+scm_to_buffer_mode (SCM mode, const char *func_name)
+{
+  int c_mode;
+
+  c_mode = scm_to_int (mode);
+  if (c_mode != _IONBF && c_mode != _IOFBF && c_mode != _IOLBF)
+    scm_out_of_range (func_name, mode);
+
+  return c_mode;
+}
+
 SCM_DEFINE (scm_setvbuf, "setvbuf", 2, 1, 0, 
             (SCM port, SCM mode, SCM size),
-           "Set the buffering mode for @var{port}.  @var{mode} can be:\n"
+           "Set the input and output buffering mode for @var{port}.  "
+           "@var{mode} can be:\n"
            "@table @code\n"
            "@item _IONBF\n"
            "non-buffered\n"
@@ -162,61 +169,163 @@
            "@end table")
 #define FUNC_NAME s_scm_setvbuf
 {
-  int cmode;
-  long csize;
-  scm_t_port *pt;
-
   port = SCM_COERCE_OUTPORT (port);
 
-  SCM_VALIDATE_OPFPORT (1,port);
-  cmode = scm_to_int (mode);
-  if (cmode != _IONBF && cmode != _IOFBF && cmode != _IOLBF)
-    scm_out_of_range (FUNC_NAME, mode);
+  SCM_VALIDATE_OPFPORT (1, port);
+
+  if (SCM_INPUT_PORT_P (port))
+    scm_setvbuf_input (port, mode, size);
+
+  if (SCM_OUTPUT_PORT_P (port))
+    scm_setvbuf_output (port, mode, size);
+
+  return SCM_UNSPECIFIED;
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_setvbuf_input, "setvbuf-input", 2, 1, 0,
+           (SCM port, SCM mode, SCM size),
+           "Set the input buffering mode for @var{port}, with the "
+           "@var{mode} and @var{size} arguments akin to those of "
+           "@code{setvbuf}.")
+#define FUNC_NAME s_scm_setvbuf_input
+{
+  scm_t_port *c_port;
+  int c_mode;
+  size_t c_size;
+  unsigned char *new_buf;
 
-  if (cmode == _IOLBF)
+  SCM_VALIDATE_FPORT (1, port);
+  SCM_VALIDATE_OPINPORT (1, port);
+
+  c_port = SCM_PTAB_ENTRY (port);
+  c_mode = scm_to_buffer_mode (mode, FUNC_NAME);
+  if (SCM_UNBNDP (size))
     {
-      SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) | SCM_BUFLINE);
-      cmode = _IOFBF;
+      if (c_mode == _IOFBF)
+       c_size = -1;
+      else
+       c_size = 0;
+    }
+  else
+    {
+      c_size = scm_to_int (size);
+      if (c_size < 0 || (c_mode == _IONBF && c_size > 0))
+       scm_out_of_range (FUNC_NAME, size);
+    }
+
+  if (c_port->read_buf == c_port->putback_buf)
+    {
+      /* Silently discard buffered and put-back chars.  */
+      c_port->read_buf = c_port->saved_read_buf;
+      c_port->read_pos = c_port->saved_read_pos;
+      c_port->read_end = c_port->saved_read_end;
+      c_port->read_buf_size = c_port->saved_read_buf_size;
+    }
+
+  if (c_size == 0)
+    {
+      new_buf = &c_port->shortbuf, c_size = 1;
+      if (c_port->read_buf != &c_port->shortbuf)
+       scm_gc_free (c_port->read_buf, c_port->read_buf_size,
+                    gc_port_buffer_hint);
     }
   else
     {
-      SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) & 
~(scm_t_bits)SCM_BUFLINE);
+      if (c_port->read_buf != &c_port->shortbuf)
+       new_buf = scm_gc_realloc (c_port->read_buf,
+                                 c_port->read_buf_size, c_size,
+                                 gc_port_buffer_hint);
+      else
+       new_buf = scm_gc_malloc (c_size, gc_port_buffer_hint);
     }
 
+  c_port->read_buf = new_buf;
+  c_port->read_end = new_buf;
+  c_port->read_pos = new_buf;
+  c_port->read_buf_size = c_size;
+
+  if ((c_port->read_buf_size == 0) && (c_port->write_buf_size == 0))
+    SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) | SCM_BUF0);
+  else
+    SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) & ~SCM_BUF0);
+
+  return SCM_UNSPECIFIED;
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_setvbuf_output, "setvbuf-output", 2, 1, 0,
+           (SCM port, SCM mode, SCM size),
+           "Set the output buffering mode for @var{port}, with the "
+           "@var{mode} and @var{size} arguments akin to those of "
+           "@code{setvbuf}.")
+#define FUNC_NAME s_scm_setvbuf_output
+{
+  scm_t_port *c_port;
+  int c_mode;
+  size_t c_size;
+  unsigned char *new_buf;
+
+  SCM_VALIDATE_FPORT (1, port);
+
+  c_port = SCM_PTAB_ENTRY (port);
+  c_mode = scm_to_buffer_mode (mode, FUNC_NAME);
   if (SCM_UNBNDP (size))
     {
-      if (cmode == _IOFBF)
-       csize = -1;
+      if (c_mode == _IOFBF)
+       c_size = -1;
       else
-       csize = 0;
+       c_size = 0;
     }
   else
     {
-      csize = scm_to_int (size);
-      if (csize < 0 || (cmode == _IONBF && csize > 0))
+      c_size = scm_to_int (size);
+      if (c_size < 0 || (c_mode == _IONBF && c_size > 0))
        scm_out_of_range (FUNC_NAME, size);
     }
 
-  pt = SCM_PTAB_ENTRY (port);
+  /* Note: line buffering is only supported by `fport_write ()'.  */
+  if (c_mode == _IOLBF)
+    {
+      SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) | SCM_BUFLINE);
+      c_mode = _IOFBF;
+    }
+  else
+    SCM_SET_CELL_WORD_0 (port,
+                        SCM_CELL_WORD_0 (port) & ~(scm_t_bits) SCM_BUFLINE);
 
-  /* silently discards buffered and put-back chars.  */
-  if (pt->read_buf == pt->putback_buf)
+  if (c_size == 0)
     {
-      pt->read_buf = pt->saved_read_buf;
-      pt->read_pos = pt->saved_read_pos;
-      pt->read_end = pt->saved_read_end;
-      pt->read_buf_size = pt->saved_read_buf_size;
+      new_buf = &c_port->shortbuf, c_size = 1;
+      if (c_port->write_buf != &c_port->shortbuf)
+       scm_gc_free (c_port->write_buf, c_port->write_buf_size,
+                    gc_port_buffer_hint);
     }
-  if (pt->read_buf != &pt->shortbuf)
-    scm_gc_free (pt->read_buf, pt->read_buf_size, "port buffer");
-  if (pt->write_buf != &pt->shortbuf)
-    scm_gc_free (pt->write_buf, pt->write_buf_size, "port buffer");
+  else
+    {
+      if (c_port->write_buf != &c_port->shortbuf)
+       new_buf = scm_gc_realloc (c_port->write_buf,
+                                 c_port->write_buf_size, c_size,
+                                 gc_port_buffer_hint);
+      else
+       new_buf = scm_gc_malloc (c_size, gc_port_buffer_hint);
+    }
+
+  c_port->write_buf = new_buf;
+  c_port->write_end = new_buf + c_size;
+  c_port->write_pos = new_buf;
+  c_port->write_buf_size = c_size;
+
+  if ((c_port->read_buf_size == 0) && (c_port->write_buf_size == 0))
+    SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) | SCM_BUF0);
+  else
+    SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) & ~SCM_BUF0);
 
-  scm_fport_buffer_add (port, csize, csize);
   return SCM_UNSPECIFIED;
 }
 #undef FUNC_NAME
 
+
 /* Move ports with the specified file descriptor to new descriptors,
  * resetting the revealed count to 0.
  */
@@ -459,15 +568,16 @@
   pt = SCM_PTAB_ENTRY(port);
   {
     scm_t_fport *fp
-      = (scm_t_fport *) scm_gc_malloc (sizeof (scm_t_fport), "file port");
+      = (scm_t_fport *) scm_gc_malloc (sizeof (scm_t_fport),
+                                      gc_file_port_hint);
 
     fp->fdes = fdes;
     pt->rw_random = SCM_FDES_RANDOM_P (fdes);
     SCM_SETSTREAM (port, fp);
     if (mode_bits & SCM_BUF0)
-      scm_fport_buffer_add (port, 0, 0);
+      fport_buffer_add (port, 0);
     else
-      scm_fport_buffer_add (port, -1, -1);
+      fport_buffer_add (port, -1);
   }
   SCM_SET_FILENAME (port, name);
   scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
@@ -583,8 +693,6 @@
 }
 #endif /* !__MINGW32__ */
 
-static void fport_flush (SCM port);
-
 /* fill a port's read-buffer with a single read.  returns the first
    char or EOF if end of file.  */
 static int
@@ -892,10 +1000,10 @@
   if (pt->read_buf == pt->putback_buf)
     pt->read_buf = pt->saved_read_buf;
   if (pt->read_buf != &pt->shortbuf)
-    scm_gc_free (pt->read_buf, pt->read_buf_size, "port buffer");
+    scm_gc_free (pt->read_buf, pt->read_buf_size, gc_port_buffer_hint);
   if (pt->write_buf != &pt->shortbuf)
-    scm_gc_free (pt->write_buf, pt->write_buf_size, "port buffer");
-  scm_gc_free (fp, sizeof (*fp), "file port");
+    scm_gc_free (pt->write_buf, pt->write_buf_size, gc_port_buffer_hint);
+  scm_gc_free (fp, sizeof (*fp), gc_file_port_hint);
   return rv;
 }
 
--- orig/libguile/fports.h
+++ mod/libguile/fports.h
@@ -49,6 +49,8 @@
 
 SCM_API SCM scm_setbuf0 (SCM port);
 SCM_API SCM scm_setvbuf (SCM port, SCM mode, SCM size);
+SCM_API SCM scm_setvbuf_input (SCM port, SCM mode, SCM size);
+SCM_API SCM scm_setvbuf_output (SCM port, SCM mode, SCM size);
 SCM_API void scm_evict_ports (int fd);
 SCM_API SCM scm_open_file (SCM filename, SCM modes);
 SCM_API SCM scm_fdes_to_port (int fdes, char *mode, SCM name);


reply via email to

[Prev in Thread] Current Thread [Next in Thread]