From: ludovic.courtes@laas.fr (Ludovic Courtès)
To: Guile-Devel <guile-devel@gnu.org>
Subject: Re: Unbuffered socket I/O
Date: Thu, 01 Mar 2007 11:44:01 +0100 [thread overview]
Message-ID: <87bqjd6y6m.fsf@laas.fr> (raw)
In-Reply-To: <874pp69ex0.fsf@zip.com.au> (Kevin Ryde's message of "Thu, 01 Mar 2007 07:59:39 +1100")
[-- Attachment #1: Type: text/plain, Size: 1085 bytes --]
Hi,
Attached is an updated patch. Ok to apply?
Kevin Ryde <user42@zip.com.au> writes:
>> Besides, how about applying the change I originally proposed to HEAD?
>
> No, principally because unbuffered is an advertised feature.
Hey, it's only advertised as long as we advertise it! ;-)
I mean, if we start nitpicking about such changes in HEAD, then we'll
never implement a Unicode-capable port I/O interface, will we? :-)
> Even if
> it was year zero you'd probably still choose them to start unbuffered,
> since there's various ways to do comms and it can be up to an
> application how much might be desirable. For block operations for
> instance it's certainly not wanted.
The thing is that port buffering in Guile does not necessarily have a
direct relationship to OS-level buffering. This is clear for input,
where one can have unbuffered OS-level file descriptor and where
port-level input buffering only impacts performance, not semantics.
Conversely, port-level output buffering does have a visible impact from
the outside world.
Thanks for your comments!
Ludovic.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: The improved patch. --]
[-- Type: text/x-patch, Size: 8079 bytes --]
--- orig/doc/ref/posix.texi
+++ mod/doc/ref/posix.texi
@@ -472,6 +472,21 @@
@end defvar
@end deffn
+@deffn {Scheme Procedure} setvbuf-input port size
+@deffnx {C Function} scm_setvbuf_input (port, size)
+Use a @var{size}-byte input buffer for @var{port}, which must be a
+file or socket port. Note that buffered data is lost if the input
+buffer contains more than @var{size} bytes. Thus, it may be necessary
+to call @code{drain-input} prior to @code{setvbuf-input}.
+@end deffn
+
+@deffn {Scheme Procedure} setvbuf-output port size
+@deffnx {C Function} scm_setvbuf_output (port, size)
+Use a @var{size}-byte output buffer for @var{port}, which must be a
+file or socket port. The current output buffer may be flushed as a
+result of this operation.
+@end deffn
+
@deffn {Scheme Procedure} fcntl port/fd cmd [value]
@deffnx {C Function} scm_fcntl (object, cmd, value)
Apply @var{cmd} on @var{port/fd}, either a port or file descriptor.
--- orig/libguile/fports.c
+++ mod/libguile/fports.c
@@ -86,14 +86,20 @@
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)
+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);
@@ -118,7 +124,7 @@
if (SCM_INPUT_PORT_P (port) && read_size > 0)
{
- pt->read_buf = scm_gc_malloc (read_size, "port buffer");
+ pt->read_buf = scm_gc_malloc (read_size, gc_port_buffer_hint);
pt->read_pos = pt->read_end = pt->read_buf;
pt->read_buf_size = read_size;
}
@@ -130,7 +136,7 @@
if (SCM_OUTPUT_PORT_P (port) && write_size > 0)
{
- pt->write_buf = scm_gc_malloc (write_size, "port buffer");
+ pt->write_buf = scm_gc_malloc (write_size, gc_port_buffer_hint);
pt->write_pos = pt->write_buf;
pt->write_buf_size = write_size;
}
@@ -208,15 +214,140 @@
pt->read_buf_size = pt->saved_read_buf_size;
}
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 (pt->write_buf, pt->write_buf_size, gc_port_buffer_hint);
scm_fport_buffer_add (port, csize, csize);
return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
+SCM_DEFINE (scm_setvbuf_input, "setvbuf-input", 2, 0, 0,
+ (SCM port, SCM size),
+ "Use a @var{size}-byte input buffer for @var{port}, which "
+ "must be a file or socket port. Note that buffered data "
+ "is lost if the input buffer contains more than @var{size} "
+ "bytes. Thus, it may be necessary to call "
+ "@code{drain-input} prior to @code{setvbuf-input}.")
+#define FUNC_NAME s_scm_setvbuf_input
+{
+ scm_t_port *pt;
+ size_t c_size;
+
+ SCM_VALIDATE_FPORT (1, port);
+ c_size = scm_to_size_t (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)
+ /* Discard all the buffered input. */
+ c_offset = c_end = 0;
+
+ if (c_size == 0)
+ {
+ new_buf = &pt->shortbuf, c_size = 1;
+ if (pt->read_buf != &pt->shortbuf)
+ scm_gc_free (pt->read_buf, pt->read_buf_size,
+ gc_port_buffer_hint);
+ }
+ else
+ {
+ if (pt->read_buf != &pt->shortbuf)
+ new_buf = scm_gc_realloc (pt->read_buf,
+ pt->read_buf_size, c_size,
+ gc_port_buffer_hint);
+ else
+ new_buf = scm_gc_malloc (c_size, gc_port_buffer_hint);
+ }
+
+ 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);
+ 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, 0, 0,
+ (SCM port, SCM size),
+ "Use a @var{size}-byte output buffer for @var{port}, which "
+ "must be a file or socket port. The current output buffer "
+ "may be flushed as a result of this operation.")
+#define FUNC_NAME s_scm_setvbuf_output
+{
+ scm_t_port *pt;
+ size_t c_size;
+
+ SCM_VALIDATE_FPORT (1, port);
+ c_size = scm_to_size_t (size);
+
+ pt = SCM_PTAB_ENTRY (port);
+
+ if (pt->write_buf_size != c_size)
+ {
+ size_t c_offset, c_end;
+ unsigned char *new_buf;
+
+ do_it:
+ c_end = pt->write_end - pt->write_buf;
+ c_offset = pt->write_pos - pt->write_buf;
+
+ if (c_offset > c_size)
+ {
+ /* Flush the output buffer so that it can be shrunk. */
+ fport_flush (port);
+ goto do_it;
+ }
+
+ if (c_size == 0)
+ {
+ new_buf = &pt->shortbuf, c_size = 1;
+ if (pt->write_buf != &pt->shortbuf)
+ scm_gc_free (pt->write_buf, pt->write_buf_size,
+ gc_port_buffer_hint);
+ }
+ else
+ {
+ if (pt->write_buf != &pt->shortbuf)
+ new_buf = scm_gc_realloc (pt->write_buf,
+ pt->write_buf_size, c_size,
+ gc_port_buffer_hint);
+ else
+ new_buf = scm_gc_malloc (c_size, gc_port_buffer_hint);
+ }
+
+ 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);
+ else
+ 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.
*/
@@ -459,7 +590,8 @@
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);
@@ -583,8 +715,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 +1022,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 @@
\f
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 size);
+SCM_API SCM scm_setvbuf_output (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);
[-- Attachment #3: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel
next prev parent reply other threads:[~2007-03-01 10:44 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-02-23 17:09 Unbuffered socket I/O Ludovic Courtès
2007-02-23 22:36 ` Neil Jerram
2007-02-25 22:57 ` Kevin Ryde
2007-02-26 14:07 ` Ludovic Courtès
2007-02-26 20:32 ` Neil Jerram
2007-02-26 23:35 ` Kevin Ryde
2007-02-27 9:11 ` Ludovic Courtès
2007-02-28 21:24 ` Kevin Ryde
2007-03-01 9:10 ` Ludovic Courtès
2007-03-04 23:48 ` Kevin Ryde
2007-03-07 0:20 ` Kevin Ryde
2007-03-07 0:30 ` Kevin Ryde
2007-03-07 4:45 ` Kevin Ryde
2007-03-07 9:40 ` Ludovic Courtès
2007-02-26 23:27 ` Kevin Ryde
2007-02-28 9:47 ` Ludovic Courtès
2007-02-28 20:59 ` Kevin Ryde
2007-03-01 10:44 ` Ludovic Courtès [this message]
2007-03-05 23:15 ` Kevin Ryde
2007-08-07 16:01 ` Ludovic Courtès
2007-05-13 17:13 ` `scm_std_select ()' call in `fport_wait_for_input ()' Ludovic Courtès
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/guile/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87bqjd6y6m.fsf@laas.fr \
--to=ludovic.courtes@laas.fr \
--cc=guile-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).