[rust-dev] Mutiplexing I/O within a task

Tony Arcieri bascule at gmail.com
Sun Jul 6 15:09:25 PDT 2014


On Sat, Jul 5, 2014 at 8:07 AM, Nat Pryce <nat.pryce at gmail.com> wrote:

> I've been trying to write tasks that wait for both I/O and channel
> communication. I've been told that, to maximise communication performance,
> channels do not support selecting of both channels and I/O objects.
>  Therefore a program should signal to a task that is waiting for I/O over
> another I/O object, such as a pipe, not by sending a message over a
> channel.  Fair enough.
>
> What API should I use to wait for multiple I/O objects within a task?  Is
> there a runtime-independent API for this?
>

Sounds like you might want something like a Java NIO Selector. These
Selectors bake in a pipe as a wakeup mechanism as well:

http://docs.oracle.com/javase/7/docs/api/java/nio/channels/Selector.html

However, there is more than one way to do it ;) Earlier I sent this message
out about providing I/O "as a service" by having a dedicated task (or pool
of tasks) that performs I/O operations for you, sending completions back as
messages over the normal std::comm message protocol, ala Erlang's ioserver.
Perhaps it's relevant to bring up again?

---------- Forwarded message ----------
From: Tony Arcieri <bascule at gmail.com>
Date: Wed, Jan 29, 2014 at 1:40 AM
Subject: Multiplexing I/O the Erlang way: an "ioserver"
To: "rust-dev at mozilla.org" <rust-dev at mozilla.org>


libnative (fast, low-latency native I/O) and libgreen (libuv backed and
great for large numbers of infrequent/idle messengers) are both awesome and
provide some great options for network server I/O, particularly since you
can mix-and-match them.

However there's a seemingly unsolved problem in Rust: multiplexing I/O
operations with channels. I think this can be fixed in a purely additive
manner (i.e everything else that exists today is great!) with a new feature
borrowed from Erlang: the ioserver (noting that Erlang also implements
libgreen-style synchronous I/O in addition to ioserver)

Ignoring Erlang for a second, the naive solution to the I/O + channel
multiplexing is magic select API that can handle both. In my experience
(i.e. I wrote and support this approach in a system some people actually
use) the backend implementation, especially across POSIX and Windows, ends
up quite ugly.

There's a better option though... if you want to multiplex IO with channel
messages, turn everything into channel messages in one place: the "ioserver"

Solution: Centralize I/O event loops into a separate thread (pool if
necessary, probably not). Erlang and 0MQ work this way. Erlang implements
an API called a "port" to talk to the ioserver. In Rust perhaps you could
have a type that implements the same trait as channels?

The ioserver becomes the central place to optimize the nitty gritty of
taking I/O requests from the rest of the program and multiplexing them with
other (i.e channel) events in the system. The basic formula is:

- Have the ioserver block for events using libuv or what have you, and have
an OS-specific I/O handle (i.e. a pipe) to unblock the selector in the
event of in-process communication
- Use lock-free mechanisms so if the selector isn't stuck in a system call
we can use more efficient coordination of registering and unregistering I/O
op interests

(Note: libuv is an I/O completions library and not a selector library so
the proposed solution is admittedly a bit handwavey)

Mixing a pipe-based wakeup mechanism with lock-free operations to eliminate
unnecessary system calls has been used somewhat successfully in the Netty
I/O library for the JVM.

-- 
Tony Arcieri
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/rust-dev/attachments/20140706/6018d626/attachment.html>


More information about the Rust-dev mailing list