On 30/04/2013 12:17 PM, james wrote:
This is what I call blocking synchronous read, a 'pull' model. Its
hardly different
from a threaded rogram with a userspace switch and sync to async call
translation,
whether N:1 or N:M. Granted that the compiler support for task switch
points and
segmented stack make the tasks more lightweight.
Ok. Then yes, we're presenting a 'pull' model. On top of an AIO model.
which itself may (on many kernels or C libraries) map to a pull model
(and goodness knows what combination of push, pull and poll happening
inside the hardware). You may be surprised how often an "AIO" call in a
C library winds up resulting in a thread spawning and making a blocking
call. But whatever.
It may well be what most people want most of the time, but history is so
far unkind
to N:M threading, and people used to writing highly scalable servers (a
minority,
perhaps) are used to thinking in terms of event processing.
This response is a bit hard to take. It amounts to "what most people
want most of the time is not what a few people want some of the time, so
we should force everyone to do the latter". This is not how library
design works.
High performance servers tend to use a mixture of thread pools, event
interfaces, offload devices like sendfile/TransmitFile/splice,
scatter/gather, multiple processes and every other trick under the sun.
There's no way to make those tricks all play nicely with a composable,
friendly, sequential, "what most users want most of the time"
programming model.
We'll expose what we can with the caveats that each API requires. We'll
make the APIs as findable and usable as we can. We're _not_ going to
require users to write to the most elaborate possible high-performance
interface when they want to open a file and read bytes from it. That's a
simple task and demands a simple API. We'll try to make it fast, not
block other tasks, and compose nicely.
(It's also the case that the failure of M:N threading _in C_ mostly
relates to the kernel threads getting fast enough that 1:1 was viable.
To support sequential execution in C code and multiplex it at the kernel
level and still win performance benchmarks against M:N. Which is the
opposite of the argument you're making. But anyways.)
The design focus would appear to on synchronous semantics. I would
prefer async
and an easy way to chain things together and wait with a future. I
would at least ask
that the lower level API not be second-class citizen.
The "design focus" is (presently) on what people keep asking for: "I
would like to be able to read lines from a file without the other tasks
on my thread blocking and without having to wire up an overly-complex
callback thing against libuv's low level interface".
What makes an API a second class citizen? If we expose both, and 99% of
users who want to open a file and read it prefer:
let in = File::new_read("foo.txt");
let out = File::new_write("bar.txt");
for in.lines |line| {
if line.contains("bleh") {
out.write(line);
}
}
// control gets here => lines are written
over
let in = File::new_read("foo.txt");
do in.read |buf| {
let out = File::new_write("bar.txt");
do out.create |fh| {
let lines = str::lines(buf);
fn write_line(i: int) {
if i < lines.len() && lines[i].contains("bleh") {
do fh.write(lines[i]) |fh| {
write_line(i+1);
}
}
}
write_line(0);
}
}
// NB: control arrived here before any
// of the above I/O happened.
is it our fault that users prefer the first, and will you think we've
done an unfair job and made it "second class" if so?
-Graydon
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev