From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Andy Wingo Newsgroups: gmane.lisp.guile.devel Subject: non-blocking i/o in guile Date: Tue, 17 May 2016 22:28:06 +0200 Message-ID: <87k2ish1k9.fsf@pobox.com> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain X-Trace: ger.gmane.org 1463536783 23455 80.91.229.3 (18 May 2016 01:59:43 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Wed, 18 May 2016 01:59:43 +0000 (UTC) To: guile-devel@gnu.org Original-X-From: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Wed May 18 03:59:31 2016 Return-path: Envelope-to: guile-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1b2qll-0004Dp-P7 for guile-devel@m.gmane.org; Wed, 18 May 2016 03:59:30 +0200 Original-Received: from localhost ([::1]:42139 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1b2qlk-0003jJ-PN for guile-devel@m.gmane.org; Tue, 17 May 2016 21:59:28 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:50698) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1b2qS9-0006d9-1t for guile-devel@gnu.org; Tue, 17 May 2016 21:39:18 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1b2lbH-0005aC-5r for guile-devel@gnu.org; Tue, 17 May 2016 16:28:22 -0400 Original-Received: from pb-sasl1.pobox.com ([64.147.108.66]:52961 helo=sasl.smtp.pobox.com) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1b2lbH-0005ZM-13 for guile-devel@gnu.org; Tue, 17 May 2016 16:28:19 -0400 Original-Received: from sasl.smtp.pobox.com (unknown [127.0.0.1]) by pb-sasl1.pobox.com (Postfix) with ESMTP id 32B7C1CB65 for ; Tue, 17 May 2016 16:28:15 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=pobox.com; h=from:to :subject:date:message-id:mime-version:content-type; s=sasl; bh=d Y8gkBEeSijrwDautDe0lws+JA0=; b=qfipYxgkQrqcBU0H9MDd8sFWaDJpigndd W5ZGl/y6PdXwt+Y0utO1H9oEmawR986BP83ZliERe0lhGmGvFxB+kMy0T2J8m4kb 8MhPaFZjikrxRqFD91v29AsY4sasQNAk8g34IXKjss4ARvub29rUZEI23gZITAEt aVjS2X2EIk= DomainKey-Signature: a=rsa-sha1; c=nofws; d=pobox.com; h=from:to:subject :date:message-id:mime-version:content-type; q=dns; s=sasl; b=Qa7 zaSrC/eDqIb+l+NHq1mc6CV3mdqmJ29FcYx+/Y49IZzXxeaJ+w0tQ7JgTapkwriQ 5N9d+T70CaVOc8vKrAkuxkYwb7e9CKdPim/cqw260fL4Qklua8q5Igg60Xg+hlrX p07defA8axChKoZbxvLXhQJJF8vhyeIjulfjRgo4= Original-Received: from pb-sasl1. (unknown [127.0.0.1]) by pb-sasl1.pobox.com (Postfix) with ESMTP id 2AD4E1CB64 for ; Tue, 17 May 2016 16:28:15 -0400 (EDT) Original-Received: from clucks (unknown [88.160.190.192]) (using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by pb-sasl1.pobox.com (Postfix) with ESMTPSA id 21AB91CB63 for ; Tue, 17 May 2016 16:28:13 -0400 (EDT) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux) X-Pobox-Relay-ID: E196B042-1C6D-11E6-BEFB-0D7A5CA9D4A7-02397024!pb-sasl1.pobox.com X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 64.147.108.66 X-BeenThere: guile-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Developers list for Guile, the GNU extensibility library" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Original-Sender: "guile-devel" Xref: news.gmane.org gmane.lisp.guile.devel:18317 Archived-At: Hi :) This is a mail for people interested in the implementation of non-blocking buffered I/O. If you're mostly interested from the user side, there's probably not much of interest here. Here goes nothing :) I have been reworking Guile's I/O routines. In Guile master, when you implement a port, you provide read and/or write functions. These functions have a C prototype like this: size_t (*read) (SCM port, SCM dst, size_t start, size_t count) size_t (*write) (SCM port, SCM src, size_t start, size_t count) "dst" and "src" are bytevectors. The read function fills the bytevector, returning the number of bytes filled, and the write function empties the bytevector, returning the number of bytes written. If there is an error when reading or writing, then the read/write functions should throw an error. Otherwise the semantics are that it's a blocking operation: each read or write should cause a nonzero number of bytes to be read or written. Reading 0 bytes means there is EOF. Writing 0 bytes is probably an error, though who knows. Besides the bit about exceptions, this is basically the semantics of read(2) and write(2). Internally to Guile, callers of read functions are generally happy with fewer than COUNT bytes. Callers of write functions will generally loop until COUNT bytes are written. Now, how to make this fit with non-blocking I/O? Initially I thought that it would be sufficient to add a kind of sentinel return value that would indicate the equivalent of EWOULDBLOCK. Then we'd have to have some interface to get a file descriptor or other waitable (more on this later) from the port. That waitable could be added to a poll set, or we could layer blocking I/O on top of nonblocking I/O by calling poll(2) in a loop with the read or write. This would be pretty gnarly but I think it could have worked -- except two things: blocking local file access, and Windows. I explain. It turns out that even if you set a file descriptor to nonblocking, and you use only nonblocking operations on it, if that file descriptor is backed by a local file system, operations on it will block. The FD will always poll as readable/writable. Linux has this problem, and async I/O (AIO) doesn't help; glibc implements AIO in user-space with a new thread per AIO operation. Terrible. FreeBSD does the same but with kernel threads, AFAIU. The upshot is that to reliably do non-blocking I/O over local files you need to use thread pools. I was willing to punt on non-blocking local I/O (and I still am), but then Windows. Windows doesn't have "poll". Instead what they have is async "I/O completion ports". It's interesting, because it's an edge-triggered system rather than a level-triggered system: async read/write operations trigger async completions, instead of the POSIX case where you poll on an FD to see when you could operate on it without blocking. I find http://tinyclouds.org/iocp-links.html to be the only piece of sanity in the entire Internet when it comes to async I/O between POSIX and Windows. Go ahead and read it -- it's very clear. What the IOCP thing means is that the original design of polling on fd's wasn't going to work. We need to instead have a way for a caller to say "I support non-blocking I/O and so if you would block on your I/O, please don't, return me a promise or something instead". Happily it would be possible for this interface to hide a thread-pool for local files, if that were a thing. My proposal is to change the prototype of the read/write operations to be: size_t (*read) (SCM port, SCM dst, size_t start, size_t count, waitable_t *waitable) size_t (*write) (SCM port, SCM src, size_t start, size_t count, waitable_t *waitable) We can change API/ABI as the port implementation API/ABI is changing in master anyway relative to 2.0. See NEWS. The semantics would be that if the user provides no WAITABLE pointer, then the read or write is blocking. If the user provides a WAITABLE pointer, the read or write *may* be async. waitable_t or scm_t_waitable or whatever we call it is a platform-specific define that may either be "int" or "HANDLE". If the return value is 0 then the caller should check the WAITABLE pointer to see if there is an async completion (FD or handle) to wait on. Guile's C code will never provide a WAITABLE value, as the whole point of doing non-blocking I/O is to suspend the Scheme coroutine while the I/O is happening, and you can't do that from C. But from Scheme we'll somehow make it make sense... not sure how. Maybe an extra box argument. Dunno. I would really appreciate reviews from people that have done high-performance non-blocking I/O with systems like Java NIO or libuv. Your thoughts are very welcome. Regards, Andy