[Top][All Lists]
[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: |
Wed, 28 Feb 2007 10:47:58 +0100 |
User-agent: |
Gnus/5.110006 (No Gnus v0.6) Emacs/21.4 (gnu/linux) |
Hi,
Kevin Ryde <address@hidden> writes:
> address@hidden (Ludovic Courtès) writes:
>>
>> Right. That's an incompatible change if the _output_ is buffered.
>> Input can be buffered, though, without this being visible by users.
>
> Alas, that too is in an incompatible change, because recv! ignores
> buffering.
Hmm, right.
> Sounds good. Perhaps `setvbuf-input' and `setvbuf-output' for the two
> directions. They could go in 1.8 too if you're careful with the
> implementation.
Below is a tentative patch (I chose more meaningful names since they
don't relate to `setvbuf(3)'). The issue is that it duplicates fport
buffering logic, but I couldn't think of a nice way to factorize this
with, e.g., `scm_fport_buffer_add ()'. What do you think?
Besides, how about applying the change I originally proposed to HEAD?
That would require changing `recv!', `send', etc. so that they are
somehow "buffering-aware". Ideally, it would be nice if they didn't
break through the port abstraction as they currently do, but I don't
know how that could be implemented (what should be the meaning of
`MSG_DONTROUTE' on an input string buffer? ;-)).
>> It
>> does nothing for O_NONBLOCK streams and waits for events otherwise.
>> Since, for blocking streams, `read ()' does not return until either EOF
>> is reached or at least one octet was read, `fport_wait_for_input ()'
>> seems redundant.
>
> Ahh, hang on, I wonder if it's a hack to do an "exit guile" while
> blocked within a read(), thus allowing gc to run in other threads.
Indeed, `scm_std_select ()' does a "leave guile".
> If that's true then presumably the write side is afflicted too, as
> well as various other potentially blocking operations like
> read-string!/partial and gethost.
Afflicted by what?
Thanks,
Ludovic.
--- orig/libguile/fports.c
+++ mod/libguile/fports.c
@@ -90,10 +90,10 @@
/* 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)
+scm_fport_buffer_add (SCM port, long read_size, long write_size)
#define FUNC_NAME "scm_fport_buffer_add"
{
scm_t_port *pt = SCM_PTAB_ENTRY (port);
@@ -217,6 +217,115 @@
}
#undef FUNC_NAME
+SCM_DEFINE (scm_set_port_input_buffer_size_x, "set-port-input-buffer-size!",
+ 2, 0, 0,
+ (SCM port, SCM size),
+ "Use a @var{size}-octet input buffer for @var{port}, which "
+ "must be a file or socket port. The current input buffer "
+ "of @var{port} must contain less than @var{size} octets.")
+#define FUNC_NAME s_scm_set_port_input_buffer_size_x
+{
+ scm_t_port *pt;
+ size_t c_size;
+
+ SCM_VALIDATE_FPORT (1, port);
+ c_size = scm_to_uint (size);
+
+ pt = SCM_PTAB_ENTRY (port);
+
+ if (pt->read_buf_size != c_size)
+ {
+ size_t c_offset, c_end;
+ unsigned char *new_buf;
+
+ c_end = pt->read_end - pt->read_buf;
+ c_offset = pt->read_pos - pt->read_buf;
+
+ if (c_offset > c_size)
+ scm_wrong_type_arg_msg (FUNC_NAME, 1, port,
+ "Input buffer must be flushed before it "
+ "can be shrunk");
+
+ if (c_size == 0)
+ new_buf = &pt->shortbuf, c_size = 1;
+ else
+ {
+ if (pt->read_buf != &pt->shortbuf)
+ new_buf = scm_gc_realloc (pt->read_buf,
+ pt->read_buf_size, c_size,
+ "port buffer");
+ else
+ new_buf = scm_gc_malloc (c_size, "port buffer");
+ }
+
+ pt->read_buf = new_buf;
+ pt->read_end = new_buf + c_end;
+ pt->read_pos = new_buf + c_offset;
+ pt->read_buf_size = c_size;
+
+ if ((pt->read_buf_size == 0) && (pt->write_buf_size == 0))
+ SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) | SCM_BUF0);
+ }
+
+ return SCM_UNSPECIFIED;
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_set_port_output_buffer_size_x, "set-port-output-buffer-size!",
+ 2, 0, 0,
+ (SCM port, SCM size),
+ "Use a @var{size}-octet output buffer for @var{port}, which "
+ "must be a file or socket port. The current output buffer of "
+ "@var{port} must contain less than @var{size} octets.")
+#define FUNC_NAME s_scm_set_port_output_buffer_size_x
+{
+ scm_t_port *pt;
+ size_t c_size;
+
+ SCM_VALIDATE_FPORT (1, port);
+ c_size = scm_to_uint (size);
+
+ pt = SCM_PTAB_ENTRY (port);
+
+ if (pt->write_buf_size != c_size)
+ {
+ size_t c_offset, c_end;
+ unsigned char *new_buf;
+
+ c_end = pt->write_end - pt->write_buf;
+ c_offset = pt->write_pos - pt->write_buf;
+
+ if (c_offset > c_size)
+ scm_wrong_type_arg_msg (FUNC_NAME, 1, port,
+ "Output buffer must be flushed before it "
+ "can be shrunk");
+
+ if (c_size == 0)
+ new_buf = &pt->shortbuf, c_size = 1;
+ else
+ {
+ if (pt->write_buf != &pt->shortbuf)
+ new_buf = scm_gc_realloc (pt->write_buf,
+ pt->write_buf_size, c_size,
+ "port buffer");
+ else
+ new_buf = scm_gc_malloc (c_size, "port buffer");
+ }
+
+ pt->write_buf = new_buf;
+ pt->write_end = new_buf + c_end;
+ pt->write_pos = new_buf + c_offset;
+ pt->write_buf_size = c_size;
+
+ if ((pt->read_buf_size == 0) && (pt->write_buf_size == 0))
+ SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) | SCM_BUF0);
+ }
+
+ return SCM_UNSPECIFIED;
+}
+#undef FUNC_NAME
+
+
/* Move ports with the specified file descriptor to new descriptors,
* resetting the revealed count to 0.
*/
--- 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_set_port_input_buffer_size_x (SCM port, SCM size);
+SCM_API SCM scm_set_port_output_buffer_size_x (SCM port, 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);