summaryrefslogtreecommitdiff
path: root/doc/ref/api-io.texi
diff options
context:
space:
mode:
authorAndy Wingo <wingo@pobox.com>2016-05-20 14:51:51 +0200
committerAndy Wingo <wingo@pobox.com>2016-05-20 14:57:27 +0200
commit534139e45852f5b59ef4a75c99d757c7456ce19c (patch)
treee411c113728b4db03d7c51e66459c7e55563520b /doc/ref/api-io.texi
parent8b6f4df3f4e26bb4fe148507b481182fc14578a4 (diff)
downloadguile-534139e45852f5b59ef4a75c99d757c7456ce19c.tar.gz
Support for non-blocking I/O
* doc/ref/api-io.texi (I/O Extensions): Document read_wait_fd / write_wait_fd members. (Non-Blocking I/O): New section. * libguile/fports.c (fport_read, fport_write): Return -1 if the operation would block. (fport_wait_fd, scm_make_fptob): Add read/write wait-fd implementation. * libguile/ports-internal.h (scm_t_port_type): Add read_wait_fd / write_wait_fd. * libguile/ports.c (default_read_wait_fd, default_write_wait_fd): New functions. (scm_make_port_type): Initialize default read/write wait fd impls. (trampoline_to_c_read, trampoline_to_scm_read) (trampoline_to_c_write, trampoline_to_scm_write): To Scheme, a return of #f indicates EWOULDBLOCk. (scm_set_port_read_wait_fd, scm_set_port_write_wait_fd): New functions. (port_read_wait_fd, port_write_wait_fd, scm_port_read_wait_fd) (scm_port_write_wait_fd, port_poll, scm_port_poll): New functions. (scm_i_read_bytes, scm_i_write_bytes): Poll if the read or write would block. * libguile/ports.h (scm_set_port_read_wait_fd) (scm_set_port_write_wait_fd): Add declarations. * module/ice-9/ports.scm: Shunt port-poll and port-{read,write}-wait-fd to the internals module. * module/ice-9/sports.scm (current-write-waiter): (current-read-waiter): Implement. * test-suite/tests/ports.test: Adapt non-blocking test to new behavior. * NEWS: Add entry.
Diffstat (limited to 'doc/ref/api-io.texi')
-rw-r--r--doc/ref/api-io.texi87
1 files changed, 87 insertions, 0 deletions
diff --git a/doc/ref/api-io.texi b/doc/ref/api-io.texi
index 5b200977b..313204593 100644
--- a/doc/ref/api-io.texi
+++ b/doc/ref/api-io.texi
@@ -20,6 +20,7 @@
* Port Types:: Types of port and how to make them.
* R6RS I/O Ports:: The R6RS port API.
* I/O Extensions:: Implementing new port types in C.
+* Non-Blocking I/O:: How Guile deals with EWOULDBLOCK.
* BOM Handling:: Handling of Unicode byte order marks.
@end menu
@@ -2302,6 +2303,24 @@ It should write out bytes from the supplied bytevector @code{src},
starting at offset @code{start} and continuing for @code{count} bytes,
and return the number of bytes that were written.
+@item read_wait_fd
+@itemx write_wait_fd
+If a port's @code{read} or @code{write} function returns @code{(size_t)
+-1}, that indicates that reading or writing would block. In that case
+to preserve the illusion of a blocking read or write operation, Guile's
+C port run-time will @code{poll} on the file descriptor returned by
+either the port's @code{read_wait_fd} or @code{write_wait_fd} function.
+Set using
+
+@deftypefun void scm_set_port_read_wait_fd (scm_t_port_type *type, int (*wait_fd) (SCM port))
+@deftypefunx void scm_set_port_write_wait_fd (scm_t_port_type *type, int (*wait_fd) (SCM port))
+@end deftypefun
+
+Only a port type which implements the @code{read_wait_fd} or
+@code{write_wait_fd} port methods can usefully return @code{(size_t) -1}
+from a read or write function. @xref{Non-Blocking I/O}, for more on
+non-blocking I/O in Guile.
+
@item print
Called when @code{write} is called on the port, to print a port
description. For example, for a file port it may produce something
@@ -2384,6 +2403,74 @@ operating system inform Guile about the appropriate buffer sizes for the
particular file opened by the port.
@end table
+@node Non-Blocking I/O
+@subsection Non-Blocking I/O
+
+Most ports in Guile are @dfn{blocking}: when you try to read a character
+from a port, Guile will block on the read until a character is ready, or
+end-of-stream is detected. Likewise whenever Guile goes to write
+(possibly buffered) data to an output port, Guile will block until all
+the data is written.
+
+Interacting with ports in blocking mode is very convenient: you can
+write straightforward, sequential algorithms whose code flow reflects
+the flow of data. However, blocking I/O has two main limitations.
+
+The first is that it's easy to get into a situation where code is
+waiting on data. Time spent waiting on data when code could be doing
+something else is wasteful and prevents your program from reaching its
+peak throughput. If you implement a web server that sequentially
+handles requests from clients, it's very easy for the server to end up
+waiting on a client to finish its HTTP request, or waiting on it to
+consume the response. The end result is that you are able to serve
+fewer requests per second than you'd like to serve.
+
+The second limitation is related: a blocking parser over user-controlled
+input is a denial-of-service vulnerability. Indeed the so-called ``slow
+loris'' attack of the early 2010s was just that: an attack on common web
+servers that drip-fed HTTP requests, one character at a time. All it
+took was a handful of slow loris connections to occupy an entire web
+server.
+
+In Guile we would like to preserve the ability to write straightforward
+blocking networking processes of all kinds, but under the hood to allow
+those processes to suspend their requests if they would block.
+
+To do this, the first piece is to allow Guile ports to declare
+themselves as being nonblocking. This is currently supported only for
+file ports, which also includes sockets, terminals, or any other port
+that is backed by a file descriptor. To do that, we use an arcane UNIX
+incantation:
+
+@example
+(let ((flags (fcntl socket F_GETFL)))
+ (fcntl socket F_SETFL (logior O_NONBLOCK flags)))
+@end example
+
+Now the file descriptor is open in non-blocking mode. If Guile tries to
+read or write from this file descriptor in C, it will block by polling
+on the socket's @code{read_wait_fd}, to preserve the illusion of a
+blocking read or write. @xref{I/O Extensions} for more on that internal
+interface.
+
+However if a user uses the new and experimental Scheme implementation of
+ports in @code{(ice-9 sports)}, Guile instead calls the value of the
+@code{current-read-waiter} or @code{current-write-waiter} parameters on
+the port before re-trying the read or write. The default value of these
+parameters does the same thing as the C port runtime: it blocks.
+However it's possible to dynamically bind these parameters to handlers
+that can suspend the current coroutine to a scheduler, to be later
+re-animated once the port becomes readable or writable in the future.
+In the mean-time the scheduler can run other code, for example servicing
+other web requests.
+
+Guile does not currently include such a scheduler. Currently we want to
+make sure that we're providing the right primitives that can be used to
+build schedulers and other user-space concurrency patterns. In the
+meantime, have a look at 8sync (@url{https://gnu.org/software/8sync})
+for a prototype of an asynchronous I/O and concurrency facility.
+
+
@node BOM Handling
@subsection Handling of Unicode byte order marks.
@cindex BOM